【问题标题】:Why doesn't this attempt at using sys_write do anything?为什么这种使用 sys_write 的尝试没有做任何事情?
【发布时间】:2018-04-27 12:12:37
【问题描述】:

这里是:

.SECTION .data
    msg: .string "AAAA"

.SECTION .text

.globl _start

_start:
    mov $1, %rax
    mov $1, %rdi
    mov msg, %rsi
    mov $4, %rdx
    syscall

这段代码不仅没有段错误,它也没有输出任何东西。
根据我的阅读,程序应该调用 sys_exit,否则会出现段错误,但这不会发生。

【问题讨论】:

  • 试试mov $msg, %rsi。指令mov msg, %rsimsg 的前八个字节移动到%rsi,这不是您想要的。
  • syscall 之后会发生什么?使用像gdb 这样的调试器单步即可找出答案。或使用strace ./my_prog。顺便说一句,在使用gcc -static -nostdlib foo.s 构建后,它对我来说是段错误,正如预期的那样
  • 这是在带有 gcc7.3.0 的 Arch Linux 上,ld 来自 binutils 2.29.1,这在静态可执行文件中会出现段错误。您是如何构建可执行文件的?
  • 使用英特尔语法。你那里没有这个问题。
  • @sivizius: mov rsi, msg 仍然是 GAS 的 .intel_syntax noprefix 模式下的负载,因为它是 MASM 风格,而不是 NASM 风格。你需要mov esi, OFFSET msg,或lea rsi, [rip + msg]

标签: linux assembly x86-64 system-calls gnu-assembler


【解决方案1】:
mov msg, %rsi

该指令会将“msg”处的数据解释为 64 位值,并将该值加载到寄存器 rsi 中。该指令不会将“msg”的地址加载到寄存器rsi。这可以通过(注意$)来完成:

mov $msg, %rsi

根据我的阅读,程序应该调用 sys_exit,否则会出现段错误,但这不会发生。

您必须知道为什么会发生段错误:

CPU 不知道程序的“结束”在哪里。 CPU也无法区分指令和数据。

例如,字节 0x8A, 0x07 可能表示 mov (%rdi),%al 或者它们可能表示数字 1930 - CPU 不知道。

当到达程序末尾时,CPU 将尝试读取程序之后的字节并将它们解释为指令。

现在三种情况是可能的:

  • 因为 RAM 在 x86 系统上以 4096 字节块的形式进行管理。因此,根据程序的长度,最多 4095 字节的“未使用”RAM 会跟随您的程序。

    CPU 会将 RAM 中的(随机)字节解释为(汇编)指令并执行这些指令。

    当到达 4096 字节块的末尾时,会发生段错误。

  • 4095 字节包含导致段错误的指令(在到达块末尾之前)。

  • 这 4095 个字节表示导致程序无任何异常或无限循环退出的指令。

所以也许你的情况是第三种情况。

【讨论】:

  • @fuz:将lea message(%rip), %rsi 用于 64 位静态地址。 mov $msg, %rsi 绝不是任何事物的最佳选择,无论是 PIE 还是非 PIE 可执行文件,无论它选择 64 位立即数还是 32 位符号扩展。 (在非 PIE 可执行文件中,最有效的选择是 mov $msg, %esi,一条 5 字节指令。)在我的系统上,mov $msg, %rsi 选择 movq,而不是 movabs,因此它不会在 PIE 中链接可执行文件。
  • TL:DR:您的两个不错的选择是 lea msg(%rip), %rsi (PIC) 或 mov $msg, %esi(非 PIC,静态地址适合 32 位)
猜你喜欢
  • 2021-08-11
  • 2021-12-20
  • 2021-05-06
  • 1970-01-01
  • 2011-07-25
  • 2015-06-30
  • 1970-01-01
  • 2023-04-06
相关资源
最近更新 更多