ARM汇编语言实现peek()_ARM汇编之访问C语言结构体数据

前言

本文的写作目的在于装逼,没有要产生实际价值的意思。

前几天在做编译器的项目,有一个项目团队成员一直在问我ARM汇编能不能读C语言的结构体。我心想,我这生成ARM汇编的代码是用C++写的呀,又不是用汇编写的,干嘛问我这个?我跟他说,能啊。还非要我出demo。

作为一个《汇编语言程序设计》课程绩点只有5.0的渣渣,我的内心有一万只草泥马奔腾而过。好吧,菜鸡的我没说什么,默默地用二十分钟准备了相关代码、AXD Debugger仿真截图,并且标注了C语言定义的全局变量在内存中的位置、寄存器的数值。

虽然并没有什么卵用,但是能拿来装逼鸭hhhh。基于这个问题,我稍微地做了一下进一步的讨论。

先来点开胃小菜

我们先从这个简单的结构体开始,做一个实验,验证ARM能访问C语言结构体的数据。

struct 

先写C语言的代码,目的是为了声明并初始化一个结构体变量,让ARM汇编进行访问。

#include 

然后再来写配套的ARM汇编。

 area armFunc, code, readonly
 code32
 export myFunc

myFunc ldr r4,[r0]     ;读取t.a,其值存储在R4寄存器
       ldr r5,[r0,#4]  ;读取t.b,其值存储在R5寄存器
       ldr r6,[r0,#8]  ;读取t.c,其值存储在R6寄存器
       b . ;程序卡在这里,方便我们停下来观察

       end

AXD Debugger仿真结果如下:

0f04c4a6ab2d19f5fd6cd616faf85fe6.png
AXD Debugger仿真结果(1)

我们对这个仿真结果做一个简单的分析。一些关键的地方我已经在图上用不同颜色的框标记出来。程序从main函数开始执行,并且为结构体t的三个成员分别进行初始化。它们的值按照小端序依次存放在内存中(红框、绿框和紫框)。

再调用myFunc函数,将结构体t的指针(0x000083FC)作为参数传递过去(根据ATPCS规范可知)。在myFunc里面利用基址变址寻址将结构体t的数据读取出来。黄框中寄存器的数值分别为三个成员的初值。

万一,类型长度不是4字节呢?

在上面给的汇编代码里,地址偏移量总是为4的倍数。这是因为int类型的数据长度正好是4字节。那么,如果用的数据类型的长度不是4字节呢?

还是举一个结构体的例子。

struct 

在这个例子里,包含了char、short和int三种类型的成员。它们代表着字节数据、半字数据和字数据(字长32位)。相应地,C语言代码和对应的ARM汇编代码也要作出改动。

C语言代码如下:

struct 

ARM汇编代码如下:

 area armFunc, code, readonly
 code32
 export myFunc

myFunc ldr r4,[r0]     ;读取t.a,其值存储在R4寄存器
       ldrs r5,[r0,#4]  ;读取t.b,其值存储在R5寄存器
       ldrh r6,[r0,#6]  ;读取t.c,其值存储在R6寄存器
       b . ;程序卡在这里,方便我们停下来观察

       end

相应的仿真结果:

5883b35882a9fb809b54d7c32f62cae7.png
AXD Debugger仿真结果(2)

程序仍然能正确读取结构体的数据。但是观察绿框可以发现:成员b占用了两个字节的空间。这是由于编译器在分配myData2各成员在内存中的位置时,以半字为单位进行字节对齐。不同的编译器在编译C语言代码时,对于字节对齐的规定有可能不同,需要具体情况具体分析。回到这个仿真结果来:因此在后续访问成员c的指令中,地址偏移量为4+2=6,而不是4+1=5。在编写结构体访问的相关指令时,要注意结构体各成员在内存空间中的分布

算法举例:二叉树的递归先序遍历

在访问结构体数据的基础上,我们还可以用ARM汇编做链表的访问、二叉树的遍历等。这里稍微举个栗子:二叉树的先序遍历,采用递归实现。

先写一段C语言代码,主要用来准备用来先序遍历的二叉树,以及编写输出的相关代码。

#include

先序遍历的算法主体则放在了ARM汇编里。

 area armFunc, code, readonly
 code32
 export preVisit
 import myPrint

preVisit STMFD sp!,{r4,fp,lr}
         ;调用myPrint函数输出data成员
         LDR r4,[r0]
         STMFD sp!,{r0}
         MOV r0,r4
         BL myPrint
         LDMFD sp!,{r0}
         ;递归调用preVisit遍历左子树
         LDR r4,[r0,#4]
         CMP r4,#0
         BEQ right
         STMFD sp!,{r0}
         MOV r0,r4
         BL preVisit
         LDMFD sp!,{r0}
         ;递归调用preVisit遍历右子树
right    LDR r4,[r0,#8]
         CMP r4,#0
         BEQ funcEnd
         STMFD sp!,{r0}
         MOV r0,r4
         BL preVisit
         LDMFD sp!,{r0}
funcEnd  LDMFD sp!,{r4,fp,pc}

         end

如果没有猜错的话,输出结果应该是ABCDEFG:

12a14ce48e8b0e3c6aa5dc06c1984170.png
二叉树先序遍历结果

后记

在写作本文的过程,发现自己在不知不觉中巩固了所学知识,提高了自己的知识迁移能力。比如说用ARM汇编写二叉树的先序遍历,既巩固了自己的数据结构知识,又能将数据结构的实现迁移到ARM汇编上。

参考文献

《汇编语言程序设计——基于ARM体系结构》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值