关于从 Assembly 调用 C 函数,反之亦然

About calling C function from Assembly and vice versa

我尝试过从 C 调用 ASM,反之亦然。至少目前它运行完美,但我有疑问。这是我的代码:

test.S 已关注:

.text               

.global _start   


.global _main       
.type _main, @function


.global writeMe                              
.type writeMe, @function    

_start:

        #; Write hello world for 5 times.
    #; Jump exit and call C function after that.
    #; C function calls writeMe assembly function
    #; Exit with syscall

    xorl %ecx, %ecx             #; ecx = 0
    call    _get_eip            #; get eip without labels. Just for research.
    pushl   %eax                #; push to stack
    incl %ecx               #; ++ecx
    pushl %ecx              #; push to stack

    movl    $len,%edx           #; tell length of string
    movl    $msg,%ecx               #; tell string position
    movl    ,%ebx                 #; fd = stdout
    movl    ,%eax                 #; syscall = write
    int     [=10=]x80               #; perform call

    popl %ecx               #; pop counter

    movl %ecx, %eax             #; eax = ecx
    cmpl [=10=]x5, %eax             #; compare 0x5 and eax
    je _exit                #; eax == 0x5, jump exit

    _jmp:
        popl    %eax            #; pop instruction pointer
        jmpl    %eax            #; jmp

    _exit:
        call    _main           #; call C function
        movl    [=10=],%ebx             #; EXIT_SUCCESS
        movl    ,%eax             #; syscall = exit
        int     [=10=]x80               #; perform call
        ret

_get_eip:                   #; function for getting eip
    popl %eax               #; pop eip
    pushl %eax              #; push again to return
    ret                 #; return location

writeMe:                    #; function for writing, called from C
    popl (__eip)                #; pop return location
    popl %ecx               #; pop first argument, msg
    popl %edx               #; pop second argument, len

    movl , %ebx               #; fd = stdout
    movl , %eax               #; syscall = write
    int [=10=]x80               #; perform call

    pushl (__eip)               #; push return location
    ret                 #; return location

writeMe2:                   #; function for writing, called from C
    popl %ecx               #; pop return location
    popl %ecx               #; pop first argument, msg
    popl %edx               #; pop second argument, len

    movl , %ebx               #; fd = stdout
    movl , %eax               #; syscall = write
    int [=10=]x80               #; perform call

    subl [=10=]x0C, %esp            #; restore stack
    ret

.data                          

__eip:  .long

msg:
    .ascii    "Hello, world!\n[=10=]"   
    len = . - msg                

main.C 已关注:

extern void writeMe(const char *msg, int len);

int _strlen(const char *msg) {
    int _len = 0;
    while (*msg++ != 0x0)
        _len++;
    return _len; 
}

void _main() {

    const char * szmsg = "Hello, world!\n";
    writeMe(szmsg, _strlen(szmsg));
}

我的输出符合我的预期。

Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!

我的问题如下:

1)

.type writeMe, @function

这段代码是什么意思? "GCC" 的信息?它有什么作用?我必须这样做吗?

2)

我必须写这个通知操作吗?如果函数在 C 文件中声明?

.type _main, @function

_main是在C文件中声明的,我一定要写吗?

3)

popl (__eip)                #; pop return location
popl %ecx                   #; pop first argument, msg
popl %edx                   #; pop second argument, len
........
pushl (__eip)               #; push return location

我在writeMe中使用过这段代码,安全吗?换句话说,我可以弹出参数,还是 GCC 会自动弹出它?

popl %ecx               #; pop return location
popl %ecx               #; pop first argument, msg
popl %edx               #; pop second argument, len
....
subl [=15=]x0C, %esp        #; restore stack

我在第二个函数中使用了这段代码。我问你,哪一个是安全正确的?

4) 从 C 调用汇编函数后是否需要恢复寄存器? (我听说我必须恢复EDI,但其他人呢?)

感谢您的所有回复。

1) 设置函数的符号类型。不需要,除非在特殊情况下,例如共享库。

2) 不,编译器已经为 C 中定义的函数完成了。

3) 这两个都是错误的。您应该访问相对于 esp 的参数,或者在设置标准堆栈框架之后,相对于 ebp.

4) 您应该阅读相应的 ABI 文档以获取有关调用约定的信息。通常,您可以使用 eaxecxedx,其余必须保留。