Вызов функций C с языка ассемблера x86

Возможно ли генерировать функции языка ассемблера из C-функций с помощью GCC, чтобы они могли быть вызваны из языковой программы ассемблера? Я знаю, что gcc компилирует C в машинный код (который можно легко разобрать на язык ассемблера), и я уже знаю, что возможно встроенные функции языка ассемблера в C, но Я еще не нашел способ вызвать функции C из ассемблерных программ, что в основном является обратным.

Здесь я пытаюсь встроить функцию C в программу сборки x86. Если вложение невозможно, то есть ли другой способ вызвать функцию C из программы ассемблерной программы?

.686p
.model flat,stdcall
.stack 2048

.data

.code
start:

invoke  ExitProcess, 0

printSomething PROC ;now I'm attempting to inline a C function here
    void printSomething(thingToPrint){
        printf("This is a C function that I want to invoke from an assembly language program.");
        printf("There must be some way to do this - is it possible somehow?");
    }
printSomething ENDP

end start

Ответ 1

Я собираюсь из памяти здесь, поэтому я могу быть немного на деталях или двух. Однако, я надеюсь, будет достаточно, чтобы вы пошли в правильном направлении.

Вам нужно будет сообщить ассемблеру GCC, что ваша подпрограмма printSomething() не определена в вашем файле сборки. В 'C' вы должны использовать ключевое слово extern. Для сборки вам нужно будет использовать .globl.

.globl printSomething

Если вы используете другой ассемблер, чем GCC, ключевое слово может отличаться.

Следующий большой вопрос: "Как передать аргументы"? Это очень зависит от вашего процессора и ОС. Поскольку заголовок вашего вопроса указывает x86, я собираюсь предположить, что вы используете либо 16-битные, либо 32-битные режимы, и стандартный x86 ABI (в отличие от x86-64, который также отличается от Windows и Linux). Параметры C передаются вызываемой процедуре, нажимая их в стек. Они помещаются в стек справа налево.

Таким образом,

printSomething (arg1, arg2, arg3, arg4);

переводит на...

pushl arg4
pushl arg3
pushl arg2
pushl arg1
call  printSomething
addl  $0x10, %esp

Вы можете спросить себя, что это за

addl $0x10, %esp

? Мы передали (ака толкнули) четыре 32-битных аргумента в процедуру (в стек). Хотя подпрограмма знает, что ожидать этих аргументов, она НЕ несет ответственности за то, чтобы выскочить из стека. За это отвечает вызывающий. Итак, после того, как мы вернемся из подпрограммы, мы корректируем указатель стека, чтобы отбросить четыре 32-битных аргумента, которые мы ранее вставляли в стек.

В приведенном выше примере я предполагаю, что мы работаем в 32-битном режиме. Если бы это был 16-битный режим, это было бы...

pushw arg4
pushw arg3
pushw arg2
pushw arg1
call  printSomething
addw  $0x8, %sp

Я понимаю, что в вашем примере printSomething() принимает только один (1) аргумент, и в моем примере я использовал четыре (4). Просто отредактируйте мой пример как нужно.

Для окончательных шагов вам нужно будет скомпилировать как ваши C, так и файлы сборки в объектные файлы, связать файлы объектов и затем выполнить.

Надеюсь, это поможет.

Ответ 2

Для x86_64 обратите внимание, что вы должны быть осторожны с некоторыми дополнительными вещами: