从 C 调用汇编函数时出现分段错误错误

Posted

技术标签:

【中文标题】从 C 调用汇编函数时出现分段错误错误【英文标题】:Segmentation fault error when calling assembly function from C 【发布时间】:2022-01-04 19:44:50 【问题描述】:

我目前正在尝试将汇编函数链接到我的 C 代码驱动程序以完成大学作业。在执行程序时,我得到一个 seg fault 错误。

下面将包括我的 C 文件、ASM 文件中的内容以及来自 GDB 调试器的信息。

C 代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void add(char*, char*); //would extern be needed here maybe?

int main()

    int choice;

    char num1[3];
    char num2[3];

    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, 1024, stdin);

                num1[strlen(num1) - 1] = '\0';

                printf("\nPlease enter a second number: ");

                fgets(num2, 1024, stdin);

                num2[strlen(num2) - 1] = '\0';

                add(num1, num2);

                printf("\nResult: %s\n", num2);

            case 2:

            case 3:

            case 4:

            case 5:

                printf("\nThanks for using!\n");

                break;

        

    while(choice != 5);

    return 0;


这里需要注意的一点是,我的教授特别说明我将这两个数字作为字符串读取,然后在汇编中使用atoi() 函数将字符串转换为int。

现在,我的 ASM 代码:

BITS 32
GLOBAL add
EXTERN atoi

section .data

section .bss

section .text

add:
    push ebp
    mov ebp, esp

    push eax
    call atoi
    push ebx
    call atoi

    mov eax, [ebp+8]
    mov ebx, [ebp+12]
    add eax, ebx

    pop ebx
    ret

由于我需要从我的 Assembly 函数中调用 atoi(),因此我认为有必要使用堆栈。

最后,GDB 调试器在说什么:

Program received signal SIGSEGV, Segmentation fault.
0xffffcdbc in ?? ()

关于调试器错误的说明:在单步执行程序时,一旦到达add(num1, num2),就会出现此错误。

对于其他一些重要信息,我正在使用 GCC 编译器、NASM 编译器、Intel Assembler i386,并通过 VirtualBox 在虚拟机中运行 Debian 10 x86_64。

任何关于此事的帮助将不胜感激!

【问题讨论】:

pop ebx 可能是pop ebp 的拼写错误? 【参考方案1】:

我看到的第一个问题是你用的:

fgets(num1, 1024, stdin); 但是num1只有3个字节作为缓冲区,但这不是分段错误的根本原因。

另一个问题是您将add 函数声明为:

void add(char*, char*);。我认为将其声明为 int add(char*, char*); 并将此函数的结果用作两个数字的总和会更容易。

问题出在汇编代码中,您没有使用正确的参数。例如在这部分:

    push eax
    call atoi
    push ebx
    call atoi

您使用eaxebx 作为atoi 的参数,但add 函数的参数在[ebp + 8][ebp+12] 中。调用后需要确保栈是干净的,需要使用add esp, 4(因为只有一个参数)

要记住的另一件事是,在call atoi 之后,结果将存储在eax 寄存器中,因为您在atoi 之后调用atoi,您将丢失第一个结果。您需要存储第一个atoi(在堆栈/局部变量上)的结果,然后将其添加到第二次调用atoi 的下一个结果中。我将把我的 C 代码和程序集版本放在 32 位上。

C 代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int add(char*, char*); //would extern be needed here maybe?

int main()

    int choice;
    int sum;

    char num1[3];
    char num2[3];

    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: ");
                scanf("%s", num1);

                printf("\nPlease enter a second number: ");
                scanf("%s", num2);

                sum = add(num1, num2);
                printf("\nResult: %d\n", sum);

            case 2:

            case 3:

            case 4:

            case 5:

                printf("\nThanks for using!\n");

                break;

        

    while(choice != 5);

    return 0;



汇编代码:

BITS 32
GLOBAL add
EXTERN atoi

section .data

section .bss

section .text

add:
    push ebp
    mov ebp, esp

    push dword [ebp + 8]
    call atoi
    add esp, 4

    push eax
    
    push dword [ebp + 12]
    call atoi
    add esp, 4
    
    pop ecx

    add eax, ecx

    leave
    ret

【讨论】:

太棒了!所以,我最初也有同样的想法,将 add 函数作为 int 函数,但出于某种奇怪的原因,我的教授要求我将该函数作为 void ... 感谢您指出您所做的事情!我还在学习如何在 ASM 和 C 之间传递东西,这很有帮助。 那么 asm 文件需要进行一些更改。您需要用来自eax 的整数覆盖[ebp+8][ebp+12]。但要小心,因为[ebp+8[ebp+12] 是地址,而eax 基本上是一个整数。 最好写mov [ebp-4], eax而不是add esp, 4push eax @SepRoland 是的,但更难理解。 @MocanuGabriel 我一定会研究如何做到这一点@Sep Roland 我也会研究并弄清楚那是什么哈哈!我的 ASM 课程只是一门入门课程,但我很想了解更多。谢谢你们的帮助和意见!

以上是关于从 C 调用汇编函数时出现分段错误错误的主要内容,如果未能解决你的问题,请参考以下文章

在调用函数中取消引用指针时出现分段错误

在C中调用fgets时出现分段错误

在 C 中创建大型数组时出现分段错误

弹出 x86 堆栈时出现分段错误

设置堆栈后在 C 中调用 printf 时出现分段错误

调用 globfree() 时出现分段错误