前面涉及的技术,其实还没有让我们触及到Android的内部原理,为了更好的掌握Android的运行机制,我们必须深入一些。
先看一张图:
那么很清楚,Dalvik虚拟机是必须了解的。
3.1 Dalvik虚拟机的特点
概述
- 体积小,内存占用小
- DEX文件格式,体积更小,速度更快
- 常量池32位索引
- 寄存器架构,拥有指令系统
- 生命周期管理、堆栈管理、线程管理、安全和异常管理、垃圾回收
- Android系统进程对应Dalvik虚拟机
Dalvik虚拟机与Java虚拟机区别
大体来说,两者的区别在:字节码,体积,架构。这个不细说,上代码。
public class Hello{
public int foo(int a, int b){
return (a+b)*(a-b);
}
public static void main(String[] argc){
Hello hello = new Hello();
System.out.println(hello.foo(5,3));
}
}
以上内容存为Hello.java。执行:
javac Hello.java
生成Hello.class文件。执行:
rem dx命令文件和后面用到的dexdump.exe都在android-sdk\build-tools中,原书说是在android-sdk\platform-tools下,自己看吧
dx --dex --output=Hello.dex Hello.class
生成Hello.dex文件。执行:
javap -c -classpath . Hello > java.log
java.log中可以看到如下代码:
public int foo(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: iload_1
4: iload_2
5: isub
6: imul
7: ireturn
使用dexdump,执行以下命令:
dexdump.exe -d Hello.dex > dalvik.log
dalvik.log中有如下代码:
Hello.foo:(II)I
0000: add-int v0, v3, v4
0002: sub-int v1, v3, v4
0004: mul-int/2addr v0, v1
0005: return v0
从上面代码可以清楚看到,java虚拟机的栈架构与dalvik虚拟机的寄存器架构之间的差异。
Dalvik虚拟机如何执行程序
上图参考自<<内核剖析>>一书。
Dalvik虚拟机JIT
- method方式:以函数或方法为单位进行编译
- trace方式:以trace为单位进行编译
3.2 Dalvik汇编语言
1.Dalvik指令格式
位描述约定:
- 每16位用空格分开
- 每个字母表示四位,从高字节开始,排列到低字节,用竖线”|”分割
- A-Z单个大写字母作为四位操作码,op表示八位操作码
- “∅”表示所有位均为0
指令格式标识约定:
- 三个字符,前两位数字,后一位字母
- 第一个数字表示指令由多少个16位的字组成
- 第二个数字表示最多使用多少个寄存器,特殊标记’r’
- 第三个字母为类型码,表示指令用到的额外数据的类型。末尾如果多出一个字母,s表示静态链接,i表示内联处理。
语法约定:
- 每条指令从操作码开始,后跟参数,参数个数不定,参数之间采用逗号分开。
- 每条指令的参数从指令第一部分开始,op位于低八位,高八位可以是一个八位参数也可以是两个四位参数,还可以为空,如果指令超过16位,则后面部分依次作为参数。
- 如果参数采用”vX”的方式表示,表明它是一个寄存器,如v0,v1等。用v不用r,避免与芯片寄存器产生冲突。
- “#+X”表示常量数字
- “+X”表示相对指令的地址偏移
- “kind@X”常量池索引值
2.DEX文件反汇编工具
主流反汇编工具BakSmali与Dedexer。可以在以下地址下载:
https://bitbucket.org/JesusFreke/smali/downloads
https://sourceforge.net/projects/dedexer/files/
下载后,分别运行一下命令:
java -jar baksmali.jar -o baksmaliout Hello.dex
java -jar ddx.jar -d ddxout Hello.dex
得到的文件分别是baksmaliout/Hello.dex与ddxout/Hello.ddx,可以比较下差异。后面将采用Smali语法格式。
这里说一下,在第一篇中曾经提到dex2jar工具,其中有许多好用的工具,好好研究。
3.Dalvik寄存器
寄存器都是32位,虚拟寄存器范围v0~v65535。
4.两种寄存器表示方法—v命名法与p命名法
p命名法方便判断寄存器是局部变量寄存器还是参数寄存器。
5.Dalvik字节码的类型、方法与字段
- 类型
- 方法
格式:
Lpackage/name/ObjectName;->MethodName(III)Z
举个具体例子:
method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
转成java应该是:
String method(int, int[][], int, String, Object[])
- 字段
格式:
Lpackage/name/ObjectName;->FieldName:Ljava/lang/String
3.3 Dalvik指令集
1.指令特点
- 目标destination到源source
- 名称后缀:-wide,-boolean,-byte,-char,-short,-int,-long,-float,-double,-object,-string,-class,-void
- 字节码后缀:/
- 每个字母4位
2.空操作指令
助记符nop,值00
3.数据操作指令
//原型: move destination, source
move vA, vB
move/from16 vAA, vBBBB
move/16 vAAAA, vBBBB
move-wide vA, vB
move-wide/from16 vAA, vBBBB
move-object vA, vB
move-object /from16 vAA, vBBBB
move-object/16 vAAAA, vBBBB
move-result vAA
move-result-wide vAA
move-result-object vAA
move-exception vAA
4.返回指令
//基础字节码:return
return-void
return vAA
return-wide vAA
return-object vAA
5.数据定义指令
//基础字节码:const
const/4 vA, #+B
const/16 vAA, #+BBBB
6.锁指令
monitor-enter vAA
monitor-exit vAA
7.实例操作指令
check-cast vAA, type@BBBB
8.数组操作指令
array-length vA, vB
9.异常指令
throw vAA
10.跳转指令
goto +AA
if-test vA, vB, +CCCC
11.比较指令
cmpkind vAA, vBB, vCC
12.字段操作指令
iinstanceop vA, vB, field@CCCC
sstaticop vAA, field@BBBB
13.方法调用指令
invoke-kind{vC,vD,vE,vF,vG}, meth@BBBB
invoke-kind/range{vCCCC .. vNNNN}, meth@BBBB
14.数据转换指令
unop vA,vB
15.数据运算指令
binop vAA, vBB, vCC
3.4 Dalvik指令集练习
编写smali文件
.class public LHelloWorld;
.super Ljava/lang/Object;
.method public static main([Ljava/lang/String;)V
.registers 4
.parameter
.prologue
nop
nop
nop
nop
const/16 v0, 0x8
const/4 v1, 0x5
const/4 v2, 0x3
move v1,v2
new-array v0,v0,[I
array-length v1,v0
new-instance v1,Ljava/lang/StringBuilder;
invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
if-nez v0, :cond_0
goto :goto_0
:cond_0
int-to-float v2,v2
add-float v2,v2,v2
cmpl-float v0, v2,v2
sget-object v0,Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "Hello World!!!!!!"
invoke-virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
:goto_0
return-void
.end method
以上代码存为HelloWorld.smali文件
编译smali文件
执行命令(注意,此系列博文都会尽量使用前文已经获取的工具,此处就是dex2jar中的工具,以后不再说明了):
d2j-smali.bat -o classes.dex HelloWorld.smali
生成classes.dex文件,压缩成HelloWorld.zip。
测试运行
运行Android模拟器执行以下命令:
adb push HelloWorld.zip /data/local
adb shell dalvikvm -cp /data/local/HelloWorld.zip HelloWorld
得到输出结果:Hello World!!!!!!