Генерирование функций во время выполнения в C

Я хотел бы сгенерировать функцию во время выполнения в C. И под этим я имею в виду, что я бы хотел выделить некоторую память, указать на нее и выполнить ее с помощью указателя функции. Я понимаю, что это очень сложная тема, и мой вопрос наивен. Я также понимаю, что есть некоторые очень надежные библиотеки, которые делают это (например, nanojit ).

Но я хотел бы изучить технику, начиная с основ. Может ли кто-нибудь знающий дать мне очень простой пример в C?

EDIT: Ответ ниже велик, но вот тот же пример для Windows:

#include  #define MEMSIZE 100*1024*1024 typedef void (*func_t)(void); int main() { HANDLE proc = GetCurrentProcess(); LPVOID p = VirtualAlloc( NULL, MEMSIZE, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE); func_t func = (func_t)p; PDWORD code = (PDWORD)p; code[0] = 0xC3; // ret if(FlushInstructionCache( proc, NULL, 0)) { func(); } CloseHandle(proc); VirtualFree(p, 0, MEM_RELEASE); return 0; } 

    Как уже было сказано другими плакатами, вам нужно хорошо знать свою платформу.

    Игнорируя проблему литья указателя объекта на указатель функции, технически, UB, вот пример, который работает для x86 / x64 OS X (и, возможно, Linux тоже). Весь сгенерированный код – это возврат к вызывающему.

     #include  #include  typedef void (*func_t)(void); int main() { /* * Get a RWX bit of memory. * We can't just use malloc because the memory it returns might not * be executable. */ unsigned char *code = mmap(NULL, getpagesize(), PROT_READ|PROT_EXEC|PROT_WRITE, MAP_SHARED|MAP_ANON, 0, 0); /* Technically undefined behaviour */ func_t func = (func_t) code; code[0] = 0xC3; /* x86 'ret' instruction */ func(); return 0; } 

    Очевидно, что это будет отличаться на разных платформах, но в нем описываются основы: получить исполняемый раздел памяти, написать инструкции, выполнить инструкции.

    Это требует, чтобы вы знали свою платформу. Например, что такое конвенция вызова C на вашей платформе? Где хранятся параметры? Какой регистр содержит возвращаемое значение? Какие регистры должны быть сохранены и восстановлены? Как только вы это знаете, вы можете по существу написать код C, который собирает код в блок памяти, а затем переводит эту память в указатель функции (хотя это технически запрещено в ANSI C и не будет работать в зависимости от того, будет ли ваша платформа отмечена на некоторых страницах памяти как неисполняемый, так называемый бит NX).

    Простой способ сделать это – просто написать код, скомпилировать его, затем разобрать и посмотреть, какие байты соответствуют инструкциям. Вы можете написать некоторый C-код, который заполняет выделенную память этой коллекцией байтов, а затем выводит ее на указатель функции соответствующего типа и выполняет.

    Вероятно, лучше всего начать с чтения соглашений о вызовах для вашей архитектуры и компилятора. Затем научитесь писать сборку, которая может быть вызвана из C (т. Е. Следует за вызовом).

    Если у вас есть инструменты, они могут помочь вам немного упростить некоторые вещи. Например, вместо того, чтобы пытаться создать правильную prolog / эпилог функции, я могу просто закодировать это в C:

     int foo(void* Data) { return (Data != 0); } 

    Затем (MicrosoftC под Windows) передайте его в «cl / Fa / c foo.c». Затем я могу посмотреть на «foo.asm»:

     _Data$ = 8 ; Line 2 push ebp mov ebp, esp ; Line 3 xor eax, eax cmp DWORD PTR _Data$[ebp], 0 setne al ; Line 4 pop ebp ret 0 

    Я мог бы также использовать «dumpbin / all foo.obj», чтобы увидеть, что точными байтами функции были:

     00000000: 55 8B EC 33 C0 83 7D 08 00 0F 95 C0 5D C3 

    Просто сэкономит мне время на то, чтобы байт был точно прав …