В процессе программирования модулей ядра Linux нередко приходится прибегать к помощи ассемблера. Традиционно разработчики ядра используют встроенный ассемблер GCC (который по сути является шлюзом в GAS), но у этого подхода есть куча недостатков, что вызывает желание использовать посторонний ассемблер и раздельную трансляцию. Кстати, знаменитый Свен Шрайбер в своей книги по ядру Винды пишет, что он на этом пути наловил столько BSOD'ов, что в итоге остановился на встроенном же ассемблере. А встроенный ассемблер в Visual C++ имхо намного лучше своего аналога в GCC. Да и раздельная трансляция (как и вообще написание модулей ядра) в Linuxe намного проще.
Но я сначала решил просто поэкспериментировать и написать простенькую функцию на асме и интегрировать её в код на Си.
Пишем функцию:
madd:
push ebp
mov ebp, esp
mov eax, [ebp+8]
add eax, [ebp+12]
pop ebp
ret
Этот простенький "сумматор" вставляем в код нашего модуля:
#include
#include
extern int madd(int, int);
int init_module(void)
{
printk(KERN_ALERT "Summa 11 i 6 ravna %d\n", madd(11,6));
return 0;
}
После компиляции и загрузки модуля все срабатывает, но результат получается сильно не похожим на 17.
Дизассемблер выдаёт нечто совершенно невменяемое. А именно, вызов функции madd осуществляется вот так:
mov eax, 6
mov edx, 0Bh
call madd
То есть вызов, вопреки ожиданиям, никак не соответствует си-соглашению, о чём вызываемая функция конечно не догадывается, возвращая в итоге "винигрет".
Скорее всего проблема в оптимизации, но как заставить GCC НЕ оптимизировать этот участок кода? Фактически мне удалось решить эту проблему, заменив объявление функции madd, незаконно приписав ей переменное число параметров.
extern int madd(int, ...);
В итоге компилятор утихомирился и передал параметры как положено - через стек.
Но никто не гарантирует этого в общем случае, поэтому очень хотелось бы знать, как обязать GCC следовать строго соглашению cdecl.