含Intel汇编乘法函数的C程序触发段错误问题排查
问题描述
编写了一个C程序调用自定义Intel汇编乘法函数,计算两数乘积后用printf输出,通过Makefile编译链接,但运行触发段错误,错误发生在printf@plt位置,收到SIGSEGV信号。
相关代码
Makefile
output: main main: multiply.o gcc -g -m32 resources/multiply.o main.c -o main multiply.o: nasm -f elf32 resources/multiply.asm -o resources/multiply.o
头文件 multiply.h
#ifndef MULTIPLY_H #define MULTIPLY_H int multiply(int x,int y); #endif
C源文件 main.c
#include <stdio.h> #include "my_library/multiply.h" int main(){ int result = multiply(5,6); printf("%i",result); return 0; }
汇编函数 multiply.asm
global multiply multiply: push ebp mov ebp,esp mov eax,[ebp+8] ;arg2 mov edx,[ebp+12] ;arg1 mov ebx,0 ;prepping the counter sub edx,1 mov ecx,eax ;saving arg1 loop_s: cmp ebx,edx ;comparing ebx to edx, if ebx >= edx, break out of the loop jge loop_e add ebx,1 add eax,ecx jmp loop_s loop_e: mov ebx,0 ;reset ebx to it's initial state (before the loop) mov edx,0 mov esp,ebp pop ebp ret
错误现象
预期输出30,实际触发段错误,错误发生在0x56556050 <printf@plt>。
段错误发生后的寄存器状态
eax 0xffffe030 -8144 ecx 0x5 5 edx 0x0 0 ebx 0x0 0 esp 0xffffccfc 0xffffccfc ebp 0xffffcd28 0xffffcd28 esi 0xffffcdf4 -12812 edi 0xf7ffcb80 -134231168 eip 0x56556050 0x56556050 <printf@plt> eflags 0x10292 [ AF SF IF RF ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x63 99
段错误发生前的寄存器状态
eax 0x1e 30 ecx 0x5 5 edx 0x0 0 ebx 0x0 0 esp 0xffffcd10 0xffffcd10 ebp 0xffffcd28 0xffffcd28 esi 0xffffcdf4 -12812 edi 0xf7ffcb80 -134231168 eip 0x565561fb 0x565561fb <main+47> eflags 0x282 [ SF IF ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x63 99
尝试改用mul edx指令实现乘法,仍触发相同段错误。
错误原因与修复方法
核心问题:破坏调用约定的寄存器规则
在x86 32位的cdecl调用约定中,ebx、esi、edi、ebp属于非易失性寄存器,函数调用后必须保持原值不变。你的汇编函数修改了ebx和ecx,仅重置了ebx却未恢复初始值,也没有在函数开头保存这些寄存器的状态,导致调用者(main函数)的寄存器环境被破坏,进而干扰后续printf的执行流程,引发段错误。
另外,代码中参数读取顺序有误:cdecl约定下参数从右到左压栈,[ebp+8]是第一个参数x,[ebp+12]是第二个参数y,当前注释写反了,虽测试用例结果不受影响,但属于潜在问题。
修复步骤
- 保存并恢复非易失性寄存器:函数开头将需要修改的非易失性寄存器压栈保存,结束前弹出恢复。
- 修正参数读取顺序:调整寄存器赋值,对应正确的参数位置。
- 优化乘法实现(可选):用
mul指令替换循环累加,提升执行效率。
修复后的汇编代码
global multiply multiply: push ebp mov ebp,esp ; 保存非易失性寄存器 push ebx push ecx ; 读取正确的参数:[ebp+8]是x,[ebp+12]是y mov eax, [ebp+8] ; x mov edx, [ebp+12] ; y ; 方法1:用mul指令实现乘法(eax = eax * edx,结果低32位在eax) mul edx ; 方法2:如果保留循环累加,可替换上面的mul指令 ; mov ecx, eax ; 保存x的值 ; mov eax, 0 ; 初始化结果为0 ; loop_s: ; add eax, ecx ; dec edx ; jnz loop_s ; 恢复非易失性寄存器 pop ecx pop ebx mov esp,ebp pop ebp ret
验证修复
重新编译运行程序,将正确输出30,不会触发段错误。
内容的提问来源于stack exchange,提问作者yonx
相关产品推荐
相关产品推荐

