本文档介绍了 .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 编码值均由 1-5 个字节组成,共同表示一个 32 位的值。每个字节均已设置其最高有效位(序列中的最后一个字节除外,其最高有效位已清除)。每个字节的剩余 7 位均为载荷,即第一个字节中有 7 个最低有效位,第二个字节中也是 7 个,依此类推。对于有符号 LEB128 (sleb128
),序列中最后一个字节的最高有效载荷位会进行符号扩展,以生成最终值。在无符号情况 (uleb128
) 下,任何未明确表示的位都会被解译为 0
。
双字节 LEB128 值的按位图 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
第一个字节 | 第二个字节 | ||||||||||||||
1 |
bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 | 0 |
bit13 | bit12 | bit11 | bit10 | bit9 | bit8 | bit7 |
变体 uleb128p1
用于表示一个有符号值,其表示法是编码为 uleb128
的值加 1。这使得 -1
的编码(或被视为无符号值 0xffffffff
)成为一个单字节(但没有任何其他负数),并且该编码在下面这些情况下非常实用:所表示的数值必须为非负数或 -1
(或 0xffffffff
);不允许任何其他负值(或不太可能需要使用较大的无符号值)。
以下是这类格式的一些示例:
编码序列 | As sleb128 |
As 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[] | 静态链接文件中使用的数据。本文档尚未指定本区段中数据的格式。此区段在未链接文件中为空,而运行时实现可能会在适当的情况下使用这些数据。 |
位字段、字符串和常量定义
DEX_FILE_MAGIC
嵌在 header_item 中
常量数组/字符串 DEX_FILE_MAGIC
是字节列表,这类字节必须出现在 .dex
文件的开头,以便系统将其原样识别。该值会特意包含一个换行符("\n"
或 0x0a
)和空字节("\0"
或 0x00
),以便协助检测某些形式的损坏问题。该值还可以将格式版本号编码为 3 个十进制数字;随着格式的演变,预计该值会单调递增。
ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x39 0x00 } = "dex\n039\0"
注意:Android 9.0 版本中新增了对 039
版格式的支持,其中引入了两个新字节码 const-method-handle
和 const-method-type
。(字节码集合的总结表中介绍了这些字节码。)在 Android 10 中,版本 039
扩展了 DEX 文件格式,以包含仅适用于启动类路径上的 DEX 文件的隐藏 API 信息。
注意:Android 8.0 版本中新增了对 038
版格式的支持。038
版本中添加了新字节码(invoke-polymorphic
和 invoke-custom
)和用于方法句柄的数据。
注意:Android 7.0 版本中新增了对 037
版格式的支持。在 037
版本之前,大多数 Android 版本都使用过 035
版格式。035
版与 037
版之间的唯一区别是,是否添加默认方法以及是否调整 invoke
。
注意:至少有两种早期版本的格式已在广泛提供的公开软件版本中使用。例如,009
版本已用于 M3 版 Android 平台(2007 年 11 月至 12 月),013
版本已用于 M5 版 Android 平台(2008 年 2 月至 3 月)。在有些方面,这些早期版本的格式与本文档中所述的版本存在很大差异。
ENDIAN_CONSTANT 和 REVERSE_ENDIAN_CONSTANT
嵌在 header_item 中
常量 ENDIAN_CONSTANT
用于表示在文件中发现的字节序。虽然标准的 .dex
格式采用小端字节序,但具体实现可能会选择执行字节交换。如果实现遇到其 endian_tag
为 ENDIAN_CONSTANT
(而非 REVERSE_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
中使用。
修改了 UTF-8 编码
考虑到继续对旧版提供支持,.dex
格式会采用按实际标准修改后的 UTF-8 形式(下文称为 MUTF-8)对其字符串数据进行编码。除以下几点以外,此形式与标准 UTF-8 形式是完全相同的:
- 仅使用单字节、双字节和三字节编码。
U+10000
…U+10ffff
范围中的码位被编码为代理对,其中每个码位均表示为一个三字节编码值。- 码位
U+0000
采用双字节格式形式编码。 - 纯 null 字节(值
0
)表示字符串的末尾,这是标准的 C 语言解释。
上述前两项可以概括为:MUTF-8 是用于 UTF-16 的编码格式,而不是用于 Unicode 字符的更直接的编码格式。
后两项说明,MUTF-8 既可在字符串中包含代码点 U+0000
,同时仍可将其作为 C 样式的 Null 终止字符串进行操作。
不过,特殊编码 U+0000
意味着,与一般 UTF-8 不同的是,针对一对 MUTF-8 字符串调用标准 C 函数 strcmp()
的结果并不一定表示不相等字符串的带正确符号的比较结果。当需要考虑排序问题(而不仅仅是相等问题)时,用于比较 MUTF-8 字符串的最简单方法是对其字符进行逐个解码,然后比较解码后的值。(不过,也许会有更智能的实现方式。)
如需详细了解字符编码,请参阅 Unicode 标准。实际上,MUTF-8 比 UTF-8 本身更接近相对而言不太为人熟知的编码 CESU-8。
encoded_value 编码
嵌在 annotation_element 和 encoded_array_item 中
encoded_value
是(几乎)任意层次结构数据的编码片。这种编码非常精简,易于解析。
名称 | 格式 | 说明 |
---|---|---|
(value_arg << 5) | value_type | ubyte | 一种字节,用于表示紧跟后面的 value 及高 3 位中可选澄清参数的类型。请参阅下文,了解各种 value 定义。在大多数情况下,value_arg 会以字节为单位将紧跟后面的 value 的长度编码为 (size - 1) ;例如,0 表示该值需要 1 个字节;7 表示该值需要 8 个字节;不过,也存在下述例外情况。
|
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 | 此注解中 name-value 映射的数量 |
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 |
S | short |
C | char |
I | int |
J | long |
F | float |
D | double |
Lfully/qualified/Name; | 类 fully.qualified.Name |
[descriptor | descriptor 的数组,可递归地用于“数组的数组”,但维数不能超过 255。
|
项和相关结构
本部分包含可能在 .dex
文件中出现的各个顶级项的定义。
header_item
出现在 header 区段中
对齐:4 个字节
名称 | 格式 | 说明 |
---|---|---|
magic | ubyte[8] = DEX_FILE_MAGIC | 魔法值。如需了解详情,请参阅上文中“DEX_FILE_MAGIC ”下的讨论。
|
checksum | uint | 文件剩余内容(除 magic 和此字段之外的所有内容)的 adler32 校验和;用于检测文件损坏情况
|
signature | ubyte[20] | 文件剩余内容(除 magic 、checksum 和此字段之外的所有内容)的 SHA-1 签名(哈希);用于对文件进行唯一标识
|
file_size | uint | 整个文件(包括标头)的大小,以字节为单位 |
header_size | uint = 0x70 | 头文件(整个区段)的大小,以字节为单位。此项允许至少一定程度的向后/向前兼容性,而不会使格式失效。 |
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 | 从文件开头到映射项的偏移量。该偏移量(必须为非零值)应该是到 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 | data 区段的大小(以字节为单位)。该数值必须是 sizeof(uint) 的偶数倍。 |
data_off | uint | 从文件开头到 data 区段开头的偏移量。
|
map_list
出现在 data 区段中
引用自 header_item
对齐:4 个字节
这是文件全部内容(依序显示)的列表。该列表包含关于 header_item
的一些冗余信息,但这些冗余信息存在的目的是提供一种用于遍历整个文件的简单方式。指定类型在映射中最多只能出现一次,但除了格式其余部分所隐含的限制条件(例如,header
区段必须先显示,随后是 string_ids
区段等等)之外,对于类型可以什么顺序显示,则没有任何限制。此外,映射条目必须按初始偏移量进行排序,不得重叠。
名称 | 格式 | 说明 |
---|---|---|
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)编码”,了解有关该数据格式的详情和讨论。
注意:将 Unicode 常规地编码为 UTF-16 时,字符串可以包含(编码形式的)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 字段的数量,且 static 字段所对应的元素应采用相对应的 field_list 中所声明的相同顺序。每个数组元素的类型均必须与其相应字段的声明类型相匹配。
如果该数组中的元素比 static 字段中的少,剩余字段将使用适当类型的 0 或 null 进行初始化。
|
call_site_id_item
出现在 call_site_ids 区段中
对齐:4 个字节
名称 | 格式 | 说明 |
---|---|---|
call_site_off | uint | 从文件开头到调用点定义的偏移量。该偏移量应该位于数据区段,且其中的数据应采用下文中“call_site_item”指定的格式。 |
call_site_item
出现在 data 区段中
对齐:无(字节对齐)
call_site_item 是一个 encoded_array_item,其元素与提供给引导程序链接器方法的参数相对应。前 3 个参数为:
- 表示引导程序链接器方法的方法句柄 (VALUE_METHOD_HANDLE)。
- 引导程序链接器应解析的方法名称 (VALUE_STRING)。
- 与要解析的方法名称类型相对应的方法类型 (VALUE_METHOD_TYPE)。
任何附加参数均为传递给引导程序链接器方法的常量值。这些参数会按顺序传递,而不进行任何类型转换。
表示引导程序链接器方法的方法句柄必须具有返回类型 java.lang.invoke.CallSite
。前 3 个参数类型为:
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 数量。如果此值为非零值,则这些项会显示为 insns 数组(正好位于此实例中 tries 的后面)。
|
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 | 此列表中捕获类型的数量。如果为非正数,则该值是捕获类型数量的负数,捕获数量后跟一个“全部捕获”处理程序。例如,size 为 0 表示捕获类型为“全部捕获”,而没有明确类型的捕获。size 为 2 表示有两个明确类型的捕获,但没有“全部捕获”类型的捕获。size 为 -1 表示有一个明确类型的捕获和一个“全部捕获”类型的捕获。
|
handlers | encoded_type_addr_pair[abs(size)] | abs(size) 编码项的流(一种捕获类型对应一项),按照对类型进行测试时应遵循的顺序排列。
|
catch_all_addr | uleb128(可选) | “全部捕获”处理程序的字节码地址。只有当 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
字节结尾。
该状态机由 5 个寄存器组成。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 | 该集合的大小(以条目数表示) |
entries | 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 | 包含可以使用的非 SDK 接口的列表(无论应用的目标 API 级别是什么)。 |
blacklist | 2 | 包含不能使用的非 SDK 接口的列表(无论应用的目标 API 级别是什么)。访问其中任何一个接口都会导致运行时错误。 |
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
说明了所有形式化方法参数,甚至包括源代码中没有明确声明或隐式声明的参数,因此数组的大小可能不同于签名或其他元数据信息(仅基于源代码中声明的显式参数)的大小。此外,MethodParameters
也不包含任何关于实际方法签名中不存在的类型注释接收器参数的信息。
名称 | 格式 | 说明 |
---|---|---|
names | String[] | 关联方法的形式参数的名称。数组不得为空;但如果没有任何形式参数,该项必须为空。如果使用该索引的形式参数没有名称,数组中的值必须为 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[] | 抛出的异常类型的数组 |