macOS x86_64架构汇编Hello World程序逐行疑问求解
疑问逐条解答
- 你的理解正确。
global _main是NASM的伪指令,作用是将_main符号导出为全局可见,链接器在链接时会将这个全局符号识别为程序的入口点,macOS下用gcc链接时默认的入口符号也是_main,符合常规写法。 - 你的理解基本正确。
section .text是段定义伪指令,声明后续内容属于代码段,链接时会把所有输入文件的.text段合并到最终可执行文件的代码段,操作系统加载程序时会将代码段映射为可读可执行的内存页,禁止写入修改。 - 可以这么理解。
_main本质是一个自定义标签,标记了代码段中某条指令的内存地址,因为我们将它声明为入口点,所以操作系统加载完程序后会从这个标签对应的地址开始执行指令,写法和普通函数标签完全一致。 - 这个十六进制数是macOS x86_64架构下的系统调用号。macOS的BSD系系统调用号统一带有
0x2000000的偏移量,4对应write系统调用,组合后就是0x2000004。执行syscall指令时,内核会优先读取rax寄存器的值判断要调用哪个内核函数,所以这里是提前指定要执行的系统调用类型。 - 1对应stdout标准输出的认知是对的。macOS x86_64系统调用遵循System V AMD64 ABI,前6个参数依次存放在rdi、rsi、rdx、r10、r8、r9寄存器中。
write系统调用的第一个参数是文件描述符,这条指令就是把1写入rdi寄存器,给write调用传递第一个参数,指定输出到标准输出。 - 这是NASM支持的局部标签关联写法,
.len是str的附属标签,NASM允许通过父标签名.子标签名的方式引用附属标签的值,这种写法可以避免全局标签命名冲突,让关联变量的属性定义更清晰。 - syscall的执行逻辑是:CPU执行到syscall指令时,会从用户态切换到内核态,内核首先读取rax寄存器的系统调用号匹配对应的内核处理函数,再按ABI约定从指定寄存器读取参数,执行完内核逻辑后将返回值写入rax寄存器,再切回用户态继续执行syscall后的指令。
- 和write系统调用的逻辑一致,
0x2000001是macOS下exit系统调用的编号,内核读取rax的值判断要执行退出进程的操作,所以必须提前将编号写入rax寄存器。 - 你的理解完全正确。同寄存器异或的结果就是0,这种写法的机器码比
mov rdi, 0更短,执行效率更高,是汇编中常用的寄存器清零技巧。这里rdi存储的是exit系统调用的第一个参数,也就是程序退出状态码,0表示正常退出。 - 这里的syscall就是触发执行rax指定的
exit系统调用,作用是让操作系统终止当前进程,释放进程占用的所有资源,执行后不会返回用户态继续执行后续代码。 - 理解基本正确。
str是标签,存储的是后面字符串的首地址,db是定义字节的伪指令,会把字符串每个字符的ASCII码依次存入当前.data段的内存地址中。 - 你对长度计算的理解完全正确,
$是NASM的内置变量,表示当前汇编地址,$ - str就是当前地址减去str标签的地址,正好是前面字符串的字节长度,equ是伪指令,相当于把.len定义为这个常量值,不会占用实际内存空间。点号开头的标签是NASM的局部标签规则:只要标签以.开头,就会自动归属到上一个非局部标签下,适合用来定义变量、结构体的关联属性。
相关学习建议
- 先熟悉System V AMD64 ABI的寄存器传参规则,这是所有x86_64类UNIX系统(包括macOS、Linux)汇编的基础
- 注意macOS的系统调用号比Linux多了
0x2000000的偏移,不要混用两个系统的汇编代码 - 入门阶段可以先用C写对应逻辑,用
gcc -S生成汇编代码对照学习,理解编译器的生成逻辑 - 写代码时注意参数对应的寄存器不要写错,原代码中把长度参数写入rdi就是典型的寄存器用错问题
内容的提问来源于stack exchange,提问作者not_here_to_play
相关产品推荐
相关产品推荐

