GNU ARM汇编

目  录

1   GNU ARM汇编...3

1.1       基本语法...3

1.2    GNU ARM汇编伪指令...3

2   ATPCS约定...3

2.1       寄存器使用规定...3

3       汇编与C语言对照...5

1        GNU ARM汇编

不同的汇编器对汇编语言的语法要求不一样。目前常用的ARM汇编环境有以下两种:

u      ARMASM:ARM公司的汇编器,适合在Windows平台下使用

u      GNU ARM ASM:GNU交叉编译工具链中的汇编器,适合于Linux开发平台。

本文主要讨论GNU ARM语法

1.1       基本语法

在GNU ARM汇编中,一行语句的基本格式如下:

标签:指令 @注释

解释如下:

u      标签可以代表“标签处的代码”也可以代表“标签处的数据的地址” 。

u      指令包括两种:ARM指令、汇编伪指令。

应用举例:

.text @ 代码段开始                                           

.global add @ 声明add为全局标签,这样它就可以在其它文件中使用

add: @ 标签                                                      

          add  r0, r0, r1 @ 将两个参数相加,结果放在r0            

          mov  pc, lr  @函数调用返回                                

1.2       GNU ARM汇编伪指令

伪指令均是以小数点开头。

此处略去具体的GNU ARM汇编伪指令,如需参考建议阅读何永琪主编的《嵌入式Linux系统实用开发》4.4节相关内容(原书P158页)。

2         ATPCS约定

在汇编语言与C语言混合编程的情况下,或者是各个源文件不是由同一个编译器进行编译的情况下,所有的汇编代码的生成(或编写)都必须遵循统一调用接口规范,这才能保证链接出来的程序功能是正确(即所写的汇编代码可直接与C语言相互调用)。

2.1       寄存器使用规定

ATPCS对通用寄存器的用途做了一些规定,并且根据不同用途为寄存器定义了别名,如下表2-1-1所示。


表2-1-1 通用寄存器的用途与别名

寄存器

别名

用途

r0

A1

函数参数和返回值

r1

A2

函数参数和返回值

r2

A3

函数参数

r3

A4

函数参数

r4

V1

变量

r5

V2

变量

R6

V3

变量

r7

V4

变量

R8

V5

变量

r9

V6, sb

变量,或静态数据基址

r10

V7, sl

变量,或栈限制

r11

V8, fp

变量,或帧指针

r12

Ip

函数调用中间临时寄存器

r13

Sp

栈指针

r14

Lr

链接寄存器

r15

Pc

程序计数器

r0 - r3 这4个寄存器用来传递函数调用第1到第4个参数,更多的参数则必须需要通过栈来传递。而r0同时用来存放函数调用的返回值。被调用的子程序在返回前无须恢复这些寄存器的内容。

r4 – r11 这8个寄存器可作为一般的临时变量使用(即局部变量),子程序进入时必须保存这些寄存器的值,在返回前必须返回前必须恢复这些寄存器的值【以便恢复上一个局部变量的值】。其中,r11(fp)是函数的帧指针。函数在栈中保存的所有的局部信息,包括返回地址、局部变量等构成了函数的帧。fp指向函数帧的第一个字,在函数执行过程中,fp保存不变。

r12(ip) 在函数调用时,用保存栈指针的临时寄存器。

r13(sp)

r14(lr) 用于保存子程序的返回地址。

r15(pc) 程序计数器,不能用作其它用途。

应用举例,见表2-1-2:

原sp(调用新函数前的栈指针)

Data(有数据)

4

fp(由于调用新的函数,而导致栈延长,这是新延长的栈开始的地方,我们称之为函数帧的指针,所有的信息。变量从这里开始存入)

Pc

0(新函数开始的地方)

 

Lr(内含子程序返回地址)

-4

 

ip(原sp)

-8

 

fp(原fp)

-12

 

a1

-16

 

a2

-20

Sp

a3

-24

3         汇编与C语言对照

汇编语言的结构:

①    开头声明

②    数据初始化

④函数(入栈、操作、出栈)

③    被初始化的数据的地址(文字池)

注意:②和③联系比较大,但为了让③处在不会被执行的地方,所以将③放在④之后。

