GNU ARM 汇编基础笔记

Posted Naisu Xu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GNU ARM 汇编基础笔记相关的知识,希望对你有一定的参考价值。

目的

汇编的核心就是使用各种指令来编码完成需求,而指令这个东西其实就是最底层二进制的机器码的基础上做了一层语义化的替换,比如用 ADD 代表数字逻辑上的加减,用 MOV 代表数据传递等等,方便程序员进行阅读和编写。不过对于现在的各种高级语言而言汇编已经不怎么方便了。现在还在用汇编的主要是底层开发中一些特定需求的实现必须用到汇编。对于嵌入式开发而言了解汇编还是有一定需求的。

汇编主要是用各种指令来完成功能的编写,不同的处理器而言其架构和使用的指令集不同,不同的编译器下具体的语法差异也蛮大,但基础的语法思路和常用的指令基本上大家差异都不大。这篇文章将以 GNU ARM 汇编 为基础进行介绍。

模拟器 VisUAL

在这里插入图片描述

VisUAL has been developed as a cross-platform tool to make learning ARM Assembly language easier. In addition to emulating a subset of the ARM UAL instruction set, it provides visualisations of key concepts unique to assembly language programming and therefore helps make programming ARM assembly more accessible.

VisUAL是一个跨平台的可视化ARM汇编模拟器,可以方便的学习和演示使用ARM汇编指令。官方主页如下:
https://salmanarif.bitbucket.io/visual/index.html

VisUAL中用户可以访问的主要是 R0 ~ R13SPLRPC 这几个寄存器,以及内存地址 0x00010000 ~ 0xFFFFFFFC 这些区域。对于ARM来说 SP 是堆栈指针、 LR 用来存放返回地址、 PC 存放正在取的指令。
在这里插入图片描述
VisUAL模拟支持了部分常用的指令指令列表可以在下面找到:
https://salmanarif.bitbucket.io/visual/supported_instructions.html
VisUAL中可以使用 Ctrl + Space 显示当前行指令的详细语法说明:
在这里插入图片描述

常用指令

每种架构下其指令可能有几十条到上百条,这里只介绍些常用的指令。

内存访问指令

指令说明
ADR取相对于PC的地址到寄存器
LDR取数据到寄存器
LDM批量取数据到寄存器
STR将寄存器上的数据保存到内存
STM批量到寄存器数据内存
PUSH数据入栈
POP数据出栈

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通用数据处理指令

指令功能
ADD加法
SUB减法
AND按位与
ORR按位或
EOR按位异或
BIC按位清零
LSL逻辑左移
LSR逻辑右移
CMP比较
CMN源数据取反后比较
TST测试
MOV移动数据
MVN源数据取反后移动

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分支和控制指令

指令功能
B跳转
BL跳转并将返回地址保存到LR寄存器中

在这里插入图片描述
在这里插入图片描述

其它指令

指令功能
DCD数据定义
END程序结束

GNU汇编语法

VisUAL只能用来演示指令,下面的一些内容在VisUAL中可能无法正常使用。

伪指令

在汇编代码中经常可以见到以 . 开头的字段,这些字段都是伪指令。伪指令只是一些标识信息。下面是一部分常见的伪指令:

伪指令描述
.arm声明接下来的程序使用ARM指令集
.thumb声明接下来的程序使用Thumb指令集
.arch声明目标架构
.global全局声明标志
.local局部声明标志
.weak弱声明相当于C语言中的__weak
.section段标记
.ascii定义一串字符串
.asciz .string定义一串字符串,自动补充结束符
数据将在连续的空间上
.byte声明一个字节变量
多个变量可以一次性声明,使用逗号分隔
变量将在连续的空间上
.hword声明一个2字节变量
.word声明一个4字节变量
.fill重复填充数据
.set .equ给符号(变量)复制
语法:.set/.equ symbol, expression
.type设置一个符号的属性值
语法:.type name , description
description取值如下:
%function 表示该符号用来表示一个函数名
%object表示该符号用来表示一个数据对象
.balign数据对齐指令
.align数据对齐指令
.macro .endm宏定义
.if .else .endif条件编译

分段

使用.section伪指令可以对程序进行分段,比如下面这样:

.section .bss  @未初始化数据段
.section .data @已初始化数据段
.section .text @可执行代码段(程序段)

当然上面的 .bss .data .text 这些都是汇编中已经定义的段,使用的时候可以省略前面的 .section 。

标号

在汇编代码中以 : 结尾的字段是标号,标号有点类似于C语言中的函数名,可以使用标号来跳转调用等。

_start 标号是一个特殊的标号,是程序的入口点,相当于C语言中的main函数。

注释

在汇编中使用 @ 符号表示注释,其后面的内容为注释内容。有点编译器中你也可以使用 /* 注释内容 */ 的方式进行注释

与C语言混合使用

从这里开始的演示均在树莓派4B中进行,理论上只要CPU是ARM架构的电脑上装个主流的Linux发行版就可以进行测试。

汇编程序中调用C程序

准备下面程序保存为 main.c 文件:

#include <stdio.h>
#include <stdlib.h> 

void main(void)
{
	printf("Hello world!\\n");
	exit(0);
}

准备下面程序保存为 start.s 文件:

.text
.global _start @ 使_start标号全局可见
_start:        @ _start标号是程序入口
	bl main    @ 跳转到main函数

在这里插入图片描述
汇编程序中调用C程序直接使用函数名就行,如果函数的传入参数不大于四个则依次把参数放入R0~R3寄存器即可(如果参数是double或是long类型的话一个参数会占用连续的两个寄存器):
在这里插入图片描述
如果函数的传入参数大于四个,那么多出来的那些参数需要放入堆栈中,可以编写C程序然后使用gcc -S编译成汇编文件查看具体的处理方式。

函数如果有返回值,返回值会放入R0寄存器(如果是double或是long类型的会放入R0和R1)。

C程序中调用汇编程序

准备下面程序保存为 fun.s 文件:

.text
.global fun        @ 使fun这个符号(函数名)外部可见
fun: 
	add r4, r0, r1 @ 将传入的两个参数相加
	mov r0, r4     @ 装载返回值
	mov pc, lr     @ 函数返回

准备下面程序保存为 main.c 文件:

#include <stdio.h>

extern int fun(int a, int b); // 声明fun函数,该函数功能在上面汇编代码中实现

void main(void)
{
	printf("%d\\n", fun(200, 33));
}

在这里插入图片描述
汇编中的符号就相当于C语言的函数名,把这个符号使用 .global 声明为全局的就可以被其它文件使用了。

C程序中内嵌汇编代码

使用 __asm 指令可以在C程序中直接内嵌汇编代码,当然这样使用的汇编代码部分有很多功能限制。

总结

相比于很多高级语言来说,汇编的语法其实很简单,更多内容可以参考下面连接:

《GNU ARM Assembler Quick Reference》
链接: https://pan.baidu.com/s/1VS7HgKtDxH0OMmkiGPZBEA 提取码: grw3

《ARM and Thumb-2 Instruction Set Quick Reference Card》
链接: https://pan.baidu.com/s/1PcSYCf7–mlEtOlqGoRnUg 提取码: ksg3

《C语言与汇编语言之间的函数调用》 - 纫秋兰以为佩

以上是关于GNU ARM 汇编基础笔记的主要内容,如果未能解决你的问题,请参考以下文章

GNU ARM汇编快速入门

GNU ARM 汇编指令

arm gnu 汇编器中的 IMPORT 替代品是啥?

ARM汇编伪指令

博文连载ARM编译器ARM汇编与ARM GNU汇编

关于 GNU ARM 汇编程序的意外警告