汇编语言是高度平台相关的,在不同的平台上还会有不同的汇编编译器. 汇编语言不是纯机器语言,在汇编语言的代码里主要有编译器处理的伪指令,和能一一映射为机器指令的指令. 不同的编译器有不同的格式,对应arm常见的两种arm官方的armasm(codewarrie)和gnu的GAS(gnu asm). 这两种是不同语句格式,这里主要讨论gnu asm,Android NDK自带了gnu编译器. 路径为:
${NDK installed dir}\toolchains\arm-linux-androideabi-x.x\prebuilt\windows-x86_64\bin\arm-linux-androideabi-as.exe
${NDK installed dir}\toolchains\arm-linux-androideabi-x.x\prebuilt\windows-x86_64\bin\arm-linux-androideabi-ld.exe
在同一目录下还有其他有用的工具.
label: instruction|directive|pseudo-instruction; comment
label :可选的,在preprocess阶段指示该条指令的地址,用来程序跳转和变量的定义.label需要和:紧挨着中间不能有空格,label:前后可以可以有空格.
instruction:直接对应一个机器指令,如mov add 等
pseudo-instruction: 并不是真实的机器指令,编译器会把这个指令翻译成机器指令,比如 LDR R0,=immediate;
编译器会根据立即数的大小转化成不同机器指令,如立即数小于255会转化为mov,否则会转化为LDR
不含等号的指令.
directive: 这个是给编译器用的,directive指令都是以.
开头的如.section .align
等等, 具体介绍
关于汇编中section
和relocate
相关的介绍
c和汇编实现对比
系统函数调用
系统函数定义位置${NDK installed dir}\platforms\android-21\arch-arm\usr\include\asm\unistd.h
对应函数参数的定义${NDK installed dir}、platforms\android-21\arch-arm\usr\include\unistd.h
系统调用的形式
/*write hello*/
mov R7, #4
mov R0, #1
ldr R2, =len1
ldr R1, =string
svc 0
.section .data
string:
.ascii "Hello World\n"
len1 = . - string
上面的代码是调用system call write
函数打印hello world
的代码.
所有的系统调用函数都可以在上面的系统函数定义的文件中找到.
在系统调用中R7
表示系统调用的id,比如上面的write函数对应的值为4,read函数对应的值为3.
systemcall中关于write函数的定义为:
ssize_t write(int fd, const void *buf, size_t count);
可见第一个参数为fd,上面的例子中为打印字符串到标准输出,所以第一个参数R0为标准输出的fd 为 1.
第二个参数R1为要输的内存的地址,上面的例子中赋值为字符串的地址.
第三个参数为输出字符的长度R2,上面的例子是通过preporocesor计算得到的,实际就是字符串的长度.
svc为执行系统调用的软中断指令,同(swi).
E:\code\android\test-ams>arm-linux-androideabi-objdump -d test.o
test.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e3a07004 mov r7, #4
4: e3a00001 mov r0, #1
8: e59f2060 ldr r2, [pc, #96] ; 70 <_start+0x70>
c: e59f1060 ldr r1, [pc, #96] ; 74 <_start+0x74>
10: ef000000 svc 0x00000000
14: e3a07003 mov r7, #3
18: e3a00000 mov r0, #0
1c: e3a02001 mov r2, #1
20: e59f1050 ldr r1, [pc, #80] ; 78 <_start+0x78>
24: ef000000 svc 0x00000000
28: e3a07004 mov r7, #4
2c: e3a00001 mov r0, #1
30: e59f2044 ldr r2, [pc, #68] ; 7c <_start+0x7c>
34: e59f1044 ldr r1, [pc, #68] ; 80 <_start+0x80>
38: ef000000 svc 0x00000000
3c: e3a07004 mov r7, #4
40: e3a00001 mov r0, #1
44: e3a02001 mov r2, #1
48: e59f1028 ldr r1, [pc, #40] ; 78 <_start+0x78>
4c: ef000000 svc 0x00000000
50: e3a07004 mov r7, #4
54: e3a00001 mov r0, #1
58: e3a02001 mov r2, #1
5c: e59f1020 ldr r1, [pc, #32] ; 84 <_start+0x84>
60: ef000000 svc 0x00000000
64: e3a00000 mov r0, #0
68: e3a07001 mov r7, #1
6c: ef000000 svc 0x00000000
70: 0000000c .word 0x0000000c
74: 00000000 .word 0x00000000
78: 00000017 .word 0x00000017
7c: 0000000b .word 0x0000000b
80: 0000000c .word 0x0000000c
84: 00000018 .word 0x00000018
这里想说的是上面的代码关于pc偏移地址的计算.要在当前的地址基础上加上8才是pc的地址.
以第一个8: e59f2060 ldr r2, [pc, #96] ; 70 <_start+0x70>
为例,当前地址为8,pc地址为8+8=0x10,偏移96后的地址为 0x10+0x60(96)=0x70,正好为后面注释里写的地址.
附上一个hello world,同时读入一个字符的代码(没有优化,写的很烂).
.section .text
.global _start
_start:
/*write hello*/
mov R7, #4
mov R0, #1
ldr R2, =len1
ldr R1, =string
svc 0
/*read one byte*/
mov R7, #3
mov R0, #0
mov R2, #1
ldr R1, =char
svc 0
/* write after read*/
mov R7, #4
mov R0, #1
ldr R2, =len2
ldr R1, =str2
svc 0
/* write byte*/
mov R7, #4
mov R0, #1
mov R2, #1
ldr R1, =char
svc 0
mov R7, #4
mov R0, #1
mov R2, #1
ldr R1, =str3
svc 0
mov R0, #0
mov R7, #1
svc 0
.section .data
string:
.ascii "Hello World\n"
len1 = . - string
str2:
.ascii "after read:"
len2 = . - str2
char:
.byte 0x21
str3:
.ascii "\n"
附录: Introducing ARM assembly language
Whirlwind Tour of ARM Assembly