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

时间:2015-07-16 21:12:14

标签: c assembly x86 calling-convention gas

我尝试过从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    $1,%ebx                 #; fd = stdout
    movl    $4,%eax                 #; syscall = write
    int     $0x80               #; perform call

    popl %ecx               #; pop counter

    movl %ecx, %eax             #; eax = ecx
    cmpl $0x5, %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    $0,%ebx             #; EXIT_SUCCESS
        movl    $1,%eax             #; syscall = exit
        int     $0x80               #; 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 $1, %ebx               #; fd = stdout
    movl $4, %eax               #; syscall = write
    int $0x80               #; 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 $1, %ebx               #; fd = stdout
    movl $4, %eax               #; syscall = write
    int $0x80               #; perform call

    subl $0x0C, %esp            #; restore stack
    ret

.data                          

__eip:  .long

msg:
    .ascii    "Hello, world!\n\0"   
    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));
}

我的输出就像我预期的那样。

  

你好,世界!
  你好,世界!
  你好,世界!
  你好,世界!
  你好,世界!
  你好,世界!

我的问题随之而来:

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 $0x0C, %esp        #; restore stack

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

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

感谢您的所有回复。

1 个答案:

答案 0 :(得分:3)

1)设置要运行的符号类型。除特殊情况外,不需要它,例如共享库。

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

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

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