免责声明:本文提供的所有内容仅供学习、分享与交流,不保证内容的正确性,亦不对内容可能带来的风险担责。如若转载,请署名以及注明原始出处。本文仅代表个人的立场和观点,并不代表任何公司或组织。
本系列的其他文章
LiteOS Studio零成本学习ARM汇编 一 环境搭建
LiteOS Studio零成本学习ARM汇编 二 初识谢娘
基于LiteOS Studio & Qemu零成本学习ARM 汇编——0x03 再上层楼
下面的全部操作仅适用Window 10 64bits 环境。
本文会基于数组求和、计算字符串长度两个例子来熟悉更多的汇编器指令assembler directives。
0x01 数组求和
汇编程序代码如下,该段代码引入了2个汇编器指令.byte、.align:
.text .global _startentry: b _start @ 跳过数据段arr: .byte 10, 20, 25 @ 只读的bytes数组eoa: @ 紧接着bytes数组的地址,eoa: End Of Array + 1 .align_start: ldr r0, =eoa @ r0 = &eoa 取eoa标签所在的地址 ldr r1, =arr @ r1 = &arr 取arr标签,字节数组的首地址 mov r3, #0 @ r3 = 0 赋值0loop: ldrb r2, [r1], #1 @ r2 = *r1++ 取数组的值,然后指向下一个数组元素 add r3, r2, r3 @ r3 += r2 对获取的值进行求和 cmp r1, r0 @ 判断是否 (r1 != r2) bne loop @ goto loopstop: b stop
.byte指令:
指令.byte {,} … 定义一个或多个Byte,并为之分配连续的内存空间。参数可以是整形字面量,二进制(0b|0B前缀)、八进制(0前缀)、十进制,或十六进制(0x|0X前缀),参数也能是字符常量,单引号括起来字符常量。
.align 指令:
ARM指令占用32位,4 bytes的内存,第一个byte的地址必须是4的倍数。为了满足这个,.align指令可以填充字节来满足下一个字节的地址是4的倍数。当字节、半字节插入的时候,会需要使用.align对齐。如下图所示,汇编代码的第7行使用了.align指令,对应的反汇编文件第15行填充空字节。否则_start 指令从00000007处开始,不满足4的倍数。填充一个字节后,从00000008处开始,下一指令_start的第一个字节满足4的倍数。
![c59c11871291b31b0dda47734b4ce5d1.png](https://img-blog.csdnimg.cn/img_convert/c59c11871291b31b0dda47734b4ce5d1.png)
.align指令
GDB 单步调试:
从https://gitee.com/huawei_liteos_studio/arm_assembly/tree/master/SumAnArray检出示例代码工程,使用LiteOS Studio打开工程。编译、开始调测,我们打开build目录下面的asm文件,并且分栏展示,F10单步调试,单步两次后,界面如下:
执行完毕第9行汇编代码,从反汇编文件中可以看到eoa的地址即0x00000007赋值给r0寄存器。
执行完毕第10行汇编代码,从反汇编文件中可以看到arr字节数组的首地址即0x00000004赋值给r1寄存器。
![24bfd6c35602df4bb1a317d1808fe382.png](https://img-blog.csdnimg.cn/img_convert/24bfd6c35602df4bb1a317d1808fe382.png)
F10 GDB 单步调试
执行完毕第12行汇编代码,从r1寄存器的值0x00000004位数组的第一个地址,该地址对应的数值为0x0a即10,放入寄存器r2。 r1寄存器指向下一个字节数组的地址。
执行完毕第13行汇编代码,依次对数组元素进行求和,放入r3寄存器。
![7d7f667157d8eadb7bf12d931817ce9b.png](https://img-blog.csdnimg.cn/img_convert/7d7f667157d8eadb7bf12d931817ce9b.png)
F10 GDB调试数组求和
执行完毕第14行汇编代码,比较r0、r1寄存器,r0为数组末尾元素的下一个地址。r1指向字节数组的元素地址,读取数组元素后,会指向下一个地址,读完所有的数组元素后r0、r1元素都会指向0x00000007这个地址。当cmp指令条件成立,跳出loop循环。依次读取的数组元素为0x0a、0x14、0x19,即 10、20、25,从反汇编文件11-12行中可以看出来。
0x02 计算字符串长度
汇编程序代码如下,该段代码引入了2个汇编器指令.asciz、.equ:
.text .global _start b _startstr: .asciz "Hello World" .equ nul, 0 .align_start: ldr r0, =str @ r0 = &str取字符串的地址 mov r1, #0loop: ldrb r2, [r0], #1 @ r2 = *(r0++) add r1, r1, #1 @ r1 += 1 cmp r2, #nul @ if (r1 != nul) bne loop @ goto loop sub r1, r1, #1 @ r1 -= 1stop: b stop
.asciz指令
.asciz指令接收字符串字面量作为参数,字符串字面量是双引号包含的字符序列,这些会被汇编到连续的内存地址。汇编器自动在字符串的末尾插入nul字符(0)。.ascii指令和.asciiz指令一样,区别是.ascii指令不会自动在末尾增加nul。
.equ指令
汇编器维护一个符号表,来映射标签名称和内存地址。当汇编器遇到标签定义的时候,会在符号表中建立一个入口。当汇编器遇到标签引用时,替换标签为从符号表中对应的地址。
使用汇编器指令.equ,会在符号表中增加一个条目,映射名称和值,这个值不需要一定是地址。当汇编器遇到这些名称的时候,替换为相应的值。这些标签名称、equ定义的名称被称为符号名称。
指令的语义如下:
.equ name, expression
注意:.byte、.equ指令本身不分配内存,他们只在符号表中创建条目。
GDB 单步调试:
从https://gitee.com/huawei_liteos_studio/arm_assembly/tree/master/StringLength检出示例代码工程,使用LiteOS Studio打开工程。编译、开始调测,我们打开build目录下面的asm文件,并且分栏展示,F10单步调试,单步调试如之前操作。String字符串在内存的存储可以通过反汇编进行查看,界面如下:
![1cc57bd896f09b96a53591693e32218b.png](https://img-blog.csdnimg.cn/img_convert/1cc57bd896f09b96a53591693e32218b.png)
字符串存储
0x03 参考资料
http://www.bravegnu.org/gnu-eprog/asm-directives.html