【问题标题】:What does this Assembly code do, with ascii?这个汇编代码用 ascii 做什么?
【发布时间】:2026-02-05 20:05:01
【问题描述】:

我正在尝试理解以下汇编代码(x86-64 att):

msg: .ascii "This is an examp" # please note it's examp not example

_start:

  mov $msg, %rsi
  mov $1, %rdi
  mov $1, %rdx
  mov $1, %rax
  mov $0, %rbx
  mov $16, %r9
  call exm


exm:
  cmp %rbx, %r9
  je end
  test $1, %rbx
  jnz skip
  syscall

skip:
  inc %rsi
  inc %rbx
  call exm

end:
  ret
  1. 将字符串存储在寄存器中是什么意思?寄存器不是持有 0/1 吗?

  2. 如果它正在转换 ascci ti chars,我知道 char 大小是 1 个字节,我们的字符串是 16 个字母,所以我们需要 16 个字节,而寄存器只有 8 个字节。怎么样?

  3. 因为 rdi 是 1,我们正在从屏幕读取,这是真的吗?

  4. 如果系统调用失败,它将返回 0,如果存储在 rax 中,则返回值,这是否意味着存在无限循环的可能性?

【问题讨论】:

  • 您有一个名为msg1 的标签,但您在实际代码中使用了msg。请澄清哪一个是正确的。
  • @mediocrevegetable1 非常感谢您的指正,已更新
  • 你试过运行它吗?您是否尝试过在 GDB 中单步执行它来观察寄存器值?这应该告诉你 %rdi 得到一个地址,而不是 ASCII 字节。此外,strace ./a.out 是一个很好的方法,可以在您开始弄清楚它是如何/为什么制造它们之前,了解它制造了什么系统调用。

标签: assembly ascii x86-64 system-calls cpu-registers


【解决方案1】:

将字符串存储在寄存器中是什么意思?寄存器不是持有 0/1 吗?

我不知道你说的0/1是什么意思,但是msg是一个标签,基本上等于字符串的起始地址。所以mov $msg, %rsi将字符串"This is an examp"的起始地址移动到%rsi中。

如果它正在转换 ascci ti chars,我知道 char 大小是 1 个字节,我们的字符串是 16 个字母,所以我们需要 16 个字节,而寄存器只有 8 个字节。怎么样?

同样,寄存器只保存字符串的起始地址。

因为 rdi 是 1,我们正在从屏幕读取,这是真的吗?

不从屏幕读取,写入屏幕。您正在使用write 系统调用,您可以在其中输入要写入%rdi 的文件描述符。 1stdout 的文件描述符,所以它会写入终端屏幕。

如果系统调用失败,它将返回 0,如果存储在 rax 中,则返回值,这是否意味着存在无限循环的可能性?

write 在错误时返回 -errno(所以是一个负数),而不是 0。查看 man page 中可能出现的错误,我看不出您的程序有任何可能的错误原因。由于您的程序依赖来自write 的返回值1 来继续调用write,因此如果确实发生错误,您将调用一个不存在的系统调用(因为没有负数系统调用),在这种情况下,什么都不会发生,rax 将被设置为-ENOSYS(似乎是我所做的测试中的-38),这又不是一个有效的系统调用号。我已经尝试编辑您的程序以故意以无效的系统调用号而不是1 开头,它不会导致无限循环,它只是不打印任何内容并退出。因此,您的程序似乎不会导致无限循环。

我不确定适当的 write 调用是否可以在某些情况下从您的程序返回 0(意味着它什么也不打印),但如果确实如此,那么下一个调用将是 read 和您的程序 冻结,直到你输入一些字符串(read 的参数与write 非常相似,所以尽管是一个完全不同的系统调用,但它似乎仍然可以正常运行,尽管它看起来像msg.text 中定义,你不能写入它并且-EFAULT 也将被返回)。同样,这只是假设在某些情况下write 返回 0;我认为这极不可能,甚至可能是不可能的。

【讨论】:

  • 原始 asm 系统调用错误返回 -4095 .. -1 范围内的 RAX = -errno 代码;是 glibc 包装器将其转换为 -1 并设置 errno。所以除非你可以写信返回-EPERM,否则它不会是-1。例如-EBADF-9-EFAULT-14(参见 asm-generic/errno-base.h)。
  • 更重要的是,程序的行为不以write 返回值为条件。 R9 和 RBX 不会被系统调用修改。有一个if(i == max) break,还有一个测试 RBX 是奇数还是偶数,但它不看 RAX。
  • @PeterCordes 没有意识到 errno 的问题,所以修复了,谢谢。至于返回值,是的,我假设 OP 是在询问由write 引起的内部错误是否会导致冻结,因为他们没有检查rax,但是您的观点是有道理的,因此也将其纳入答案:)
  • 哦,我刚刚意识到它确实依赖write(fd, pointer, 1) = 1作为下一个write的电话号码!! (我认为__NR_write=STDOUT_FILENO 是故意选择的,还有__NR_read=STDIN_FILENO 用于x86-64,也许是为了帮助记住哪个是哪个,来自i386 Linux 的不同呼叫号码,其中__NR_write=4 所以这不起作用。)所以以防万一如果失败,稍后的syscall 指令将只返回-ENOSYS,当使用 rax = 某个设置了高位的数字调用时,它不是系统调用表的有效索引。
  • @PeterCordes 你是对的,我完全忽略了这一点!再次编辑了最后一部分,我认为现在是正确的。