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

Debugging cedecl调用约定--编译的asm指令导致崩溃,debugging,compiler-construction,assembly,Debugging,Compiler Construction,Assembly,将其更多地视为伪代码。如果有一些宏或其他元素,你觉得应该包括在内,让我知道 我对组装相当陌生。我在大学时就在pic处理器上编程,但后来什么都没有 这里的问题(分段错误)是“编译函数入口,设置堆栈帧”或“推%ebp”之后的第一条指令。以下是我对这两个说明的了解: 保存并更新%ebp: 现在我们在新函数中,我们需要一个由%ebp指向的新的本地堆栈帧,所以这是通过保存当前的%ebp(它属于前一个函数的帧)并使其指向堆栈的顶部来完成的 一旦%ebp被更改,它现在可以直接引用函数的参数8(%ebp)、1

将其更多地视为伪代码。如果有一些宏或其他元素,你觉得应该包括在内,让我知道

我对组装相当陌生。我在大学时就在pic处理器上编程,但后来什么都没有

这里的问题(分段错误)是“编译函数入口,设置堆栈帧”或“推%ebp”之后的第一条指令。以下是我对这两个说明的了解:

保存并更新%ebp:

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

一旦%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中的一个float

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
导致segfault,则堆栈指针未指向有效堆栈。控制如何达到这一点?您在哪个平台上,运行时环境有什么奇怪的地方吗?在函数的入口,
%esp
应指向堆栈上调用方的返回地址。是吗

除此之外,整个函数非常奇怪。您可以在fp控制字中设置舍入位,然后不执行任何受舍入影响的操作。该函数所做的只是复制一些数据,但在可以使用整数寄存器的情况下使用浮点寄存器来完成。然后是伪的
emm
,这是在使用MMX指令之后,而不是在执行x87计算之后需要的


编辑查看斯科特(原始提问者)的答案,了解坠机的实际原因。

问题与代码无关。将
-z execstack
添加到链接器修复了该问题。

另一方面,您使用的是Intel还是AT&T汇编语法?您似乎正在使用AT&T(基于
mov%esp,%ebp
行);unixwiz的示例使用Intel。@outis:是的,我在这里使用AT&T。通过调用函数指针可以访问控件。函数指针的内容是我在问题中提供的函数中正在编译的内容。obj->run是函数指针。我在Linux上。我不知道如何检查第一段中的最后一个问题。至于舍入位,该函数中的循环用于编译许多不同的场景,因此我认为它是有目的的。但我没有写这篇文章,所以我对它没有积极的看法。另外,AvsNumber(JIT环境中的C类型变量)是一个浮点,所以我认为使用浮点寄存器是合适的。
(gdb) x/x $ebp
0xbffff168: 0xbffff188
(gdb) x/x $esp
0xbffff14c: 0x0804e481