Arm匯編學習筆記(二)——編寫編譯并執行依賴外部模塊的匯編代碼以及PIC代碼分析

ARM 293瀏覽

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 asmn"  .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 asmn"字符串的絕對地址(str是匯編代碼中代表字符串的標簽,這里只是為了方便大家看,包括所有的編程語言的符號都一樣,在實際的機器代碼中是沒有這些標簽和符號的,有的只有數據和地址)。在代碼鏈接成可執行文件的時候str會被分配一個地址,但是如果在加載到內存中的時候內存地址與被分配的地址不一樣,那么在運行的過程中就會出錯。

所以一般在我們編程的過程中都會加上一個編譯參數-fPIC,生成位置無關的代碼。
下面我們舉一個位置無關的例子給大家看,看下面的C代碼:
#include <stdio.h>  int main(void) {   printf("Hello CrossCompilen");   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。這是非常重要的,要謹記!!
就分析這么多,如有錯誤,歡迎指正!
七星彩走势图2元网官网