.dex
檔案是 Dalvik 字節碼的傳輸格式。要使文件成為有效的.dex
文件,存在某些語法和語義約束,並且運行時需要僅支援有效的 .dex 文件。
一般 .dex 完整性約束
一般完整性約束與.dex
檔案的較大結構有關,如.dex
格式中詳細描述的。
識別符 | 描述 |
---|---|
G1 | .dex 檔的magic 數必須是dex\n035\0 或dex\n037\0 。 |
G2 | 校驗和必須是除magic 和checksum 和欄位之外的整個文件內容的 Adler-32 校驗和。 |
G3 | 簽章必須是整個檔案內容的 SHA-1 雜湊值( magic 、 checksum 和signature 除外)。 |
G4 | file_size 必須與實際檔案大小(以位元組為單位)相符。 |
G5 | header_size 值必須為: 0x70 |
G6 | endian_tag 必須具有以下值之一: ENDIAN_CONSTANT 或REVERSE_ENDIAN_CONSTANT |
G7 | 對於每個link 、 string_ids 、 type_ids 、 proto_ids 、 field_ids 、 method_ids 、 class_defs 和data 部分, offset 和size 欄位必須皆為零或均非零。在後一種情況下,偏移量必須是四位元組對齊的。 |
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 | 對於每個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> 指令的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 偽指令不能透過控制流程存取。 |