gpt4 book ai didi

c++ - 调用但不跳转到 rax 中的地址时出现段错误

转载 作者:行者123 更新时间:2023-11-28 01:39:46 29 4
gpt4 key购买 nike

我正在使用类似汇编程序的 API(它不是真正的汇编程序,但它可以发出机器代码),我正在调试和玩弄它。它专门用于 System V x86_64 ABI,因此我将只讨论 SysV 调用约定等。

出于某种原因,当我为了测试目的发出一些像这样的人为设计的代码时

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 指令处,但我不知道伪汇编程序出了什么问题。它可能会发出错误的操作码操作数或其他东西,但我不确定。原始发出的机器代码对于错误代码看起来像这样,以及它应该表示的操作码,如一些简单的调试语句打印的那样

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不是真正的指令[助记符];最后的 vr 对我来说只是一个调试注释,它是一种“将 imm64 移至 reg”的指令。

备注:subadd 是将堆栈对齐到 16 字节边界上,我认为这在这个 ABI 中是必需的。它们可以更好地写成 push raxpop rax(或者 pop rcx 如果 rax 是需要一个返回值),但忽略它,除非它是搞乱调用的(例如,如果 rsp 没有被正确修改)。

最佳答案

是的,在 System V ABI 中,堆栈在每个call 指令之前 与 16 字节边界对齐。因此,在函数入口处,它需要另外 8 个字节(而不是 1 个字节)才能到达下一个 16 字节边界。请记住,在 C 中,指针差异按 sizeof(type) 缩放,但在 asm 中它们不是。

是的,push rax/pop rcx 将是一个不错的选择,如果它还不需要推送奇数,这就是 clang/LLVM 所做的调用保留寄存器或保留任何额外的堆栈空间。如果您确实需要为局部变量保留任何堆栈空间,请使用将使 rsp 保持 16 字节对齐的偏移量。


顺便说一句,当立即数适合符号扩展的 8 位值(即 if ((int8_t) imm == imm)).此外,如果您需要添加/减去 +128,请注意 -128 适合 imm8,因此您可以添加 rsp,-128(例如在奇数之后push 指令)。

如果您知道代码运行的地址,您应该使用call rel32 编码,而不是寄存器间接调用。但是你是对的,跳转到任意 64 位地址需要这个 mov r64, imm64 序列,而不是直接的 call


您是否使用调试器找出 hello_world 崩溃的地方?也许如果它调用 printf(而不是 puts),它会忘记将 al 归零(使用 xor eax,eax ) 以指示 XMM 寄存器中没有 FP args,所以 printf 可能使用了一些 16 字节 SSE 对齐所需的存储到堆栈?

RSP 甚至没有 qword 对齐是非常糟糕的,但我不希望它崩溃任何会崩溃的东西 8 字节对齐(但不是 16)。

关于c++ - 调用但不跳转到 rax 中的地址时出现段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47730624/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com