Smali 语法解析,面试必备

2021/9/6 23:40:11

本文主要是介绍Smali 语法解析,面试必备,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

我们首先看一下生成的 Hello.smali 文件内容:

.class public LHello;
.super Ljava/lang/Object;
.source "Hello.java"

# static fields
.field private static HELLO_WORLD:Ljava/lang/String;

# direct methods
.method static constructor <clinit>()V
    .registers 1

    .prologue
    .line 3
    const-string v0, "Hello World!"

    sput-object v0, LHello;->HELLO_WORLD:Ljava/lang/String;

    return-void
.end method

.method public constructor <init>()V
    .registers 1

    .prologue
    .line 1
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method

.method public static main([Ljava/lang/String;)V
    .registers 3

    .prologue
    .line 6
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

    sget-object v1, LHello;->HELLO_WORLD:Ljava/lang/String;

    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    .line 7
    return-void
.end method 

文件头

首先看一下文件头部分:

.class public LHello;       // 类名
.super Ljava/lang/Object;   // 父类名
.source "Hello.java"        // 源文件名称 

.class 后面是 访问修饰符和当前类,这里类名用 LHello 表示。那么这个 L 代表什么呢?其实之前的 Class 文件中也出现过这种表示方法,JVM 的字节码指令和 Dalvik 的字节码指令有很多地方都是类似的。Java 中分为基本类型和引用类型,DalviK 对这两种类型分别有不同的描述方法。对于基本类型和 Void 类型,都是用一个大写字母表示。对于引用类型,使用字母 L 加上对象类型的全限定名来表示。具体规则如下表所示:

Java 类型类型描述符
charC
byteB
shortS
intI
longJ
floatF
doubleD
booleanZ
voidV
对象L
数组[

基本类型的表示很简单,int 用 I 表示即可。对象的表示,如上图中父类 Object 的表示方法 Ljava/lang/Object;,再比如 String 类型,就用 Ljava/lang/String 表示。

对于数组,DalviK 有特殊的表示方法 [ 后面跟上数组元素的类型。int[] 的表示方式就是 [I, String[] 的表示方法是 [Ljava/lang/String;。二维数组用 [[ 表示,[[Ljava/lang/String 就是指 String[][],以此类推。

字段表示

# static fields
.field private static HELLO_WORLD:Ljava/lang/String; 

smali 中的字段以 .field 开头,并有 # static field(静态字段) 或者 # instance field(实例字段) 的注释。.field 之后分别是 访问修饰符,字段名称,冒号以及字段类型描述符。这句 smali 就声明了一个 String 类型名称为 HELLO_WORLD 的私有静态字段。

方法表示

smali 中的方法以 .method 开头。Hello.smali 中包含了三个方法,clinit , initmain 方法。main 方法是我们自己编写的,而 clinitinit 方法则是 javac 编译时生成的。下面进行逐一分析:

clinit

.method static constructor <clinit>()V
    .registers 1

    .prologue
    .line 3
    const-string v0, "Hello World!"

    sput-object v0, LHello;->HELLO_WORLD:Ljava/lang/String;

    return-void
.end method 

clinit 方法会进行静态变量的初始化,静态代码块的执行等操作,该方法在类被加载的时候调用。逐行分析该方法的执行逻辑:

  • .registers 1 : 该方法需要使用的寄存器数量。之前已经提到,DalviK VM 是基于寄存器的,字节码可以使用的虚拟寄存器个数可达 65536 个,每个寄存器 32 位,64 位的数据使用相邻两个寄存器表示。最终,所有的虚拟寄存器都会被映射到真实的物理寄存器上。一般情况下,我们使用字母 v 表示局部变量使用的寄存器,使用字母 p 表示参数所使用的寄存器,且局部变量使用的寄存器排列在前,参数使用的寄存器排列在后。这里就表示 clinit 方法仅使用了一个寄存器。

  • .prologue : 表示逻辑代码的开始处

  • .line 3 : 表示 java 源文件中的行数

  • const-string v0, "Hello World!" : 将字符串 Hello World! 的引用移到寄存器 v0 中。

  • sput-object v0, LHello;->HELLO_WORLD:Ljava/lang/String; : 前缀 ssputsget 指令用于静态字段的读写操作。将寄存器 v0 存储的字符串引用赋值给 HELLO_WORLD 字段,结合上一句字节码,这里完成了静态变量 HELLO_WORLD 的赋值工作,也验证了 clinit 方法的确进行了静态变量的初始化。

  • return-void : 表示该方法无返回值

  • .end method : 表示方法执行结束

到这里,clinit 方法就执行结束了。下面分析 init 方法。

init

.method public constructor <init>()V
    .registers 1

    .prologue
    .line 1
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method 

其余各项与 clinit 方法相同,我们直接看执行的代码逻辑:

invoke-direct {p0}, Ljava/lang/Object;-><init>()V 

invoke-direct 用于调用非 static 直接方法(也就是说,本质上不可覆盖的实例方法,即 private 实例方法或构造函数)。显然,这里调用的是默认构造函数。

main

.method public static main([Ljava/lang/String;)V
    .registers 3

    .prologue
    .line 6
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

    sget-object v1, LHello;->HELLO_WORLD:Ljava/lang/String;

    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    .line 7
    return-void
.end method 

最后是 main 方法,从上述 smali 代码我们可以看到 main 方法使用了 3 个寄存器,无返回值(那是肯定的),执行的具体代码是下面三行:

sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

sget-object v1, LHello;->HELLO_WORLD:Ljava/lang/String;

# 总结

Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。**所以:贵在坚持!**

上面分享的字节跳动公司2021年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
![](https://www.www.zyiz.net/i/ll/?i=img_convert/00d748fb27f23d73a0a9395a913556d2.png)

**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

**【Android高级架构视频学习资源】**

 诸多细节。
[外链图片转存中...(img-MYLs318M-1630940389273)]

**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

**【Android高级架构视频学习资源】**

Android部分精讲视频领取学习后更加是如虎添翼!进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!


这篇关于Smali 语法解析,面试必备的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程