You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含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,当前注释写反了,虽测试用例结果不受影响,但属于潜在问题。

修复步骤

  1. 保存并恢复非易失性寄存器:函数开头将需要修改的非易失性寄存器压栈保存,结束前弹出恢复。
  2. 修正参数读取顺序:调整寄存器赋值,对应正确的参数位置。
  3. 优化乘法实现(可选):用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 03:53:26