先說一下環境: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 main
spa
.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 lr
,bx 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壓入棧