Skip to content

Instantly share code, notes, and snippets.

@usbuild
Last active December 29, 2018 09:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save usbuild/4dfd73891511dfe5f33be95495647018 to your computer and use it in GitHub Desktop.
Save usbuild/4dfd73891511dfe5f33be95495647018 to your computer and use it in GitHub Desktop.
implement C closure! you can pass closure to external c apis via function pointer.
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#define CODE_SIZE 200
typedef union slot_t {
struct {
void *ptr;
uint64_t args[20];
uint64_t bind_args[2];
uint64_t r15;
unsigned char code[CODE_SIZE];
};
union slot_t *next;
} slot_t;
typedef struct {
slot_t *slots;
slot_t *free_slot;
int max_cbs;
} binder_t;
binder_t *create_binder(int max_cbs) {
binder_t *b = malloc(sizeof(binder_t));
b->slots = (slot_t *)mmap(NULL, sizeof(slot_t) * max_cbs,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memset(b->slots, 0, sizeof(slot_t) * max_cbs);
for (int i = 0; i < max_cbs - 1; ++i) {
(b->slots[i]).next = &b->slots[i + 1];
}
b->slots[max_cbs - 1].next = NULL;
b->free_slot = &b->slots[0];
b->max_cbs = max_cbs;
return b;
}
void release_binder(binder_t *b) {
if (b->slots) {
munmap(b->slots, b->max_cbs * sizeof(slot_t));
}
}
slot_t *alloc_slot(binder_t *b) {
if (!b->free_slot) return NULL;
slot_t *s = b->free_slot;
b->free_slot = s->next;
return s;
}
void free_slot(binder_t *b, slot_t *s) {
s->next = b->free_slot;
b->free_slot = s;
}
void unbind(binder_t *b, void *ptr) {
free_slot(b, (slot_t *)(ptr + offsetof(slot_t, ptr)));
}
#define EX(_) _[0], _[1], _[2], _[3], _[4], _[5], _[6], _[7]
void *bind1st(binder_t *b, void *ptr, void *a) {
slot_t *s = alloc_slot(b);
uint64_t pval = (uint64_t)ptr;
unsigned char *pv = (unsigned char *)&pval;
uint64_t prval = (uint64_t)(&s->r15);
unsigned char *pr15 = (unsigned char *)&prval;
uint64_t args = (uint64_t)(&s->args);
unsigned char *pargs = (unsigned char *)&args;
s->bind_args[0] = (uint64_t)a;
uint64_t bargs = (uint64_t)(&s->bind_args);
unsigned char *pbargs = (unsigned char *)&bargs;
unsigned char code[] = {
0x49, 0xbe, EX(pr15), // movabs r14, xxxx
0x4d, 0x89, 0x3e, // movabs r15, [r14]
0x49, 0xbf, EX(pv), //
// setup args
0x49, 0xbe, EX(pargs), // movabs r14, xxx
0x49, 0x89, 0x3e, // mov [r14], rdi
0x49, 0x89, 0x76, 0x08, // mov [r14 + 8], rsi
0x49, 0x89, 0x56, 0x10, //
0x49, 0x89, 0x56, 0x18, //
0x49, 0x89, 0x46, 0x20, //
0x49, 0x89, 0x4e, 0x28, // mov [r14 + 40], r9
0x49, 0xbe, EX(pbargs), //
0x49, 0x8b, 0x3e, // mov rdi, [r14]
0x49, 0xbe, EX(pargs), //
0x49, 0x8b, 0x36, // mov rsi, [r14]
0x41, 0xff, 0xd7, // call r15
0x4d, 0x8b, 0x3e, // restore r15
0xc3, // ret
};
memcpy(s->code, code, sizeof(code));
return s->code;
}
static void func(void *what) { printf("func %s\n", (char*)what); }
static void func2(void *what, int val) { printf("func2 %s %d\n", (char*)what, val); }
typedef void (*func_t)();
typedef void (*func2_t)(int);
int main(int argc, const char *argv[]) {
binder_t *b = create_binder(200);
void *ptr = bind1st(b, &func, "1024");
((func_t)(ptr))();
void *ptr2 = bind1st(b, &func2, "2048");
((func2_t)(ptr2))(666);
unbind(b, ptr);
unbind(b, ptr2);
release_binder(b);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment