Android smali语法学习

来源:互联网 发布:欧洲自由行多少钱知乎 编辑:程序博客网 时间:2024/04/30 07:45

dalvik字节码有两种类型,原始类型和引用类型。对象和数组是引用类型,其它都是原始类型。

V void,只能用于返回值类型
Z boolean
B byte
S short
C char
I int
J long(64位)
F float
D double(64位)
对象以Lpackage/name/ObjectName;的形式表示。
前面的L表示这是一个对象类型,package/name/是该对象所在的包,ObjectName是对象的名字,“;”表示对象名称的结束。
相当于java中的package.name.ObjectName。例如:Ljava/lang/String;相当于java.lang.String

数组的表示形式
? [I——表示一个整型一维数组,相当于java中的int[]。
? 对于多维数组,只要增加[就行了。[[I相当于int[][],[[[I相当于int[][][]。注意每一维的最多255个。
? 对象数组的表示:[Ljava/lang/String;表示一个String对象数组。
方法
表示形式:Lpackage/name/ObjectName;->MethodName(III)Z
? Lpackage/name/ObjectName;表示类型,MethodName是方法名。III为参数(在此是3个整型参数),Z是返回类型(bool型)。
? 方法的参数是一个接一个的,中间没有隔开。
一个更复杂的例子:
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;
即包名,字段名和各字段类型。
寄存器
在dalvik字节码中,寄存器都是32位的,能够支持任何类型。64位类型(Long和Double型)用2个寄存器表示。
有两种方式指定一个方法中有多少寄存器是可用的。.registers指令指定了方法中寄存器的总数。.locals指令表明了方法中非参寄存器的数量。
方法的传参
? 当一个方法被调用的时候,方法的参数被置于最后N个寄存器中。如果一个方法有2个参数,5个寄存器(v0-v4),那么参数将置于最后2个寄存器——v3和v4。
? 非静态方法中的第一个参数总是调用该方法的对象。
例如,非静态方法LMyObject;->callMe(II)V有2个整型参数,另外还有一个隐含的LMyObject;参数,所以总共有3个参数。
假如在该方法中指定了5个寄存器(v0-v4),以.registers方式指定5个或以.locals方式指定2个(即2个local寄存器+3个参数寄存器)。
当该方法被调用的时候,调用该方法的对象(即this引用)存放在v2中,第一个整型参数存放在v3中,第二个整型参数存放在v4中。
对于静态方法除了没有隐含的this参数外其它都一样。
寄存器的命名方式
有两种方式——V命名方式和P命名方式。P命名方式中的第一个寄存器就是方法中的第一个参数寄存器。
在下表中我们用这两种命名方式来表示上一个例子中有5个寄存器和3个参数的方法。
v0 第一个local register
v1 第二个local register
v2 p0 第一个parameter register
v3 p1 第二个parameter register
v4 p2 第三个parameter register
你可以用任何一种方式来引用参数寄存器——他们没有任何差别。
注意:baksmali默认对参数寄存器使用P命名方式。如果想使用V命名方式,可以使用-pl—no-parameter-registers选项。
使用P命名方式是为了防止以后如果要在方法中增加寄存器,需要对参数寄存器重新进行编号的缺点。
Long/Double值
Long和double类型是64位的,需要2个寄存器(切记切记)。
例如,对于非静态方法LMyObject;->MyMethod(IJZ)V,参数分别是LMyObject;,int,long,bool。故该方法需要5个寄存器来存储参数。

p0 this
p1 I
p2,p3 J
p4 Z

补充:
# static fields 定义静态变量的标记
# instance fields 定义实例变量的标记
# direct methods 定义静态方法的标记
# virtual methods 定义非静态方法的标记
构造函数的返回类型为V,名字为<init>。

if-eq p1, v0, :cond_8 表示如果p1和v0相等,则执行cond_8的流程:
:cond_8
invoke-direct {p0}, Lcom/paul/test/a;->d()V
调用com.paul.test.a的d()方法
if-ne p1, v0, :cond_b 表示不相等则执行cond_b的流程:
:cond_b
const/4 v0, 0x0
invoke-virtual {p0, v0}, Lcom/paul/test/a;->setPressed(Z)V
invoke-super {p0, p1, p2}, Landroid/view/View;->onKeyUp(ILandroid/view/KeyEvent;)Z
move-result v0

大概意思就是调用com.paul.test.a的setPressed方法,然后再调用父类View的onKeyUp方法,最后 return v0

举两个例子:

sget-object v5, Lcom/google/youngandroid/runtime;->Lit227:Lgnu/mapping/SimpleSymbol;
获取com.google.youngandroid.runtime中的Lit227字段存入v5寄存器,相当于
gnu.mapping.SimpleSymbol localVariable = com.google.youngandroid.runtime.Lit227;

sput-object v0, Lcom/google/youngandroid/runtime;->Lit78:Lkawa/lang/SyntaxTemplate;
Likewise, this is setting the value of a static field. i.e.
设置com.google.youngandroid.runtime.Lit78的值为v0寄存器中的kawa.lang.SyntaxTemplate类型变量的值。相当于com.google.youngandroid.runtime.Lit78 = kawa.lang.SyntaxTemplate localVariable;
剩下的比较简单你应该能明白了。

下面我们来看一个简单的例子:

 

view sourceprint?
01.publicclass MainActivity extendsActivity {
02. 
03.@Override
04.protectedvoid onCreate(Bundle savedInstanceState) {
05.super.onCreate(savedInstanceState);
06.setContentView(R.layout.activity_main);
07.TextView textview1 = (TextView)this.findViewById(R.id.text);
08.textview1.setText(R.string.hello_world);
09.}
10.}
生成的smali:

 

 

view sourceprint?
01..classpublic Lcom/example/hello/MainActivity;
02..superLandroid/app/Activity;
03..source"MainActivity.java"
04. 
05. 
06.# direct methods
07..methodpublic constructor <init>()V
08..locals0
09. 
10..prologue
11..line14
12.invoke-direct {p0}, Landroid/app/Activity;-><init>()V
13. 
14.return-void
15..end method
16. 
17. 
18.# virtual methods
19..methodprotected onCreate(Landroid/os/Bundle;)V
20..locals2
21..parameter"savedInstanceState"
22. 
23..prologue
24..line18
25.invoke-super{p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
26. 
27..line19
28.const/high36 v1,0x7f03
29. 
30.invoke-virtual {p0, v1}, Lcom/example/hello/MainActivity;->setContentView(I)V
31. 
32..line20
33.const/high36 v1,0x7f0a
34. 
35.invoke-virtual {p0, v1}, Lcom/example/hello/MainActivity;->findViewById(I)Landroid/view/View;
36. 
37.move-result-object v0
38. 
39.check-cast v0, Landroid/widget/TextView;
40. 
41..line21
42..local v0, textview1:Landroid/widget/TextView;
43.constv1, 0x7f070002
44. 
45.invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(I)V
46. 
47..line22
48.return-void
49..end method

我们重点来看下onCreate()这个的方法。

 

.method protected onCreate(Landroid/os/Bundle;)V
.locals 2
.parameter "savedInstanceState"

.prologue
.line 18
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V // 这是调用super也就是Activity.onCreate(Bundle) p0:this,p1:Bundle

.line 19
const/high36 v1, 0x7f03 //把0x7f03这个常量赋值给v1 ,0x7f03就是R.layout.activity_main

invoke-virtual {p0, v1}, Lcom/example/hello/MainActivity;->setContentView(I)V // 调用MainActivity.setContentView(v1),p0:this,v1就是R.layout.activity_main

.line 20
const/high36 v1, 0x7f0a // 把0x7f0a 这个常量赋值给v1 ,0x7f0a 就是R.id.text

invoke-virtual {p0, v1}, Lcom/example/hello/MainActivity;->findViewById(I)Landroid/view/View; // 调用View = MainActivity.findViewById(v1),p0:this,v1:R.id.text

move-result-object v0 // 把上一个指令的输出结果move到v0,就是给textview1赋值

check-cast v0, Landroid/widget/TextView;// 把v0强转成TextView

.line 21
.local v0, textview1:Landroid/widget/TextView;
const v1, 0x7f070002 // 把int常量0x7f070002放到v1中,就是读出来R.string.hello_world

invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(I)V //调用TextView.setText(v1) TextView是v0,参数是v1

.line 22
return-void
.end method

还是挺简单的一个例子。跟class字节码非常向。我们看一下同样的代码在class中和smali中有啥不一样的。

 

view sourceprint?
1.publicvoid multiply(inta, int b){
2.intresult = a * b;
3.System.out.println(result);
4.}
smali表示:

 

# virtual methods
.method public multiply(II)V
.locals 2
.parameter "a"
.parameter "b"

.prologue
.line 26
mul-int v0, p1, p2 // p1和p2相乘,结果放到v0中

.line 27
.local v0, result:I
sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; // 获取System.out的引用,放到v1中

invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V // 调用PrintStream.println(v0)

.line 28
return-void
.end method

class表示:

public void multiply(int, int);
Code:
Stack=2, Locals=4, Args_size=3
0: iload_1 // 本地变量1压栈
1: iload_2 // 本地变量2压栈
2: imul // 栈中取上面的2个做乘法
3: istore_3 // 结果出栈,放到本地变量3中
4: getstatic#5; //Field java/lang/System.out:Ljava/io/PrintStream; // System.out压栈
7: iload_3 // 本地变量3压栈
8: invokevirtual#6; //Method java/io/PrintStream.println:(I)V // 调用PrintStream.println
11: return
LineNumberTable:
line 6: 0
line 7: 4
line 8: 11

上面可以很清晰的看出来,实现同样的功能,基于寄存器要比基于栈的指令数要少。int result = a * b;在smali中就一条指令,而在class中要4条指令。

关于smali所有指令的格式和含义可以参考:http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html
0 0
原创粉丝点击