先说一下环境: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压入栈