.dex
文件是 Dalvik 字節碼的傳輸格式。文件要成為有效的.dex
文件有一定的語法和語義約束,並且運行時需要僅支持有效的 .dex 文件。
一般 .dex 完整性約束
一般完整性約束與.dex
文件的較大結構有關,如.dex
格式中詳細描述的那樣。
標識符 | 描述 |
---|---|
G1 | .dex 文件的magic 數必須是dex\n035\0 或dex\n037\0 。 |
G2 | 校驗和必須是除magic 和checksum 字段外的整個文件內容的 Adler-32 校驗和。 |
G3 | 簽名必須是除magic 、 checksum 和和signature 之外的整個文件內容的 SHA-1 哈希。 |
G4 | file_size 必須與實際文件大小(以字節為單位)匹配。 |
G5 | header_size 必須具有值: 0x70 |
G6 | endian_tag 必須具有以下值: ENDIAN_CONSTANT 或REVERSE_ENDIAN_CONSTANT |
七國集團 | 對於每個link 、 string_ids 、 type_ids 、 proto_ids 、 field_ids 、 method_ids 、 class_defs 和data 部分, offset 和size 字段必須要么都是零,要么都是非零。在後一種情況下,偏移量必須是四字節對齊的。 |
八國集團 | 除了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 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 | 對於每個string_id_item , string_data_off 字段必須包含對data 部分的有效引用。對於引用的string_data_item , data 字段必須包含有效的 MUTF-8 字符串,並且utf16_size 必須與字符串的解碼長度匹配。 |
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 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 | 名稱以“<”開頭的方法只能由 VM 隱式調用,而不是由源自.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> 指令必須緊跟在一個invoke-<kind> 指令之前(在insns 數組中)。唯一的例外是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 偽指令。 |