汇编源程序一般用于体系最基本的初始化:初始化仓库指针、设置页表、操作 ARM的协处理器等。这些初始化作业完成后就能够跳转到C代码main函数中碑文。
1、GNU汇编语言查办格局
任何Linux汇编行都是如下结构:[
linstruction为指令
ldirective为伪操作
lpseudo-instruction为伪指令
l
lcomment为查办的注释
下面界说一个”add”的函数,终究回来两个参数的和:
.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
留意:
lARM指令,伪指令,伪操作,寄存器名能够悉数为大写字母,也可悉数为小写字母,但不可巨细写混用。
l假如查办太长,能够将一条查办分几行来书写,内行末用“\”一共换行(即下一行与本行为同一查办)。“\”后不能有任何字符,包括空格和制表符(Tab)。
2、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}
lN:为0~99之间的数字。
lroutname:当时部分规模的称号(为符号),一般为该变量效果规模的称号(用ROUT伪操作界说的)。
部分变量引证的语法格局:%{F|B}{A|T}N{routname}
l%:一共引证操作
lN:为部分变量的数字号
lroutname:为当时效果规模的称号(用ROUT伪操作界说的)
lF:指示编译器只向前查找
lB:指示编译器只向后查找
lA:指示编译器查找宏的一切嵌套层次
lT:指示编译器查找宏的当时层次
例:运用部分符号的比如,一段循环程序
1:
subs r0, r0, #1 @每次循环使r0=r0-1
bne 1F @跳转到1标号去碑文
留意:
l假如F和B都没有指定,编译器先向前查找,再向后查找
l假如A和T都没有指定,编译器查找一切从当时层次到宏的最高层次,比当时层次低的层次不再查找。
l假如指定了routname,编译器向前查找最近的ROUT伪操作,若routname与该ROUT伪操作界说的称号不匹配,编译器陈述过错,汇编失利。
3、GNU汇编程序中的分段
<1>.section伪操作
.section
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 flagsfor ELF format files:
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” @对这一句的了解,我觉得应该是:将”Temp string \n”这个字符串存储在以标号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段之前。
4、GNU汇编语言界说进口点
汇编程序的缺省进口是_start标号,用户也能够在衔接脚本文件顶用ENTRY标志指明其它进口点。
例:界说进口点
.section .data
< initialized data here>
.section .bss
< uninitialized data here>
.section .text
.globl _start
_start:
5、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
6、GNU汇编程序中的常数
<1>十进制数以非0数字最初,如:123和9876;
<2>二进制数以0b最初,其间字母也能够为大写;
<3>八进制数以0开端,如:0456,0123;
<4>十六进制数以0x最初,如:0xabcd,0X123f;
<5>字符串常量需求用引号括起来,中心也能够运用转义字符,如: “You are welcome!\n”;
<6>当时地址以“.”一共,在GNU汇编程序中能够运用这个符号代表当时指令的地址;
<7>表达式:在汇编程序中的表达式能够运用常数或许数值, “-”一共取负数, “~”一共取补,“<>”一共不持平,其他的符号如:+、-、*、 /、%、<、<<、>、>>、|、&、^、!、==、>=、<=、&&、|| 跟C语言中的用法相似。
7、GNU ARM汇编的常用伪操作
在前面现已说到过了一些为操作,还有下面一些为操作:
l数据界说伪操作: .byte,.short,.long,.quad,.float,.string/.asciz/.ascii,重复界说伪操作.rept,赋值查办.equ/.set ;
l函数的界说;
l对齐方法伪操作 .align;
l源文件完毕伪操作.end;
l.include伪操作;
lif伪操作;
l.global/ .globl 伪操作;
l.type伪操作;
l列表操控查办;
别于GNUAS汇编的通用伪操作,下面是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:界说浮点数,如:.float0f-314159265358979323846264338327\
95028841971.693993751E-40 @- pi
l.string/.asciz/.ascii:界说多个字符串,如:
.string “abcd”, “efgh”, “hello!”
.asciz “qwer”, “sun”, “world!”
.ascii “welcome”
留意:ascii伪操作界说的字符串需求自行添加完毕字符。
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,若未指定该值,缺省填充0。(与armasm中的SPACE功用相同)
l.word
刺进一个32-bit的数据行列。(与armasm中的DCD功用相同)。能够运用.word把标识符作为常量运用。
例:
Start:
valueOfStart:
.word Start
这样程序的最初Start便被存入了内存变量valueOfStart中。
l.hword
刺进一个16-bit的数据行列。(与armasm中的DCW相同)
8、GNU ARM汇编特别字符和语法
<1>代码行中的注释符号:‘@’
<2>整行注释符号: ‘#’
<3>查办别离符号: ‘;’
<4>当即数前缀: ‘#’ 或 ‘$’