【问题标题】:Unreachable code in assembly汇编中无法访问的代码
【发布时间】:2016-05-25 22:44:50
【问题描述】:

我正在调试 linux 中的崩溃,并且正在组装函数 cxa_finalize

崩溃发生在看起来无法访问的源代码行上:

cmp    %edx,%esi             // f >= &funcs->fns[0]
jae    0xb6e17b88            // enter for loop
jmp    0xb6e17c08            // exit for loop
lea    0x0(%esi,%eiz,1),%esi // crashes here - how do we even get here since there is a 
                             // jmp above us, and nothing jumps to here
cmp    %edi,0xc(%esi)        // d == f->func.cxa.dso_handle (jumped to from below)

稍后在此方法中,在 jmpnop 之后出现的另一个 lea 0x0(%esi,%eiz,1),%esi 指令也似乎无法访问。在这种情况下,jmp 也会退出 for 循环。

这里是否存在一些范式,其中包含无法访问的指令?

编辑:当访问esi 的内存时,它并没有在lea 指令上崩溃,而是在它之后的cmp 指令上崩溃。

【问题讨论】:

  • 它可能只是你的调试器正在反汇编为可读指令的垃圾。发生这种情况的原因多种多样。
  • 知道这段代码的地址会有所帮助。例如,您知道jae 0xb6e17b88 指令不只是将jmp 指令跳转到lea 指令吗?
  • 它也可能是其他跳转的目标。顺便说一下,lea 只是对齐的填充,因此可能确实无法达到。
  • 我个人最喜欢的是一个粉碎的堆栈导致返回到代码中的错误位置。欢闹的结果。这不是比尔默里有趣,但它在那里。
  • @davidbak,是的,我知道。我在我的问题中显示了一个简化的输出,但在我的完整输出中,每一行旁边都有一个绝对地址和相对地址,所有跳转也显示相对地址,因此很容易看到所有跳转的位置的功能正在进行中。

标签: c++ c gcc assembly x86


【解决方案1】:

我找到了答案here

有时 GCC 会在代码流中插入 NOP 指令以确保正确对齐和类似的东西。 NOP 指令占用一个字节,因此您会认为可以根据需要添加任意数量的字节。但根据 Ian Lance Taylor 的说法,芯片执行一条长指令比执行许多短指令要快。因此,他们没有插入 7 个 NOP 指令,而是使用了一个 bizarro LEA,它占用了 7 个字节并且在语义上等同于 NOP

【讨论】:

  • 天啊,如果引用中没有“bizarro”这个词,我会做出这样的评论。
  • 如果对齐填充代码无论如何都应该无法访问,那么填充一个 bizarro LEA 指令而不是一系列 NOP 指令有什么优势吗?
  • @IanAbbott:可能没关系。它可能仍会在与使其无法访问的 jmp 相同的循环中被解码。 IDK 如果英特尔 SnB 系列 CPU 将分配一个新的 uop 缓存行来保存那些解码的 uop,以防以后需要它们。 (直到它解码它们之后才知道它们是nops,并且在非nop 的情况下,很可能很快就会有东西跳到那里。)所以它可能会节省循环解码,或者一些uop 缓存中的空间。
  • @IanAbbott:有趣的事实:无条件间接跳转的静态预测是下一个地址。如果这不是可能的分支目标之一,它确实有助于在没有更好的分支目标预测时使用ud2 来停止推测性执行。或者,如果您正在制作跳转表,请尝试将常见情况作为地址放在jmp rax 之后。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多