我常常看到以#{1}}指针终止的ARM堆栈跟踪(读取:Android NDK堆栈跟踪),如下所示:
lr
我知道 #00 pc 001c6c20 /data/data/com.audia.dev.qt/lib/libQtGui.so
#01 lr 80a356cc /data/data/com.audia.dev.rta/lib/librta.so
代表ARM和其他架构上的lr
,并且它是存储返回地址的快捷方式,但我不明白为什么它似乎总是存储无用的地址。在此示例中,link register
无法使用80a356cc
或addr2line
映射到任何代码。
有没有办法获得更多信息?为什么跟踪必须在gdb
地址处停止?
答案 0 :(得分:2)
最后偶然发现了答案。我只需要更加敏锐。查看以下短堆栈跟踪及其后面的信息:
#00 pc 000099d6 /system/lib/libandroid.so
#01 lr 80b6c17c /data/data/com.audia.dev.rta/lib/librta.so
code around pc:
a9d899b4 bf00bd0e 2102b507 aa016d43 28004798
a9d899c4 9801bfa8 bf00bd0e 460eb573 93004615
a9d899d4 6d842105 462b4632 200047a0 bf00bd7c
a9d899e4 b100b510 f7fe3808 2800edf4 f04fbf14
a9d899f4 200030ff bf00bd10 b097b5f0 4614af01
code around lr:
80b6c15c e51b3078 e5933038 e5932024 e51b302c
80b6c16c e1a00002 e3a01000 e3a02000 ebfeee5c
80b6c17c e1a03000 e50b303c e51b303c e1a03fa3
80b6c18c e6ef3073 e3530000 0a000005 e59f34fc
80b6c19c e08f3003 e1a00003 e51b103c ebfeebe6
现在lr
地址仍然是80xxxxxx
地址,对我们没用。
它从pc
打印的地址为000099d6
,但请查看下一部分code around pc
。第一列是地址列表(您可以从每次递增16的事实中得知。)这些地址都不像pc
地址,除非您切掉前16位。然后你会注意到a9d899d4
必须对应000099d4
,并且程序停止的代码是两个字节。
Android的堆栈跟踪似乎已经“切断”pc
地址的前2个字节,但无论出于何种原因,它都不会对叶子寄存器中的地址执行此操作。这让我们找到了解决方案:
简而言之,我能够从80b6c17c
地址中删除前16位以使其成为0000c17c
,到目前为止,它已经为我提供了有效的代码每次我都可以使用gdb
或addr2line
查询。 (编辑:我发现它实际上通常 前12位或前3位十六进制数字。您可以通过查看来自行决定堆栈跟踪输出,如上所述。)我可以确认它也是正确的代码地址。这肯定使调试变得更容易了!
答案 1 :(得分:1)
您是否拥有所有调试信息(-g3
)?
Gcc喜欢使用lr
作为普通注册。请记住,非叶函数看起来像
push {lr}
; .. setup args here etc.
bl foo ; call a function foo
; .. work with function results
pop {pc}
一旦将lr推入堆栈,编译器几乎可以自由地使用它 - lr
将仅被函数调用覆盖。因此lr
中很可能存在任何中间值。
这应该在编译器生成的调试信息中说明,以便让调试器知道它必须查看堆栈值而不是lr
。