x86-64汇编实现数组反转打印时退出码24异常排查
问题根因
退出码24对应EMFILE错误,即进程打开的文件描述符超出系统上限,这个错误和你代码里的内存越界写直接相关,核心bug有两个:
- 反转函数的内存读写位宽完全错了
你的数组是用dd定义的32位int数组,每个元素占4字节,但_reverse里交换元素时用了64位寄存器r8、r9做内存操作:
64位的mov r8, [rdi] mov r9, [rsi] mov [rdi], r9 mov [rsi], r8mov一次会读写整整8字节,除了你要交换的4字节int,还会越界覆盖元素旁边高4字节的内存。你的数组定义在.data段,后面紧跟着的就是格式字符串、以及C标准库标准IO依赖的stdout流内部结构体。这块内存被你写坏之后,stdout保存的文件描述符被改成了无效值,后续每次调用printf都会尝试新建文件流,没几次就会把进程的文件描述符配额耗光,最后抛24号错误退出。 - 调用约定遵守错误
你以为_printy退出时会还原传入的rdi值,实际上你在两个自定义函数的收尾处都硬编码把rdi改成了全局array的地址,根本没管调用者传入的原始值。另外你也没按x86-64 SysV(macOS用的就是这套调用约定)要求,保存函数内修改过的被调用者保存寄存器,这属于潜在隐患,只是当前代码里还没触发崩溃而已。
你调试器看到的“无法创建返回地址断点”提示,就是内存被越界写改坏之后,对应内存段的权限、内容不符合预期导致的,不是故障根源。
修复方法
按下面几点改就能正常跑:
- 把反转函数里的64位交换改成32位操作,用32位子寄存器存取元素,从根源上避免越界:
mov r8d, [rdi] ; r8d是r8的低32位,一次只读4字节 mov r9d, [rsi] mov [rdi], r9d mov [rsi], r8d - 删掉两个函数末尾硬编码
lea rdi, [rel array]的逻辑,改成函数入口把传入的rdi入栈保存,退出前出栈恢复,不要硬写死全局地址。 - 补全寄存器保存逻辑:函数内用到的
rbp、rbx、r12~r15这类被调用者保存寄存器,全部在入口入栈,退出前还原,避免破坏C库的运行上下文。
修复后的_reverse参考实现
_reverse: push rbp mov rbp, rsp push rdi ; 保存传入的数组起始地址 lea rsi, [rdi + 4 * (length - 1) ] .LOOP2: cmp rdi, rsi jge .DONE2 mov r8d, [rdi] mov r9d, [rsi] mov [rdi], r9d mov [rsi], r8d add rdi, 4 sub rsi, 4 jmp .LOOP2 .DONE2: xor rax, rax pop rdi ; 还原调用者传入的rdi值 pop rbp ret
_printy函数也按同样逻辑,把收尾处硬改rdi的代码删掉,入口保存rdi、退出时恢复即可。你之前连续两次调用_printy不报错,是因为没有越界写破坏C库的内部结构,等反转函数踩了stdout的内存,后续printf调用必然异常。
内容的提问来源于stack exchange,提问作者great coconut
相关产品推荐
相关产品推荐

