本文档介绍了 .dex
文件的布局和内容,这些文件用于保存一组类定义及其相关的辅助数据。
类型指南
名称 | 说明 |
---|---|
byte | 8 位有符号整数 |
ubyte | 8 位无符号整数 |
short | 16 位有符号整数,小端序 |
ushort | 16 位无符号整数,小端序 |
int | 32位有符号整数,小端序 |
uint | 32位无符号整数,小端序 |
long | 64位有符号整数,小端序 |
ulong | 64位无符号整数,小端序 |
sleb128 | 有符号 LEB128,可变长度(见下文) |
uleb128 | 无符号 LEB128,可变长度(见下文) |
uleb128p1 | 无符号 LEB128 加 1 ,可变长度(见下文) |
LEB128
LEB128(“Little-Endian Base 128”的缩写)是一种用于表示任意有符号或无符号整数值的可变长度编码。该格式借鉴自 DWARF3 规范。在 .dex
文件中,LEB128 仅用于编码 32 位数值。
每个 LEB128 编码值由一到五个字节组成,这些字节共同表示一个 32 位值。每个字节的最高有效位都已设置,但序列中的最后一个字节除外,它的最高有效位已清除。每个字节的剩余七位是有效负载,数量的最低有效七位在第一个字节中,接下来的七位在第二个字节中,依此类推。在有符号 LEB128 (sleb128
) 的情况下,序列中最后一个字节的最高有效负载位会进行符号扩展,以生成最终值。在无符号情况下 (uleb128
),任何未明确表示的位都解释为 0
。
双字节 LEB128 值的位图 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
第一个字节 | 第二个字节 | ||||||||||||||
1 |
bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | 0 |
bit13 | bit12 | bit11 | bit10 | bit9 | bit8 | bit7 |
变体 uleb128p1
用于表示有符号值,其中表示形式是值加一,编码为 uleb128
。这使得 -1
(也可以认为是无符号值 0xffffffff
)的编码(但没有其他负数)成为单字节,并且在表示的数字必须是非负数或 -1
(或 0xffffffff
),并且不允许其他负值(或不太可能需要大的无符号值)的情况下非常有用。
以下是一些格式示例
编码序列 | 作为 sleb128 |
作为 uleb128 |
作为 uleb128p1 |
---|---|---|---|
00 | 0 | 0 | -1 |
01 | 1 | 1 | 0 |
7f | -1 | 127 | 126 |
80 7f | -128 | 16256 | 16255 |
文件布局
名称 | 格式 | 说明 |
---|---|---|
header | header_item | 标头 |
string_ids | string_id_item[] | 字符串标识符列表。这些是此文件使用的所有字符串的标识符,用于内部命名(例如,类型描述符)或作为代码引用的常量对象。此列表必须按字符串内容排序,使用 UTF-16 代码点值(不以区分区域设置的方式),并且不得包含任何重复条目。 |
type_ids | type_id_item[] | 类型标识符列表。这些是此文件引用的所有类型(类、数组或原始类型)的标识符,无论是在文件中定义的还是未定义的。此列表必须按 string_id 索引排序,并且不得包含任何重复条目。 |
proto_ids | proto_id_item[] | 方法原型标识符列表。这些是此文件引用的所有原型的标识符。此列表必须按返回类型(按 type_id 索引)主序排序,然后按参数列表(字典序排序,单个参数按 type_id 索引排序)排序。该列表不得包含任何重复条目。 |
field_ids | field_id_item[] | 字段标识符列表。这些是此文件引用的所有字段的标识符,无论是在文件中定义的还是未定义的。此列表必须排序,其中定义类型(按 type_id 索引)为主序,字段名称(按 string_id 索引)为中间顺序,类型(按 type_id 索引)为次序。该列表不得包含任何重复条目。 |
method_ids | method_id_item[] | 方法标识符列表。这些是此文件引用的所有方法的标识符,无论是在文件中定义的还是未定义的。此列表必须排序,其中定义类型(按 type_id 索引)为主序,方法名称(按 string_id 索引)为中间顺序,方法原型(按 proto_id 索引)为次序。该列表不得包含任何重复条目。 |
class_defs | class_def_item[] | 类定义列表。类的顺序必须使得给定类的超类和实现的接口在列表中出现在引用类之前。此外,对于同名类的定义在列表中多次出现是无效的。 |
call_site_ids | call_site_id_item[] | 调用站点标识符列表。这些是此文件引用的所有调用站点的标识符,无论是在文件中定义的还是未定义的。此列表必须按 call_site_off 的升序排序。 |
method_handles | method_handle_item[] | 方法句柄列表。此文件引用的所有方法句柄的列表,无论是在文件中定义的还是未定义的。此列表未排序,并且可能包含逻辑上对应于不同方法句柄实例的重复项。 |
data | ubyte[] | 数据区域,包含上面列出的所有表的支持数据。不同的项具有不同的对齐要求,并且在每个项之前插入填充字节(如果需要)以实现正确的对齐。 |
link_data | ubyte[] | 静态链接文件中使用的数据。本文档未指定此部分中数据的格式。此部分在未链接的文件中为空,运行时实现可以根据需要使用它。 |
容器格式
版本 41 引入了一种新的 DEX 数据容器格式,旨在节省空间。此容器格式允许将多个逻辑 DEX 文件组合成单个物理文件。新格式主要只是先前格式文件的简单连接,但有一些不同之处
file_size
是逻辑文件的大小,而不是物理文件的大小。它可用于迭代容器中的所有逻辑文件。- 逻辑 dex 文件可以引用容器中任何后续数据(但不能引用更早的数据)。这允许 dex 文件在它们之间共享数据,例如字符串。
- 所有偏移量都相对于物理文件。没有偏移量是相对于标头的。这确保了具有偏移量的节可以在逻辑文件之间共享。
- 标头添加了两个新字段来描述容器的边界。这是一个额外的完整性检查,使代码移植到新格式更容易。
data_size
和data_off
现在未使用。数据可以分布在多个逻辑文件中,而不必是连续的。
位字段、字符串和常量定义
DEX_FILE_MAGIC
嵌入在 header_item 中
常量数组/字符串 DEX_FILE_MAGIC
是必须出现在 .dex
文件开头才能被识别为此类文件的字节列表。该值有意包含换行符 ("\n"
或 0x0a
) 和空字节 ("\0"
或 0x00
),以帮助检测某些形式的损坏。该值还以三个十进制数字编码格式版本号,随着格式的演变,预计该版本号会单调递增。
ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x39 0x00 } = "dex\n039\0"
注意: Android 10.0 版本中添加了对格式版本 040
的支持,该版本扩展了 SimpleNames 中允许的字符集。
注意: Android 9.0 版本中添加了对格式版本 039
的支持,该版本引入了两个新的字节码,const-method-handle
和 const-method-type
。(这些都在字节码集摘要表中进行了描述。)在 Android 10 中,版本 039
扩展了 DEX 文件格式,以包含隐藏的 API 信息,该信息仅适用于引导类路径上的 DEX 文件。
注意: Android 8.0 版本中添加了对格式版本 038
的支持。版本 038
添加了新的字节码(invoke-polymorphic
和 invoke-custom
)以及方法句柄的数据。
注意: Android 7.0 版本中添加了对格式版本 037
的支持。在版本 037
之前,大多数 Android 版本都使用了格式版本 035
。版本 035
和 037
之间唯一的区别是添加了默认方法和 invoke
的调整。
注意: 至少在广泛可用的公共软件版本中使用了几个早期版本的格式。例如,版本 009
用于 Android 平台的 M3 版本(2007 年 11 月至 12 月),版本 013
用于 Android 平台的 M5 版本(2008 年 2 月至 3 月)。在某些方面,这些早期版本的格式与本文档中描述的版本有很大不同。
ENDIAN_CONSTANT 和 REVERSE_ENDIAN_CONSTANT
嵌入在 header_item 中
常量 ENDIAN_CONSTANT
用于指示找到它的文件的字节序。尽管标准 .dex
格式是小端序,但实现可以选择执行字节交换。如果实现遇到标头的 endian_tag
是 REVERSE_ENDIAN_CONSTANT
而不是 ENDIAN_CONSTANT
,它将知道该文件已从预期形式进行了字节交换。
uint ENDIAN_CONSTANT = 0x12345678; uint REVERSE_ENDIAN_CONSTANT = 0x78563412;
NO_INDEX
嵌入在 class_def_item 和 debug_info_item 中
常量 NO_INDEX
用于指示索引值不存在。
注意: 此值未定义为 0
,因为实际上通常是有效的索引。
NO_INDEX
的选定值可以用 uleb128p1
编码中的单个字节表示。
uint NO_INDEX = 0xffffffff; // == -1 if treated as a signed int
access_flags 定义
嵌入在 class_def_item、encoded_field、encoded_method 和 InnerClass 中
这些标志的位字段用于指示类和类成员的可访问性和总体属性。
名称 | 值 | 对于类(和 InnerClass 注解) |
对于字段 | 对于方法 |
---|---|---|---|---|
ACC_PUBLIC | 0x1 | public :在任何地方都可见 |
public :在任何地方都可见 |
public :在任何地方都可见 |
ACC_PRIVATE | 0x2 | private :仅对定义类可见 |
private :仅对定义类可见 |
private :仅对定义类可见 |
ACC_PROTECTED | 0x4 | protected :对包和子类可见 |
protected :对包和子类可见 |
protected :对包和子类可见 |
ACC_STATIC | 0x8 | static :不是用外部 this 引用构造的 |
static :对于定义类是全局的 |
static :不接受 this 参数 |
ACC_FINAL | 0x10 | final :不可子类化 |
final :构造后不可变 |
final :不可重写 |
ACC_SYNCHRONIZED | 0x20 | synchronized :在此方法调用周围自动获取关联的锁。注意: 仅当也设置了 |
||
ACC_VOLATILE | 0x40 | volatile :特殊的访问规则,以帮助实现线程安全 |
||
ACC_BRIDGE | 0x40 | 桥接方法,由编译器自动添加,作为类型安全的桥接 | ||
ACC_TRANSIENT | 0x80 | transient :默认序列化时不保存 |
||
ACC_VARARGS | 0x80 | 最后一个参数应被编译器视为“rest”参数 | ||
ACC_NATIVE | 0x100 | native :在本地代码中实现 |
||
ACC_INTERFACE | 0x200 | interface :可多重实现的抽象类 |
||
ACC_ABSTRACT | 0x400 | abstract :不可直接实例化 |
abstract :未由此类实现 |
|
ACC_STRICT | 0x800 | strictfp :浮点运算的严格规则 |
||
ACC_SYNTHETIC | 0x1000 | 不是直接在源代码中定义的 | 不是直接在源代码中定义的 | 不是直接在源代码中定义的 |
ACC_ANNOTATION | 0x2000 | 声明为注解类 | ||
ACC_ENUM | 0x4000 | 声明为枚举类型 | 声明为枚举值 | |
(未使用) | 0x8000 | |||
ACC_CONSTRUCTOR | 0x10000 | 构造函数方法(类或实例初始化器) | ||
ACC_DECLARED_ SYNCHRONIZED |
0x20000 | 声明的 synchronized 。注意: 这对执行没有影响(除了在反射此标志本身时)。 |
InnerClass
注解,并且绝不能在 class_def_item
中出现。
Modified UTF-8 编码
作为对更轻松的旧版支持的让步,.dex
格式以事实上的标准 modified UTF-8 形式编码其字符串数据,以下简称 MUTF-8。此形式与标准 UTF-8 相同,但有以下例外
- 仅使用一字节、两字节和三字节编码。
- 范围
U+10000
…U+10ffff
中的代码点被编码为代理对,每个代理对都表示为三字节编码值。 - 代码点
U+0000
以双字节形式编码。 - 纯空字节(值
0
)表示字符串的结尾,这是标准的 C 语言解释。
以上前两项可以概括为:MUTF-8 是 UTF-16 的编码格式,而不是 Unicode 字符的更直接的编码格式。
以上最后两项使其可以同时在字符串中包含代码点 U+0000
并且仍然将其作为 C 风格的空终止字符串进行操作。
但是,U+0000
的特殊编码意味着,与正常的 UTF-8 不同,在 MUTF-8 字符串对上调用标准 C 函数 strcmp()
的结果并不总是指示不相等字符串比较的正确有符号结果。当排序(不仅仅是相等性)是一个问题时,比较 MUTF-8 字符串最直接的方法是逐字符解码它们,并比较解码后的值。(但是,也可能有更巧妙的实现。)
有关字符编码的更多信息,请参阅 Unicode 标准。MUTF-8 实际上更接近于(相对不太知名的)编码 CESU-8,而不是 UTF-8 本身。
encoded_value 编码
嵌入在 annotation_element 和 encoded_array_item 中
encoded_value
是编码的一段(几乎)任意分层结构化数据。该编码旨在既紧凑又易于解析。
名称 | 格式 | 说明 |
---|---|---|
(value_arg << 5) | value_type | ubyte | 指示紧随其后的 value 类型的字节,以及高三位中的可选澄清参数。请参阅下文了解各种 value 定义。在大多数情况下,value_arg 以字节为单位编码紧随其后的 value 的长度,如 (size - 1) ,例如,0 表示该值需要一个字节,7 表示它需要八个字节;但是,如下所述,也有例外。 |
value | ubyte[] | 表示值的字节,长度可变,并且对于不同的 value_type 字节,解释方式也不同,但始终为小端序。有关详细信息,请参阅下面的各种值定义。 |
值格式
类型名称 | value_type |
value_arg 格式 |
value 格式 |
说明 |
---|---|---|---|---|
VALUE_BYTE | 0x00 | (无;必须为 0 ) |
ubyte[1] | 有符号单字节整数值 |
VALUE_SHORT | 0x02 | size - 1 (0…1) | ubyte[size] | 有符号双字节整数值,符号扩展 |
VALUE_CHAR | 0x03 | size - 1 (0…1) | ubyte[size] | 无符号双字节整数值,零扩展 |
VALUE_INT | 0x04 | size - 1 (0…3) | ubyte[size] | 有符号四字节整数值,符号扩展 |
VALUE_LONG | 0x06 | size - 1 (0…7) | ubyte[size] | 有符号八字节整数值,符号扩展 |
VALUE_FLOAT | 0x10 | size - 1 (0…3) | ubyte[size] | 四字节位模式,向右零扩展,并解释为 IEEE754 32 位浮点值 |
VALUE_DOUBLE | 0x11 | size - 1 (0…7) | ubyte[size] | 八字节位模式,向右零扩展,并解释为 IEEE754 64 位浮点值 |
VALUE_METHOD_TYPE | 0x15 | size - 1 (0…3) | ubyte[size] | 无符号(零扩展)四字节整数值,解释为 proto_ids 部分的索引,并表示方法类型值 |
VALUE_METHOD_HANDLE | 0x16 | size - 1 (0…3) | ubyte[size] | 无符号(零扩展)四字节整数值,解释为 method_handles 部分的索引,并表示方法句柄值 |
VALUE_STRING | 0x17 | size - 1 (0…3) | ubyte[size] | 无符号(零扩展)四字节整数值,解释为 string_ids 部分的索引,并表示字符串值 |
VALUE_TYPE | 0x18 | size - 1 (0…3) | ubyte[size] | 无符号(零扩展)四字节整数值,解释为 type_ids 部分的索引,并表示反射类型/类值 |
VALUE_FIELD | 0x19 | size - 1 (0…3) | ubyte[size] | 无符号(零扩展)四字节整数值,解释为 field_ids 部分的索引,并表示反射字段值 |
VALUE_METHOD | 0x1a | size - 1 (0…3) | ubyte[size] | 无符号(零扩展)四字节整数值,解释为 method_ids 部分的索引,并表示反射方法值 |
VALUE_ENUM | 0x1b | size - 1 (0…3) | ubyte[size] | 无符号(零扩展)四字节整数值,解释为 field_ids 部分的索引,并表示枚举类型常量的值 |
VALUE_ARRAY | 0x1c | (无;必须为 0 ) |
encoded_array | 值数组,格式如下面的“encoded_array 格式”指定。 value 的大小在编码中是隐式的。 |
VALUE_ANNOTATION | 0x1d | (无;必须为 0 ) |
encoded_annotation | 子注解,格式如下面的“encoded_annotation 格式”指定。 value 的大小在编码中是隐式的。 |
VALUE_NULL | 0x1e | (无;必须为 0 ) |
(无) | null 引用值 |
VALUE_BOOLEAN | 0x1f | 布尔值 (0…1) | (无) | 单比特值;0 表示 false ,1 表示 true 。该位在 value_arg 中表示。 |
encoded_array 格式
名称 | 格式 | 说明 |
---|---|---|
size | uleb128 | 数组中元素的数量 |
values | encoded_value[size] | 一系列 size 个 encoded_value 字节序列,格式由此部分指定,并按顺序连接。 |
encoded_annotation 格式
名称 | 格式 | 说明 |
---|---|---|
type_idx | uleb128 | 注解的类型。这必须是类(而不是数组或原始)类型。 |
size | uleb128 | size |
elements | annotation_element[size] | 注解的元素,直接内联表示(而不是作为偏移量)。元素必须按 string_id 索引的升序排序。 |
annotation_element 格式
名称 | 格式 | 说明 |
---|---|---|
name_idx | uleb128 | 元素名称,表示为 string_ids 部分的索引。字符串必须符合上面定义的 MemberName 的语法。 |
value | encoded_value | 元素值 |
字符串语法
.dex
文件中有几种类型的项最终引用字符串。以下 BNF 样式定义指示这些字符串的可接受语法。
SimpleName
SimpleName 是其他事物名称的语法基础。 .dex
格式在此处允许相当大的自由度(比大多数常见的源语言要大得多)。简而言之,简单名称由任何低 ASCII 字母字符或数字、一些特定的低 ASCII 符号以及大多数非 ASCII 代码点组成,这些代码点不是控制字符、空格字符或特殊字符。从版本 040
开始,该格式还允许空格字符(Unicode Zs
类别)。请注意,代理代码点(在范围 U+d800
… U+dfff
中)本身不被视为有效的名称字符,但 Unicode 补充字符是有效的(由 SimpleNameChar 规则的最后一个备选项表示),并且它们应在文件中表示为 MUTF-8 编码中的代理代码点对。
SimpleName → | ||
SimpleNameChar (SimpleNameChar)* | ||
SimpleNameChar → | ||
'A' … 'Z' |
||
| | 'a' … 'z' |
|
| | '0' … '9' |
|
| | ' ' |
自 DEX 版本 040 起 |
| | '$' |
|
| | '-' |
|
| | '_' |
|
| | U+00a0 |
自 DEX 版本 040 起 |
| | U+00a1 … U+1fff |
|
| | U+2000 … U+200a |
自 DEX 版本 040 起 |
| | U+2010 … U+2027 |
|
| | U+202f |
自 DEX 版本 040 起 |
| | U+2030 … U+d7ff |
|
| | U+e000 … U+ffef |
|
| | U+10000 … U+10ffff |
MemberName
由 field_id_item 和 method_id_item 使用
MemberName 是类的成员名称,成员包括字段、方法和内部类。
MemberName → | |
SimpleName | |
| | '<' SimpleName '>' |
FullClassName
FullClassName 是完全限定的类名,包括可选的包说明符,后跟必需的名称。
FullClassName → | |
OptionalPackagePrefix SimpleName | |
OptionalPackagePrefix → | |
(SimpleName '/' )* |
TypeDescriptor
由 type_id_item 使用
TypeDescriptor 是任何类型的表示,包括原始类型、类、数组和 void
。请参阅下文了解各种版本的含义。
TypeDescriptor → | |
'V' |
|
| | FieldTypeDescriptor |
FieldTypeDescriptor → | |
NonArrayFieldTypeDescriptor | |
| | ('[' * 1…255) NonArrayFieldTypeDescriptor |
NonArrayFieldTypeDescriptor→ | |
'Z' |
|
| | 'B' |
| | 'S' |
| | 'C' |
| | 'I' |
| | 'J' |
| | 'F' |
| | 'D' |
| | 'L' FullClassName ';' |
ShortyDescriptor
由 proto_id_item 使用
ShortyDescriptor 是方法原型的简短形式表示,包括返回类型和参数类型,但各种引用类型(类或数组)之间没有区别。相反,所有引用类型都由单个 'L'
字符表示。
ShortyDescriptor → | |
ShortyReturnType (ShortyFieldType)* | |
ShortyReturnType → | |
'V' |
|
| | ShortyFieldType |
ShortyFieldType → | |
'Z' |
|
| | 'B' |
| | 'S' |
| | 'C' |
| | 'I' |
| | 'J' |
| | 'F' |
| | 'D' |
| | 'L' |
TypeDescriptor 语义
这是 TypeDescriptor 的每个变体的含义。
语法 | 含义 |
---|---|
V | void ;仅对返回类型有效 |
Z | boolean |
B | byte |
byte | short |
S | char |
I | int |
int | long |
J | float |
D | double |
Lfully/qualified/Name; | 类 fully.qualified.Name |
[descriptor | descriptor 数组,可递归用于数组的数组,但最多只能有 255 个维度。 |
项和相关结构
本节包含可能出现在 .dex
文件中的每个顶级项的定义。
header_item
出现在标头部分
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
magic | ubyte[8] = DEX_FILE_MAGIC | 魔术值。有关更多详细信息,请参见上面“DEX_FILE_MAGIC ”下的讨论。 |
checksum | uint | 文件其余部分(除 magic 和此字段之外的所有内容)的 adler32 校验和;用于检测文件损坏 |
signature | ubyte[20] | 文件其余部分(除 magic 、checksum 和此字段之外的所有内容)的 SHA-1 签名(哈希);用于唯一标识文件 |
file_size | uint |
整个文件的大小(包括标头),以字节为单位 (v40 或更早版本) 从该标头的开头到下一个标头或整个文件(容器)的结尾的字节距离。(v41 或更高版本) |
header_size | uint |
标头的大小(整个部分),以字节为单位。这允许至少有限的向前/向后兼容性,而不会使格式失效。 必须为 0x70 (112) 字节 (v40 或更早版本) 必须为 0x78 (120) 字节 (v41 或更高版本) |
endian_tag | uint = ENDIAN_CONSTANT | 字节序标记。有关更多详细信息,请参见上面“ENDIAN_CONSTANT 和 REVERSE_ENDIAN_CONSTANT ”下的讨论。 |
link_size | uint | 链接部分的大小,如果此文件未静态链接,则为 0 |
link_off | uint | 从文件开头到链接部分的偏移量,如果 link_size == 0 ,则为 0 。偏移量(如果非零)应指向 link_data 部分的偏移量。指向的数据的格式未在本文档中指定;此标头字段(和上一个字段)保留为运行时实现使用的挂钩。 |
map_off | uint | 从文件开头到 map item 的偏移量。偏移量(必须为非零值)应指向 data 部分的偏移量,并且数据应采用下面“map_list ”指定的格式。 |
string_ids_size | uint | 字符串标识符列表中的字符串计数 |
string_ids_off | uint | 从文件开头到字符串标识符列表的偏移量,如果 string_ids_size == 0 ,则为 0 (诚然是一个奇怪的边缘情况)。偏移量(如果非零)应指向 string_ids 部分的开头。 |
type_ids_size | uint | 类型标识符列表中元素的数量,最多 65535 个 |
type_ids_off | uint | 从文件开始到类型标识符列表的偏移量;如果 type_ids_size == 0 ,则为 0 (诚然,这是一个奇怪的边缘情况)。如果偏移量非零,则应指向 type_ids 区段的开始位置。 |
proto_ids_size | uint | 原型标识符列表中元素的数量,最多 65535 个 |
proto_ids_off | uint | 从文件开始到原型标识符列表的偏移量;如果 proto_ids_size == 0 ,则为 0 (诚然,这是一个奇怪的边缘情况)。如果偏移量非零,则应指向 proto_ids 区段的开始位置。 |
field_ids_size | uint | 字段标识符列表中元素的数量 |
field_ids_off | uint | 从文件开始到字段标识符列表的偏移量;如果 field_ids_size == 0 ,则为 0 。如果偏移量非零,则应指向 field_ids 区段的开始位置。 |
method_ids_size | uint | 方法标识符列表中元素的数量 |
method_ids_off | uint | 从文件开始到方法标识符列表的偏移量;如果 method_ids_size == 0 ,则为 0 。如果偏移量非零,则应指向 method_ids 区段的开始位置。 |
class_defs_size | uint | 类定义列表中元素的数量 |
class_defs_off | uint | 从文件开始到类定义列表的偏移量;如果 class_defs_size == 0 ,则为 0 (诚然,这是一个奇怪的边缘情况)。如果偏移量非零,则应指向 class_defs 区段的开始位置。 |
data_size | uint |
未使用 (v41 或更高版本) |
data_off | uint |
从文件开始到 未使用 (v41 或更高版本) |
container_size | uint |
此字段不存在。可以假定它等于 整个文件的大小(包括其他 dex 头部及其数据)。(v41 或更高版本) |
header_offset | uint |
此字段不存在。可以假定它等于 从文件开始到此头部开始位置的偏移量。(v41 或更高版本) |
map_list
出现在 data 区段中
从 header_item 引用
对齐:4 字节
这是文件中所有内容的列表,按顺序排列。它相对于 header_item
有一些冗余,但旨在成为一种易于使用的形式,用于遍历整个文件。给定类型在一个 map 中最多出现一次,但对类型的出现顺序没有限制,除了格式的其余部分所暗示的限制(例如,header
区段必须首先出现,然后是 string_ids
区段等)。此外,map 条目必须按初始偏移量排序,并且不得重叠。
名称 | 格式 | 说明 |
---|---|---|
size | uint | 列表中条目的数量 |
list | map_item[size] | 列表的元素 |
map_item 格式
名称 | 格式 | 说明 |
---|---|---|
type | ushort | 条目的类型;请参阅下表 |
unused | ushort | (未使用) |
size | uint | 在指示的偏移量处找到的条目数量 |
offset | uint | 从文件开始到相关条目的偏移量 |
类型代码
条目类型 | 常量 | 值 | 条目大小(字节) |
---|---|---|---|
header_item | TYPE_HEADER_ITEM | 0x0000 | 0x70 |
string_id_item | TYPE_STRING_ID_ITEM | 0x0001 | 0x04 |
type_id_item | TYPE_TYPE_ID_ITEM | 0x0002 | 0x04 |
proto_id_item | TYPE_PROTO_ID_ITEM | 0x0003 | 0x0c |
field_id_item | TYPE_FIELD_ID_ITEM | 0x0004 | 0x08 |
method_id_item | TYPE_METHOD_ID_ITEM | 0x0005 | 0x08 |
class_def_item | TYPE_CLASS_DEF_ITEM | 0x0006 | 0x20 |
call_site_id_item | TYPE_CALL_SITE_ID_ITEM | 0x0007 | 0x04 |
method_handle_item | TYPE_METHOD_HANDLE_ITEM | 0x0008 | 0x08 |
map_list | TYPE_MAP_LIST | 0x1000 | 4 + (item.size * 12) |
type_list | TYPE_TYPE_LIST | 0x1001 | 4 + (item.size * 2) |
annotation_set_ref_list | TYPE_ANNOTATION_SET_REF_LIST | 0x1002 | 4 + (item.size * 4) |
annotation_set_item | TYPE_ANNOTATION_SET_ITEM | 0x1003 | 4 + (item.size * 4) |
class_data_item | TYPE_CLASS_DATA_ITEM | 0x2000 | 隐式;必须解析 |
code_item | TYPE_CODE_ITEM | 0x2001 | 隐式;必须解析 |
string_data_item | TYPE_STRING_DATA_ITEM | 0x2002 | 隐式;必须解析 |
debug_info_item | TYPE_DEBUG_INFO_ITEM | 0x2003 | 隐式;必须解析 |
annotation_item | TYPE_ANNOTATION_ITEM | 0x2004 | 隐式;必须解析 |
encoded_array_item | TYPE_ENCODED_ARRAY_ITEM | 0x2005 | 隐式;必须解析 |
annotations_directory_item | TYPE_ANNOTATIONS_DIRECTORY_ITEM | 0x2006 | 隐式;必须解析 |
hiddenapi_class_data_item | TYPE_HIDDENAPI_CLASS_DATA_ITEM | 0xF000 | 隐式;必须解析 |
string_id_item
出现在 string_ids 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
string_data_off | uint | 从文件开始到此条目的字符串数据的偏移量。偏移量应指向 data 区段中的某个位置,并且数据应采用下面 "string_data_item " 指定的格式。偏移量没有对齐要求。 |
string_data_item
出现在 data 区段中
对齐方式:无(字节对齐)
名称 | 格式 | 说明 |
---|---|---|
utf16_size | uleb128 | 此字符串的大小,以 UTF-16 代码单元(这也是许多系统中的“字符串长度”)为单位。也就是说,这是字符串的解码长度。(编码长度由 0 字节的位置隐含。) |
data | ubyte[] | 一系列 MUTF-8 代码单元(也称为八位字节,也称为字节),后跟一个值为 0 的字节。有关数据格式的详细信息和讨论,请参阅上面的“MUTF-8(修改后的 UTF-8)编码”。注意: 包含(UTF-16 代理代码单元的编码形式)的字符串是可以接受的(即, |
type_id_item
出现在 type_ids 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
descriptor_idx | uint | 此类型的描述符字符串在 string_ids 列表中的索引。该字符串必须符合上面定义的 TypeDescriptor 的语法。 |
proto_id_item
出现在 proto_ids 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
shorty_idx | uint | 此原型的简短形式描述符字符串在 string_ids 列表中的索引。该字符串必须符合上面定义的 ShortyDescriptor 的语法,并且必须与此条目的返回类型和参数相对应。 |
return_type_idx | uint | 此原型的返回类型在 type_ids 列表中的索引 |
parameters_off | uint | 从文件开始到此原型的参数类型列表的偏移量;如果此原型没有参数,则为 0 。如果偏移量非零,则应在 data 区段中,并且那里的数据应采用下面 "type_list" 指定的格式。此外,列表中不应引用 void 类型。 |
field_id_item
出现在 field_ids 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
class_idx | ushort | 此字段的定义者在 type_ids 列表中的索引。这必须是类类型,而不是数组或原始类型。 |
type_idx | ushort | 此字段的类型在 type_ids 列表中的索引 |
name_idx | uint | 此字段的名称在 string_ids 列表中的索引。该字符串必须符合上面定义的 MemberName 的语法。 |
method_id_item
出现在 method_ids 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
class_idx | ushort | 此方法的定义者在 type_ids 列表中的索引。这必须是类或数组类型,而不是原始类型。 |
proto_idx | ushort | 此方法的原型在 proto_ids 列表中的索引 |
name_idx | uint | 此方法的名称在 string_ids 列表中的索引。该字符串必须符合上面定义的 MemberName 的语法。 |
class_def_item
出现在 class_defs 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
class_idx | uint | 此类在 type_ids 列表中的索引。这必须是类类型,而不是数组或原始类型。 |
access_flags | uint | 类的访问标志(public 、final 等)。有关详细信息,请参阅“access_flags 定义”。 |
superclass_idx | uint | 超类在 type_ids 列表中的索引;如果此类没有超类(即,它是根类,例如 Object ),则为常量值 NO_INDEX 。如果存在,则这必须是类类型,而不是数组或原始类型。 |
interfaces_off | uint | 从文件开始到接口列表的偏移量;如果没有接口,则为 0 。此偏移量应在 data 区段中,并且那里的数据应采用下面 "type_list " 指定的格式。列表中的每个元素都必须是类类型(而不是数组或原始类型),并且不得有任何重复项。 |
source_file_idx | uint | 包含此类原始源文件(或至少大部分)的文件名在 string_ids 列表中的索引;如果缺少此信息,则为特殊值 NO_INDEX 。任何给定方法的 debug_info_item 可能会覆盖此源文件,但预期大多数类仅来自一个源文件。 |
annotations_off | uint | 从文件开始到此类的注解结构的偏移量;如果此类上没有注解,则为 0 。如果偏移量非零,则应在 data 区段中,并且那里的数据应采用下面 "annotations_directory_item " 指定的格式,并且所有条目都将此类作为定义者引用。 |
class_data_off | uint | 从文件开始到与此条目关联的类数据的偏移量;如果此类没有类数据,则为 0 。(例如,如果此类是标记接口,则可能是这种情况。)如果偏移量非零,则应在 data 区段中,并且那里的数据应采用下面 "class_data_item " 指定的格式,并且所有条目都将此类作为定义者引用。 |
static_values_off | uint | 从文件开始到 static 字段的初始值列表的偏移量;如果没有初始值(并且所有 static 字段都将使用 0 或 null 初始化),则为 0 。此偏移量应在 data 区段中,并且那里的数据应采用下面 "encoded_array_item " 指定的格式。数组的大小不得大于由此类声明的 static 字段的数量,并且元素与相应的 field_list 中声明的 static 字段顺序相同。每个数组元素的类型必须与其对应的字段的声明类型匹配。如果数组中的元素少于 static 字段的数量,则剩余字段将使用类型适当的 0 或 null 初始化。 |
call_site_id_item
出现在 call_site_ids 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
call_site_off | uint | 从文件开始到调用站点定义的偏移量。偏移量应在 data 区段中,并且那里的数据应采用下面 "call_site_item" 指定的格式。 |
call_site_item
出现在 data 区段中
对齐方式:无(字节对齐)
call_site_item 是一个 encoded_array_item,其元素对应于提供给引导链接器方法的参数。前三个参数是
- 表示引导链接器方法的方法句柄 (VALUE_METHOD_HANDLE)。
- 引导链接器应解析的方法名称 (VALUE_STRING)。
- 与要解析的方法名称类型相对应的方法类型 (VALUE_METHOD_TYPE)。
任何其他参数都是传递给引导链接器方法的常量值。这些参数按顺序传递,不进行任何类型转换。
表示引导链接器方法的方法句柄必须具有返回类型 java.lang.invoke.CallSite
。前三个参数类型是
java.lang.invoke.Lookup
java.lang.String
java.lang.invoke.MethodType
任何其他参数的参数类型由其常量值确定。
method_handle_item
出现在 method_handles 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
method_handle_type | ushort | 方法句柄的类型;请参阅下表 |
unused | ushort | (未使用) |
field_or_method_id | ushort | 字段或方法 ID,具体取决于方法句柄类型是访问器还是方法调用器 |
unused | ushort | (未使用) |
方法句柄类型代码
常量 | 值 | 说明 |
---|---|---|
METHOD_HANDLE_TYPE_STATIC_PUT | 0x00 | 方法句柄是静态字段设置器(访问器) |
METHOD_HANDLE_TYPE_STATIC_GET | 0x01 | 方法句柄是静态字段获取器(访问器) |
METHOD_HANDLE_TYPE_INSTANCE_PUT | 0x02 | 方法句柄是实例字段设置器(访问器) |
METHOD_HANDLE_TYPE_INSTANCE_GET | 0x03 | 方法句柄是实例字段获取器(访问器) |
METHOD_HANDLE_TYPE_INVOKE_STATIC | 0x04 | 方法句柄是静态方法调用器 |
METHOD_HANDLE_TYPE_INVOKE_INSTANCE | 0x05 | 方法句柄是实例方法调用器 |
METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR | 0x06 | 方法句柄是构造函数方法调用器 |
METHOD_HANDLE_TYPE_INVOKE_DIRECT | 0x07 | 方法句柄是直接方法调用器 |
METHOD_HANDLE_TYPE_INVOKE_INTERFACE | 0x08 | 方法句柄是接口方法调用器 |
class_data_item
从 class_def_item 引用
出现在 data 区段中
对齐方式:无(字节对齐)
名称 | 格式 | 说明 |
---|---|---|
static_fields_size | uleb128 | 此条目中定义的静态字段的数量 |
instance_fields_size | uleb128 | 此条目中定义的实例字段的数量 |
direct_methods_size | uleb128 | 此条目中定义的直接方法的数量 |
virtual_methods_size | uleb128 | 此条目中定义的虚方法的数量 |
static_fields | encoded_field[static_fields_size] | 定义的静态字段,表示为编码元素的序列。字段必须按 field_idx 升序排序。 |
instance_fields | encoded_field[instance_fields_size] | 定义的实例字段,表示为编码元素的序列。字段必须按 field_idx 升序排序。 |
direct_methods | encoded_method[direct_methods_size] | 定义的直接方法(static 、private 或构造函数中的任何一种),表示为编码元素的序列。方法必须按 method_idx 升序排序。 |
virtual_methods | encoded_method[virtual_methods_size] | 定义的虚方法(不是 static 、private 或构造函数的方法),表示为编码元素的序列。此列表不应包含继承的方法,除非被此条目表示的类覆盖。方法必须按 method_idx 升序排序。虚方法的 method_idx 不得与任何直接方法相同。 |
注意: 所有元素的 field_id
和 method_id
实例都必须引用相同的定义类。
encoded_field 格式
名称 | 格式 | 说明 |
---|---|---|
field_idx_diff | uleb128 | 此字段的标识在 field_ids 列表中的索引(包括名称和描述符),表示为与列表中前一个元素的索引的差值。列表中的第一个元素的索引直接表示。 |
access_flags | uleb128 | 字段的访问标志(public 、final 等)。有关详细信息,请参阅“access_flags 定义”。 |
encoded_method 格式
名称 | 格式 | 说明 |
---|---|---|
method_idx_diff | uleb128 | 此方法的标识在 method_ids 列表中的索引(包括名称和描述符),表示为与列表中前一个元素的索引的差值。列表中的第一个元素的索引直接表示。 |
access_flags | uleb128 | 方法的访问标志(public 、final 等)。有关详细信息,请参阅“access_flags 定义”。 |
code_off | uleb128 | 从文件开始到此方法的代码结构的偏移量;如果此方法是 abstract 或 native ,则为 0 。偏移量应指向 data 区段中的某个位置。数据的格式由下面 "code_item " 指定。 |
type_list
从 class_def_item 和 proto_id_item 引用
出现在 data 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
size | uint | 列表中条目的数量 |
list | type_item[size] | 列表的元素 |
type_item 格式
名称 | 格式 | 说明 |
---|---|---|
type_idx | ushort | type_ids 列表中的索引 |
code_item
从 encoded_method 引用
出现在 data 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
registers_size | ushort | 此代码使用的寄存器数量 |
ins_size | ushort | 此代码所属方法的传入参数的字数 |
outs_size | ushort | 此代码进行方法调用所需的传出参数空间的字数 |
tries_size | ushort | 此实例的 try_item 的数量。如果非零,则这些 tries 数组会紧跟在此实例中的 insns 之后出现。 |
debug_info_off | uint | 从文件开始到此代码的调试信息(行号 + 局部变量信息)序列的偏移量;如果根本没有信息,则为 0 。如果偏移量非零,则应指向 data 区段中的某个位置。数据的格式由下面 "debug_info_item " 指定。 |
insns_size | uint | 指令列表的大小,以 16 位代码单元为单位 |
insns | ushort[insns_size] | 字节码的实际数组。insns 数组中代码的格式由配套文档 Dalvik 字节码 指定。请注意,尽管这被定义为 ushort 数组,但有些内部结构更喜欢四字节对齐。此外,如果这恰好是一个字节序交换的文件,则交换仅在单个 ushort 实例上完成,而不是在更大的内部结构上完成。 |
padding | ushort (可选) = 0 | 两个字节的填充,使 tries 四字节对齐。仅当 tries_size 非零且 insns_size 为奇数时,此元素才存在。 |
tries | try_item[tries_size] (可选) | 指示代码中捕获异常的位置以及如何处理异常的数组。数组的元素在范围上必须是非重叠的,并且从低地址到高地址按顺序排列。仅当 tries_size 非零时,此元素才存在。 |
handlers | encoded_catch_handler_list (可选) | 表示捕获类型列表和关联处理程序地址列表的字节。每个 try_item 都有一个指向此结构的字节偏移量。仅当 tries_size 非零时,此元素才存在。 |
try_item 格式
名称 | 格式 | 说明 |
---|---|---|
start_addr | uint | 此条目覆盖的代码块的起始地址。该地址是对第一个覆盖指令开始处的 16 位代码单元的计数。 |
insn_count | ushort | 此条目覆盖的 16 位代码单元的数量。最后一个覆盖的代码单元(包括)是 start_addr + insn_count - 1 。 |
handler_off | ushort | 从关联的 encoded_catch_hander_list 开始到此条目的 encoded_catch_handler 的字节偏移量。这必须是指向 encoded_catch_handler 的开始位置的偏移量。 |
encoded_catch_handler_list 格式
名称 | 格式 | 说明 |
---|---|---|
size | uleb128 | 此列表的大小,以条目为单位 |
list | encoded_catch_handler[handlers_size] | 处理程序列表的实际列表,直接表示(不作为偏移量),并按顺序连接 |
encoded_catch_handler 格式
名称 | 格式 | 说明 |
---|---|---|
size | sleb128 | 此列表中捕获类型的数量。如果为非正数,则这是捕获类型的数量的负数,并且捕获之后会跟一个 catch-all 处理程序。例如:size 为 0 表示存在 catch-all,但没有显式类型的捕获。size 为 2 表示存在两个显式类型的捕获,并且没有 catch-all。size 为 -1 表示存在一个类型化的捕获以及一个 catch-all。 |
handlers | encoded_type_addr_pair[abs(size)] | abs(size) 编码条目的流,每种捕获类型一个,按应测试类型的顺序排列。 |
catch_all_addr | uleb128 (可选) | catch-all 处理程序的字节码地址。仅当 size 为非正数时,此元素才存在。 |
encoded_type_addr_pair 格式
名称 | 格式 | 说明 |
---|---|---|
type_idx | uleb128 | 要捕获的异常类型在 type_ids 列表中的索引 |
addr | uleb128 | 关联的异常处理程序的字节码地址 |
debug_info_item
从 code_item 引用
出现在 data 区段中
对齐方式:无(字节对齐)
每个 debug_info_item
定义一个受 DWARF3 启发的字节码状态机,该状态机在解释时会为 code_item
发射位置表和(可能)局部变量信息。该序列以可变长度头部开始(其长度取决于方法参数的数量),后跟状态机字节码,并以 DBG_END_SEQUENCE
字节结束。
状态机由五个寄存器组成。address
寄存器表示关联的 insns_item
中的指令偏移量,以 16 位代码单元为单位。address
寄存器在每个 debug_info
序列开始时从 0
开始,并且只能单调递增。line
寄存器表示应与状态机发射的下一个位置表条目关联的源行号。它在序列头部中初始化,并且可能会在正方向或负方向上更改,但绝不能小于 1
。source_file
寄存器表示行号条目引用的源文件。它被初始化为 class_def_item
中的 source_file_idx
的值。其他两个变量 prologue_end
和 epilogue_begin
是布尔标志(初始化为 false
),指示发射的下一个位置是否应被视为方法序言或方法尾声。状态机还必须跟踪每个寄存器中最后一个活动的局部变量的名称和类型,以用于 DBG_RESTART_LOCAL
代码。
头部如下:
名称 | 格式 | 说明 |
---|---|---|
line_start | uleb128 | 状态机的 line 寄存器的初始值。不表示实际位置条目。 |
parameters_size | uleb128 | 编码的参数名称的数量。每个方法参数应有一个,不包括实例方法的 this (如果有)。 |
parameter_names | uleb128p1[parameters_size] | 方法参数名称的字符串索引。编码值 NO_INDEX 表示没有可用于关联参数的名称。类型描述符和签名从方法描述符和签名中推断出来。 |
字节码值如下:
名称 | 值 | 格式 | 参数 | 说明 |
---|---|---|---|---|
DBG_END_SEQUENCE | 0x00 | (无) | 终止 code_item 的调试信息序列 |
|
DBG_ADVANCE_PC | 0x01 | uleb128 addr_diff | addr_diff :要添加到地址寄存器的量 |
在不发射位置条目的情况下前进地址寄存器 |
DBG_ADVANCE_LINE | 0x02 | sleb128 line_diff | line_diff :行寄存器要更改的量 |
在不发射位置条目的情况下前进行寄存器 |
DBG_START_LOCAL | 0x03 | uleb128 register_num uleb128p1 name_idx uleb128p1 type_idx |
register_num :将包含局部变量的寄存器name_idx :名称的字符串索引type_idx :类型的类型索引 |
在当前地址引入局部变量。name_idx 或 type_idx 都可以是 NO_INDEX ,以指示该值未知。 |
DBG_START_LOCAL_EXTENDED | 0x04 | uleb128 register_num uleb128p1 name_idx uleb128p1 type_idx uleb128p1 sig_idx |
register_num :将包含局部变量的寄存器name_idx :名称的字符串索引type_idx :类型的类型索引sig_idx :类型签名的字符串索引 |
在当前地址引入带有类型签名的局部变量。name_idx 、type_idx 或 sig_idx 中的任何一个都可以是 NO_INDEX ,以指示该值未知。(但是,如果 sig_idx 为 -1 ,则可以使用操作码 DBG_START_LOCAL 更有效地表示相同的数据。)注意: 有关处理签名的注意事项,请参阅下面“ |
DBG_END_LOCAL | 0x05 | uleb128 register_num | register_num :包含局部变量的寄存器 |
将当前活动的局部变量标记为在当前地址超出作用域 |
DBG_RESTART_LOCAL | 0x06 | uleb128 register_num | register_num :要重启的寄存器 |
在当前地址重新引入局部变量。名称和类型与指定寄存器中最后一个活动的局部变量相同。 |
DBG_SET_PROLOGUE_END | 0x07 | (无) | 设置 prologue_end 状态机寄存器,指示添加的下一个位置条目应被视为方法序言的结尾(方法断点的适当位置)。prologue_end 寄存器由任何特殊操作码(>= 0x0a )清除。 |
|
DBG_SET_EPILOGUE_BEGIN | 0x08 | (无) | 设置 epilogue_begin 状态机寄存器,指示添加的下一个位置条目应被视为方法尾声的开始(在方法退出之前暂停执行的适当位置)。epilogue_begin 寄存器由任何特殊操作码(>= 0x0a )清除。 |
|
DBG_SET_FILE | 0x09 | uleb128p1 name_idx | name_idx :源文件名的字符串索引;如果未知,则为 NO_INDEX |
指示所有后续行号条目都引用此源文件名,而不是 code_item 中指定的默认名称 |
特殊操作码 | 0x0a…0xff | (无) | 前进 line 和 address 寄存器,发射位置条目,并清除 prologue_end 和 epilogue_begin 。有关描述,请参见下文。 |
特殊操作码
值介于 0x0a
和 0xff
(包括)之间的操作码会少量移动 line
和 address
寄存器,然后发射新的位置表条目。增量的公式如下:
DBG_FIRST_SPECIAL = 0x0a // the smallest special opcode DBG_LINE_BASE = -4 // the smallest line number increment DBG_LINE_RANGE = 15 // the number of line increments represented adjusted_opcode = opcode - DBG_FIRST_SPECIAL line += DBG_LINE_BASE + (adjusted_opcode % DBG_LINE_RANGE) address += (adjusted_opcode / DBG_LINE_RANGE)
annotations_directory_item
从 class_def_item 引用
出现在 data 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
class_annotations_off | uint | 从文件开始到直接在类上进行的注解的偏移量;如果该类没有直接注解,则为 0 。如果偏移量非零,则应指向 data 区段中的某个位置。数据的格式由下面 "annotation_set_item " 指定。 |
fields_size | uint | 由此条目标注的字段计数 |
annotated_methods_size | uint | 由此条目标注的方法计数 |
annotated_parameters_size | uint | 由此条目标注的方法参数列表计数 |
field_annotations | field_annotation[fields_size] (可选) | 关联的字段注解列表。列表的元素必须按 field_idx 升序排序。 |
method_annotations | method_annotation[methods_size] (可选) | 关联方法注解列表。列表中的元素必须按 method_idx 升序排序。 |
parameter_annotations | parameter_annotation[parameters_size] (可选) | 关联方法参数注解列表。列表中的元素必须按 method_idx 升序排序。 |
注意: 所有元素的 field_id
和 method_id
实例都必须引用相同的定义类。
field_annotation 格式
名称 | 格式 | 说明 |
---|---|---|
field_idx | uint | field_ids 列表的索引,用于标识被注解的字段 |
annotations_off | uint | 从文件起始位置到字段注解列表的偏移量。此偏移量应指向 data 部分中的某个位置。数据格式由下文的“annotation_set_item ”指定。 |
method_annotation 格式
名称 | 格式 | 说明 |
---|---|---|
method_idx | uint | method_ids 列表的索引,用于标识被注解的方法 |
annotations_off | uint | 从文件起始位置到方法注解列表的偏移量。此偏移量应指向 data 部分中的某个位置。数据格式由下文的“annotation_set_item ”指定。 |
parameter_annotation 格式
名称 | 格式 | 说明 |
---|---|---|
method_idx | uint | method_ids 列表的索引,用于标识被注解参数的方法 |
annotations_off | uint | 从文件起始位置到方法参数注解列表的偏移量。此偏移量应指向 data 部分中的某个位置。数据格式由下文的“annotation_set_ref_list ”指定。 |
annotation_set_ref_list
引用自 parameter_annotations_item
出现在 data 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
size | uint | 列表中条目的数量 |
list | annotation_set_ref_item[size] | 列表的元素 |
annotation_set_ref_item 格式
名称 | 格式 | 说明 |
---|---|---|
annotations_off | uint | 从文件起始位置到被引用注解集的偏移量;如果此元素没有注解,则为 0 。如果偏移量非零,则应指向 data 部分中的某个位置。数据格式由下文的“annotation_set_item ”指定。 |
annotation_set_item
引用自 annotations_directory_item、field_annotations_item、method_annotations_item 和 annotation_set_ref_item
出现在 data 区段中
对齐:4 字节
名称 | 格式 | 说明 |
---|---|---|
size | uint | 集合的大小,以条目数计 |
条目 | annotation_off_item[size] | 集合的元素。元素必须按 type_idx 升序排序。 |
annotation_off_item 格式
名称 | 格式 | 说明 |
---|---|---|
annotation_off | uint | 从文件起始位置到注解的偏移量。此偏移量应指向 data 部分中的某个位置,并且该位置的数据格式由下文的“annotation_item ”指定。 |
annotation_item
引用自 annotation_set_item
出现在 data 区段中
对齐方式:无(字节对齐)
名称 | 格式 | 说明 |
---|---|---|
visibility | ubyte | 此注解的预期可见性(见下文) |
annotation | encoded_annotation | 编码的注解内容,格式由上文“encoded_value 编码”下的“encoded_annotation 格式”描述。 |
可见性值
以下是 annotation_item
中 visibility
字段的选项
名称 | 值 | 说明 |
---|---|---|
VISIBILITY_BUILD | 0x00 | 仅在构建时(例如,在编译其他代码期间)可见 |
VISIBILITY_RUNTIME | 0x01 | 旨在运行时可见 |
VISIBILITY_SYSTEM | 0x02 | 旨在运行时可见,但仅对底层系统可见(而不是对常规用户代码可见) |
encoded_array_item
从 class_def_item 引用
出现在 data 区段中
对齐方式:无(字节对齐)
名称 | 格式 | 说明 |
---|---|---|
value | encoded_array | 表示编码数组值的字节,格式由上文“encoded_value 编码”下的“encoded_array 格式”指定。 |
hiddenapi_class_data_item
本节包含每个类使用的受限接口的数据。
注意: 隐藏 API 功能是在 Android 10.0 中引入的,仅适用于启动类路径中类的 DEX 文件。下文描述的标志列表可能会在 Android 的未来版本中扩展。如需了解详情,请参阅关于非 SDK 接口限制的说明。
名称 | 格式 | 说明 |
---|---|---|
size | uint | 节的总大小 |
offsets | uint[] | 由 class_idx 索引的偏移量数组。索引 class_idx 处的零数组条目表示此 class_idx 没有数据,或者所有隐藏 API 标志均为零。否则,数组条目为非零,并包含从此节开头到此 class_idx 的隐藏 API 标志数组的偏移量。 |
flags | uleb128[] | 每个类的隐藏 API 标志的串联数组。可能的标志值在下表中描述。标志的编码顺序与字段和方法在类数据中的编码顺序相同。 |
限制标志类型
名称 | 值 | 说明 |
---|---|---|
whitelist | 0 | 可以自由使用并作为正式文档化的 Android 框架 软件包索引 的一部分受到支持的接口。 |
greylist | 1 | 无论应用的 目标 API 级别 如何,都可以使用的非 SDK 接口。 |
blacklist | 2 | 无论应用的 目标 API 级别 如何,都不能使用的非 SDK 接口。访问其中一个接口会导致运行时错误。 |
greylist‑max‑o | 3 | 除非受到限制,否则可用于 Android 8.x 及更低版本的非 SDK 接口。 |
greylist‑max‑p | 4 | 除非受到限制,否则可用于 Android 9.x 的非 SDK 接口。 |
greylist‑max‑q | 5 | 除非受到限制,否则可用于 Android 10.x 的非 SDK 接口。 |
greylist‑max‑r | 6 | 除非受到限制,否则可用于 Android 11.x 的非 SDK 接口。 |
系统注解
系统注解用于表示关于类(以及方法和字段)的各种反射信息。客户端(非系统)代码通常只能间接访问此信息。
系统注解在 .dex
文件中表示为可见性设置为 VISIBILITY_SYSTEM
的注解。
dalvik.annotation.AnnotationDefault
出现在注解接口的方法中
每个希望指示默认绑定的注解接口都附加有 AnnotationDefault
注解。
名称 | 格式 | 说明 |
---|---|---|
value | Annotation | 此注解的默认绑定,表示为此类型的注解。注解无需包含注解定义的所有名称;缺少的名称只是没有默认值。 |
dalvik.annotation.EnclosingClass
出现在类中
每个被定义为另一个类的成员的类,或者匿名但未在方法体中定义的类(例如,合成内部类),都附加有 EnclosingClass
注解。每个具有此注解的类还必须具有 InnerClass
注解。此外,一个类不能同时具有 EnclosingClass
注解和 EnclosingMethod
注解。
名称 | 格式 | 说明 |
---|---|---|
value | Class | 最接近词法作用域此类的类 |
dalvik.annotation.EnclosingMethod
出现在类中
每个在方法体内部定义的类都附加有 EnclosingMethod
注解。每个具有此注解的类还必须具有 InnerClass
注解。此外,一个类不能同时具有 EnclosingClass
注解和 EnclosingMethod
注解。
名称 | 格式 | 说明 |
---|---|---|
value | Method | 最接近词法作用域此类的方法 |
dalvik.annotation.InnerClass
出现在类中
每个在另一个类的定义的词法作用域内定义的类都附加有 InnerClass
注解。任何具有此注解的类还必须具有EnclosingClass
注解或EnclosingMethod
注解。
名称 | 格式 | 说明 |
---|---|---|
name | String | 此类最初声明的简单名称(不包括任何软件包前缀)。如果此类是匿名的,则名称为 null 。 |
accessFlags | int | 类的最初声明的访问标志(由于源语言和目标虚拟机执行模型之间的不匹配,可能与有效标志不同) |
dalvik.annotation.MemberClasses
出现在类中
每个声明成员类的类都附加有 MemberClasses
注解。(成员类是具有名称的直接内部类。)
名称 | 格式 | 说明 |
---|---|---|
value | Class[] | 成员类的数组 |
dalvik.annotation.MethodParameters
出现在方法中
注意: 此注解是在 Android 7.1 之后添加的。在早期 Android 版本中,其存在将被忽略。
MethodParameters
注解是可选的,可用于提供参数元数据,例如参数名称和修饰符。
当运行时不需要参数元数据时,可以安全地从方法或构造函数中省略该注解。可以使用 java.lang.reflect.Parameter.isNamePresent()
检查参数是否存在元数据,如果信息不存在,则关联的反射方法(例如 java.lang.reflect.Parameter.getName()
)将在运行时回退到默认行为。
当包含参数元数据时,编译器必须包含为生成的类(例如枚举)的信息,因为参数元数据包括参数是否为合成或强制的。
MethodParameters
注解仅描述单个方法参数。因此,为了代码大小和运行时效率,对于没有参数的构造函数和方法,编译器可以完全省略该注解。
下文记录的数组的大小必须与和方法关联的 method_id_item
dex 结构的大小相同,否则将在运行时抛出 java.lang.reflect.MalformedParametersException
。
即:method_id_item.proto_idx
-> proto_id_item.parameters_off
-> type_list.size
必须与 names().length
和 accessFlags().length
相同。
由于 MethodParameters
描述了所有形式方法参数,甚至包括那些在源代码中未显式或隐式声明的参数,因此数组的大小可能与仅基于源代码中声明的显式参数的 Signature 或其他元数据信息不同。MethodParameters
也不包含关于实际方法签名中不存在的类型注解接收器参数的任何信息。
名称 | 格式 | 说明 |
---|---|---|
names | String[] | 关联方法的形式参数名称。数组不得为 null,但如果没有形式参数,则必须为空。如果具有该索引的形式参数没有名称,则数组中的值必须为 null。 如果参数名称字符串为空或包含“.”、“;”、“[”或“/”,则将在运行时抛出 java.lang.reflect.MalformedParametersException 。 |
accessFlags | int[] | 关联方法的形式参数的访问标志。数组不得为 null,但如果没有形式参数,则必须为空。 该值是具有以下值的位掩码
java.lang.reflect.MalformedParametersException 。 |
dalvik.annotation.Signature
出现在类、字段和方法中
Signature
注解附加到每个类、字段或方法,这些类、字段或方法是根据比 type_id_item
可表示的更复杂的类型定义的。.dex
格式未定义签名的格式;它仅仅意味着能够表示源语言实现该语言语义成功所需的任何签名。因此,虚拟机实现通常不解析(或验证)签名。签名只是传递给更高级别的 API 和工具(例如调试器)。因此,签名的任何使用都应编写为不假设仅接收有效的签名,而是显式地保护自身免受遇到语法无效签名的可能性。
由于签名字符串往往具有大量重复内容,因此 Signature
注解被定义为字符串数组,其中重复的元素自然地引用相同的基础数据,并且签名被视为数组中所有字符串的串联。关于如何将签名分解为单独的字符串没有规则;这完全取决于生成 .dex
文件的工具。
名称 | 格式 | 说明 |
---|---|---|
value | String[] | 此类或成员的签名,作为要连接在一起的字符串数组 |
dalvik.annotation.Throws
出现在方法中
每个声明为抛出一个或多个异常类型的方法都附加有 Throws
注解。
名称 | 格式 | 说明 |
---|---|---|
value | Class[] | 抛出的异常类型数组 |