Thunk: Difference between revisions

From vegard.wiki
Jump to navigation Jump to search
Content added Content deleted
(→‎Definition: fix ridiculous typo)
 
Line 10: Line 10:


#ifndef PAGE_SIZE
#ifndef PAGE_SIZE
#define PAGE_SIZE 4095
#define PAGE_SIZE 4096
#endif
#endif



Latest revision as of 18:32, 14 February 2020

It is possible in C to "bake" one or more arguments into a function pointer that takes no arguments, thus solving the problem of libraries that take a callback function without a data pointer.

Below is one possible implementation for x86-64 that bakes a single argument into a thunk. Error handling may need to be adjusted for specific use cases.

Definition

#include <sys/mman.h>
#include <stdlib.h>

#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif

typedef void (*unthunked_fn)(void *);
typedef void (*thunked_fn)(void);

thunked_fn make_thunk(unthunked_fn fn, void *data)
{
        void *ptr = mmap(NULL, PAGE_SIZE,
                PROT_READ | PROT_WRITE | PROT_EXEC,
                MAP_PRIVATE | MAP_ANONYMOUS,
                -1, 0);
        if (ptr == MAP_FAILED)
                return NULL;

        struct {
                unsigned char movabs_rdi[10];
                unsigned char movabs_rax[10];
                unsigned char jmpq[2];
        } *page = ptr;

        /* movabs $..., %rdi */
        page->movabs_rdi[0] = 0x48;
        page->movabs_rdi[1] = 0xbf;
        for (unsigned int i = 0; i < 8; ++i)
                page->movabs_rdi[2 + i] = (unsigned long) data >> (8 * i);

        /* movabs $..., %rax */
        page->movabs_rax[0] = 0x48;
        page->movabs_rax[1] = 0xb8;
        for (unsigned int i = 0; i < 8; ++i)
                page->movabs_rax[2 + i] = (unsigned long) fn >> (8 * i);

        /* jmpq %rax */
        page->jmpq[0] = 0xff;
        page->jmpq[1] = 0xe0;

        /* Optional; only needed for CPUs with W^X */
        mprotect(ptr, PAGE_SIZE, PROT_READ | PROT_EXEC);

        return (thunked_fn) page;
}

void free_thunk(thunked_fn fn)
{
        munmap((void *) fn, PAGE_SIZE);
}

Usage

#include <stdio.h>

static void print_str(void *data)
{
        printf("print_str: %s\n", (const char *) data);
}

int main(int argc, char *argv[])
{
        thunked_fn fn = make_thunk(print_str, "Hello world!");

        /* Prints "Hello world!" */
        fn();

        return 0;
}

See also