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