構建第一個ARM程序

先說一下環境:linux

Device: Raspberry Pi Zero W
System: Linux raspberrypi 4.14.98+ #1200 Tue Feb 12 20:11:02 GMT 2019 armv6l GNU/Linux
Compiler: as、gcc
複製代碼

代碼shell

.global main
 main:
    mov  r0, #0x1
    bx lr
複製代碼

而後編譯運行bash

as -o first.o first.s
gcc -o first first.o
./first
複製代碼

不出意外的話你講看到輸出什麼都沒有,這樣就沒意思了,從新運行下函數

./first ; echo $?
1
複製代碼

不出意外你將會看到輸出1,echo $?的意思是獲取最後命令的退出狀態,什麼是退出狀態,你寫一個c程序的main方法返回的那個數字就是,0表明沒錯誤,其餘就表示有錯誤ui


接着來一步一步的看這個程序 首先是第一行的.global mainspa

.global main
複製代碼

main表示每一個程序的入口,你若是不寫main,那你沒發用gcc連接,並且你會獲得相似這樣的錯誤:調試

/usr/lib/gcc/arm-linux-gnueabihf/6/../../../arm-linux-gnueabihf/crt1.o: In function `_start':
(.text+0x34): undefined reference to `main'
collect2: error: ld returned 1 exit status
Makefile:4: recipe for target 'first' failed
make: *** [first] Error 1
複製代碼

因此仍是乖乖的用main,.global的做用是把main聲明爲全局的,主要是爲了讓連接器知道,它是一個GNU彙編器指令,這種指令的做用是讓彙編器作一些特殊的事;以.開頭,後面跟指令和參數。code

接着是main:,這就至關於函數名了cdn

而後是 mov r0, #0x1,將1添加到r0;多說一句,mov不是移動的意思,是複製的意思,好比 mov r0, r1 不是把r1移動到r0,是複製,由於執行完了後r1並無變。而後 r0 寄存器在arm中是用做返回值的。blog

最後是 bx lrbx lr等價於mov pc,lr,lr是寄存器R14,這個寄存器保存的是函數返回地址;這裏的解釋是我從網上抄的,反正我看不太明白這函數返回地址的含義;我用一個特別容易理解的辦法解釋,那就是調試;


我用匯編簡單寫了一個Hello World的程序:

.global main
 main:
    push    {lr}
    ldr     r0,=fmt
    ldr     r1,=str
    bl      printf
    mov     r0, #0
    pop     {pc}
    
.data
fmt:    .asciz  "%s\n"
str:    .asciz  "Hello World"
複製代碼

特別簡單,編譯連接運行

pi@raspberrypi:~/Documents/Arm/Program $ as -o hello.o hello.s && gcc -o hello hello.s
pi@raspberrypi:~/Documents/Arm/Program $ ./hello
Hello World
pi@raspberrypi:~/Documents/Arm/Program $ 
複製代碼

沒任何問題,開始調試gdb hello,下個斷點

gef➤  br main
Breakpoint 1 at 0x10444
gef➤  
複製代碼

而後運行run,能夠看到程序停在了main方法

咱們只須要關注 lr 寄存器,此時$lr : 0xb6e7a678,ni ni si 後

lr寄存器變了,$lr : 0x00010450,反彙編main方法能夠看到lr的地址指向printf後面的指令

因此能夠得知,lr寄存器的做用是保存上一個調用本方法的下一條指令地址,emmm咋感受有點繞,反正你們看到這調試應該都懂了;

其實還能夠說說push,咱們能夠回到剛run的時候,那時候push {lr}已經執行了,lr寄存器內容是0xb6e7a678,而sp棧寄存器的內容是0xbefff17c,這個地址的內容正是棧頂,內容就是lr的內容

因此 push {lr} 就是把lr壓入棧

相關文章
相關標籤/搜索