【问题标题】:Segfault when loading from [esp] in 64-bit code以 64 位代码从 [esp] 加载时出现段错误
【发布时间】:2019-06-27 03:02:50
【问题描述】:

我是 x86 程序集的新手,我正在尝试构建一个 hello world 程序。我正在尝试制作一个子程序,将单个字节写入标准输出,但我遇到了问题。

mov ebx, [esp+1] 行(在我调用子例程时加载传递的字节)会导致段错误。

我已尝试将 ebx 寄存器与自身异或,以确保它是空的,以确保它不会与系统调用混淆

_start:
    push 32h
    call _writeByte

    ; This just jumps to an exit routine
    jmp  _exit

_writeByte:
    ; This line causes the problem. If I remove it the program works fine
    mov  ebx, [esp+1]
    xor  ebx, ebx

    mov  eax, 1
    mov  edi, 1
    mov  esi, tmp
    mov  edx, 1
    syscall

    ret

为什么程序会出现段错误?

【问题讨论】:

  • 你标记了这个 x86-64。你是64位模式吗?您应该使用rsp 而不是esp,因为后者只是低 32 位,可能指向无效地址。
  • ebx 是一个 32 寄存器。如果要加载单个字节,请使用blbh。问题可能是未对齐的读取,但不确定。另外,esp 指向当前的返回地址。添加1 不足以引起争论。
  • 只是处理mov ebx, [esp+1] 失败的原因。在 64 位代码中,堆栈指针可能跨越 >= 4gb 的地址。大多数(不是全部)操作系统(包括 Linux 和 MacOS)上的 64 位代码 - 堆栈指针位于 >= 4gb 的地址,因此堆栈的内存地址应该使用 RSP(而不是 ESP)。当然那行不做很多时候在下一条指令中将整个 64 位寄存器 RBX 设置为零。
  • 那个 mov 到 EBX 当然会移动 4 个字节,而不仅仅是 1 个字节。如果您想移动一个字节并将值零扩展到 RBX 的所有高位,您可以使用 movzx ebx, byte [rsp+1] 。如果您打算只使用一个字节并希望稍后与 BL(RBX 寄存器的低 8 位)进行比较,您可以这样做 mov bl, [rsp+1]
  • 更不用说[rsp]是返回地址,所以要获取推送参数的第一个字节,他需要使用[rsp+8]

标签: assembly x86 segmentation-fault x86-64 nasm


【解决方案1】:

我处于 x64 模式,就像 cmets 中建议的一群人使用 mov ebx, [rsp+8] 工作一样,因为 esp 只是寄存器的低 4 个字节。堆栈位于虚拟地址空间的低 4 GiB 之外,因此 ESP != RSP 和 [esp] 将是一个未映射的页面。

请注意,x86-64 调用约定在寄存器中传递前几个 args,而不是在堆栈中,因此您通常根本不想这样做(除非您的函数有很多 args)。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-26
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
相关资源
最近更新 更多