【问题标题】:Assembly Return int to C function segfaults汇编返回 int 到 C 函数段错误
【发布时间】:2013-05-08 08:24:22
【问题描述】:

我正在完成一个汇编程序,它用给定的替换字符替换字符串中的字符。汇编代码调用 C 函数,而汇编程序本身是从我的 .c 文件中的 main 调用的。但是,当尝试从汇编程序到 C 完成并返回最终的 int 值时,我得到了段错误。我的 .asm 文件如下:

; File: strrepl.asm
; Implements a C function with the prototype:
;
;   int strrepl(char *str, int c, int (* isinsubset) (int c) ) ;
;
; 
; Result: chars in string are replaced with the replacement character and string is returned.

    SECTION .text
    global  strrepl


_strrepl:   nop
strrepl:
    push    ebp         ; set up stack frame
    mov ebp, esp

    push    esi         ; save registers
    push    ebx
    xor eax, eax
    mov ecx, [ebp + 8]      ;load string (char array) into ecx
    jecxz   end         ;jump if [ecx] is zero
    mov al, [ebp + 12]      ;move the replacement character into esi
    mov edx, [ebp + 16]     ;move function pointer into edx

firstLoop:

    xor eax, eax

    mov edi, [ecx]
    cmp edi, 0
    jz  end

    mov edi, ecx        ; save array


    movzx   eax, byte [ecx]     ;load single byte into eax  
    push    eax         ; parameter for (*isinsubset)
    mov edx, [ebp + 16]         
    call    edx         ; execute (*isinsubset)


    mov ecx, edi        ; restore array
    cmp eax, 0
    jne secondLoop  
    add esp, 4          ; "pop off" the parameter
    mov ebx, eax        ; store return value
    add ecx, 1
    jmp firstLoop

secondLoop:
    mov eax, [ebp+12]
    mov [ecx], al
    mov edx, [ebp+16]
    add esp, 4
    mov ebx, eax
    add ecx, 1
    jmp     firstLoop

end:
    pop ebx         ; restore registers
    pop esi
    mov esp, ebp        ; take down stack frame
    pop ebp
    mov eax, 9
    push    eax         ;test
    ret

我的 c 文件是:

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

//display *((char *) $edi)
// These functions will be implemented in assembly:
//

int strrepl(char *str, int c, int (* isinsubset) (int c) ) ;


int isvowel (int c) {

   if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') 
      return 1 ;

   if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') 
      return 1 ;

   return 0 ;
}

int main(){
    char *str1;
    int r;


    str1 = strdup("ABC 123 779 Hello World") ;
    r = strrepl(str1, '#', &isdigit) ;
    printf("str1 = \"%s\"\n", str1) ;
    printf("%d chararcters were replaced\n", r) ;
    free(str1) ;
    return 0;
}

在我的汇编代码中,你可以看到结尾

mov eax, 9
push    eax 

我只是想将值 9 返回到值“r”,它是 C 文件中的一个 int。这只是一个测试,看看我是否可以在 c 文件中将 int 返回到 r。最终,我会将被替换的字符数返回给 r。但是,我需要弄清楚为什么上面的以下代码会出现段错误。有任何想法吗?

【问题讨论】:

    标签: c assembly segmentation-fault


    【解决方案1】:
    mov     eax, 9
    push    eax          ; NOT a good idea
    ret
    

    这是一个的错误。它会根据堆栈中最低的东西返回,而你刚刚将一些东西压入堆栈,几乎可以肯定它不是有效的返回地址。

    大多数函数只需将代码放入eax 即可返回代码(这当然取决于调用约定,但这是一个很常见的约定),通常不需要将其推入堆栈,当然也有很多缺点这样做。

    【讨论】:

    • 我明白了。如果我错了,请纠正我,但如果我想从程序集返回一个值到 c,我会将它存储在 EAX 寄存器中,不是吗?
    • 不,不是“不” :-) 我的意思是,是的,通常你只需将它推入 eax 并返回。
    【解决方案2】:

    返回值通常存储在 X86 32 位机器上的 EAX 中。因此,将其存储在 EAX 中后将其推入堆栈是错误的,因为它返回的函数将尝试使用 EAX 中的内容作为 IP 的值(指令指针)

    不带参数的 Ret 将返回地址从堆栈中弹出并跳转到它。

    source

    【讨论】:

    • 我明白了,所以与其在返回之前将其压入堆栈,我应该简单地将 eax 更改为 9,然后不​​压入就返回?编辑:刚试过这个,它工作:)。谢谢你。我忘记了(就像下面的答案),大多数函数只需将其放入 eax 即可返回代码,而无需推送它。
    • @user2357446 是的,这就是你应该做的:)
    • 好吧,现在我唯一的问题是弄清楚如何存储一个计数器来计算替换的字符数。我试过简单地将计数器存储在 bl 中,然后在返回之前将 bl 存储到 [eax] 中,但是会出现段错误。我假设它是因为代码 bl 中的某处在函数调用之间被更改
    • 你应该在一个新问题中提出这个问题。
    • 问了!希望我只是忽略了一些东西
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    • 2016-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多