アセンブリ言語のGNUARM

GNUとは何ですか

GNUは当初、オープンソースの無料で無料のオペレーティングシステムを作成することを目的としたオペレーティングシステムでした。現在、オペレーティングシステムはまだ完成されていません。

GNUプロジェクト:当初の目標は、完全に無料のオペレーティングシステムGNUと対応するソフトウェアを作成することでした。

GCC:(GNUコンパイラコレクション)GNUコンパイラスイート、GNUが提供するツールの完全なセットで、アセンブラ、コンパイラとリンカ、バイナリ変換、デバッグツールなどが含まれます。

GCCの利点

  • 無料でオープンソース
  • システムの下部に近く、強力で柔軟性があります
  • クロスプラットフォーム、クロスコンパイルが簡単

GCCのデメリット

  • ツールは基本的にコマンドライン方式を採用しており、学習と使用のしきい値が高い

次に学びたいのは、GNUプロジェクトの多くの製品の1つであるGNUFORARMです。

アセンブラと命令セット

  1. アセンブラとは

    アセンブリ言語を機械語に翻訳するためのツール

  2. コンパイラとは

    高水準言語を機械語またはアセンブリ言語に翻訳するためのツール

  3. アセンブラとコンパイラの違い

    アセンブラのサービスオブジェクトはアセンブリ言語であり、コンパイラのサービスオブジェクトは高級言語です。

  4. アセンブラとアセンブリ構文ディレクティブの関係

    異なる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开发环境搭建

我们需要准备以下两个工具:

  1. GCC编译套件
  2. 安卓模拟器

GCC编译套件根据cpu架构和操作系统的不同,又分为了很多子类:

  • 纯ARM裸机: 对应arm-none-eabi工具包
  • ARM架构+Linux操作系统:对应arm-none-linux-eabi工具包

由于接下来我们选择在安卓模拟器上进行开发学习,因此我们选择arm-none-linux-eabi这套工具来进行代码的编译

工具下载

GCC工具的具体使用

伪指令和伪操作

  1. 注释

    @我是注释
    复制代码
  2. 段的声明

    • 代码段

      .text
      	@代码
      复制代码
    • 数据段

      .data
      	@代码
      复制代码
    • 自定义一个段

      .section .pangshu @定义一个名为.panghsu的段
      复制代码
  3. 函数或者标签的声明

    fun:  @在GNU环境中标签后面需要加冒号,而原生环境则不用
    	mov R0,#4
    	bx lr
    复制代码
  4. 数据的声明

    .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’。
    
    复制代码
  5. 数据的批量定义

    • 格式如下:

      .rept @重复次数
        @数据定义代码
      .endr @结束重复定义
      复制代码
    • 示例

      .rept 3
      .byte 0x23
      .endr
      复制代码
  6. 关于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;
}
复制代码

import2番目のステップは、または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;
}
复制代码

学習ツール

ナゲッツテクノロジーコミュニティのクリエイター署名プログラムの募集に参加しています。リンクをクリックして登録し、送信してください。

おすすめ

転載: juejin.im/post/7120418888147271716