调试工具之三arm堆栈与汇编(backtrace续)
一.可能会影响堆栈的因素
1.
原理
i.
通常情况下,栈向下(低地址)增长,每向栈中PUSH一个元素,栈顶就向低地址扩展,每从栈中POP一个元素,栈顶就向高地址回退。
ii.
有些编译选项会把函数的参数或局部变量放入寄存器中,但通用的做法是放在栈里, 前者只是辅助手段,且只在当前函数中使用,一旦调用下一层函数,这些值仍然要存入栈中才行。所以说编译选项对栈的结构是有影响的。
iii.
如果一些变量希望不被优化,就在其前面加上volatile, 如下:
volatile int n =
0;
2.
编译选项
i.
–O2
a)
优化级别为2
b)
-O2 时几乎所有的函数都不生成栈帧
ii.
–Os
对生成的二进制代码进行尺寸上的优化,
它在"-O2"的基础上禁止了所有为了对齐而插入的空间
iii.
–fPIC
生成可用于共享库的位置独立代码。所有的内部寻址均通过全局偏移表完成。要确定一个地址,需要将代码自身的内存位置作为表中一项插入。该选项产生可以在共享库中存放并从中加载的目标模块。
iv.
–rdynamic
–g
带有调试信息的程序,-g是加调试信息,-rdynamic是导出符号
3.
异常
有一些异常会把栈的内容写乱,此种情况下,backtrace是无效的
二.辅助工具:
1.
addr2line
i.
功能:显示代码段地址对应于源代码文件的文件名和行
ii.
用法:
addr2line
地址–e 程序名
iii.
注意:
编译时要加参数-g,
编成debug版本后,才能得到正常的line结果
2.
objdump
i.
功能:反汇编可执行文件或动态库,如上面的堆栈格式都是通过反汇编得到
ii.
用法:
objdump –S
程序名
iii.
如果想得到完整的汇编程序,在编译时加-S参数,就会产生汇编文件xxx.s
gcc main.c
-S
3.
nm
i.
功能:用于得到可执行文件或动态库的符号表
ii.
用法:
nm 程序名
三.ARM汇编的简要说明:
1.
寄存器
i.
r0-r7 普通寄存器
ii.
r8-r14备份寄存器
a)
r13,就是sp,
它记录的是栈指针(栈顶),它对应多个物理寄存器,分别在各种异常模式下记录栈地址,以免破坏现场
b)
r14,就是lr,它记录当前子程序的返回地址,它也对应多个物理寄存器,在各种异常模式下记录返回地址,以免破坏现在
iii.
r15程序计数器
a)
r15,就是pc,
它总是包含下一个要被执行的指令的位置
2.
指令
i.
sub 减法指令
a)
例如:sub sp,sp,24
b)
功能:sp=sp-24
ii.
stmfd
a)
例如:stmfd sp!, {fp, ip, lr,
pc}
b)
功能:压栈,栈由高向低增长,最先压入pc,
最后sp指向fp
c)
注意:
fp
(桢指针)应当是零或者是指向栈回溯结构的列表中的最后一个结构,提供了一种追溯程序的方式,来反向跟踪调用的函数。
iii.
str
a)
例如:str r0, [fp, #-72]
b)
功能:把源寄存器的数据传输到指定地址的存储器上,前边那个是源寄存器,它和其它指令写法相反,源寄存器写在前面。
iv.
bl
a)
例如:bl test1
b)
功能:调用函数test1,相当于call
3.
参考《ARM体系结构与编程》杜春雷编著