Dalvik系列01 - 字节码格式
一般设计
- 机器模型和调用约定是按照真实的物理机的体系结构和C风格的调用约定设计的:
- 机器模型是基于寄存器的,在字节码创建完成后,栈桢就是固定的。每个栈桢包含了一组寄存器(由该栈桢的方法决定),栈桢也包含了一些附加数据用于方法的执行,比如程序计数器(PC)、包含该方法的dex文件等。
- 对于基本类型(比如整型和浮点型),寄存器是32位的。64位的数据采用相邻的寄存器对表示,寄存器对没有对齐要求。
- 对于对象引用类型,寄存器有足够的位宽来保存对象引用。
- 从值的角度说,(Object)null==(int)0
- 方法的N个调用参数按照顺序位于栈帧的最后N个寄存器,宽的参数占用2个寄存器。对于非静态成员方法,this是第一个参数。
- 指令流的存储单元是16位的。有些指令中某些位是被忽略的,这些位必须为0。
- 指令不会无条件的将操作数限制为特定类型。例如,没有指明类型的32位寄存器move指令,并不会限制于整型或者浮点型。
- 有单独的,可枚举的,经过索引的常量池用于存储字符串,类型,字段和方法。
- 字面量数据包括在指令中。
- 实际上,一个方法需要超过16个寄存器并不常见,但是需要8个以上的寄存器是相当常见的。因此许多指令将寻址范围限制为前16个寄存器。在合理的情况下,指令可以将寻址范围扩大到前256个寄存器。除此之外,有些指令的寄存器寻址范围更大,包括一对 catch-all move指令,寻址范围可以达到v0-v65535。当有些指令需要的数据不在寻址范围内时,就需要将将数据从原有的寄存器放到比较低的寄存器中,当操作完成后,将结果在放回高寄存器中去。
- 存在一些用于保持可变长度数据载荷的“伪指令”,这些数据被常规指令所引用(比如:fill-array-data)。在正常的指令执行过程中不会遇到这些指令。除此之外,有些指令必须位于偶数的字节码偏移(实际上由于字节码的存储单元是以2字节为单位的,所以实际上是四字节对齐)。为了满足这些指令的要求,dex生成工具必须插入一个nop指令作为一个"空格",否则这类指令将无法对齐。最后,虽然不是必须的,大多数工具会在函数的末尾插入这些指令,否则 it would likely be the case that additional instructions would be needed to branch around them.
- 当被安装到一个正在运行的系统时,有些指令可能会被修改,改变它们的格式,被称作“安装时静态链接优化".这可以使得代码执行更快。
- 人类语法与助记符
- 先目标后源的指令参数顺序
-
有些操作码(opcode)有一个用于消除歧义的后缀,指明了它操作的数据类型:
- 32-bit的数据类型没有标记
- 64-bit的数据类型有”-wide"标记
- 类型相关的操作码(opcode)带有其类型的后缀或者一个缩写,包括:-boolean -byte -char -short -int -long -float -double -object -string -class -void.
- 有些操作码有一个用于消除歧义的后缀,用于区分有相同语义,但是指令布局或者选项不同的指令。这些后缀和指令采用一个斜杠'/'进行分割。(用于消除人类模糊)
- 例如指令"move-wide/from16 vAA, vBBBB"
- move是基本的操作码(opcode),指明了基本操作(传送一个寄存器的值)
- wide是一个名字后缀,指明指令操作一个宽(64bit)的数据
- from16是操作码后缀,指明操作码有一个16位的寄存器作为源
- vAA是目标寄存器(通过操作暗示;再次提及:目标参数在第一个),必须在v0-v255
- vBBBB是源寄存器,需要在v0-v65535
- 请参考<Dalvik系列02 - 指令格式>,获取各种指令格式的详细信息(在"Op & Format一列")
- 参考dex文件格式文档获取字节码在整个文件中的位置
字节码汇总
参考链接:https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html
浙公网安备 33010602011771号