什么是GNU
GNU最开始其实是一个操作系统,旨为打造一个开源免费自由的操作系统,目前操作系统还在完善中
GNU计划: 最初目标是创建一套完全自由的操作系统GNU 和相应的软件
GCC :(GNU Compiler Collection)GNU编译器套件,GNU提供的一整套的工具集,这套工具集中包含了汇编器,编译器和链接器,二进制转换,调试工具等
GCC优势:
- 免费开源
- 贴近系统底层,功能强大,灵活性高
- 跨平台,方便交叉编译
GCC劣势:
- 工具基本采用命令行方式,学习和使用门槛较高
接下来我们要学习的就是GNU计划众多的产物之一GNU FOR ARM
汇编器与指令集
- 什么是汇编器 将汇编语言翻译成机器码的工具
- 什么是编译器 将高级语言翻译成机器语言或者汇编语言的工具
- 汇编器和编译器的区别 汇编器的服务对象是汇编语言,编译器的服务对象是高级语言
- 汇编器和汇编语法伪指令的关系 不同的CPU对应不同的指令集 ,不同的汇编器对应不同的伪指令集和汇编语法。 每种汇编器都可以有自己的伪指令集和自己的语法
使用不同的汇编器汇编同一个cpu架构的汇编代码,所对应的指令绝对是一致的,但伪指令各有千秋
代码语言:javascript复制;使用ARM官方的汇编器
AREA test, CODE
mov R3,#5
END
;使用GNU的汇编器
.text ;伪指令
mov R3,#5 ;传送指令皆为mov
.end
常见的汇编器
- MASM汇编器:微软旗下专为x86架构打造的一款汇编器,支持8086汇编和win32汇编
- GNU汇编器 : 简称为GAS,是GNU旗下的一款免费开源跨平台汇编器其子集中包含了支持多种架构的汇编器,比如
GNU FOR ARM
就是单独面向ARM架构的汇编器,此外还有GNU FOR X86
等 - NASM汇编器: 是一款面向x86架构的汇编器,支持8086汇编和win32汇编,同时可跨平台, 免费开源
- ARMASM汇编器:ARM官方原生的汇编器,集成在了ADS工具上,适用于ARM架构,我们也一般称之为ADS汇编器
两种ARM汇编器的各自用途
- ARMASM汇编器:一般用于windows平台
- GAS汇编器:支持windows平台和linux平台,方便跨平台交叉编译
由于移动设备如安卓和iphone底层都是采用GNU的编译环境,我们如果要进行移动端的开发,那么势必需要掌握GNU ARM, 同时和ADS和KEIL收费工具相比,GUN工具全部免费,方便开发者进行使用
如果你是从事android开发,有兴趣可以去翻NDK r17以下版本的库,里面用的编译工具就是GCC
GNU ARM开发环境搭建
我们需要准备以下两个工具:
- GCC编译套件
- 安卓模拟器
GCC编译套件根据cpu架构和操作系统的不同,又分为了很多子类:
- 纯ARM裸机: 对应
arm-none-eabi
工具包 - ARM架构 Linux操作系统:对应
arm-none-linux-eabi
工具包
由于接下来我们选择在安卓模拟器上进行开发学习,因此我们选择arm-none-linux-eabi
这套工具来进行代码的编译
工具下载
GCC工具的具体使用
伪指令和伪操作
注释
段的声明
- 代码段 .text @代码
自定义一个段
函数或者标签的声明
数据的声明
数据的批量定义
- 格式如下: .rept @重复次数 @数据定义代码 .endr @结束重复定义
关于align
反汇编后的结果:
指令和伪指令的区别
- 指令: 有与之对应的机器码,能被cpu所识别,和编译器无关
- 伪指令:没有与之对应的机器码,无法被cpu识别,只能被编译器识别,不同编译器伪指令不一样 不同的CPU对应不同的指令集;不同的汇编器对应不同的语法和伪指令集
例子:ARM原生编译器和GNU FOR ARM
两种汇编器语法对比一览表
GNU ARM汇编 | ADS ARM汇编 |
---|---|
“@”或“/…/” | “;” |
.include | GET |
.equ | EQU |
.global | EXPORT |
.extern | IMPORT |
.long | DCD |
.end | END |
entry: | ENTRY |
.text | AREA Init,CODE,READONLY |
.data | AREA Block,DATA,READWRITE |
.macro | MACRO |
.endm | MEND |
汇编语言和C语言交互
1.引入其他源文件函数
使用import
或者extern
伪指令
;使用import伪指令
AREA code, CODE
import fun1 ;导入其他源文件中名为fun1的函数
END
;使用extern伪指令
AREA code, CODE
extern fun1
END
两者区别:
import
:不管当前文件是否使用该引入的函数,该标签都会加入当前文件符号表,即为静态引用extern
:只有当前文件使用了该函数,才会将此标签加入符号表,即为动态引用
2.导出当前源文件中函数供其他文件访问
使用export
或者global
伪指令
;使用import伪指令
AREA code, CODE
export fun ;导出fun函数供其他源文件使用
fun
mov R0,#4
bx lr
END
3.外链汇编之C语言调汇编函数
第一步,在汇编原文件中将函数暴露出来给供外部调用,使用export
或者global
伪指令:
AREA code, CODE
export arm_strcpy ;或者使用global
arm_strcpy
loop
ldrb R4,[R0],#1 ;如果使用ldr 那么将偏移值改成4
cmp R4,#0
beq over
strb R4,[R1],#1
b loop
over
END
第二步,在C文件中引用汇编中的函数,C文件中只能使用extern
伪指令:
extern arm_strcpy(char *src,char*des);
int main2(){
char *a="hello pangshu";
char b[64];
arm_strcpy(a,b);
}
4.外链汇编之汇编调c语言函数
第一步,在C文件中编写好函数
代码语言:javascript复制int c_sum(int a,int b){
return a b;
}
第二步, 在汇编文件中引入函数,使用import
或者extern
伪指令
AREA code, CODE
import c_sum
mov R0,#1 ;第一个参数
mov R1,#2 ;第二个参数
END
第三步, 使用BL指令调用函数
代码语言:javascript复制AREA code, CODE
import c_sum
mov R0,#1 ;第一个参数
mov R1,#2 ;第二个参数
BL c_sum
END
在ARM中函数参数使用R0~R3这三个寄存器来进行传递,最多传递4个参数,超过4个参数使用栈进行处理,函数返回值通过R0进行传递
5.内嵌汇编
GNU内嵌汇编,格式如下:
代码语言:javascript复制int main2(){
__asm__( //大括号改成中括号
"mov R5,#0x00000005n" //汇编指令需要使用引号包裹,多条语句之间使用回车换行符进行分隔
"mov R6,#0x00000005"
); //需要以分号结尾
return 0;
}
学习工具
- 在线ARM汇编编辑器:https://azm.azerialabs.com/