下面的例子是一个算术运算的汇编与C的对照,首先是C语言

/*文件名:test.c*/

/*说明:算术运算*/

 

int v1 = 1;

static int v2 = 2;

 

int main(void)

{

      int vr;

      int v3 = 3, v4 = 4;

      vr = (v1 + v2 ) – ( v3 + v4 )

      return vr;

}

以下为汇编语言的内容(请结合表2-1-2理解本程序):

            .file    “test.c”

            .global v1 @声明v1为全局标签

            .data @ 数据段开始

            .align 2 @地址与4的倍数对齐

            .type  v1,  %object  @v1标签代表数据对象

            .size   v1,  4 @对象v1的长度为4

v1:

            .word   1  @存放v1的初始值1

            .align   2  @地址与4的倍数对齐

            .type   v2,  %object @v2标签代表数据对象

            .size    v2, 4 @对象v2的长度为4

v2:

            .word  2  @存放v2的初始值2

.text @ 代码段开始

.align    2

.global   main @ 声明main为全局标签

.type   main,  %function @ main标签代表函数

main:

            @ args = 0, pretend = 0, frame = 12

            @ frame_needed = 1, uses_anonymous_args = 0

            @入栈及开辟存放局部变量的空间

            mov    ip, sp @暂时用ip保存栈指针

            stmfd   sp!, {fp, ip, lr, pc} @入栈

            sub     fp, ip, #4 @ fp指向入栈的第一个元素(函数帧的开始)

            sub     sp, ip, #12 @在栈上开辟3个整形数的空间,用于存放局部变量

            @v3,v4赋初值

            mov    r3, #3

            str     r3, [fp, #-20] @将v3赋值为3

            mov    r3, #4

            str     r3, [fp, #-24] @将v4赋值为4

            @计算表达式v1+v2,将结果放到r1

            ldr     r3, .L2 @将变量v1的地址加载到r3

            ldr     r2, .L2+4 @将变量v2的地址加载到r2

            ldr     r1, [r3, #0] @将变量v1的值加载到r1

            ldr     r3, [r2, #0] @将变量v2的值加载到r3

            add    r1, r1, r3 @ v1+v2的结果放到r1中

            @计算表达式(v3+v4),将结果放到r3

            ldr     r2, [fp, #-20] @将变量v3的值加载到r2上

            ldr     r3, [fp, #-24] @将变量v4的值加载到r3上

            add    r3, r2, r3 @ (v1+v2)+(v3+v4)的结果放到r3中

            @(v1+v2)-(v3+v4)的值放到r3中,保存,返回

            rsb     r3, r3, r1 @将(v1+v2) - (v3+v4)的值放到r3之中

            str      r3, [fp, #-16] @将结果保存到vr中

            ldr      r3, [fp, #-16] @将vr的值加载到r3中

            mov    r0, r3 @将(v1+v2) - (v3+v4)的值放到r0中返回

            @出栈

            sub     sp, fp, #12 @重设栈指针准备返回

            ldmfd   sp, {fp, sp, pc} @返回

           

.L3:

            .align  2

.L2

            .word   v1 @变量v1的地址,即标号v1【使.L2获得v1的指针】

            .word   v2 @变量v2的地址,即标号v2

            .size    main,  .-main @ main函数的大小,当前位置减去main标号处

            .ident   “GCC: (GNU) 3.4.4”

这里有点4需要注意的:

①    全局变量都被放在数据段上,数据段中保存的其实是变量的初始值

②    未用static声明的变量会被声明为.global,表示他可以链接到其它文件。

③    加载全局变量实际上使用的是文字池的方法,即将变量地址放在代码段中某个不会执行到的位置,使用时先加载变量的地址,然后通过变量的地址得到变量的值。

④    每条汇编指令只能执行一个简单的运算,计算的中间结果使用寄存器保存。如果表达式非常复杂以至于无法用寄存器保存所有的中间结果,则还会在栈上开辟局部变量来保存。【非静态的局部变量都放在栈上,通过帧指针和偏移量的方式来访问。帧指针(fp)在开始时设好,整个函数执行期间不会该变(而期间sp会改变)

你可能感兴趣的:(GNU ARM汇编)