【发布时间】:2016-08-13 03:16:38
【问题描述】:
代码由我正在研究的编译器生成。但是我不知道为什么它会给我这个错误。没有注册冲突。当我尝试调用 callq *%rbx 时,我的程序立即崩溃,并收到一条错误消息“由信号 SIGSEGV 终止(地址边界错误)”
我了解该程序似乎试图访问非法内存,但我不知道哪里出错了。谁能给我一个提示?
编辑:完成的汇编代码是here,我的编译器试图编译的代码是here。 runtime.c 是here
.globl _main
_main:
pushq %rbp
movq %rsp, %rbp
pushq %r12
pushq %rbx
pushq %r14
pushq %r13
subq $0, %rsp
movq $16384, %rdi
movq $16, %rsi
callq _initialize
movq _rootstack_begin(%rip), %r15
leaq o13352(%rip), %rbx
leaq z13351(%rip), %rcx
movq $1, %rdi
callq *%rcx <------ As noted in comments: z13351() clobbers %rbx
movq %rax, %rcx
movq $20, %rdi
movq %rcx, %rsi
callq *%rbx <------------- (Address boundary error)
...
【问题讨论】:
-
首先,你为什么使用寄存器间接
call指令而不是the usualcall rel32编码?您是否重用了编译器在为全局变量发出加载/存储时使用的相同代码? -
当您的
call发生时,%rbx中有什么内容?之前的函数调用是否可能会破坏%rbx(违反调用约定,至少如果您使用的是标准函数之一)。它是可执行内存的地址吗?如果这是一个 JIT 编译器,你还记得确保内存是可执行的吗?在某些系统上,malloc 和静态分配默认不是来自可执行页面。如果这不是问题,您是否为 RIP 相关的lea正确编码? -
@PeterCordes 嗨,在我的编译器中,我用
function-ref标签注释了所有函数,以便我可以将它们与局部变量区分开来。对不起,我不太明白你的第二个问题。你指的是什么全局变量? -
我试图猜测为什么你的编译器会生成如此愚蠢的代码,而不是发出
call o15954。在将 RIP 相对地址加载到寄存器后,寄存器间接call完全是脑死(并且浪费了分支预测资源)。显然,您的编译器仍处于非常早期的阶段,因此可以预期会出现脑死亡代码,但值得指出。 (请参阅x86 tag wiki 中的性能链接,尤其是 Agner Fog 的东西。)但无论如何,您将需要使用调试器。这是必不可少的。 -
众所周知,间接分支不利于性能。它还浪费了一个寄存器。代码大小也更糟,并且需要更多指令。有效的替代方法是通过标签调用函数的常规方法是使用
call o15954,因此与您在未优化的 gcc 输出中看到的代码相比,编译器发出的代码过于复杂。玩具编译器生成糟糕的代码是可以的,但认识到这一事实是个好主意。我不是说你笨,只是你的编译器发出的 asm。
标签: assembly compiler-construction x86 x86-64