Thunk: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
(new page) |
(→Definition: fix ridiculous typo) |
||
(One intermediate revision by the same user not shown) | |||
Line 10: | Line 10: | ||
#ifndef PAGE_SIZE |
#ifndef PAGE_SIZE |
||
#define PAGE_SIZE |
#define PAGE_SIZE 4096 |
||
#endif |
#endif |
||
Line 53: | Line 53: | ||
} |
} |
||
void free_thunk( |
void free_thunk(thunked_fn fn) |
||
{ |
{ |
||
munmap((void *) fn, PAGE_SIZE); |
munmap((void *) fn, PAGE_SIZE); |
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
- "C Closures as a Library": https://nullprogram.com/blog/2017/01/08/