GNUとは何ですか
GNUは当初、オープンソースの無料で無料のオペレーティングシステムを作成することを目的としたオペレーティングシステムでした。現在、オペレーティングシステムはまだ完成されていません。
GNUプロジェクト:当初の目標は、完全に無料のオペレーティングシステムGNUと対応するソフトウェアを作成することでした。
GCC:(GNUコンパイラコレクション)GNUコンパイラスイート、GNUが提供するツールの完全なセットで、アセンブラ、コンパイラとリンカ、バイナリ変換、デバッグツールなどが含まれます。
GCCの利点:
- 無料でオープンソース
- システムの下部に近く、強力で柔軟性があります
- クロスプラットフォーム、クロスコンパイルが簡単
GCCのデメリット:
- ツールは基本的にコマンドライン方式を採用しており、学習と使用のしきい値が高い
次に学びたいのは、GNUプロジェクトの多くの製品の1つであるGNUFORARMです。
アセンブラと命令セット
-
アセンブラとは
アセンブリ言語を機械語に翻訳するためのツール
-
コンパイラとは
高水準言語を機械語またはアセンブリ言語に翻訳するためのツール
-
アセンブラとコンパイラの違い
アセンブラのサービスオブジェクトはアセンブリ言語であり、コンパイラのサービスオブジェクトは高級言語です。
-
アセンブラとアセンブリ構文ディレクティブの関係
異なるCPUは異なる命令セットに対応し、異なるアセンブラは異なる疑似命令セットとアセンブリ構文に対応します。
各アセンブラは、独自の疑似命令のセットと独自の構文を持つことができます
異なるアセンブラを使用して同じCPUアーキテクチャのアセンブリコードをアセンブルする場合、対応する命令はまったく同じですが、疑似命令には独自のメリットがあります。
;使用ARM官方的汇编器
AREA test, CODE
mov R3,#5
END
;使用GNU的汇编器
.text ;伪指令
mov R3,#5 ;传送指令皆为mov
.end
复制代码
一般的なアセンブラ
- MASMアセンブラ:Microsoftが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
这套工具来进行代码的编译
伪指令和伪操作
-
注释
@我是注释 复制代码
-
段的声明
-
代码段
.text @代码 复制代码
-
数据段
.data @代码 复制代码
-
自定义一个段
.section .pangshu @定义一个名为.panghsu的段 复制代码
-
-
函数或者标签的声明
fun: @在GNU环境中标签后面需要加冒号,而原生环境则不用 mov R0,#4 bx lr 复制代码
-
数据的声明
.byte @定义单字节数据,例如:.byte 0x14; .short @定义双字节数据,例如:.short 0x1000; .long @定义四字节数据,例如:.long 0x10001000; .quad @定义8字节,如:.quad 0x1234567890abcd; .float @定义浮点数,如:.float 0f-314159265358979323846264338327/95028841971.693993751E-40 @ -pi @字符串定义 .string “abcd”, “efgh”, “hello!” .asciz “qwer”, “sun”, “world!” .ascii “welcome/0” @需要注意的是:.ascii伪操作定义的字符串需要自行添加结尾字符’/0’。 复制代码
-
数据的批量定义
-
格式如下:
.rept @重复次数 @数据定义代码 .endr @结束重复定义 复制代码
-
示例
.rept 3 .byte 0x23 .endr 复制代码
-
-
关于align
.global _start _start: mov r0, #0x12 .byte 0x12 .byte 0x34 ;.align 后跟为0,1,2或者不跟参数时都当作是4字节对齐 .align .byte 0x56 ; 2^3次方,8字节对齐 .align 3 .word 0x1 ; 2^5次访,32字节对齐 .align 5 .word 0x2 ; 填充0x12345678,直到16字节对齐 .balignl 16,0x12345678 ;字符串 .ascii "abc123" .align ;.ascii和.asciz的区别是,.asciz会在字符串后自动添加结束符\0. .asciz "def456" mov r0, #0xab ;.balign[wl] align, fill_value, max_padding .align ; .balign 后跟的align参数必须为2的次方,否则会报错,按字节填充, fill_value要为字节 .balign 8, 0x12 ; .balignw 按2字节填充 .balignw 8, 0x3456 ; .balignl 按4字节填充 .balignl 8, 0xabcdef01 复制代码
反汇编后的结果:
00000000 <_start>: 0: e3a00012 mov r0, #18 4: 3412 .short 0x3412 // .byte 0x12 和 .byte 0x34 6: 0000 .short 0x0000 // .align 引起的填充 8: 00000056 andeq r0, r0, r6, asr r0 // .byte 0x56 c: e1a00000 nop ; (mov r0, r0) // .align 3 8字节对齐 10: 00000001 .word 0x00000001 // .word 0x1 14: e1a00000 nop ; (mov r0, r0) // .align 5 ,32字节对齐 18: e1a00000 nop ; (mov r0, r0) 1c: e1a00000 nop ; (mov r0, r0) 20: 00000002 .word 0x00000002 // .word 0x2 24: 12345678 .word 0x12345678 // .balignl 16,0x12345678,填充0x12345678,直到16字节对齐 28: 12345678 .word 0x12345678 2c: 12345678 .word 0x12345678 30: 31636261 .word 0x31636261 // .ascii "abc123" 34: 3332 .short 0x3332 36: 0000 .short 0x0000 38: 34666564 .word 0x34666564 // .asciz "def456" 3c: 3635 .short 0x3635 3e: 00 .byte 0x00 3f: e3a000ab mov r0, #171 ; 0xab // mov r0, #0xab, 这里就不对齐了 43: 00 .byte 0x00 44: 12121212 .word 0x12121212 // .balign 8, 0x12 48: e1a00000 nop ; (mov r0, r0) 4c: e1a00000 nop ; (mov r0, r0) 50: e1a00000 nop ; (mov r0, r0) 54: e1a00000 nop ; (mov r0, r0) 58: e1a00000 nop ; (mov r0, r0) 5c: e1a00000 nop ; (mov r0, r0) 复制代码
指令和伪指令的区别
- 指令: 有与之对应的机器码,能被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ファイルに関数を書き込むことです
int c_sum(int a,int b){
return a+b;
}
复制代码
import
2番目のステップは、またはextern
疑似命令を使用して、アセンブリファイルに関数を導入することです。
AREA code, CODE
import c_sum
mov R0,#1 ;第一个参数
mov R1,#2 ;第二个参数
END
复制代码
3番目のステップでは、BL命令を使用して関数を呼び出します
AREA code, CODE
import c_sum
mov R0,#1 ;第一个参数
mov R1,#2 ;第二个参数
BL c_sum
END
复制代码
ARMでは、関数パラメーターは3つのレジスタR0〜R3を介して渡され、最大4つのパラメーターが渡され、4つを超えるパラメーターがスタックを使用して処理され、関数の戻り値がR0を介して渡されます。
5.インラインアセンブリ
GNUインラインアセンブリ。形式は次のとおりです。
int main2(){
__asm__( //大括号改成中括号
"mov R5,#0x00000005\n" //汇编指令需要使用引号包裹,多条语句之间使用回车换行符进行分隔
"mov R6,#0x00000005"
); //需要以分号结尾
return 0;
}
复制代码
学習ツール
- オンラインARMアセンブリエディタ:azm.azeriabs.com/
ナゲッツテクノロジーコミュニティのクリエイター署名プログラムの募集に参加しています。リンクをクリックして登録し、送信してください。