x86_64 Intel汇编中释放结构体指针触发SEGV崩溃问题
问题分析与修复
你的汇编代码崩溃的核心原因是调用函数前未遵守x86_64栈对齐约定,同时存在多余的栈操作和逻辑缺陷,具体如下:
1. 栈对齐错误(崩溃直接原因)
x86_64 SysV调用约定要求:调用任何函数前,rsp必须是16字节的倍数。你的代码在调用free时,rsp处于8字节对齐状态,违反了该约定。macOS和新版Debian对栈对齐要求严格,会触发SEGV;旧Linux Mint可能容忍不对齐,因此之前能正常运行。
同时,调用free时你额外push r11是完全多余的——free只需要rdi参数,你已经将待释放的节点指针存入rdi,无需再压栈保存r11。这行多余的代码进一步破坏了栈对齐。
2. 逻辑缺陷:未处理头节点删除
原代码仅检查并删除当前节点的下一个节点,完全忽略了头节点本身的匹配情况。如果头节点需要被删除,代码不会处理,导致内存泄漏或后续访问错误。
修复后的代码
global _ft_list_remove_if extern free section .text _ft_list_remove_if: push rbp mov rbp, rsp sub rsp, 40 ; 预留空间保存参数和临时寄存器 mov QWORD [rsp], rdi ; 保存begin_list指针 mov QWORD [rsp + 8], rsi ; 保存data_ref mov QWORD [rsp + 16], rdx ; 保存cmp函数指针 mov QWORD [rsp + 24], rcx ; 保存free_fct函数指针 mov r10, QWORD [rdi] ; r10 = 当前节点(初始为头节点) mov r11, 0 ; r11 = 前驱节点(初始为0,代表头节点无前驱) .check_head: cmp r10, 0 je .restore_stack ; 调用cmp函数检查当前节点是否匹配 mov rdx, QWORD [rsp + 16] mov rsi, QWORD [rsp + 8] mov rdi, QWORD [r10] ; 用栈帧保存寄存器,避免push/pop破坏栈对齐 mov QWORD [rbp - 8], r10 mov QWORD [rbp - 16], r11 call rdx ; 恢复寄存器 mov r10, QWORD [rbp - 8] mov r11, QWORD [rbp - 16] cmp eax, 0 jne .move_to_next ; 当前节点需要删除 mov rax, QWORD [r10 + 8] ; rax = 当前节点的next指针 cmp r11, 0 je .delete_head ; 无前驱,说明是头节点 ; 处理中间节点:更新前驱节点的next mov QWORD [r11 + 8], rax jmp .delete_node .delete_head: ; 更新链表头指针(*begin_list) mov QWORD [rdi], rax .delete_node: ; 释放节点的data mov rcx, QWORD [rsp + 24] mov rdi, QWORD [r10] mov QWORD [rbp - 8], r10 mov QWORD [rbp - 16], r11 call rcx mov r10, QWORD [rbp - 8] mov r11, QWORD [rbp - 16] ; 释放节点本身 mov rdi, r10 call free wrt ..plt ; Linux环境用该行;macOS改为call _free ; 删除后直接跳到下一个节点 mov r10, rax jmp .check_head .move_to_next: ; 无需删除,移动到下一个节点,更新前驱 mov r11, r10 mov r10, QWORD [r10 + 8] jmp .check_head .restore_stack: add rsp, 40 pop rbp ret
修复说明
- 栈对齐修复:使用
rbp偏移的栈帧保存临时寄存器,避免push/pop破坏rsp对齐,确保调用外部函数前rsp始终是16字节倍数。 - 移除多余操作:删除调用
free时冗余的push r11和pop r11操作,精简栈操作逻辑。 - 头节点处理:新增头节点检查分支,当头节点匹配时,直接更新传入的
*begin_list指针,保证链表头正确更新。 - 遍历逻辑优化:改为检查当前节点而非下一个节点,确保所有节点(包括最后一个)都能被处理,避免遗漏。
编译注意事项
- Linux环境:将函数名改为
ft_list_remove_if(去掉下划线),保留call free wrt ..plt。 - macOS环境:函数名保留
_ft_list_remove_if,将call free wrt ..plt替换为call _free。
内容的提问来源于stack exchange,提问作者matteobu02
相关产品推荐
相关产品推荐

