ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Smali 语法解析,面试必备

2021-09-06 23:02:43  阅读:274  来源: 互联网

标签:lang Ljava String 必备 method v0 面试 寄存器 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.icode9.com/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中高级、架构师对你更是如鱼得水,赶快领取吧!

标签:lang,Ljava,String,必备,method,v0,面试,寄存器,Smali
来源: https://blog.csdn.net/m0_61418142/article/details/120147089

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有