连续sys_write系统调用未按预期工作,是OS X上的NASM Bug吗?
macOS NASM汇编宏输出问题:无法正常显示两个单词
我正在用NASM学习macOS下的x86_64汇编,尝试写一个变体版的“Hello, World”程序——通过宏分别输出两个单词,但程序没法正常运行。
我的情况
- 核心思路:定义一个输出字符串的宏,分别调用它输出两个不同的单词
- 编译运行命令:(补充你的实际命令)
# 示例命令,替换为你的实际命令 nasm -f macho64 program.asm -o program.o ld -o program program.o -lSystem -syslibroot `xcrun -sdk macosx --show-sdk-path` -e _start -arch x86_64 ./program - 预期结果:(补充你的预期输出,例如
Hello World) - 实际结果:(补充你的实际输出,例如无内容、乱码、仅输出单个单词等)
我忽略了什么?该如何修复?
嘿,我之前在macOS上用NASM写汇编时也踩过类似的坑!结合x86_64 macOS汇编的特性,你大概率是遇到了以下几个常见问题之一,咱们一个个排查:
1. 系统调用号用错了
macOS的系统调用规则和Linux完全不一样!Linux的write调用号是1,但macOS需要在常规调用号前加上0x2000000的前缀,比如write是0x2000004,exit是0x2000001。如果用了Linux的调用号,程序直接会崩溃或者无输出。
2. 宏没有处理寄存器冲突
macOS的系统调用会破坏rax、rdi、rsi、rdx这些传参寄存器,如果你的宏在调用系统调用后没有保存/恢复这些寄存器,第二次调用宏时可能会用到被破坏的旧值,导致输出异常。
3. 字符串长度计算错误
如果手动输入字符串长度时出错(比如少算、多算字符数),会导致输出乱码、截断,或者输出内存里的垃圾数据。建议用equ $ - str的方式自动计算长度,避免手动失误。
4. 编译链接命令不完整
macOS的ld链接器需要指定系统库路径和入口点:
- 必须加
-lSystem链接系统库 - 用
-e _start指定入口函数(macOS汇编里入口点需要下划线前缀) - 用
-syslibroot指定SDK路径,否则可能找不到系统库
5. 入口点或退出逻辑缺失
如果程序没有正确的_start入口,或者调用完输出后没有执行exit系统调用,程序可能会崩溃或者出现未知行为。
修复后的示例代码
给你一个能正常运行的参考实现,完美解决上述问题:
%macro print 2 ; 保存会被系统调用破坏的寄存器 push rax push rdi push rsi push rdx mov rax, 0x2000004 ; macOS write系统调用号 mov rdi, 1 ; 文件描述符:标准输出 mov rsi, %1 ; 传入字符串地址 mov rdx, %2 ; 传入字符串长度 syscall ; 恢复寄存器,避免影响后续调用 pop rdx pop rsi pop rdi pop rax %endmacro section .data hello_str db 'Hello', 0x20 ; 0x20是空格字符 hello_len equ $ - hello_str ; 自动计算字符串长度 world_str db 'World', 0x0a ; 0x0a是换行符 world_len equ $ - world_str section .text global _start ; 声明入口点 _start: print hello_str, hello_len print world_str, world_len ; 正常退出程序 mov rax, 0x2000001 mov rdi, 0 syscall
用以下命令编译运行:
nasm -f macho64 hello.asm -o hello.o ld -o hello hello.o -lSystem -syslibroot $(xcrun -sdk macosx --show-sdk-path) -e _start -arch x86_64 ./hello
运行后会输出:
Hello World
如果你的代码和这个示例有差异,可以把你的源代码、预期/实际输出补充出来,我再帮你精准排查!
内容的提问来源于stack exchange,提问作者The Sauralph
相关产品推荐
相关产品推荐

