构建第一个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压入栈

相关文章
相关标签/搜索