以下内容源于网络资源的学习与整理,如有侵权请告知删除。
参考博客
(2)GNU ARM 汇编基础 - wanli1024 - 博客园
(3)GNU ARM 汇编简介_niepangu的博客-CSDN博客
(4)ARM编译器(一)ARM汇编与ARM GNU汇编 | 骏的世界
汇编器用于将汇编语言文件转化为二进制的目标文件,目前常用的ARM汇编器有以下两种:
(1)ARM公司的汇编器,适合在Windows平台下使用。
(2)GNU工具链中的汇编器,适合于Linux开发平台。
不同的汇编器对汇编语言的语法要求不一样。GNU汇编器是GNU工具集的一部分,针对的是多种处理器架构,这意味着GNU汇编器的语法不同于ARM公司汇编器的语法。但是实际上两者的汇编指令基本相同(见博文ARM官方汇编指令),只是伪操作不同,因此文本主要说明ARM官方汇编器和GNU汇编器中的伪操作。
(1)格式:AREA 段名 属性1,属性2,…,属性n
(2)作用:定义一个代码段或数据段。
(3)特别说明:
属性 | 描述 |
CODE | 用于定义代码段,默认为READONLY。 |
DATA | 用于定义数据段,默认为READWRITE。 |
READONLY | 指定本段为只读,代码段默认为READONLY。 |
READWRITE | 指定本段为可读可写,数据段的默认属性为READWRITE。 |
ALIGN | 使用方式为ALIGN表达式。可执行文件的代码段和数据段默认是按字对齐的,表达式的取值范围为0~31,相应的对齐方式为2表达式次方。 |
COMMON | 该属性定义一个通用的段,不包含任何的用户代码和数据。各源文件中同名的COMMON段共享同一段存储单元。 |
(4)代码示例:
AREA Init,CODE,READONLY ;定义了一个代码段,段名为Init,属性为只读。
(1)格式:ALIGN{表达式 { ,偏移量}}
(2)作用:通过添加填充字节的方式,使当前位置满足一定的对其方式。
(3)特别说明:表达式的值用于指定对齐方式,可能的取值为2的幂,如1、2、4、8、16等。若未指定表达式,则将当前位置对齐到下一个字的位置。偏移量也为一个数字表达式,若使用该字段,则当前位置的对齐方式为:2的表达式次幂+偏移量。另外注意,在AREA中使用和单独使用ALIGN时,格式与对齐的计算方式不一样(见代码示例)。
(4)代码示例:
@例子1
AREA Init,CODE,READONLY,ALIGN=3 @指定后面的指令为8字节对齐
;....
END
@例子2
ALIGN 4 表示4字节地址对齐
(1)格式:CODE16(或CODE32 )
(2)作用:CODE16(或CODE32 )伪指令通知编译器,其后的指令序列为16位的Thumb指令(或32位的ARM指令)。
(3)特别说明:若在汇编源程序中同时包含ARM指令和Thumb指令时,可用CODE16伪指令通知编译器其后的指令序列为16位的Thumb指令,用CODE32伪指令通知编译器其后的指令序列为32位的ARM指令。因此,在使用ARM指令和Thumb指令混合编程的代码里,可用这两条伪指令进行切换,但注意它们只通知编译器其后指令的类型,并不能对处理器进行状态的切换。
(4)代码示例:
AREA Init,CODE,READONLY ;.... CODE32 @通知编译器其后的指令为32位的ARM指令 LDR R0,=NEXT+1 @将跳转地址放入寄存器R0 BX R0 @程序跳转到新的位置执行,并将处理器切换到Thumb工作状态 ;.... CODE16 @通知编译器其后的指令为16位的Thumb指令 NEXT LDR R3,=0x3FF ;.... END @程序结束
(1)格式:ENTRY
(2)作用:用于指定汇编程序的入口点。
(3)特别说明:在一个完整的汇编程序中至少要有一个ENTRY,也可以有多个(此时程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个ENTRY(可以没有)。
(4)代码示例:
AREA Init,CODE,READONLY ENTRY ;指定应用程序的入口点 ;.....
(1)格式:END
(2)作用:用于通知编译器已经到了源程序的结尾。
(3)代码示例:
AREA Init,CODE,READONLY ;...... END @指定应用程序的结尾
(1)格式:EXPORT 标号 [,WEAK]
(2)作用:声明一个全局标号,其他源文件可以使用这个标号。WEAK表示碰上其他同名标号时,其他标号优先。
(1)格式:IMPORT 标号,[,WEAK]
(2)作用:表示当前文件中引用的标号在其他源文件中。WEAK表示找不到该标号时也不报错,一般将该标号置为0,如果是B 或BL指令用到该标号,该指令置为nop。
(3)特别说明:该标号会加入到当前源文件的符号表中。
(1)格式:EXTERN 标号,[,WEAK]
(2)作用:和IMPORT一样,但如果当前文件没有引用该标号,该标号不会加入到当前源文件的符号表中。
(1)格式:INCLUDE pathname
(2)作用:将一个源文件包含到当前的源文件中。
(1)格式:name EQU expression
(2)作用:对一个常量标号赋值,等同于C语言中用#define定义一个常量
(3)特别说明:name是符号名, expression是寄存器相关或者程序相关的固定值。
(4)代码示例:
num EQU 2 ;为符号num赋值为2
(1)格式: {label} SPACE expr
(2)作用:用于分配一片连续内存单元,并用0初始化。
(3)特别说明:SPACE可用%代替;label是一个标号, 可选;expr是分配的内存字节数。
(4)代码示例:
stack SPACE 100 ;分配100个字节内存单元,并用0初始化。标号stack是这片空间的起始地址
(1)格式:{label} DCB expr {,expr}
(2)作用:用于分配段字节内存单元,并用伪操作中的expr初始化。
(3)特别说明:label是一个标号,可选;expr可以是-128~255的数值或者字符串。
(4)代码示例:
string DCB "HELLO" ;为HELLO字符串分配空间,string是这块空间的起始地址
(1)格式: {label} DCD expr, {,expr}
(2)作用:用于分配段字内存单元(分配的内存都是字对齐,DCDU并不严格字对齐),并用伪操作中的expr初始化。
(3)特别说明:label是一个标号,可选,表示这块内存单元的首地址。expr是数字表达式或程序中的标号。DCD 可用 & 代替。
(4)代码示例:
data DCD 1,2,3,4 ;分配字对齐的字单元空间,并初始化为1、2、3、4
伪操作
语法格式
作用
GBLA
GBLA Varible
声明一个全局的算术变量,并将其初始化为0
GBLL
GBLL Varible
声明一个全局的逻辑变量,并将其初始化成{FALSE}
GBLS
GBLS Varible
声明一个全局的字符串变量,并将其初始化成空串
LCLA
LCLA Varible
声明一个局部的算术变量,并将其初始化为0
LCLL
LCLL Varible
声明一个局部的逻辑变量,并将其初始化成{FALSE}
LCLS
LCLS Varible
声明一个局部的字符串变量,并将其初始化成空串
SETA
SETA Varible expr
给一个全局或局部算术变量赋值
SETL
SETL Varible expr
给一个全局或局部逻辑变量赋值
SETS
SETS Varible expr
给一个全局或局部字符串变量赋值
RLIST
name LIST {list of registers}
为一个通用寄存器列表定义名称
CN
name CN expr
为一个协处理器的寄存器定义名称
CP
name CP expr
为一个协处理器定义名称
DN/SN
name DN/SN expr
DN/SN为一个双精度/单精度的VFP寄存器定义名称
FN
name FN expr
为一个FPA浮点寄存器定义名称
LTORG
LTONG
声明一个数据缓冲池(文字池)的开始
MAP
MAP expr {, base-register}
定义一个结构化的内存表(storage map)的首地址
FIELD
{label} FIELF expr
定义一个结构化内存表中的数据域
SPACE
{label} SPACE expr
分配一块连续内存单元,并用0初始化
DCB
{label} DCB expr {,expr}..
分配一块字节内存单元,并用expr初始化
DCD/ DCDU
{label} DCD/DCDU expr {,expr}…
分配一块字内存单元, 并用expr初始化
DCDO
{label} DCDO expr {,expr}…
分配一块字对齐的字内存单元, 并用expr初始化
DCFD/DCFDU
{label} DCFD{U} fpliteral
,{,fpliteral}…
为双精度的浮点数分配字对齐的内存单元
DCFS/DCFSU
{label} DCFS{U} fpliteral
,{,fpliteral}…
为单精度的浮点数分配字对齐的内存单元
DCI
{label} DCI expr, {expr}…
ARM代码分配一段字对齐的内存单元,填充expr(二进制指令码),THUMB代码中,分配一段半字对齐的半字内存单元。
DCQ/ DCQU
{label} DCQ{U} {-} literal,
{, {-} literal}…
分配一段以双字(8个字节)为单位的内存
DCW/DCWU
{label} DCW{U} {-} literal,
{, {-} literal}…
DCW用于分配一段半字对齐的半字内存单元
(1)ARM GNU汇编中的伪操作,一般都以“.”开头。
(2)ARM GNU汇编中的伪操作,可以分为以下几类。
- 数据定义伪操作
- 符号定义伪操作
- 代码控制伪操作
- 预定义控制伪操作
- 其他伪操作
常见的数据定义伪操作如表格所示。
数据定义伪操作 格式 作用 举例与备注 .byte
.byte expr {,expr}…
分配一段字节内存单元,并用expr初始化。即定义单字节数据。
.byte 1,2,0b01,0x34,072,'s' .hword/.short
.hword expr {,expr}…
分配一段半字内存单元,并用expr初始化。即定义双字节数据
.short 0x1234 .ascii
.ascii expr {,expr}…
定义字符串expr(不会自动增加/0结束符,需要自行添加结尾字符'\0'。)
.ascii "welcome\0" .asciz/.string
.asciz expr {,expr}…
定义字符串expr(会自动增加/0为结束符)
.string "abcd", "efgh", "hello!"
.asciz "qwer", "sun", "world!"
.floar/.single
.float expr {,expr}…
分配一段字内存单元,并用32位IEEE单精度浮点数expr初始化内存单元 .double
.doubel expr {,expr}…
定义64bit IEEE浮点数expr
.word/.long/.int
.word expr {,expr}…
分配一段字内存单元,并用expr初始化
.long 0x12345678 .fill
.fill repeat {,size} {,value}
分配一段字节内存单元,用size个字节value填充repeat次(size默认为1,value默认为0)
.zero
.zero size
分配一段size个字节的内存单元,并用0填充内存
.space/.skip
.space size, {,value}
分配一段size个字节的内存单元,用value将内存初始化
.quad .qua expr {,expr}… 分配一段双字内存单元,并用expr初始化双字内存单元 .quad 0x1234567890abcd .octa .octa expr{,expr}… 分配一段四字内存单元,并用expr初始化四字内存单元 .ltorg .ltorg 声明一个数据缓冲池(literal pool),即在当前段的当前地址(字对齐)产生一个文字池 .int .int expressions 定义一个整型。
常见的符号定义伪操作如表格所示。
符号定义伪操作 格式 作用 举例与备注 .equ / .set .equ symbol,expr 将symbol定义为expr .equ abc 3 @即abc=3 .equiv .equiv symbol 将symbol定义为expr,若symbol已定义则出错 .global/.globl .globl symbol 将symbol定义为全局标号 .extern .extern symbol 声明symbol为一个外部变量
常见的代码控制伪操作如表格所示。
代码控制伪操作
格式
作用
举例与备注 .section
.section expr
定义一个段,expr可以是.text、.data、.bss
.text
.text {subsection}
将定义符开始的代码编译到代码段或代码子段(subsection)
.data
.data{subsection}
将定义符开始的代码编译到数据段或数据子段(subsection)
.bss
.bss{subsection}
将变量存放到.bss段或.bss的子段(subsection)
.cond 16/.thumb
.code 16/.thumb
表示之后的汇编指令使用THUMB指令集
.code 32/.arm
.code 32/.arm
表示之后的汇编指令使用ARM指令集
.end
.end
标记汇编文件的结束
.include
.include "filename"
将一个源文件包含到当前源文件中
.align/.balign
.align {alignment} {,fill},{max}
通过填充字节使当前位置满足一定的对齐格式
比如“ .balignl 4 0xdeadbeef ”表示4字节对齐,l表示以四字节为单位填充,填充的内容是0xdeadbeef。 .org .org offset{,expr} 指定从当前地址加上offset开始存放代码,并且从当前地址到当前地址加上offset之间的内存单元,用零或指定的数据进行填充
常见的预定义控制伪操作如下所示。
宏定义伪操作 格式 作用 举例与备注 .macro
标识宏定义的开始 用.macro及.endm定义一段代码,称为宏定义体。 .exitm 中途跳转出宏 .endm 标识宏定义的结束 .if 条件判断语句 .else 条件判断语句 .endif 条件结束语句 .include .include "file_name" 包含文件标识 .incbin .incbin "file"[,skip[,count]] 将原封不动地一个二进制文件编译到当前文件中。
skip表明是从文件开始处跳过skip个字节开始读取文件,count是读取的字数。
伪操作 格式 作用 举例与备注 .err .err 使编译结果产生错误报告 .eject .eject 在汇编符号列表文件中插入一分页符 .list .list 产生汇编列表(从.list到.nolist) .nolist .nolist 汇编列表结束处。再次使用.list产生汇编列表 .title .title "title_name" 使用title_name作为标题 .sbttl .sbttl "title_name" 使用title_name作为子标题 .print string 打印输出信息到标准输出 .fail .fail expr 编译汇编文件时产生警告 .req req name, expr 为一个特定的寄存器定义名称 .unreq .unreq 寄存器别名 取消一个寄存器的别名。注意被取消的别名必须事先定义过,否则编译器就会报错。这个伪操作也可以用来取消系统预制的别名。 .rept .rept 重复次数
数据定义
.endr @结束重复定义
重复定义伪操作。 .rept 3
.byte 0x23
.endr
相当于
.byte 0x23
.byte 0x23
.byte 0x23
ARM官方汇编、ARM GNU汇编,它们的伪操作的区别与联系如下。
两种开发环境下的汇编代码,有较多不同的点,主要是符号及伪操作的不同。
ARM官方汇编伪操作 ARM GNU汇编伪操作 备注
;
@
注释
#0x
#&
十六进制立即数
IFDEF、IF
.ifdef、.if
不完全相同
ELSE
.else
ELSEIF
.elseif
ENDIF
.endif
LTORG
.ltorg
OR
|
OR
AND
&
AND
SHL
<<
Shift left
SHR
>>
Shift right
MACRO
.macro
开始宏
END .end MEND
.endm
结束宏
INCLUDE
.include
DCB
.word
定义一个字数据
DCW
.short
DCD
.long
DCB
.byte
RN
.req
EXPORT .global GBLA .global IMPORT .extern ENTRY ENTRY: CODE16 .thumb CODE32 .arm % .fill NUM SETA 16 .equ NUM,16 NUM EQU 25 .equ NUM,25 AREA WORD, CODE, READONLY
.text
AREA BLOCK, DATE, READWRITE
.data