-
-
Save qookei/5d8916c4ca77f77c8b2cbb268d9380a3 to your computer and use it in GitHub Desktop.
C with "classes"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdint.h> | |
| #include <stddef.h> | |
| #define __USE_GNU | |
| #include <signal.h> | |
| #include <ucontext.h> | |
| #define OBJ_MAGIC_HIGH 0xDEADBEEF00000000 | |
| #define OBJ_MAGIC_MASK 0xFFFFFFFF00000000 | |
| #define FOO_OBJ_ID 1 | |
| #define BAR_OBJ_ID 2 | |
| struct foo { | |
| // impl detail | |
| uint64_t obj_magic__; | |
| int (*swap_i)(int i); | |
| void (*log_self)(); | |
| // impl detail | |
| char swap_i_call_impl__[8]; | |
| char log_self_call_impl__[8]; | |
| // member values | |
| int i; | |
| }; | |
| struct bar { | |
| // impl detail | |
| uint64_t obj_magic__; | |
| void (*log_foo)(struct foo *f); | |
| void (*log_self)(); | |
| // impl detail | |
| char log_foo_call_impl__[8]; | |
| char log_self_call_impl__[8]; | |
| // member values | |
| const char *name; | |
| }; | |
| int foo_swap_i_impl(struct foo *this, int i) { | |
| int old_i = this->i; | |
| this->i = i; | |
| return old_i; | |
| } | |
| void foo_log_self_impl(struct foo *this) { | |
| printf("Hello this is [struct foo %p], my i is %d\n", this, this->i); | |
| } | |
| void bar_log_foo_impl(struct bar *this, struct foo *foo) { | |
| printf("Hello this is [struct bar %p], named '%s', given foo says:\n\t", this, this->name); | |
| foo->log_self(); | |
| } | |
| void bar_log_self_impl(struct bar *this) { | |
| printf("Hello this is [struct bar %p], named '%s'\n", this, this->name); | |
| } | |
| struct foo *new_foo() { | |
| struct foo *f = malloc(sizeof(struct foo)); | |
| f->obj_magic__ = OBJ_MAGIC_HIGH + FOO_OBJ_ID; | |
| f->swap_i_call_impl__[0] = 0x0f; // | ud2 | |
| f->swap_i_call_impl__[1] = 0x0b; // / | |
| f->swap_i = (int(*)(int))f->swap_i_call_impl__; | |
| f->log_self_call_impl__[0] = 0x0f; // | ud2 | |
| f->log_self_call_impl__[1] = 0x0b; // / | |
| f->log_self = (void(*)())f->log_self_call_impl__; | |
| f->i = 0; | |
| // memory barrier to prevent initialization being moved after | |
| // member function calls at -O3 | |
| asm volatile ("" : "+m"(*f) :: "memory"); | |
| return f; | |
| } | |
| struct bar *new_bar() { | |
| struct bar *f = malloc(sizeof(struct bar)); | |
| f->obj_magic__ = OBJ_MAGIC_HIGH + BAR_OBJ_ID; | |
| f->log_foo_call_impl__[0] = 0x0f; // | ud2 | |
| f->log_foo_call_impl__[1] = 0x0b; // / | |
| f->log_foo = (void(*)(struct foo *))f->log_foo_call_impl__; | |
| f->log_self_call_impl__[0] = 0x0f; // | ud2 | |
| f->log_self_call_impl__[1] = 0x0b; // / | |
| f->log_self = (void(*)(int))f->log_self_call_impl__; | |
| f->name = "Unnamed"; | |
| // memory barrier to prevent initialization being moved after | |
| // member function calls at -O3 | |
| asm volatile ("" : "+m"(*f) :: "memory"); | |
| return f; | |
| } | |
| static void impl_sig_handler(int sig, siginfo_t *si, void *ctx) { | |
| ucontext_t *uctx = ctx; | |
| if (sig == SIGSEGV) { | |
| if (!(uctx->uc_mcontext.gregs[REG_ERR] & (1 << 4))) | |
| // Not an instruction fetch fault | |
| goto err; | |
| } | |
| uint64_t fault_ptr = (uint64_t)si->si_addr; | |
| if (fault_ptr & 7) | |
| goto err; | |
| uint64_t *search_ptr = (uint64_t *)fault_ptr; | |
| void *base = NULL; | |
| // Limit search depth to 800 bytes | |
| for (int i = 0; i < 100; i++) { | |
| if ((*search_ptr & OBJ_MAGIC_MASK) == OBJ_MAGIC_HIGH) { | |
| base = search_ptr; | |
| break; | |
| } | |
| search_ptr--; | |
| } | |
| if (!base) | |
| goto err; | |
| uint32_t object_id = *search_ptr & ~OBJ_MAGIC_MASK; | |
| uint64_t method_offset = fault_ptr - (uint64_t)base; | |
| int was_resolved = 1; | |
| switch (object_id) { | |
| case FOO_OBJ_ID: { | |
| switch (method_offset) { | |
| case offsetof(struct foo, swap_i_call_impl__): { | |
| uctx->uc_mcontext.gregs[REG_RIP] = (uint64_t)foo_swap_i_impl; | |
| uctx->uc_mcontext.gregs[REG_RSI] = uctx->uc_mcontext.gregs[REG_RDI]; | |
| uctx->uc_mcontext.gregs[REG_RDI] = (uint64_t)base; | |
| } break; | |
| case offsetof(struct foo, log_self_call_impl__): { | |
| uctx->uc_mcontext.gregs[REG_RIP] = (uint64_t)foo_log_self_impl; | |
| uctx->uc_mcontext.gregs[REG_RDI] = (uint64_t)base; | |
| } break; | |
| default: | |
| was_resolved = 0; | |
| } | |
| } break; | |
| case BAR_OBJ_ID: { | |
| switch (method_offset) { | |
| case offsetof(struct bar, log_foo_call_impl__): { | |
| uctx->uc_mcontext.gregs[REG_RIP] = (uint64_t)bar_log_foo_impl; | |
| uctx->uc_mcontext.gregs[REG_RSI] = uctx->uc_mcontext.gregs[REG_RDI]; | |
| uctx->uc_mcontext.gregs[REG_RDI] = (uint64_t)base; | |
| } break; | |
| case offsetof(struct bar, log_self_call_impl__): { | |
| uctx->uc_mcontext.gregs[REG_RIP] = (uint64_t)bar_log_self_impl; | |
| uctx->uc_mcontext.gregs[REG_RDI] = (uint64_t)base; | |
| } break; | |
| default: | |
| was_resolved = 0; | |
| } | |
| } break; | |
| default: | |
| was_resolved = 0; | |
| } | |
| if (!was_resolved) { | |
| err: | |
| signal(sig, SIG_DFL); | |
| } | |
| } | |
| void setup_runtime() { | |
| struct sigaction s; | |
| s.sa_flags = SA_SIGINFO; | |
| s.sa_sigaction = impl_sig_handler; | |
| sigemptyset(&s.sa_mask); | |
| sigaction(SIGSEGV, &s, 0); | |
| sigaction(SIGILL, &s, 0); | |
| } | |
| int main() { | |
| setup_runtime(); | |
| struct foo *f = new_foo(); | |
| struct bar *b = new_bar(); | |
| f->swap_i(32); | |
| f->log_self(); | |
| b->log_self(); | |
| b->log_foo(f); | |
| b->name = "Named bar"; | |
| b->log_self(); | |
| b->log_foo(f); | |
| free(f); | |
| free(b); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
no.