Search code examples
cassemblyx86calling-conventiongnu-assembler

About calling C function from Assembly and vice versa


I've tried some calling ASM from C and vice versa. It worked perfect at least for now but I have questions. Here is my code:

test.S followed:

.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 followed:

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));
}

My output is like what I expected.

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

My questions followed:

1)

.type writeMe, @function

What does this code mean? Information for "GCC" ? What does it do? Do I have to do that?

2)

Do I have to write this informing op. if function declared in C file?

.type _main, @function

_main is declared in C file, do I have to write ?

3)

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

I've used this code in writeMe, is it safe? In other words, can I pop arguments, or will GCC pop it automatically?

popl %ecx               #; pop return location
popl %ecx               #; pop first argument, msg
popl %edx               #; pop second argument, len
....
subl $0x0C, %esp        #; restore stack

I've used this code in second function. I am asking you, which one is safe and correct?

4) Do I need to restore registers after calling assembly function from C ? (I heard I have to restore EDI but what about others?)

Thanks for all your replies.


Solution

  • 1) Sets the type of the symbol to function. It's not needed, except in special cases, for example shared libraries.

    2) No, that has been done by the compiler for the functions defined in C.

    3) Both of those are wrong. You should access arguments relative to esp or, after setting up a standard stack frame, relative to ebp.

    4) You should read the appropriate ABI documentation for information about the calling convention. Typically, you can use eax, ecx and edx the rest must be preserved.