О вызове функции C из ассемблера и наоборот

Я пробовал вызывать ASM из C и наоборот. По крайней мере, на данный момент это работало идеально, но у меня есть вопросы. Вот мой код:

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)

Должен ли я написать это информирование op. если функция объявлена ​​в файле 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, но как насчет других?)

Спасибо за все ваши ответы.


person syscherry    schedule 16.07.2015    source источник
comment
Несколько полезных ссылок можно найти по адресу stackoverflow.com/tags/x86/info. esp: как использовать отладчик для вашего ассемблерного кода и ссылки на руководства/учебники.   -  person Peter Cordes    schedule 18.07.2015


Ответы (1)


1) Устанавливает тип символа для работы. Это не нужно, за исключением особых случаев, например разделяемых библиотек.

2) Нет, это было сделано компилятором для функций, определенных в C.

3) Оба варианта неверны. Вы должны получить доступ к аргументам относительно esp или, после настройки стандартного стекового фрейма, относительно ebp.

4) Вам следует прочитать соответствующую документацию по ABI для получения информации о соглашении о вызовах. Как правило, вы можете использовать eax, ecx и edx, остальные должны быть сохранены.

person Jester    schedule 16.07.2015