NASM汇编命令行传参打开文件失败求助:sys_open调用异常
嘿,我之前也踩过这个一模一样的坑!当硬编码文件名能正常运行,但命令行传参就触发sys_open失败,返回0xffffffffffffffffe(也就是64位下的**-2**,对应ENOENT错误——看似是文件不存在,但大概率是你对命令行参数的处理出了问题),给你拆解几个最常见的问题和解决思路:
1. 你可能没正确获取argv[1]的地址
在x86_64 Linux环境下,程序启动时的栈布局是固定的:
- 栈顶
rsp首先指向参数个数argc - 紧接着是
argv[0](程序自身路径的指针) - 再往后才是
argv[1](第一个命令行参数的指针)
很多新手会犯的错是:直接取rsp+8当作argv[1],但如果程序启动后你做过push操作修改了rsp,或者没考虑栈的初始结构,就会拿到错误的指针。
给你一个正确获取argv[1]的示例代码:
section .text global _start _start: ; 先取出argc,检查是否传入了至少一个参数 pop rdi ; rdi = argc cmp rdi, 2 jl exit_no_arg ; 如果argc < 2,说明没传路径参数 ; 跳过argv[0](程序自身路径),取出argv[1] pop rsi ; rsi = argv[0](没用,跳过) pop rsi ; rsi = argv[1](我们要的文件路径指针) ; 调用sys_open mov rax, 2 ; x86_64下sys_open的系统调用号 mov rdi, rsi ; 第一个参数:文件路径指针 mov rsi, 0 ; 第二个参数:O_RDONLY(只读模式) mov rdx, 0 ; 第三个参数:权限(只读打开时可设为0) syscall ; 检查系统调用是否失败 cmp rax, -1 jl exit_open_fail ; 后续读取逻辑...
2. 先确认你拿到的参数内容是对的
你看到的ENOENT错误,大概率是因为sys_open拿到的路径根本不是你输入的内容。可以加个调试步骤:把argv[1]的内容输出到终端,看看是不是正确的路径。
示例调试代码(在获取argv[1]之后添加):
; 输出argv[1]的内容到stdout mov rdx, 200 ; 假设路径最长200字节,可根据实际调整 mov rsi, rsi ; 要输出的内容指针(就是argv[1]) mov rdi, 1 ; stdout的文件描述符 mov rax, 1 ; sys_write的系统调用号 syscall
如果输出的是乱码或者不完整的路径,那肯定是你获取argv[1]的方式错了。
3. 检查系统调用的参数顺序
x86_64 Linux的系统调用参数顺序是严格固定的:rdi→rsi→rdx→r10→r8→r9,返回值存在rax里。
sys_open的正确参数要求是:
rdi: 文件路径的字符串指针rsi: 打开标志(比如O_RDONLY=0,O_WRONLY=1)rdx: 创建文件时的权限(只有打开标志包含O_CREAT时才需要,否则设为0即可)
如果你把参数顺序搞反了(比如把路径放到rsi,标志放到rdi),那sys_open肯定会失败。
4. 用strace精准定位问题
如果上面的方法都没找到问题,直接用strace工具调试——它会把程序所有的系统调用、参数和返回值都打印出来,一眼就能看到sys_open到底传入了什么路径,以及具体的错误原因。
运行命令:
strace ./your_program /path/to/your/target/file
在输出里找open相关的行,比如:
open("/wrong/path/you/passed", O_RDONLY) = -2 ENOENT (No such file or directory)
这样就能明确看到是不是路径参数传错了。
内容的提问来源于stack exchange,提问作者user4833046

