汇编源程序通常用于系统最基本的初始化:初始化堆栈指针、设置页表、操做 ARM的协处理器等。这些初始化工做完成后就能够跳转到C代码main函数中执行。express
一、 GNU汇编语言语句格式编程
任何Linux汇编行都是以下结构:[<label>:][<instruction or directive or pseudo-instruction>} @comment框架
l instruction为指令函数
l directive为伪操做编码
l pseudo-instruction为伪指令spa
l <label>: 为标号, GNU汇编中,任何以冒号结尾的标识符都被认为是一个标号,而不必定非要在一行的开始。指针
l comment为语句的注释code
下面定义一个"add"的函数,最终返回两个参数的和:orm
.section .text, “x”对象
.global add @ give the symbol “add” external linkage
add:
ADD r0, r0, r1 @ add input arguments
MOV pc, lr @ return from subroutine
@ end of program
注意:
l ARM指令,伪指令,伪操做,寄存器名能够所有为大写字母,也可所有为小写字母,但不可大小写混用。
l 若是语句太长,能够将一条语句分几行来书写,在行末用“\”表示换行(即下一行与本行为同一语句)。“\”后不能有任何字符,包含空格和制表符(Tab)。
二、 GNU汇编程序中的标号symbol(或label)
标号只能由a~z,A~Z,0~9,“.”,_等(由点、字母、数字、下划线等组成,除局部标号外,不能以数字开头)字符组成。
Symbol的本质:表明它所在的地址,所以也能够看成变量或者函数来使用。
l 段内标号的地址值在汇编时肯定;
l 段外标号的地址值在链接时肯定。
Symbol的分类:3类(依据标号的生成方式)。
<1> 基于PC的标号。基于PC的标号是位于目标指令前的标号或者程序中数据定义伪操做前的标号。这种标号在汇编时将被处理成PC值加上(或减去)一个数字常量,经常使用于表示跳转指令”b”等的目标地址,或者代码段中所嵌入的少许数据。
<2> 基于寄存器的标号。基于寄存器的标号经常使用MAP和FIELD来定义,也能够用EQU来定义。这种标号在汇编时将被处理成寄存器的值加上(或减去)一个数字常量,经常使用于访问数据段中的数据。
<3> 绝对地址。绝对地址是一个32位数据。它能够寻址的范围为[0,232-1]便可以直接寻址整个内存空间。
特别说明:局部标号Symbol
局部标号主要在局部范围内使用,并且局部标号能够重复出现。它由两部组成:开头是一个0-99直接的数字,后面紧接一个一般表示该局部变量做用范围的符号。局部变量的做用范围一般为当前段,也能够用ROUT来定义局部变量的做用范围。
局部变量定义的语法格式:N{routname}
l N:为0~99之间的数字。
l routname:当前局部范围的名称(为符号),一般为该变量做用范围的名称(用ROUT伪操做定义的)。
局部变量引用的语法格式:%{F|B}{A|T}N{routname}
l %:表示引用操做
l N:为局部变量的数字号
l routname:为当前做用范围的名称(用ROUT伪操做定义的)
l F:指示编译器只向前搜索
l B:指示编译器只向后搜索
l A:指示编译器搜索宏的全部嵌套层次
l T:指示编译器搜索宏的当前层次
例:使用局部符号的例子,一段循环程序
1:
subs r0, r0, #1 @每次循环使r0=r0-1
bne 1F @跳转到1标号去执行
注意:
l 若是F和B都没有指定,编译器先向前搜索,再向后搜索
l 若是A和T都没有指定,编译器搜索全部从当前层次到宏的最高层次,比当前层次低的层次再也不搜索。
l 若是指定了routname,编译器向前搜索最近的ROUT伪操做,若routname与该ROUT伪操做定义的名称不匹配,编译器报告错误,汇编失败。
三、 GNU汇编程序中的分段
<1> .section伪操做
.section <section_name> {,”<flags>”}
Starts a new code or data section. Sections in GNU are called .text, a code section, .data, an initialized data section, and .bss, an uninitialized data section.
These sections have default flags, and the linker understands the default names(similar directive to the armasm directive AREA).The following are allowable .section flags for ELF format files:
<Flag> Meaning
a allowable section
w writable section
x executable section
中文解释:
用户能够经过.section伪操做来自定义一个段,格式以下:
.section section_name [, "flags"[, %type[,flag_specific_arguments]]]
每个段以段名为开始, 如下一个段名或者文件结尾为结束。这些段都有缺省的标志(flags),链接器能够识别这些标志。(与arm asm中的AREA相同)。下面是ELF格式容许的段标志flags:
<标志> 含义
a 容许段
w 可写段
x 执行段
例:定义一个“段”
.section .mysection @自定义数据段,段名为 “.mysection”
.align 2
strtemp:
.ascii "Temp string \n\0" @ 对这一句的理解,我以为应该是:将"Temp string \n\0"这个字符串存储在以标号strtemp:
@为起始地址的一段内存空间里
<2> 汇编系统预约义的段名
l .text @代码段
l .data @初始化数据段 .data Read-write initialized long data.
l .bss @未初始化数据段
l .sdata @ .sdata Read-write initialized short data.
l .sbss @
注意:源程序中.bss段应该在.text段以前。
四、 GNU汇编语言定义入口点
汇编程序的缺省入口是_start标号,用户也能够在链接脚本文件中用ENTRY标志指明其它入口点。
例:定义入口点
.section .data
< initialized data here>
.section .bss
< uninitialized data here>
.section .text
.globl _start
_start:
<instruction code goes here>
五、 GNU汇编程序中的宏定义
格式以下:
.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
六、 GNU汇编程序中的常数
<1> 十进制数以非0数字开头,如:123和9876;
<2> 二进制数以0b开头,其中字母也能够为大写;
<3> 八进制数以0开始,如:0456,0123;
<4> 十六进制数以0x开头,如:0xabcd,0X123f;
<5> 字符串常量须要用引号括起来,中间也可使用转义字符,如: “You are welcome!\n”;
<6> 当前地址以“.”表示,在GNU汇编程序中可使用这个符号表明当前指令的地址;
<7> 表达式:在汇编程序中的表达式可使用常数或者数值, “-”表示取负数, “~”表示取补,“<>”表示不相等,其余的符号如:+、-、*、 /、%、<、<<、>、>>、|、&、^、!、==、>=、<=、&&、|| 跟C语言中的用法类似。
七、 GNU ARM汇编的经常使用伪操做
在前面已经提到过了一些为操做,还有下面一些为操做:
l 数据定义伪操做: .byte,.short,.long,.quad,.float,.string/.asciz/.ascii,重复定义伪操做.rept,赋值语句.equ/.set ;
l 函数的定义;
l 对齐方式伪操做 .align;
l 源文件结束伪操做.end;
l .include伪操做;
l if伪操做;
l .global/ .globl 伪操做 ;
l .type伪操做 ;
l 列表控制语句 ;
别于GNU AS汇编的通用伪操做,下面是ARM特有的伪操做:
.reg ,.unreq ,.code ,.thumb ,.thumb_func ,.thumb_set, .ltorg ,.pool
<1> 数据定义伪操做
l .byte:单字节定义,如:.byte 1,2,0b01,0x34,072,'s' ;
l .short:定义双字节数据,如:.short 0x1234,60000 ;
l .long:定义4字节数据,如:.long 0x12345678,23876565
l .quad:定义8字节,如:.quad 0x1234567890abcd
l .float:定义浮点数,如:.float 0f-314159265358979323846264338327\
95028841971.693993751E-40 @ - pi
l .string/.asciz/.ascii:定义多个字符串,如:
.string "abcd", "efgh", "hello!"
.asciz "qwer", "sun", "world!"
.ascii "welcome\0"
注意:ascii伪操做定义的字符串须要自行添加结尾字符'\0'。
l .rept:重复定义伪操做, 格式以下:
.rept 重复次数
数据定义
.endr @结束重复定义
例:
.rept 3
.byte 0x23
.endr
l .equ/.set: 赋值语句, 格式以下:
.equ(.set) 变量名,表达式
例:
.equ abc, 3 @让abc=3
<2> 函数的定义伪操做
l 函数的定义,格式以下:
函数名:
函数体
返回语句
通常的,函数若是须要在其余文件中调用, 须要用到.global伪操做将函数声明为全局函数。为了避免至于在其余程序在调用某个C函数时发生混乱,对寄存器的使用咱们须要遵循APCS准则。函数编译器将处理函数代码为一段.global的汇编码。
l 函数的编写应当遵循以下规则:
a. a1-a4寄存器(参数、结果或暂存寄存器,r0到r3 的同义字)以及浮点寄存器f0-f3(若是存在浮点协处理器)在函数中是没必要保存的;
b. 若是函数返回一个不大于一个字大小的值,则在函数结束时应该把这个值送到 r0 中;
c. 若是函数返回一个浮点数,则在函数结束时把它放入浮点寄存器f0中;
d. 若是函数的过程改动了sp(堆栈指针,r13)、fp(框架指针,r11)、sl(堆栈限制,r10)、lr(链接寄存器,r14)、v1-v8(变量寄存器,r4 到 r11)和 f4-f7,那么函数结束时这些寄存器应当被恢复为包含在进入函数时它所持有的值。
<3> .align .end .include .incbin伪操做
l .align:用来指定数据的对齐方式,格式以下:
.align [absexpr1, absexpr2]
以某种对齐方式,在未使用的存储区域填充值. 第一个值表示对齐方式,4, 8,16或 32. 第二个表达式值表示填充的值。
l .end:代表源文件的结束。
l .include:能够将指定的文件在使用.include 的地方展开,通常是头文件,例如:
.include “myarmasm.h”
l .incbin伪操做能够将原封不动的一个二进制文件编译到当前文件中,使用方法以下:
.incbin "file"[,skip[,count]]
skip代表是从文件开始跳过skip个字节开始读取文件,count是读取的字数.
<4> ..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不相 等, 那么编译器将编译下面的代码.
<5> .global .type .title .list
l .global/ .globl :用来定义一个全局的符号,格式以下:
.global symbol 或者 .globl symbol
l .type:用来指定一个符号的类型是函数类型或者是对象类型, 对象类型通常是数据, 格式以下:
.type 符号, 类型描述
例:
.globl a
.data
.align 4
.type a, @object
.size a, 4
a:
.long 10
例:
.section .text
.type asmfunc, @function
.globl asmfunc
asmfunc:
mov pc, lr
<6> 列表控制语句:
.title:用来指定汇编列表的标题,例如:
.title “my program”
.list:用来输出列表文件.
<7> ARM特有的伪操做
l .reg: 用来给寄存器赋予别名,格式以下:
别名 .req 寄存器名
l .unreq: 用来取消一个寄存器的别名,格式以下:
.unreq 寄存器别名
注意被取消的别名必须事先定义过,不然编译器就会报错,这个伪操做也能够用来取消系统预制的别名, 例如r0, 但若是没有必要的话不推荐那样作。
l .code伪操做用来选择ARM或者Thumb指令集,格式以下:
.code 表达式
若是表达式的值为16则代表下面的指令为Thumb指令,若是表达式的值为32则代表下面的指令为ARM指令.
l .thumb伪操做等同于.code 16, 代表使用Thumb指令, 相似的.arm等同于.code 32
l .force_thumb伪操做用来强制目标处理器选择thumb的指令集而无论处理器是否支持
l .thumb_func伪操做用来指明一个函数是thumb指令集的函数
l .thumb_set伪操做的做用相似于.set, 能够用来给一个标志起一个别名, 比.set功能增长的一点是能够把一个标志标记为thumb函数的入口, 这点功能等同于.thumb_func
l .ltorg用于声明一个数据缓冲池(literal pool)的开始,它能够分配很大的空间。
l .pool的做用等同.ltorg
l .space <number_of_bytes> {,<fill_byte>}
分配number_of_bytes字节的数据空间,并填充其值为fill_byte,若未指定该值,缺省填充0。(与armasm中的SPACE功能相同)
l .word <word1> {,<word2>} …
插入一个32-bit的数据队列。(与armasm中的DCD功能相同)。可使用.word把标识符做为常量使用。
例:
Start:
valueOfStart:
.word Start
这样程序的开头Start便被存入了内存变量valueOfStart中。
l .hword <short1> {,<short2>} …
插入一个16-bit的数据队列。(与armasm中的DCW相同)
八、 GNU ARM汇编特殊字符和语法
<1> 代码行中的注释符号: ‘@’
<2> 整行注释符号: ‘#’
<3> 语句分离符号: ‘;’
<4> 当即数前缀: ‘#’ 或 ‘$’