.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 plus than size of map entry 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列表的有效引用。引用的字符串必须是有效的短描述符。此外,return_type_idx字段必须是type_ids区段的有效索引,并且parameters_off字段必须为零或指向data区段的有效偏移量。如果为非零,参数列表不得包含任何空白条目。 | 
| 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伪指令。 | 
