返回

如何在 x86_64 中使用 GAS AT&T 汇编编写“Hello, World!”程序?

Linux

使用 GAS AT&T 汇编在 x86_64 中编写“Hello, World!”程序

引言

对于任何想要涉足汇编语言世界的人来说,编写一个简单的“Hello, World!”程序都是一个绝佳的起点。本指南将向你展示如何使用 GAS AT&T 汇编语言在 x86_64 系统上完成此任务,同时深入探讨汇编和系统调用。

汇编程序代码

我们的汇编程序将生成一个 x86_64 可执行文件,在运行时将打印“Hello, World!”。以下是其代码:

.section .rodata
msg: .ascii "Hello, World!\n"
.set msglen, (. - msg)

.section .text
.global main
main:
  mov $1, %rax
  mov $1, %rdi
  lea msg(%rip), %rsi
  mov $msglen, %rdx
  syscall

  mov $60, %rax
  mov $0, %rdi
  syscall

文件扩展名:.s vs .S

值得注意的是,我们使用 .s 作为汇编程序的文件扩展名,而不是 .S。虽然两者都是有效的,但 GAS AT&T 汇编器对扩展名比较灵活。

然而,在使用 GCC 以 System V AT&T 汇编语言模式汇编程序时,你需要使用 .S 扩展名,因为 GCC 预期 System V AT&T 汇编程序使用该扩展名。

生成汇编程序代码

使用 gcc -S demo.i 命令生成汇编程序代码时,可能会生成 demo.s 文件而不是 demo.S 文件。这是因为 -S 选项指示 GCC 以汇编语言而不是可执行代码生成输出。在这种情况下,生成的汇编程序代码将使用 GAS AT&T 语法,因为 GCC 默认使用 GAS 作为其汇编器。

程序正确性

你的汇编程序代码是正确的,并且应该能够在 x86_64 系统上正常运行。以下是每行的说明:

  • .section .rodata:定义一个只读数据节,其中包含要打印的字符串。
  • msg: .ascii "Hello, World!\n":定义一个 ASCII 字符串,其中包含要打印的文本。
  • .set msglen, (. - msg):计算 msg 字符串的长度并将其存储在 msglen 符号中。
  • .section .text:定义一个文本节,其中包含程序的代码。
  • .global main:声明 main 函数为全局符号。
  • main::定义 main 函数的入口点。
  • mov $1, %rax:将系统调用号 1 (用于 write 系统调用) 复制到 %rax 寄存器。
  • mov $1, %rdi:将文件符 1 (用于标准输出) 复制到 %rdi 寄存器。
  • lea msg(%rip), %rsi:加载 msg 字符串的地址到 %rsi 寄存器。
  • mov $msglen, %rdx:将 msglen 符号的值 (字符串长度) 复制到 %rdx 寄存器。
  • syscall:执行系统调用。
  • mov $60, %rax:将系统调用号 60 (用于 exit 系统调用) 复制到 %rax 寄存器。
  • mov $0, %rdi:将退出状态 0 复制到 %rdi 寄存器。
  • syscall:执行系统调用,导致程序退出。

常见问题解答

1. 如何避免“未定义的引用:main”错误?

确保 main 函数声明为全局符号(使用 .global main)。

2. 如何链接汇编程序代码?

使用 ld 命令链接汇编程序对象文件(.o)和 C 库(.so)。

3. 如何在 x86 系统上汇编程序?

使用 as86 汇编器,而不是 GAS。

4. 如何使用 -m32 选项编译为 32 位 x86?

在 GCC 命令中使用 -m32 选项,例如:gcc -m32 hello.c

5. 如何在 Windows 上汇编 GAS AT&T 汇编程序代码?

需要使用兼容的汇编器,例如 NASM 或 JWasm。

结论

使用 GAS AT&T 汇编语言在 x86_64 系统上编写“Hello, World!”程序是一项相对简单的任务。了解文件扩展名、生成汇编程序代码、确保程序正确性以及解决常见问题对于成功至关重要。通过遵循本指南,你可以开始使用汇编语言探索计算机系统的底层工作原理。