Rambler's
Top100

Взаимодействие кода на ассемблере и Си

Автор: lonesome TSH/Digital Daemons
Дата: .02.2003
Раздел: Низкоуровневое программирование в Linux

Иногда (особенно часто это случается при разработке ОС) перед программистом встает задача обеспечения взаимодействия между различными модулями, одна часть которых написана на ассемблере для повышения быстродействия, а другая - на Си (или каком-нибудь другом высокоуровневом языке программирования). Взаимодействие между ними (скомпилированными как разные объектные файлы) осуществляется следующим образом (я покажу на примере NASM и GCC):
Для того чтобы функция, написанная на NASM стала доступна из GCC, ее необходимо объявить глобальной:
global function_name
Если же программа на ассемблере использует какую-нибудь функцию экспортируемую из модуля, написанного на Си, то ее необходимо объявить внешней:
extern function_name

Конвенции вызова функций

Конвенция вызова функций используемая в Си предполагает передачу аргументов в стеке в обратном порядке, т.е., например, вместо
printf("%i",value);
на ассемблере необходимо написать:
push dword value
push dword format
call printf
К тому же вызванная функция не очищает стек от параметров, поэтому это должна сделать вызывающая функция, например:
add esp, 8 - если были переданы два параметра

Доступ к параметрам

Если функция, написанная на ассемблере, была вызвана из программы, написанной на Си, то доступ к переданным параметрам можно получить следующим образом:
push ebp - EBP будет использоваться
mov ebp, esp - сохранить значение ESP
mov eax, [ebp+8] - для того, чтобы запросить последний параметр из списка, к нему надо обратиться как к ebp+8 (первые четыре байта в стеке - это адрес возврата, помещенный туда командой call, а вторые четыре байта - это сохраненный в начале функции регистр EBP), для получения второго - ebp+12 и т.д.
Значение esp необходимо сохранять, потому что в процессе исполнения функции оно может меняться
Такая функция должна завершиться командами pop ebp и ret

В некоторых форматах объектных файлов, компилятор будет добавлять подчеркивание к адресу функции, поэтому чтобы функция, написанная на ассемблере, была доступна как function_name, ее необходимо объявить как _function_name

Rambler's Top100