Dynamically creating functions in C

前端 未结 10 1583
-上瘾入骨i
-上瘾入骨i 2020-12-09 10:52

How can I dynamically create a function in C?

I try to summarize my C problem as follows:

  • I have a matrix and I want to be able to use some function

相关标签:
10条回答
  • 2020-12-09 11:16

    As unwind already mentioned, "creating code at runtime" is not supported by the language and will be a lot of work.

    I haven't used it myself, but one of my co-workers swears by Lua, an "embedded language". There is a Lua C API which will (theoretically, at least) allow you to perform dynamic (scripted) operations.

    Of course, the downside would be that the end user may need some sort of training in Lua.

    It may be a dumb question, but why does the function have to be generated within your application? Similarly what advantage does the end-user get from generating the function themselves (as opposed to selecting from one or more predefined functions that you provide)?

    0 讨论(0)
  • 2020-12-09 11:28

    C is a compiled language. You can't create code at run-time "in C"; there is no specific C support to emit instructions to memory and so on. You can of course try just allocating memory, making sure it's executable, and emit raw machine code there. Then call it from C using a suitable function pointer.

    You won't get any help from the language itself though, this is just like generating code and calling it in BASIC on an old 8-bit machine.

    0 讨论(0)
  • 2020-12-09 11:28

    If you really need to dynamically create the functions, maybe an embedded C interpreter could help. I've just googled for "embedded C interpreter" and got Ch as a result:

    http://www.softintegration.com/

    Never heard of it, so I don't know anything about it, but it seems to be worth a look.

    0 讨论(0)
  • 2020-12-09 11:29

    Since you want to generate a function that follows a simple recipe, this shouldn't be too tricky to do with some inline assembly and a block of executable/writable memory.

    This approach feels a bit hacky so I wouldn't recommend it in production code. Due to the use of inline assembly this solution works only on Intel x86-64 / AMD64, and will need to be translated to work with other architectures.

    You might prefer this to other JIT-based solutions as it does not depend on any external library.

    If you would like a longer explanation of how the below code works, leave a comment and I'll add it.

    For security reasons, the code page should be marked PROT_READ|PROT_EXEC after a function is generated (see mprotect).

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <sys/mman.h>
    
    int snippet_processor(char *buffer, double value, int action);
    
    enum snippet_actions {
        S_CALC_SIZE,
        S_COPY,
    };
    
    typedef double (*callback_t) (unsigned int, unsigned int);
    
    int main(int argc, char **argv) {
    
        unsigned int pagesize = 4096;
        char *codepage = 0;
        int snipsz = 0;
    
        callback_t f;
    
        /* allocate some readable, writable and executable memory */
        codepage = mmap(codepage,
            pagesize,
            PROT_READ | PROT_WRITE | PROT_EXEC,
            MAP_ANONYMOUS | MAP_PRIVATE,
            0,
            0);
    
        // generate one function at `codepage` and call it
        snipsz += snippet_processor(codepage, 12.55, S_COPY);
        f = (callback_t) (codepage);
        printf("result :: %f\n", f(1, 2));
    
        /* ensure the next code address is byte aligned
         * - add 7 bits to ensure an overflow to the next byte.
         *   If it doesn't overflow then it was already byte aligned.
         * - Next, throw away any of the "extra" bit from the overflow,
         *   by using the negative of the alignment value 
         *   (see how 2's complement works.
         */
        codepage += (snipsz + 7) & -8;
    
        // generate another function at `codepage` and call it
        snipsz += snippet_processor(codepage, 16.1234, S_COPY);
        f = (callback_t) (codepage);
        printf("result :: %f\n", f(1, 2));
    }
    
    int snippet_processor(char *buffer, double value, int action) {
        static void *snip_start = NULL; 
        static void *snip_end = NULL; 
        static void *double_start = NULL; 
        static int double_offset_start = 0;
        static int size;
    
        char *i, *j;
        int sz;
    
        char *func_start;
        func_start = buffer;
    
        if (snip_start == NULL) {
            asm volatile(
                // Don't actually execute the dynamic code snippet upon entry
                "jmp .snippet_end\n"
    
                /* BEGIN snippet */
                ".snippet_begin:\n"
                "movq .value_start(%%rip), %%rax\n"
                "movd %%rax, %%xmm0\n"
                "ret\n"
    
                /* this is where we store the value returned by this function */
                ".value_start:\n"
                ".double 1.34\n"
                ".snippet_end:\n"
                /* END snippet */
    
                "leaq .snippet_begin(%%rip), %0\n"
                "leaq .snippet_end(%%rip), %1\n"
                "leaq .value_start(%%rip), %2\n"
                : 
                "=r"(snip_start),
                "=r"(snip_end),
                "=r"(double_start)
            );
            double_offset_start = (double_start - snip_start);
            size = (snip_end - snip_start);
        }
    
        if (action == S_COPY) {
            /* copy the snippet value */
            i = snip_start;
            while (i != snip_end) *(buffer++) = *(i++); 
    
            /* copy the float value */
            sz = sizeof(double);
            i = func_start + double_offset_start; 
            j = (char *) &value;
    
            while (sz--) *(i++) = *(j++); 
        }
    
        return size;
    }
    
    0 讨论(0)
提交回复
热议问题