在开发中,可读的编程语言要编译成二进制的字节码格式才能被机器识别。在HarmonyOS Next开发中,arkts会编译成方舟字节码。方舟字节码长什么样呢?我们以一个demo编译出的abc文件:
二进制就是长这样,怎么去理解呢?就需要理解方舟字节码的文件格式。
字节码文件布局
上面图中看到有个可以认识的几个字母PANDA,什么意思呢?先了解下字节码文件布局。
字节码文件起始于Header结构。文件中的所有结构均可以从Header出发,直接或间接地访问到。字节码文件中结构的引用方式包括偏移量和索引。偏移量是一个32位长度的值,表示当前结构的起始位置在字节码文件中相对于文件头的距离,从0开始计算。索引是一个16位长度的值,表示当前结构在索引区域中的位置。
Header
先来看看Header结构:
名称 | 格式 | 说明 |
---|---|---|
magic | uint8_t[8] | 文件头魔数,值必须是’P’ ‘A’ ‘N’ ‘D’ ‘A’ ‘\0’ ‘\0’ ‘\0’。 |
checksum | uint32_t | 字节码文件除文件头魔数和本校验字段之外的内容的adler32校验和。 |
version | uint8_t[4] | 字节码文件的版本号 (Version) 。 |
file_size | uint32_t | 字节码文件的大小,以字节为单位。 |
foreign_off | uint32_t | 一个偏移量,指向外部区域。外部区域中仅包含类型为ForeignClass或ForeignMethod的元素。foreign_off指向该区域的第一个元素。 |
foreign_size | uint32_t | 外部区域的大小,以字节为单位。 |
num_classes | uint32_t | ClassIndex结构中元素的数量,即文件中定义的Class的数量。 |
class_idx_off | uint32_t | 一个偏移量,指向ClassIndex。 |
num_lnps | uint32_t | LineNumberProgramIndex结构中元素的数量,即文件中定义的Line number program的数量。 |
lnp_idx_off | uint32_t | 一个偏移量,指向LineNumberProgramIndex。 |
reserved | uint32_t | 方舟字节码文件内部使用的保留字段。 |
reserved | uint32_t | 方舟字节码文件内部使用的保留字段。 |
num_index_regions | uint32_t | IndexSection结构中元素的数量,即文件中IndexHeader的数量。 |
index_section_off | uint32_t | 一个偏移量,指向IndexSection。 |
- magic:最开始的0x50、0x41、0x4e、0x44、0x41对应的就是PANDA的ASCII码,接下来是三个0
- checksum:四个字节的校验位0x5d、0xe8、0xde、0xdf
- version:自个字节的版本号0x0C、0x00、0x06、0x00
- …
Version
字节码版本号由4个部分组成,格式为:主版本号.次版本号.特性版本号.编译版本号。
名称 | 格式 | 说明 |
---|---|---|
主版本号 | uint8_t | 标识整体架构调整引入的字节码文件格式变更。 |
次版本号 | uint8_t | 标识局部架构调整或者重大特性调整引入的字节码文件格式变更。 |
特性版本号 | uint8_t | 标识中小特性引入的字节码文件格式变更。 |
编译版本号 | uint8_t | 标识缺陷修复引入的字节码文件格式变更。 |
ForeignClass
描述字节码文件中的外部类。外部类在其他文件中声明,并在当前字节码文件中被引用。
名称 | 格式 | 说明 |
---|---|---|
name | String | 外部类的名称,命名遵循TypeDescriptor语法。 |
ForeignMethod
描述字节码文件中的外部方法。外部方法在其他文件中声明,并在当前字节码文件中被引用。
名称 | 格式 | 说明 |
---|---|---|
class_idx | uint16_t | 一个指向该方法所从属的类的索引,指向一个在ClassRegionIndex中的位置,该位置的值是一个指向Class或ForeignClass的偏移量。 |
reserved | uint16_t | 方舟字节码文件内部使用的保留字段。 |
name_off | uint32_t | 一个偏移量,指向字符串,表示方法名称。 |
index_data | uleb128 | 方法的MethodIndexData数据。 |
通过ForeignMethod的偏移量,可以找到适当的IndexHeader以解析class_idx。 |
此外还有Class、Class、ClassAccessFlag、ClassTag、Field、FieldTag、Method、MethodIndexData、MethodTag、Code、TryBlock、CatchBlock、Annotation、AnnotationElementTag、AnnotationElement、Value formats、LineNumberProgramIndex、DebugInfo、IndexSection、IndexHeader、ClassRegionIndex、Type、MethodStringLiteralRegionIndex、LiteralArray、Literal,这里不一一介绍。
接下来介绍下字节码文件数据类型。
字节码文件数据类型
整型
名称 | 说明 |
---|---|
uint8_t | 8-bit无符号整数。 |
uint16_t | 16-bit无符号整数,采用小端字节序。 |
uint32_t | 32-bit无符号整数,采用小端字节序。 |
uleb128 | leb128编码的无符号整数。 |
sleb128 | leb128编码的有符号整数。 |
字符串
名称 | 格式 | 说明 |
---|---|---|
utf16_length | uleb128 | 值为len << 1 | is_ascii,其中len是字符串在UTF-16编码中的大小,is_ascii标记该字符串是否仅包含ASCII字符,可能的值是0或1。 |
data | uint8_t[] | 以’\0’结尾的MUTF-8编码字符序列。 |
TaggedValue
名称 | 格式 | 说明 |
---|---|---|
tag | uint8_t | 表示数据种类的标记。 |
data | uint8_t[] | 根据不同的标记,data是不同类型的数据或者为空。 |
字节码文件中所有的多字节值均采用小端字节序。