上一篇学习了armlinux汇编的helloworld,用到了arm汇编的一些基本指令,如mov、ldr、swi,需要详细的arm体系架构的信息可以去arm官网下载arm_architecture_reference_manual.pdf文件,上面有很详细的信息。本文接着上次,继续讲一下如何用汇编调用c函数。
另外,如果觉得那个手册太冗长,或者是英文苦手的话,可以在本站下载这个短小精悍的reference card(中文版哦):
https://lengzzz.com/download/QRC0001_UAL.pdf
现在进入正题:
上一篇中,重点讲述了在arm-linux下的系统调用的方法,但是更多时候,我们可能会与一些c库进行交互,今天学习了汇编调用c库函数的方法。
先上例子:
1 | .section .rodata |
例子很简单, 功能就是计算24并输出2 * 4 = 8. 先使用mul指令计算出24的值放在r1寄存器中, 然后将printf的格式串fmt放到r0中, 然后调用printf函数.
要汇编这个程序不能用昨天简单的指令了. 因为, printf是libc库中的函数, 必须要和c库进行连接:
1 | as mul.s -o mul.o |
相比较上一篇中的连接指令, 多了两条, 一个是-dynamic-linker /lib/ld-linux-armhf.so.3`另一个是-lc
第一个是指令在man pages中的介绍是:
–dynamic-linker=file
Set the name of the dynamic linker. This is only meaningful when
generating dynamically linked ELF executables. The default dynamic
linker is normally correct; don’t use this unless you know what you
are doing.
是用来设置动态链接器的, 在不同的设备上ld-linux-armhf.so.3的名字可能都不一样, 但一般都在lib文件夹中并且以ld开头. 如果没有这条指令的话, 运行程序的时候会提示file not found
第二条是-lc, 和gcc的用法一样, 表示连接libc库. 如果不加这个指令的话连接时会提示undefined “printf”.
现在介绍一下bl指令. bl是branch(分支) link(连接)的缩写(和x86架构的call ret相比意思太含糊了), branch的意思就是跳转(分支)到其他地方执行, link的意思是将当前的pc(Program Counter程序计数器, 保存着当前程序运行到哪里了)寄存器保存到lr(Link Register连接寄存器), 以便调用函数之后函数可以返回回来. 当printf函数运行结束时, 会调用mov pc, lr来返回.
再来说一说arm的寄存器吧. 在用户态, arm的寄存器一共有这几个:
r0 | r1 | r2 | r3 | r4 | r5 | r6 | r7 |
r8 | r9 | r10 | r11 | r12 | r13(sp) | r14(lr) | r15(pc) |
CPSR |
算是比较多的. 其中sp表示堆栈指针(Stack Pointer), lr代表连接寄存器(Link Register), pc表示程序计数器(Program Counter).
具体函数调用时参数传递和返回值用那些寄存器在eabi中都有介绍. 之后将会进行学习.