在WSL-Ubuntu(VS Code环境)运行汇编代码时为何出现段错误?
问题分析与解决
你的段错误并不是push rcx/pop rcx本身的逻辑问题,而是违反了x86-64 System V调用约定的栈对齐要求导致的。
核心原因
在x86-64 Linux(包括WSL-Ubuntu)的调用约定中,要求执行call指令调用函数前,栈指针rsp必须是16字节对齐的。因为call会将8字节的返回地址压入栈,进入被调用函数时rsp会偏移8字节,此时被调用函数的栈帧才能保持16字节对齐——很多标准库函数比如putchar的实现依赖这个对齐要求(比如使用SSE指令或分配对齐的栈空间),一旦对齐被破坏就会触发段错误。
看你的代码流程:
- 调用
print_row时,call指令压入8字节返回地址,此时rsp是8字节偏移(未对齐)。 - 执行
push rbx后,rsp恢复16字节对齐。 - 进入循环后执行
push rcx,rsp再次偏移8字节(回到未对齐状态),此时直接call putchar就违反了栈对齐要求,触发段错误。
修复方案
有两种简单的修复方式:
方案1:调整栈对齐后再调用putchar
在push rcx后额外压入一个临时寄存器(比如rax)让栈重新对齐,调用完成后再弹出:
; print_row(10, '<') 重复打印指定字符N次——此例为打印10次'<' print_row: mov ecx, edi ; 传入的重复次数num push rbx ; 保存被调用者保存寄存器的值 mov ebx, esi ; 使用被调用者保存寄存器存储字符值 sub ecx, 1 loop_print: mov edi, ebx push rcx ; 保存rcx寄存器的值 push rax ; 临时压入,让栈恢复16字节对齐 call putchar ; 调用putchar函数 pop rax ; 弹出临时值 pop rcx ; 恢复rcx的值 sub ecx, 1 jns loop_print ; 当ecx非负时继续循环 mov edi, 10 ; 末尾打印换行符'\n' push rax ; 同样需要对齐栈 call putchar pop rax pop rbx ; 恢复rbx调用前的原始值 ret
方案2:使用被调用者保存寄存器存储循环计数
x86-64中r12-r15属于被调用者保存寄存器,标准库函数不会修改它们,因此可以用这些寄存器存储循环次数,避免频繁的栈操作,同时天然满足栈对齐要求:
; print_row(10, '<') 重复打印指定字符N次——此例为打印10次'<' print_row: push rbx ; 保存被调用者保存寄存器 push r12 mov r12d, edi ; 用r12d存储循环次数(32位足够) mov ebx, esi dec r12d ; 初始减1,匹配原逻辑 loop_print: mov edi, ebx call putchar ; 调用前栈是对齐的,无需额外操作 dec r12d jns loop_print ; 非负则继续循环 mov edi, 10 ; 打印换行 call putchar pop r12 ; 恢复寄存器 pop rbx ret
额外说明
- 你代码中
mov al, 0是多余的:putchar不是可变参数函数,调用前不需要设置rax(只有可变参数函数才需要用al传递浮点参数个数)。 - 原循环逻辑是正确的:初始
ecx = num-1,循环中每次减1直到ecx为-1,刚好执行num次打印。
内容的提问来源于stack exchange,提问作者Tomislav 12
相关产品推荐
相关产品推荐

