Arm汇编学习笔记(二)——编写编译并执行依赖外部模块的汇编代码以及PIC代码分析
2016-08-12 11:43
766 查看
1. 编译依赖外部模块的汇编代码并执行
创建test.S汇编文件,并输入如下内容:.global main .extern printf .text main: stmfd sp!, {r11, lr} ldr r0, =str bl printf ldmfd sp!, {r11, pc} .data str: .asciz "Hello asm\n" .end
上面代码调用到了libc库中的printf函数。
输入下面命令编译成目标文件
arm-linux-androideabi-as test.S -o test.o得到test.o目标文件。
输入下面命令编译成可执行文件
arm-linux-androideabi-ld test.o ~/Softwares/Android/android-ndk-r9d/platforms/android-19/arch-arm/usr/lib/crtbegin_dynamic.o ~/Softwares/Android/android-ndk-r9d/platforms/android-19/arch-arm/usr/lib/crtend_android.o -l ~/Softwares/Android/android-ndk-r9d/platforms/android-19/arch-arm/usr/lib/libc.so -I /system/bin/linker -o test.out得到可执行文件test.out,将其push到android设备中运行可以打印出Hello asm字符串。
2. PIC位置无关代码分析
在上面的例子中我们编写的汇编代码实际上使用的是位置相关代码,首先看一下位置无关以及位置相关代码的定义。位置无关码:CPU取指时,总是相对于本条执行指令的相对地址去取指。比如指行一个ADD指令时,PC要取下一指令的地址,就在原来的基础上+4。这就不管你代码放在存储器的任何位置,只要他们的相对地址没有改变,就能正常执行程序。 位置相关码:可以这样来说,就是CPU每次取指都从绝对位置去取,而不是上面的相对位置。这个绝对地址就是相对起始地址0来说的。这样,就要求你在存放程序时,必须给连接脚本所规定的一样,把代码放到指定位置。
我们上面的例子中在给printf传递参数的时候用到了str,即"Hello asm\n"字符串的绝对地址(str是汇编代码中代表字符串的标签,这里只是为了方便大家看,包括所有的编程语言的符号都一样,在实际的机器代码中是没有这些标签和符号的,有的只有数据和地址)。在代码链接成可执行文件的时候str会被分配一个地址,但是如果在加载到内存中的时候内存地址与被分配的地址不一样,那么在运行的过程中就会出错。
所以一般在我们编程的过程中都会加上一个编译参数-fPIC,生成位置无关的代码。
下面我们举一个位置无关的例子给大家看,看下面的C代码:
#include <stdio.h> int main(void) { printf("Hello CrossCompile\n"); return 0; }gcc编译生成可执行文件后我们通过ida查看其汇编代码如下图:
可以看到其汇编代码中是如何为printf传递参数的,基本上是下面三条语句
LDR R3, =(aHelloCrosscomp - 0x8298) ADD R3, PC, R3 ; MOV R0, R3 ;而aHelloCrosscomp的定义是在rodata段中,如下图:
上面的语句到底是什么意思呢?仔细分析一下就可以知道,所谓位置无关的代码就是要访问相对地址,不管可执行文件加载到内存中的任何位置,可执行文件内部的数据和指令之间的相对位置是绝对一致的,我们就可以利用这一点,通过要访问的数据的地址与当前pc值即当前指令地址之间的差值得到这个相对偏移。
那为什么在main函数后面会有"off_82A8 DCD aHelloCrosscomp - 0x8298"这样的一个数据定义呢?原因很简单,因为一条ARM指令只有32位,而aHelloCrosscomp的位置有可能很大,一条指令是放不下的,而地址的相对偏移是固定的,在编译阶段就可以确定,所以编译器直接算好了在text段函数的后面定义了一个临时的内部的数值。一定是通过pc值加上这个相对偏移,得到字符串的最终地址存储在寄存器中,而不能直接拿字符串的32位的地址去操作,一条ARM指令放不下这么多数据。
那为什么是aHelloCrosscomp - 0x8298呢?这个0x8298是怎么得到的呢?我们看代码中,真正将当前指令与相对偏移计算得到字符串地址的指令是".text:00008290 ADD R3, PC, R3"这条指令,它的地址是0x8290,而我们根据CPU三级流水线的原理知道,在执行到这条指令的时候,已经去加载后面第二条指令了,这时候pc的值是当前指令地址+0x8才对,即0x8298。这是非常重要的,要谨记!!
就分析这么多,如有错误,欢迎指正!
相关文章推荐
- android ARM 汇编学习—— 在 android 设备上编译c/cpp代码并用objdump/readelf等工具分析
- Linux netfilter 学习笔记 之二 ip 层netfilter的hook 注册以及执行hook函数的概要分析
- 32位汇编语言学习笔记(9)--分析while循环的汇编代码
- JVM学习笔记(二)------Java代码编译和执行的整个过程
- Linux netfilter 学习笔记 之九 ip层netfilter的连接跟踪模块代码分析
- 1.预处理,生成预编译文件(.文件): Gcc –E hello.c –o hello.i 2.编译,生成汇编代码(.s文件): Gcc –S hello.i –o hello.s 3.汇编,生成目标文件(.o文件): Gcc –c hello.s –o hello.o 4.链接,生成可执行文件: linux笔记
- Linux netfilter 学习笔记 之九 ip层netfilter的连接跟踪模块代码分析
- JVM学习笔记二--java代码编译和执行
- JVM学习笔记(二)------Java代码编译和执行的整个过程【转】
- JVM学习笔记(二)------Java代码编译和执行的整个过程
- JVM学习笔记(二)------Java代码编译和执行的整个过程
- EBS 学习笔记 发运模块代码编写的思路
- JVM学习笔记(1、 基本结构;2、Java代码编译和执行的整个过程3、内存管理和垃圾回收 4、 内存调优 )
- 32位汇编语言学习笔记(10)--分析for循环的汇编代码
- JVM学习笔记(二)------Java代码编译和执行的整个过程
- 32位汇编语言学习笔记(8)--分析do-while循环的汇编代码
- JVM学习笔记(二)------Java代码编译和执行的整个过程
- (转载)JVM学习笔记(二)------Java代码编译和执行的整个过程
- Lua 学习笔记(七)编译、执行外部代码块
- 朱老师ARM裸机学习笔记(七):汇编写启动代码之调用C语言