将此视为伪代码而不是任何东西。如果你觉得应该包含一些宏观或其他元素,请告诉我。
我对组装很新。我在大学时在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中的值 特别是相距甚远。通常情况下, 它们包含相似的值 - 地址在堆栈中的某个地方。
我会看看堆栈是如何设置的 在这个计划中。
他回复了对上述评论的更正。进一步投入后,他无法看到任何问题。
另一个编辑:有人回复说代码页可能没有标记为可执行文件。我如何确保它被标记为?
答案 0 :(得分:1)
如果push %ebp
导致段错误,那么您的堆栈指针不指向有效堆栈。控制如何达到这一点?你在什么平台上,运行时环境有什么奇怪之处?在函数的入口处,%esp
应该指向堆栈中调用者的返回地址。是吗?
除此之外,整个功能非常奇怪。您可以选择在fp控制字中设置舍入位,然后不执行任何受舍入影响的操作。所有函数都是复制一些数据,但是当你也可以使用整数寄存器时,使用浮点寄存器来完成它。然后是使用MMX指令后需要的虚假emms
,而不是在进行x87计算之后。
编辑请参阅Scott(原始提问者)的答案,了解崩溃的实际原因。
答案 1 :(得分:1)
问题与代码无关。将-z execstack
添加到链接器可以解决问题。