arm汇编中的标号
标号只能由 a~z, A~Z, 0~9, “.”, _等字符组成。当标号为 0~9 的数字时为局部标号。
局部标号可以重复出现,使用方法如下:
标号 f: 在引用的地方向前的标号
标号 b: 在引用的地方向后的标号
局部标号代表它所在的地址,因此也可以当作变量或者函数来使用。
arm汇编中的分段:
(1) .section 伪操作
用户可以通过 .section 伪操作来自定义一个段,格式如下:
.section section_name [, "flags"[, %type[,flag_specific_arguments]]]
每一个段以段名为开始, 以下一个段名或者文件结尾为结束。这些段都有缺省的标志(flags) ,连接器可
以识别这些标志。 (与 armasm 中的 AREA 相同)。
下面是 ELF 格式允许的段标志
<标志> 含义
a 允许段
w 可写段
x 执行段
(2)汇编系统预定义的段名
.text @代码段
.data @初始化数据段
.bss @未初始化数据段
.sdata @
.sbss @
需要注意的是,源程序中.bss 段应该在.text 之前。
定义入口点:
汇编程序的缺省入口是 start 标号,用户也可以在连接脚本文件中用 ENTRY 标志指明其它入口点。
汇编程序中的宏定义
格式如下:
.macro 宏名 参数名列表 @伪指令.macro 定义一个宏
宏体
.endm @.endm 表示宏结束
如果宏使用参数,那么在宏体中使用该参数时添加前缀“\”。宏定义时的参数还可以使用默认值。
可以使用.exitm 伪指令来退出宏。
【例】宏定义
.macro SHIFTLEFT a, b
.if \b < 0
MOV \a, \a, ASR #-\b
.exitm
.endif
MOV \a, \a, LSL #\b
.endm
汇编程序中的常数:
(1)十进制数以非 0 数字开头,如:123 和 9876;
(2)二进制数以 0b 开头,其中字母也可以为大写;
(3)八进制数以 0 开始,如:0456,0123;
(4)十六进制数以 0x 开头,如:0xabcd,0X123f;
(5)字符串常量需要用引号括起来,中间也可以使用转义字符,如: “You are welcome!\n”;
(6)当前地址以“.”表示,在汇编程序中可以使用这个符号代表当前指令的地址;
(7) 表达式:在汇编程序中的表达式可以使用常数或者数值, “-”表示取负数, “~”表示取补,“<>”表示不相
等,其他的符号如:+、 -、 *、 /、 %、 <、 <<、 >、 >>、 |、 &、 ^、 !、 ==、 >=、 <=、 &&、 || 跟 C 语言中的用法相
似。
Linux 下 ARM 汇编的常用伪操作:
在前面已经提到过了一些为操作,还有下面一些为操作:
数据定义伪操作: .byte, .short, .long, .quad, .float, .string/.asciz/.ascii。
重复定义伪操作: .rept
赋值语句: .equ/.set 。
函数的定义:
对齐方式伪操作: .align;
源文件结束伪操作: .end;
.include 伪操作;
if 伪操作;
.global/ .globl 伪操作 ;
.type 伪操作 ;
列表控制语句;
下面是 ARM 特有的伪操 :
.reg , .unreq , .code , .thumb , .thumb_func , .thumb_set, .ltorg , .pool
数据定义伪操作:
1) .byte:单字节定义,如: .byte 1,2,0b01,0x34,072,'s' ;
2) .short:定义双字节数据,如:.short 0x1234,60000 ;
3) .long:定义 4 字节数据,如:.long 0x12345678,23876565
4) .quad:定义 8 字节,如:.quad 0x1234567890abcd
5) .float:定义浮点数,如:.float 0f-314159265358979323846264338327\
6) .string/.asciz/.ascii:定义多个字符串,如:
.string "abcd", "efgh", "hello!"
.asciz "qwer", "sun", "world!"
.ascii "welcome\0"
需要注意的是: .ascii 伪操作定义的字符串需要自行添加结尾字符'\0'。
7) .rept:重复定义伪操作, 格式如下:
.rept 重复次数
数据定义
.endr @结束重复定义
例如:
.rept 3
.byte 0x23
.endr
8) .equ/.set: 赋值语句, 格式如下:
.equ(.set) 变量名,表达式
例如:
.equ abc 3 @让 abc=3
函数的定义伪操作:
1) 函数的定义
格式如下:
函数名:
函数体
返回语句
一般的,函数如果需要在其他文件中调用, 需要用到.global 伪操作将函数声明为全局函数。 为了不至于
在其他程序在调用某个 C 函数时发生混乱,对寄存器的使用我们需要遵循 APCS 准则。函数编译器将处理为
函数代码为一段.global 的汇编码。
2) 函数的编写应当遵循如下规则:
a1-a4 寄存器(参数、结果或暂存寄存器, r0 到 r3 的同义字)以及浮点寄存器 f0-f3(如果存在浮点协处
理器)在函数中是不必保存的;
如果函数返回一个不大于一个字大小的值,则在函数结束时应该把这个值送到 r0 中;
如果函数返回一个浮点数,则在函数结束时把它放入浮点寄存器 f0 中;
如果函数的过程改动了 sp(堆栈指针, r13)、 fp(框架指针, r11)、 sl(堆栈限制, r10)、 lr(连接寄存
器, r14)、 v1-v8(变量寄存器, r4 到 r11)和 f4-f7,那么函数结束时这些寄存器应当被恢复为包含在进入函
数时它所持有的值。
.align .end .include .incbin 伪操作
.align:用来指定数据的对齐方式,格式如下:
.align [absexpr1, absexpr2]
以某种对齐方式,在未使用的存储区域填充值. 第一个值表示对齐方式,4, 8,16 或 32。第二个表达式值表示填充的值。
.end:表明源文件的结束。
.include:可以将指定的文件在使用.include 的地方展开,一般是头文件。
例如:
.include “myarmasm.h”
.incbin 伪操作可以将原封不动的一个二进制文件编译到当前文件中,使用方法如下:
.incbin "file"[,skip[,count]]
skip 表明是从文件开始跳过 skip 个字节开始读取文件,count 是读取的字数。
if 伪操作
根据一个表达式的值来决定是否要编译下面的代码, 用.endif 伪操作来表示条件判断的结束, 中间可以使用.else 来决定.if 的条件不满足的情况下应该编译哪一部分代码。
.if 有多个变种:
.ifdef symbol @判断 symbol 是否定义。
.ifc string1,string2 @字符串 string1 和 string2 是否相等,字符串可以用单引号括起来。
.ifeq expression @判断 expression 的值是否为 0。
.ifeqs string1,string2 @判断 string1 和 string2 是否相等,字符 串必须用双引号括起来。
.ifge expression @判断 expression 的值是否大于等于 0。
.ifgt absolute expression @判断 expression 的值是否大于 0。
.ifle expression @判断 expression 的值是否小于等于 0。
.iflt absolute expression @判断 expression 的值是否小于 0。
.ifnc string1,string2 @判断 string1 和 string2 是否不相等, 其用法跟.ifc 恰好相反。
.ifndef symbol, .ifnotdef symbol @判断是否没有定义 symbol, 跟.ifdef 恰好相反。
.ifne expression @如果 expression 的值不是 0, 那么编译器将编译下面的代码。
.ifnes string1,string2 @如果字符串 string1 和 string2 不相 等, 那么编译器将编译下面的代码。
.global .type .title .list
.global/ .globl :用来定义一个全局的符号
格式如下:
.global symbol 或者 .globl symbol
.type:用来指定一个符号的类型是函数类型或者是对象类型, 对象类型一般是数据。
格式如下:
.type 符号, 类型描述
【例】
.section .text
.type asmfunc, @function
.globl asmfunc
asmfunc:
mov pc, lr
.title:用来指定汇编列表的标题,例如:
.title “my program”
.list:用来输出列表文件.
ARM 特有的伪操作:
1) .reg: 用来给寄存器赋予别名,格式如下:
别名 .req 寄存器名
2) .unreq: 用来取消一个寄存器的别名,格式如下:
.unreq 寄存器别名
注意被取消的别名必须事先定义过,否则编译器就会报错,这个伪操作也可以用来取消系统预制的
别名, 例如 r0, 但如果没有必要的话不推荐那样做。
3) .code 伪操作用来选择 ARM 或者 Thumb 指令集,格式如下:
.code 表达式
如果表达式的值为 16 则表明下面的指令为 Thumb 指令,如果表达式的值为 32 则表明下面的指令为
ARM 指令。
4) .thumb 伪操作等同于.code 16, 表明使用 Thumb 指令, 类似的.arm 等同于.code 32。
5) .force_thumb 伪操作用来强制目标处理器选择 thumb 的指令集而不管处理器是否支持。
6) .thumb_func 伪操作用来指明一个函数是 thumb 指令集的函数。
7) .thumb_set 伪操作的作用类似于.set, 可以用来给一个标志起一个别名, 比.set 功能增加的。
一点是可以把一个标志标记为 thumb 函数的入口, 这点功能等同于 .thumb_func。
8) .ltorg 用于声明一个数据缓冲池(literal pool)的开始,它可以分配很大的空间。
9) .pool 的作用等同 .ltorg
10) .space
分配 number_of_bytes 字节的数据空间,并填充其值为 fill_byte,若未指定该值, 缺省填充 0。(与
armasm 中的 SPACE 功能相同)
11) .word
插入一个 32-bit 的数据队列。(与 armasm 中的 DCD 功能相同)
可以使用.word 把标识符作为常量使用
12) .hword
插入一个 16-bit 的数据队列。(与 armasm 中的 DCW 相同)
GNU ARM 汇编特殊字符和语法:
代码行中的注释符号: ‘@’
整行注释符号: ‘#’
语句分离符号: ‘;’
直接操作数前缀: ‘#’ 或 ‘$’