Dynamically creating functions in C

前端 未结 10 1677
-上瘾入骨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条回答
  •  慢半拍i
    慢半拍i (楼主)
    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 
    #include 
    #include 
    #include 
    
    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;
    }
    

提交回复
热议问题