.dex
文件是 Dalvik 字节码的传输格式。文件要成为有效的 .dex
文件,必须满足某些语法和语义约束,并且运行时环境必须仅支持有效的 .dex 文件。
常规 .dex 完整性约束
常规完整性约束与 .dex
文件的较大结构有关,详见 .dex
格式。
标识符 | 说明 |
---|---|
G1 | .dex 文件的 magic 数字对于版本 35 必须为 dex\n035\0 ,对于更高版本则类似。 |
G2 | 校验和必须是整个文件内容(magic 和 checksum 字段除外)的 Adler-32 校验和。 |
G3 | 签名必须是整个文件内容(magic 、checksum 和 signature 除外)的 SHA-1 哈希值。 |
G4 |
|
G5 |
|
G6 | endian_tag 必须具有值:ENDIAN_CONSTANT 或 REVERSE_ENDIAN_CONSTANT |
G7 |
对于
|
G8 | 标头中的所有偏移量字段(map_off 除外)都必须是四字节对齐的。 |
G9 | map_off 字段必须为零或指向数据部分。在后一种情况下,data 部分必须存在。 |
G10 | link 、string_ids 、type_ids 、proto_ids 、field_ids 、method_ids 、class_defs 和 data 部分都不得相互重叠或与标头重叠。 |
G11 | 如果存在映射,则每个映射条目都必须具有有效的类型。每种类型最多可以出现一次。 |
G12 | 如果存在映射,则每个映射条目都必须具有非零的偏移量和大小。偏移量必须指向文件中相应的节(即,string_id_item 必须指向 string_ids 节),并且条目的显式或隐式大小必须与节的实际内容和大小匹配。 |
G13 | 如果存在映射,则映射条目 n+1 的偏移量必须大于或等于映射条目 n 的偏移量加上映射条目 n 的大小。这意味着条目不重叠且从低到高排序。 |
G14 | 以下类型的条目必须具有四字节对齐的偏移量:string_id_item 、type_id_item 、proto_id_item 、field_id_item 、method_id_item 、class_def_item 、type_list 、code_item 、annotations_directory_item 。 |
G15 |
对于每个 对于每个 对于引用的 |
G16 | 对于每个 type_id_item ,descriptor_idx 字段必须包含对 string_ids 列表的有效引用。引用的字符串必须是有效的类型描述符。 |
G17 | 对于每个 proto_id_item ,shorty_idx 字段必须包含对 string_ids 列表的有效引用。引用的字符串必须是有效的 shorty 描述符。此外,return_type_idx 字段必须是 type_ids 部分的有效索引,并且 parameters_off 字段必须为零或指向 data 部分的有效偏移量。如果为非零,则参数列表不得包含任何 void 条目。 |
G18 | 对于每个 field_id_item ,class_idx 和 type_idx 字段都必须是 type_ids 列表的有效索引。由 class_idx 引用的条目必须是非数组引用类型。此外,name_idx 字段必须是对 string_ids 部分的有效引用,并且引用条目的内容必须符合 MemberName 规范。 |
G19 | 对于每个 method_id_item ,class_idx 字段必须是 type_ids 部分的有效索引,并且引用条目必须是非数组引用类型。proto_id 字段必须是对 proto_ids 列表的有效引用。name_idx 字段必须是对 string_ids 部分的有效引用,并且引用条目的内容必须符合 MemberName 规范。 |
G20 | 对于每个 field_id_item ,class_idx 字段必须是 type_ids 列表的有效索引。引用条目必须是非数组引用类型。 |
静态字节码约束
静态约束是关于字节码各个元素的约束。它们通常可以在不使用控制流或数据流分析技术的情况下进行检查。
标识符 | 说明 |
---|---|
A1 | insns 数组不能为空。 |
A2 | insns 数组中的第一个操作码的索引必须为零。 |
A3 | insns 数组必须仅包含有效的 Dalvik 操作码。 |
A4 | 指令 n+1 的索引必须等于指令 n 的索引加上指令 n 的长度(考虑到可能的运算数)。 |
A5 | insns 数组中的最后一条指令必须在索引 insns_size-1 处结束。 |
A6 | 所有 goto 和 if-<kind> 目标都必须是同一方法内的操作码。 |
A7 | packed-switch 指令的所有目标都必须是同一方法内的操作码。大小和目标列表必须一致。 |
A8 | sparse-switch 指令的所有目标都必须是同一方法内的操作码。相应的表必须一致且按从低到高的顺序排序。 |
A9 | const-string 和 const-string/jumbo 指令的 B 运算数必须是字符串常量池的有效索引。 |
A10 | iget<kind> 和 iput<kind> 指令的 C 运算数必须是字段常量池的有效索引。引用的条目必须表示实例字段。 |
A11 | sget<kind> 和 sput<kind> 指令的 C 运算数必须是字段常量池的有效索引。引用的条目必须表示静态字段。 |
A12 | invoke-virtual 、invoke-super 、invoke-direct 和 invoke-static 指令的 C 运算数必须是方法常量池的有效索引。 |
A13 | invoke-virtual/range 、invoke-super/range 、invoke-direct/range 和 invoke-static/range 指令的 B 运算数必须是方法常量池的有效索引。 |
A14 | 名称以“<”开头的方法只能由虚拟机隐式调用,而不能由源自 .dex 文件的代码调用。唯一的例外是实例初始化程序,它可以由 invoke-direct 调用。 |
A15 | invoke-interface 指令的 C 运算数必须是方法常量池的有效索引。引用的 method_id 必须属于接口(而不是类)。 |
A16 | invoke-interface/range 指令的 B 运算数必须是方法常量池的有效索引。引用的 method_id 必须属于接口(而不是类)。 |
A17 | const-class 、check-cast 、new-instance 和 filled-new-array/range 指令的 B 运算数必须是类型常量池的有效索引。 |
A18 | instance-of 、new-array 和 filled-new-array 指令的 C 运算数必须是类型常量池的有效索引。 |
A19 | 由 new-array 指令创建的数组的维度必须小于 256 。 |
A20 | new 指令不得引用数组类、接口或抽象类。 |
A21 | 由 new-array 指令引用的类型必须是有效的非引用类型。 |
A22 | 以单宽度(非对)方式由指令引用的所有寄存器对于当前方法都必须有效。也就是说,它们的索引必须为非负数且小于 registers_size 。 |
A23 | 以双宽度(对)方式由指令引用的所有寄存器对于当前方法都必须有效。也就是说,它们的索引必须为非负数且小于 registers_size-1 。 |
A24 | invoke-virtual 和 invoke-direct 指令的 method_id 运算数必须属于类(而不是接口)。在版本 037 之前的 Dex 文件中,invoke-super 和 invoke-static 指令也必须如此。 |
A25 | invoke-virtual/range 和 invoke-direct/range 指令的 method_id 运算数必须属于类(而不是接口)。在版本 037 之前的 Dex 文件中,invoke-super/range 和 invoke-static/range 指令也必须如此。 |
结构字节码约束
结构约束是关于字节码的多个元素之间关系的约束。它们通常无法在不使用控制流或数据流分析技术的情况下进行检查。
标识符 | 说明 |
---|---|
B1 | 参数(寄存器和立即数值)的数量和类型必须始终与指令匹配。 |
B2 | 寄存器对绝不能被拆分。 |
B3 | 寄存器(或对)必须先赋值才能读取。 |
B4 | invoke-direct 指令必须仅在当前类或其超类之一中调用实例初始化程序或方法。 |
B5 | 实例初始化程序必须仅在未初始化的实例上调用。 |
B6 | 实例方法可能仅在已初始化的实例上调用,并且实例字段可能仅在已初始化的实例上访问。 |
B7 | 如果再次执行相同的 new-instance 指令,则在实例初始化之前,不得使用保存 new-instance 指令结果的寄存器。 |
B8 | 实例初始化程序必须在访问任何实例成员之前调用另一个实例初始化程序(同一类或超类)。例外情况是在调用另一个初始化程序之前可以赋值的非继承实例字段,以及一般的 Object 类。 |
B9 | 所有实际方法参数都必须与其各自的形式参数分配兼容。 |
B10 | 对于每个实例方法调用,实际实例必须与指令中指定的类或接口分配兼容。 |
B11 | return<kind> 指令必须与其方法的返回类型匹配。 |
B12 | 访问超类的受保护成员时,被访问实例的实际类型必须是当前类或其子类之一。 |
B13 | 存储到静态字段中的值的类型必须与字段的类型分配兼容或可转换为字段的类型。 |
B14 | 存储到字段中的值的类型必须与字段的类型分配兼容或可转换为字段的类型。 |
B15 | 存储到数组中的每个值的类型都必须与数组的组件类型分配兼容。 |
B16 | throw 指令的 A 运算数必须与 java.lang.Throwable 分配兼容。 |
B17 | 方法中最后一条可访问的指令必须是向后的 goto 或分支、return 或 throw 指令。不得有可能在底部离开 insns 数组。 |
B18 | 在通过其他一些指令重新赋值之前,前一个寄存器对的未赋值一半可能无法读取(被视为无效)。 |
B19 | move-result<kind> 指令之前必须紧跟(在 insns 数组中)invoke-<kind> 指令。唯一的例外是 move-result-object 指令,该指令也可以在 filled-new-array 指令之前。 |
B20 | move-result<kind> 指令必须紧跟在(实际控制流中)匹配的 return-<kind> 指令之后(不得跳转到)。唯一的例外是 move-result-object 指令,该指令也可以在 filled-new-array 指令之前。 |
B21 | move-exception 指令必须仅作为异常处理程序中的第一条指令出现。 |
B22 | packed-switch-data 、sparse-switch-data 和 fill-array-data 伪指令不得通过控制流访问。 |