呼叫时的segfault,但不跳到rax中的地址



我正在使用类似汇编器的API(不是真的 可以发出机器代码),我正在调试和玩耍。它是专门用于系统v x86_64 abi的

由于某种原因,当我发出这样的人为代码时,用于测试目的

builder.emit_sub(rsp, 1);
builder.emit_movq_vr(reinterpret_cast<uint64_t>(&hello_world), rax);
builder.emit_call(rax);
builder.emit_add(rsp, 1);
builder.emit_ret();

在呼叫上发生分割故障(在运行时,不是在组装时),

builder.emit_movq_vr(reinterpret_cast<uint64_t>(&hello_world), rax);
builder.emit_jmp(rax);

成功。失败点似乎是在call指令上,但是我不知道是什么在伪装。它可能会散发错误的操作数或其他内容,但我不确定。原始发射的计算机代码看起来像是这样的错误代码,以及它应该代表的opcode,如某些简单的调试语句所打印

所用的。
sub    48 81 EC 01 00 00 00
movqvr 48 B8 63 80 AA 01 01 00 00 00
call   FF D0
add    48 81 C4 01 00 00 00
ret    C3

注释:movqvr不是真正的指令[mnemonic];最后,vr只是对我的调试注释,说这是一种"移动IMM64"指令。

注释:subadd将在16字节边界上对齐堆栈,我认为这是此ABI的必要性。它们本可以更好地写成push raxpop rax(如果需要rax的返回值需要pop rcx),但是请忽略,除非是这样会弄乱呼叫(例如,如果rsp未正确修改))。

是的,在系统v ABI中,堆栈在每个call指令之前都与16字节边界对齐。因此,在函数输入时,将需要另一个 8 bytes(而不是1)才能达到下一个16字节边界。请记住,在C中,指针差异由sizeof(type)缩放,但在ASM中不是。

是的,push rax/pop rcx将是一个不错的选择,如果Clang/llvm不需要推动奇数的呼叫保存寄存器或保留任何额外的堆栈空间,则是Clang/LLVM所做的。如果您确实需要为当地人保留任何堆栈空间,请使用将使rsp 16字节对齐的偏移量。


顺便说一句,当立即拟合的8位值(即if ((int8_t)imm == imm))立即拟合时,您可以使用sub r/m64, imm8编码来保存代码大小。另外,如果您需要添加/减去 128,请注意-128适合IMM8,因此您可以add rsp, -128(例如,在使用奇数push指令之后)。

如果您知道代码将从何处运行的地址,则应使用call rel32编码,而不是寄存器调用。但是您是对的,跳到任意的64位地址需要此mov r64, imm64序列,而不是直接的call


您是否使用调试器来找出hello_world在哪里崩溃了?也许如果它调用printf(而不是puts),则忘记了零al(带有xor eax,eax)表示XMM寄存器中没有FP ARG,因此也许Printf使用了一些16个字节的SSE SSE校准商店?

? ?

让RSP甚至没有qWord-cariged都非常糟糕,但是我不希望它崩溃了任何会崩溃的8个字节对齐(但不是16)。

最新更新