C语言调用汇编函数时出现Segmentation fault段错误问题求助
问题排查与修复方案
核心问题总览
你遇到的段错误是C代码缓冲区溢出、汇编函数调用逻辑不符合C调用约定、栈帧操作错误、结果类型不匹配四类问题共同导致的,逐一修复如下:
1. C代码问题修复
问题点
- 你定义的
num1、num2数组长度只有3字节,但是调用fgets时传入了1024的读取长度,直接发生栈缓冲区溢出,破坏栈结构。 switch的case 1分支没有加break,执行完加法逻辑后会直接落到case 5分支提前退出程序。- 关于你问的
add函数声明是否要加extern:C语言中全局函数默认隐含extern属性,此处加不加都可以正常链接。
修正后C代码
#include <stdio.h> #include <stdlib.h> #include <string.h> void add(char*, char*); int main(){ int choice; // 扩大缓冲区到32字节,足够存储常规整数字符串 char num1[32]; char num2[32]; printf("Welcome to the back and forth program!\n\n"); do{ printf("What would you like to do?\n\n"); printf("1. Add two numbers together.\n"); printf("2. Find if a string is a palindrome. (ASM Version)\n"); printf("3. Find the factorial of a number.\n"); printf("4. Find if a string is a palindrome. (C Version)\n"); printf("5. Exit Program.\n\n"); printf("choose 1-5: "); scanf("%d", &choice); getchar(); while(choice < 1 || choice > 5){ printf("\nPlease choose an option between 1 and 5.\n"); scanf("%d", &choice); getchar(); } switch(choice){ case 1: printf("\n*Add two numbers together*\n\n"); printf("Please enter a number: "); // 读取长度和缓冲区匹配 fgets(num1, sizeof(num1), stdin); num1[strcspn(num1, "\n")] = '\0'; // 更安全的换行符替换逻辑 printf("\nPlease enter a second number: "); fgets(num2, sizeof(num2), stdin); num2[strcspn(num2, "\n")] = '\0'; add(num1, num2); printf("\nResult: %s\n", num2); // 新增break,避免穿透到其他分支 break; case 2: case 3: case 4: case 5: printf("\nThanks for using!\n"); break; } }while(choice != 5); return 0; }
2. 汇编代码问题修复
问题点
- 调用
atoi前没有把字符串参数压入栈,atoi取到的是栈上的垃圾值作为字符串指针,访问非法地址直接触发段错误。 - 32位C的
cdecl调用约定要求:调用方负责在函数调用后平衡栈,你两次调用atoi后都没有恢复栈指针。 - 你直接把
num1和num2的指针当整数相加,完全没有用到atoi的返回值(atoi的返回值存在eax寄存器中)。 - 相加后的结果是整数,你没有把它转换为字符串写回
num2的缓冲区,C代码用%s输出num2会把整数当字符串指针访问,必然触发非法访问。 - 没有恢复栈帧:你开头
push ebp,结尾没有pop ebp,ret指令会把栈上的ebp值当成返回地址,导致程序跳转到非法地址。
修正后汇编代码
BITS 32 GLOBAL add EXTERN atoi, sprintf ; 调用sprintf完成整数转字符串 section .text add: push ebp mov ebp, esp push ebx ; 保存cdecl约定需要保留的寄存器 push esi push edi ; 转换第一个数字字符串:num1 = [ebp+8] push dword [ebp+8] call atoi add esp, 4 ; 平衡栈 mov ebx, eax ; 把第一个整数存在ebx ; 转换第二个数字字符串:num2 = [ebp+12] push dword [ebp+12] call atoi add esp, 4 ; 平衡栈 add eax, ebx ; 两个整数相加,结果存在eax ; 把相加结果转为字符串写入num2缓冲区 push eax push dword fmt_str push dword [ebp+12] call sprintf add esp, 12 ; 三个参数,平衡栈 ; 恢复寄存器和栈帧 pop edi pop esi pop ebx pop ebp ret section .data fmt_str db "%d", 0 ; sprintf用的格式化字符串
3. 编译命令修正
因为你用的是64位系统编译32位程序,需要指定32位编译参数:
# 汇编生成32位目标文件 nasm -f elf32 add.asm -o add.o # 编译C代码并链接,加-m32指定32位程序 gcc -m32 main.c add.o -o calc
运行./calc即可正常执行。
内容的提问来源于stack exchange,提问作者TheSkyeLord
相关产品推荐
相关产品推荐

