作者:郭嘉
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
github:https://github.com/AllenWell
【基于Android的ARM汇编语言系列】章节列表
【基于Android的ARM汇编语言系列】之中的一个:ARM汇编语言开篇
【基于Android的ARM汇编语言系列】之二:C/C++程序生成ARM汇编程序的过程分析
【基于Android的ARM汇编语言系列】之三:ARM汇编语言程序结构
【基于Android的ARM汇编语言系列】之四:ARM处理器的寻址方式
【基于Android的ARM汇编语言系列】之五:ARM指令集与Thumb指令集
【基于Android的ARM汇编语言系列】之六:NEON指令集与VFP指令集
Android平台的ARM汇编是GNU ARM汇编格式。使用的汇编器是GAS(GNU Assembler),GAS有自己的一套语法结构。
具体的内容能够查阅GAS语法结构官方手冊。
我们先来看一个完整的ARM汇编程序:
C代码:
#include <stdio.h>
int main(int argc, char* argv[]){
printf("Hello ARM!\n");
return 0;
}
相应的汇编代码:
.arch armv5te
.fpu softvfp
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 6
.eabi_attribute 18, 4
.file "hello.c"
.section .rodata
.align 2
.LC0:
.ascii "Hello ARM!\000"
.text
.align 2
.global main
.type main, %function
main:
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 1, uses_anonymous_args = 0
stmfd sp!, {fp, lr}
add fp, sp, #4
sub sp, sp, #8
str r0, [fp, #-8]
str r1, [fp, #-12]
ldr r3, .L3
.LPIC0:
add r3, pc, r3
mov r0, r3
bl puts(PLT)
mov r3, #0
mov r0, r3
sub sp, fp, #4
ldmfd sp!, {fp, pc}
.L4:
.align 2
.L3:
.word .LC0-(.LPIC0+8)
.size main, .-main
.ident "GCC: (GNU) 4.4.3"
.section .note.GNU-stack,"",%progbits
以下分部分介绍这段代码的结构。
一 处理器架构定义
.arch armv5te @处理器架构
.fpu softvfp @协处理器类型
.eabi_attribute 20, 1 @接口属性
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 6
.eabi_attribute 18, 4
1.1 处理器架构
.arch指定了ARM处理器架构。armv5te表示本程序的代码能够执行在armv5te架构的处理器上执行。
1.2 协处理器类型
.fpu指定协处理器的类型。
softvfp表示使用浮点数运算库来模拟协处理器运算。还能够用vfpv2、vfpv3来指定自带的协处理器。
1.3 接口属性
.eabi_attrbute指定了一些接口。
EABI(Embedded Application Binary Interface)嵌入式应用二级制接口是ARM指定的一套接口规范。Android系统实现了它。
二 段定义
.file "hello.c"
.section .rodata
.align 2
.LC0:
.ascii "Hello ARM!\000"
.text
.align 2
.global main
.type main, %function
main:
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 1, uses_anonymous_args = 0
stmfd sp!, {fp, lr}
add fp, sp, #4
sub sp, sp, #8
str r0, [fp, #-8]
str r1, [fp, #-12]
ldr r3, .L3
.LPIC0:
add r3, pc, r3
mov r0, r3
bl puts(PLT)
mov r3, #0
mov r0, r3
sub sp, fp, #4
ldmfd sp!, {fp, pc}
.L4:
.align 2
.L3:
.word .LC0-(.LPIC0+8)
.size main, .-main
.ident "GCC: (GNU) 4.4.3"
.section .note.GNU-stack,"",%progbits
ARM中段的定义格式例如以下所看到的:
.section name , "flags", %type, flag_specific_arguments
- name:段名
- flags:段的属性。如读、写和可执行等。
- type:段的类型,如progbits表示段中含有数据。note表示段中包括的数据并不是程序本身使用。
- flag_specific_arguments:指定了一些平台相关的參数。
三 凝视与标号
GNU ARM支持两种凝视加入方式。
/* */型凝视
/* args = 0, pretend = 0, frame = 8 */
/* frame_needed = 1, uses_anonymous_args = 0 */
@型凝视
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 1, uses_anonymous_args = 0
四 汇编器指令
.file "hello.c"
.section .rodata
.align 2
.LC0:
.ascii "Hello ARM!\000"
.text
.align 2
.global main
.type main, %function
程序中全部以.开头的指令都是汇编器指令,汇编器指令是与汇编器相关的,它们并不属于ARM指令集。
- .file:指定源文件名称。
- .align:指定代码的对齐方式。后面跟的数值是2的次数方。
- .ascii:声明字符串。
- .global:声明全局符号,全局符号是指在本程序外能够訪问的符号,
- .type:指定符号的类型。
- .word:用来存放地址值。
- .size:设置指定符号的大小。
- .ident:编译器标识。无实际用途。
五 子程序与參数传递
子程序在代码表示一个独立的功能。非常多时候。子程序和代码是同样的概念。在汇编中声明函数的方式例如以下所看到的:
.global 函数名
.type 函数名 %Function
函数名:
...
函数体
...
那么函数调用过程中。參数传递的方式例如以下所看到的:
ARM汇编中规定:R0~R3这4个寄存器分别用来传递函数调用的第1到第4个參数。超出的參数通过堆栈来传递。R0寄存器用来存放函数调用的返回值。被调用的函数在返回前无需恢复这些寄存器的内容。