cedecl调用约定 - 编译的asm指令导致崩溃

时间:2010-01-22 01:10:11

标签: debugging compiler-construction assembly

将此视为伪代码而不是任何东西。如果你觉得应该包含一些宏观或其他元素,请告诉我。

我对组装很新。我在大学时在pic处理器上进行了编程,但从那以后就没有了。

此处的问题(分段错误)是“编译函数入口,设置堆栈帧”之后的第一条指令。或“推%ebp”。以下是我发现的关于这两条指令的内容:

http://unixwiz.net/techtips/win32-callconv-asm.html

  

保存并更新%ebp:

     

现在我们在新函数中,我们需要一个由%ebp指向的新的本地堆栈帧,所以这是通过保存当前的%ebp(属于前一个函数的帧)并使其指向堆栈的顶部。

    push ebp
    mov  ebp, esp    // ebp « esp
  

一旦%ebp被更改,它现在可以直接引用函数的参数为​​8(%ebp),12(%ebp)。注意,0(%ebp)是旧的基指针,4(%ebp)是旧的指令指针。

这是代码。这是来自我正在研究的项目的JIT编译器。我为学习经历做的比这更多。

IL_CORE_COMPILE(avs_x86_compiler_compile)
{
    X86GlobalData *gd = X86_GLOBALDATA(ctx);
    ILInstruction *insn;

    avs_debug(print("X86: Compiling started..."));
    /* Initialize X86 Assembler opcode context */
    x86_context_init(&gd->ctx, 4096, 1024*1024);

    /* Compile function entrance, setup stack frame*/
    x86_emit1(&gd->ctx, pushl, ebp);
    x86_emit2(&gd->ctx, movl, esp, ebp);

    /* Setup floating point rounding mode to integer truncation */
    x86_emit2(&gd->ctx, subl, imm(8), esp);
    x86_emit1(&gd->ctx, fstcw, disp(0, esp));
    x86_emit2(&gd->ctx, movl, disp(0, esp), eax);
    x86_emit2(&gd->ctx, orl, imm(0xc00), eax);
    x86_emit2(&gd->ctx, movl, eax, disp(4, esp));
    x86_emit1(&gd->ctx, fldcw, disp(4, esp));

    for (insn=avs_il_tree_base(tree); insn != NULL; insn = insn->next) {
        avs_debug(print("X86: Compiling instruction: %p", insn));
        compile_opcode(gd, obj, insn);
    }

    /* Restore floating point rounding mode */
    x86_emit1(&gd->ctx, fldcw, disp(0, esp));
    x86_emit2(&gd->ctx, addl, imm(8), esp);

    /* Cleanup stack frame */
    x86_emit0(&gd->ctx, emms);
    x86_emit0(&gd->ctx, leave);
    x86_emit0(&gd->ctx, ret);

    /* Link machine */
    obj->run = (AvsRunnableExecuteCall) gd->ctx.buf;
    return 0;
}

当调用obj-> run时,用obj作为唯一参数调用它:

obj->run(obj);

如果有帮助,这里是整个函数调用的说明。它基本上是一个赋值操作:foo=3*0.2;。 foo指向C中的浮点数。

0x8067990:  push   %ebp 
0x8067991:  mov    %esp,%ebp
0x8067993:  sub    $0x8,%esp
0x8067999:  fnstcw (%esp)
0x806799c:  mov    (%esp),%eax
0x806799f:  or     $0xc00,%eax
0x80679a4:  mov    %eax,0x4(%esp)
0x80679a8:  fldcw  0x4(%esp)
0x80679ac:  flds   0x806793c
0x80679b2:  fsts   0x805f014
0x80679b8:  fstps  0x8067954
0x80679be:  fldcw  (%esp)
0x80679c1:  add    $0x8,%esp
0x80679c7:  emms   
0x80679c9:  leave  
0x80679ca:  ret    

编辑:就像我上面说的那样,在这个函数的第一条指令中,%ebp是无效的。这也是导致分段错误的指令。那是因为它是无效的,还是我在寻找别的东西?

编辑:抓一点。我一直在输入edp而不是ebp。以下是ebp和esp的值。

(gdb) print $esp
$1 = (void *) 0xbffff14c
(gdb) print $ebp
$3 = (void *) 0xbffff168

编辑:上面的那些值是错误的。我应该使用'x'命令,如下所示:

(gdb) x/x $ebp
0xbffff168: 0xbffff188
(gdb) x/x $esp
0xbffff14c: 0x0804e481

以下是邮件列表中某人对此的回复。有人想照亮他的意思吗?如何查看堆栈的设置方式?

  

我看到的一个直接问题是   堆栈指针未正确对齐。   这是32位代码和英特尔   手册说堆栈应该是   在32位地址处对齐。那是,   特别是esp中最不重要的数字   应为0,4,8或c。

     

我还注意到ebp和ebp中的值   特别是相距甚远。通常情况下,   它们包含相似的值 -   地址在堆栈中的某个地方。

     

我会看看堆栈是如何设置的   在这个计划中。

他回复了对上述评论的更正。进一步投入后,他无法看到任何问题。

另一个编辑:有人回复说代码页可能没有标记为可执行文件。我如何确保它被标记为?

2 个答案:

答案 0 :(得分:1)

如果push %ebp导致段错误,那么您的堆栈指针不指向有效堆栈。控制如何达到这一点?你在什么平台上,运行时环境有什么奇怪之处?在函数的入口处,%esp应该指向堆栈中调用者的返回地址。是吗?

除此之外,整个功能非常奇怪。您可以选择在fp控制字中设置舍入位,然后不执行任何受舍入影响的操作。所有函数都是复制一些数据,但是当你也可以使用整数寄存器时,使用浮点寄存器来完成它。然后是使用MMX指令后需要的虚假emms,而不是在进行x87计算之后。

编辑请参阅Scott(原始提问者)的答案,了解崩溃的实际原因。

答案 1 :(得分:1)

问题与代码无关。将-z execstack添加到链接器可以解决问题。