约束

.dex 文件是 Dalvik 字节码的传输格式。要成为有效的 .dex 文件,文件必须在语法和语义上受到一定的约束;此外,要仅支持有效的 .dex 文件,还需要运行时。

.dex 的一般完整性约束

一般完整性约束涉及较大结构的 .dex 文件,详见 .dex 格式

标识符 说明
G1 .dex 文件的 magic 数值必须是 dex\n035\0dex\n037\0
G2 校验和必须是除了 magicchecksum 字段之外的整个文件内容的 Adler-32 校验和。
G3 签名必须是除了 magicchecksumsignature 之外的整个文件内容的 SHA-1 哈希值。
G4 file_size 必须与实际文件大小(以字节为单位)相匹配。
G5 header_size 的值必须为 0x70
G6 endian_tag 的值必须为 ENDIAN_CONSTANTREVERSE_ENDIAN_CONSTANT
G7 对于 linkstring_idstype_idsproto_idsfield_idsmethod_idsclass_defsdata 各区段,offsetsize 字段必须都为零或都为非零。在后一种情况下,偏移必须四字节对齐。
G8 标头中除 map_off 之外的所有偏移字段都必须四字节对齐。
G9 map_off 字段必须为零或指向数据区段。在后一种情况下,data 区段必须存在。
G10 linkstring_idstype_idsproto_idsfield_idsmethod_idsclass_defsdata 区段都不得彼此重叠或者与标头重叠。
G11 如果存在映射,则每个映射条目都必须具有有效的类型。每种类型最多可以出现一次。
G12 如果存在映射,则每个映射条目必须具有非零偏移和大小。偏移必须指向文件的相应区段(即,string_id_item 必须指向 string_ids 区段),并且项的显式或隐式大小必须与该区段的实际内容和大小相匹配。
G13 如果存在映射,则映射条目 n+1 的偏移量必须大于或等于映射条目 n plus than size of map entry n 的偏移量。这意味着条目互不重叠且从低到高排序。
G14 以下类型的条目必须具有四字节对齐的偏移量:string_id_itemtype_id_itemproto_id_itemfield_id_itemmethod_id_itemclass_def_itemtype_listcode_itemannotations_directory_item
G15 对于每个 string_id_itemstring_data_off 字段必须包含对 data 区段的有效引用。对于引用的 string_data_itemdata 字段必须包含有效的 MUTF-8 字符串,并且 utf16_size 必须与字符串的解码长度相匹配。
G16 对于每个 type_id_itemdescriptor_idx 字段必须包含对 string_ids 列表的有效引用。引用的字符串必须是有效的类型描述符。
G17 对于每个 proto_id_itemshorty_idx 字段必须包含对 string_ids 列表的有效引用。引用的字符串必须是有效的短描述符。此外,return_type_idx 字段必须是 type_ids 区段的有效索引,并且 parameters_off 字段必须为零或指向 data 区段的有效偏移量。如果为非零,则参数列表不得包含任何空白条目。
G18 对于每个 field_id_itemclass_idxtype_idx 字段都必须是 type_ids 列表的有效索引。class_idx 引用的条目必须是非数组引用类型。此外,name_idx 字段必须是对 string_ids 区段的有效引用,且引用条目的内容必须符合 MemberName 规范。
G19 对于每个 method_id_itemclass_idx 字段必须是 type_ids 区段的有效索引,且引用的条目必须是非数组引用类型。proto_id 字段必须是对 proto_ids 列表的有效引用。name_idx 字段必须是对 string_ids 区段的有效引用,且引用条目的内容必须符合 MemberName 规范。
G20 对于每个 field_id_itemclass_idx 字段必须是 type_ids 列表的有效索引。引用的条目必须是非数组引用类型。

静态字节码约束

静态约束是对字节码的各个元素的约束。通常,可以在不使用控制或数据流分析技术的情况下检查这类约束。

标识符 说明
A1 insns 数组不能为空。
A2 insns 数组中第一个运算码的索引必须为零。
A3 insns 数组必须只包含有效的 Dalvik 运算码。
A4 指令 n+1 的索引必须等于指令 n 的索引加上指令 n 的长度,同时要考虑可能的运算数。
A5 insns 数组中最后一条指令必须以索引 insns_size-1 结尾。
A6 所有 gotoif-<kind> 目标必须是同一方法中的运算码。
A7 packed-switch 指令的所有目标必须是同一方法中的运算码。目标的大小和列表必须一致。
A8 sparse-switch 指令的所有目标必须是同一方法中的运算码。相应的表必须一致,并从低到高排序。
A9 const-stringconst-string/jumbo 指令的 B 运算数必须是字符串常量池中的有效索引。
A10 iget<kind>iput<kind> 指令的 C 运算数必须是字段常量池中的有效索引。引用的条目必须表示一个实例字段。
A11 sget<kind>sput<kind> 指令的 C 运算数必须是字段常量池中的有效索引。引用的条目必须表示静态字段。
A12 invoke-virtualinvoke-superinvoke-directinvoke-static 指令的 C 运算数必须是方法常量池中的有效索引。
A13 invoke-virtual/rangeinvoke-super/rangeinvoke-direct/rangeinvoke-static/range 指令的 B 运算数必须是方法常量池中的有效索引。
A14 名称以“<”开头的方法只能由 VM 隐式调用,而不能由 .dex 文件生成的代码调用。唯一的例外是实例初始化程序,它可以由 invoke-direct 调用。
A15 invoke-interface 指令的 C 运算数必须是方法常量池中的有效索引。引用的 method_id 必须属于接口(而非类)。
A16 invoke-interface/range 指令的 B 运算数必须是方法常量池中的有效索引。引用的 method_id 必须属于接口(而非类)。
A17 const-classcheck-castnew-instancefilled-new-array/range 指令的 B 运算数必须是类型常量池中的有效索引。
A18 instance-ofnew-arrayfilled-new-array 指令的 C 运算数必须是类型常量池中的有效索引。
A19 new-array 指令创建的数组维数必须小于 256
A20 new 指令不得引用数组类、接口和抽象类。
A21 new-array 指令引用的类型必须是有效的非引用类型。
A22 指令以单精度宽度(非值对)方式形式引用的所有寄存器必须对当前方法有效。也就是说,它们的索引必须是非负数,并且小于 registers_size
A23 指令以双精度宽度(值对)形式引用的所有寄存器必须对当前方法有效。也就是说,它们的索引必须是非负数,并且小于 registers_size-1
A24 invoke-virtualinvoke-direct 指令的 method_id 运算数必须属于一个类(而不是一个接口)。在版本 037 之前的 Dex 文件中,invoke-superinvoke-static 指令也是如此。
A25 invoke-virtual/rangeinvoke-direct/range 指令的 method_id 运算数必须属于一个类(而不是一个接口)。在版本 037 之前的 Dex 文件中,invoke-super/rangeinvoke-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 或分支、returnthrow 指令。不能将 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-datasparse-switch-datafill-array-data 伪指令。