C语言排序函数转汇编后出现段错误,求排查修复建议
C语言冒泡排序转汇编后的段错误排查与修复
我把给定的C语言排序函数转成了汇编代码,但所有测试用例运行时都报段错误。预期排序结果如下:
- 排序后列表 = {abl, bal, bla, blo, bol, lab, lob, obl}
- 排序后列表 = {100, 200, 300, 400, 500, 600, 700, 800}
- 排序后列表 = {apple, banana, blueberry, kiwi, mango, orange, peach, strawberry}
我已经逐行注释了汇编代码,但还是找不到问题。作为汇编新手,求排查和修复建议。
需要复刻的C语言代码
void sort(char **list, int n) { int sorted=0, i=1, j; char *temp; while (i < n && !sorted) { j=0; sorted = 1; while (j<n-i) { if (cmp(list[j], list[j+1]) > 0) { temp = list[j]; list[j] = list[j+1]; list[j+1] = temp; sorted = 0; } j++; } i++; } } int cmp(char *str1, char *str2) { while (*str1 && (*str1 == *str2)) { str1++; str2++; } return *str1 - *str2; }
我编写的汇编代码
.global sort sort: # we push callee saved regs for the first 2 lines: pushq %rbx pushq %rbp #set the 2 registers to 0 movq $0, %rbx movq $0, %rbp #save n in ebx: movl %esi, %ebx #saving pointer list movq %rdi, %rbp #sorted here movq $0, %rdx #i movq $1, %rcx #j movq $0, %r8 #*temp being initialized: movq $0, %r9 retq #first while loop .L1: #compare i and n cmpl %ebx, %ecx #if greater than, jump to L2 jge .L2 #comparing sorted/sorted testl %edx, %edx #if not equal, go to the while loop: je .L3 .L2: #need to pop these values before we can return: popq %rbp popq %rbx ret #second while loop: .L3: #setting j = 0 movl $0, %r8d #setting sorted to 1: movl $1, %edx .inner: #bringing n: movl %ebx, %esi #n-i subl %ecx, %esi #compare n-i and j: cmpl %r8d, %esi #if greater than, jump to L4: je .L4 L8: #getting list[j]: movq (%rbp,%r8,8), %rdi #list[j+1] movq 8(%rbp,%r8,8), %rsi call cmp #test result: testb %al, %al #jump to L7 if less than or = to 0: jle .L7 #list[j] is now in rax movq (%rbp, %r8, 8), %rax #temp = list[j +1] movq 8(%rbp, %r8, 8), %r10 #list[j+1] = list[j] movq %rax, 8(%rbp, %r8, 8) #list[j] = list[j+1] movq %r10, (%rbp, %r8, 8) #set edx to zero: movl $0, %edx .L7: #incrementing j in this label incl %r8d #jmp back to inner while loop jmp .inner .L4: #incrementing i: incl %ecx #go to L1- outer while loop jmp .L1 cmp: #%al = *str1 movb (%rdi), %al #r11b = *str2 movb (%rsi), %r11b .L5: #comparing str1 pointers to each other testb %al, %al #if != 0, go to L6: je .L6 #al -r11d cmpb %r11b, %al #if != 0 go to L6 jne .L6 #rdi++ incq %rdi #rsi++ incq %rsi jmp cmp .L6: #al - r11b subb %r11b, %al ret
核心错误点与修复方案
sort函数开头错误返回
你在sort函数初始化完寄存器后直接写了retq,导致函数刚进入就返回,排序逻辑完全没执行。
修复:删除sort函数末尾的retq。外层while循环条件逻辑颠倒
C代码循环条件是i < n && !sorted,你当前的逻辑是只有sorted=0时才进入内层循环,且缺少条件不满足时的退出分支。
修复:修改.L1处的逻辑:
.L1:
cmpl %ebx, %ecx
jge .L2 # i >=n 时退出
testl %edx, %edx
jne .L2 # sorted !=0 时退出
jmp .L3 # 进入内层循环准备
3. **内层while循环条件判断错误** C代码内层循环条件是`j < n-i`,你当前只判断了`j == n-i`的情况,漏掉了`j > n-i`的场景。 修复:调整比较逻辑: ```asm .inner: movl %ebx, %esi subl %ecx, %esi # esi = n-i cmpl %esi, %r8d # 比较j和n-i jge .L4 # j >=n-i 时退出内层循环
- cmp函数逻辑错误
- 每次循环跳回
cmp开头会重新加载字符,导致无限循环或错误比较; - 返回值仅修改了8位的
%al,未扩展为32位int(x86-64中int返回值存在%eax)。
修复后的cmp函数:
- 每次循环跳回
cmp:
.L5:
movb (%rdi), %al
movb (%rsi), %r11b
testb %al, %al
je .L6
cmpb %r11b, %al
jne .L6
incq %rdi
incq %rsi
jmp .L5
.L6:
subb %r11b, %al
movzbl %al, %eax # 扩展为32位int返回值
ret
5. **寄存器违反调用约定** x86-64 System V约定中`%r10`是调用者保存寄存器,未保存直接使用会破坏调用者数据。 修复:改用被调用者保存寄存器`%r12`,并在函数开头/结尾保存恢复: ```asm sort: pushq %rbx pushq %rbp pushq %r12 # 保存r12 ... # 交换代码改用r12 movq (%rbp, %r8, 8), %rax movq 8(%rbp, %r8, 8), %r12 movq %rax, 8(%rbp, %r8, 8) movq %r12, (%rbp, %r8, 8) ... .L2: popq %r12 # 恢复r12 popq %rbp popq %rbx ret ``` --- ### 修复后的完整汇编代码 ```asm .global sort sort: # 保存被调用者保存寄存器 pushq %rbx pushq %rbp pushq %r12 # 初始化寄存器 movl %esi, %ebx # ebx = n movq %rdi, %rbp # rbp = list指针 movl $0, %edx # sorted = 0 movl $1, %ecx # i = 1 movl $0, %r8d # j = 0 # 外层while循环入口 .L1: cmpl %ebx, %ecx jge .L2 # i >=n 时退出 testl %edx, %edx jne .L2 # sorted !=0 时退出 jmp .L3 # 进入内层循环准备 # 退出处理 .L2: popq %r12 popq %rbp popq %rbx ret # 内层循环初始化 .L3: movl $0, %r8d # j = 0 movl $1, %edx # sorted = 1 .inner: movl %ebx, %esi subl %ecx, %esi # esi = n - i cmpl %esi, %r8d # 比较j和n-i jge .L4 # j >=n-i 时退出内层循环 # 调用cmp比较list[j]和list[j+1] movq (%rbp,%r8,8), %rdi movq 8(%rbp,%r8,8), %rsi call cmp # 判断cmp结果>0?不成立则跳过交换 testl %eax, %eax jle .L7 # 交换list[j]和list[j+1] movq (%rbp, %r8, 8), %rax movq 8(%rbp, %r8, 8), %r12 movq %rax, 8(%rbp, %r8, 8) movq %r12, (%rbp, %r8, 8) movl $0, %edx # sorted = 0 .L7: incl %r8d # j++ jmp .inner .L4: incl %ecx # i++ jmp .L1 cmp: .L5: movb (%rdi), %al movb (%rsi), %r11b testb %al, %al je .L6 cmpb %r11b, %al jne .L6 incq %rdi incq %rsi jmp .L5 .L6: subb %r11b, %al movzbl %al, %eax # 扩展为32位int返回值 ret
内容的提问来源于stack exchange,提问作者adviNess
相关产品推荐
相关产品推荐

