是什么在DMD堆栈跟踪的地址是什么意思?

问题描述 投票:4回答:1

我编译文件stacktrace.d:void main(){assert(false);}与ASLR关闭和运行时,我得到:

[email protected](2): Assertion failure
----------------
??:? _d_assertp [0x55586ed8]
??:? _Dmain [0x55586e20]

objdump -t stacktrace|grep _Dmain

0000000000032e0c w F .text 0000000000000019 _Dmain

如果我跑gdb -q -nx -ex start -ex 'disas /rs _Dmain' -ex q stacktrace

...
Dump of assembler code for function _Dmain:
   0x0000555555586e0c <+0>: 55  push   %rbp
   0x0000555555586e0d <+1>: 48 8b ec    mov    %rsp,%rbp
=> 0x0000555555586e10 <+4>: be 02 00 00 00  mov    $0x2,%esi
   0x0000555555586e15 <+9>: 48 8d 3d 44 c0 02 00    lea    0x2c044(%rip),%rdi        # 0x5555555b2e60 <_TMP0>
   0x0000555555586e1c <+16>:    e8 47 00 00 00  callq  0x555555586e68 <_d_assertp>
   0x0000555555586e21 <+21>:    31 c0   xor    %eax,%eax
   0x0000555555586e23 <+23>:    5d  pop    %rbp
   0x0000555555586e24 <+24>:    c3  retq   

所以,即使前两个字节0x55的只是截断了,0X ......在堆栈跟踪的指示,开始不匹配86e20。

assembly d dmd
1个回答
5
投票

OK,我只是发现,证明了我的直觉从注释感觉的源代码的一部分。

这里的加入,当它的混帐怪:https://github.com/dlang/druntime/blame/bc940316b4cd7cf6a76e34b7396de2003867fbef/src/core/runtime.d#L756

唉,提交信息是不是超级信息,但代码本身,与我的记忆一起,让我很不服气。

因此,这是在core/runtime.d库文件druntime。在撰写本文时,它正好是上线756

enum CALL_INSTRUCTION_SIZE = 1; // it may not be 1 but it is good enough to get
   // in CALL instruction address range for backtrace
callstack[numframes++] = *(stackPtr + 1) - CALL_INSTRUCTION_SIZE;

需要注意的是callstack变量存在使得当前呼叫的副本时,抛出异常。跟踪打印机,要求实际写出来的时候,将看阵列,以确定写什么。 (见,这实在是太慢了查找调试信息打印的文件/行号和函数名,所以它只做当它必须保持正常使用的例外 - 当它被抛出,后来抓住了 - 快)

反正,我记得当回溯用于打印错线。这将打印包含下一指令代码行 - 这可能是从实际的assert / throw语句源相当长的一段距离,使得打印更小的帮助。如果你看那个混帐怪链接,你会看到以前只是字面上的地址复制马上堆栈中的旧代码。

call指令的工作原理是将返回地址到堆栈,然后跳转到子程序地址。返回地址立即调用指令后,所以当CPU回来那里,它不会再次运行呼叫。这就是为什么旧代码会显示错误的行号,错误地把责任推到下面的指令。

新的代码倒带该地址一点点找回来的调用指令本身 - 从而把印刷功能上它所属的线路。但是,在x86上,有几个不同的调用指令,我什至不知道它是能够正确地倒带 - 你只能通过看操作码确定指令的实际大小,你只知道那里的操作码是,如果你知道指令的大小,或者阅读像CPU本身并没有向前序列的代码。此外,在其它处理器架构,尺寸会有所不同。

就像在该行的评论说,虽然,我们实际上并没有做到完美。这回溯的首要目标就是让看在正确的地方的用户。调试信息使用一种边框的 - 如果你在或此功能,或者源线的起始地址后,但尚未在接下来的功能/线路的起始地址,它认为你在那里。它不知道或关心的代码行分数。

因此,它大大简化了通过只是假设大小实现1 - 足以让它放回该边界。

我betcha当GDB做一些类似的内部,只是它的打印机隐藏此,表示从它的跟踪栈直接返回地址。 (顺便说一句有趣的提示:!GDB内部运行时,它传递--DRT-trapExceptions=no你的程序的命令行参数然后它将陷阱在与该程序仍在运行,而不是打印的消息,并说该程序退出,代码为1抛出点)

该druntime打印的代码也可以+1回到它打印隐藏这个内部实现破解......但MEH之前。返回地址也不是在调用实际发生,你需要在你的反汇编看上面的不管。即使GDB实际上并没有显示调用(至少不是我的老版本的它,也许是新的做)的地址。但它可能是很好,如果有人在拆卸一个值,无论grepping ...如果你想使一个公关druntime,我会支持你在那(注意我有没有权力存在,但可以通过评论帮助)。

但是,这至少明确说明了现状。

© www.soinside.com 2019 - 2024. All rights reserved.