Skip to content

Instantly share code, notes, and snippets.

@skmp
Last active June 10, 2022 09:48
Show Gist options
  • Select an option

  • Save skmp/eefb284b5e353ef4dcc5bb5784c55665 to your computer and use it in GitHub Desktop.

Select an option

Save skmp/eefb284b5e353ef4dcc5bb5784c55665 to your computer and use it in GitHub Desktop.
copyable functions
//More details: https://github.com/FEX-Emu/fex-assorted-tests-bins/tree/main/src/copyable-functions
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <sys/mman.h>
// Example of creating function specializations at run time from a "canonical" template provided by the compiler
#include "copyable-functions.h"
DECL_COPYABLE_TRAMPLOLINE(one_name, int, int, const char *)
DECL_COPYABLE_TRAMPLOLINE(two_name, int, const char *, int)
/*
// Oof, this cannot be done
template __attribute__((section("test_func_section"))) auto CopyableTrampoline<decltype(test_func)>::
trampoline_canonical(Args...) -> rv_t;
*/
int test_func (int a, const char *b);
int test_func_marshaler (int a, const char *b, decltype(&test_func) fn_ptr) {
printf("%s: Called with %d, %s, %p\n", __func__, a, b, fn_ptr);
return fn_ptr(a, b);
}
int test_func_to_call_1(int a, const char *b) { printf("%s: Called with %d, %s\n", __func__, a, b); return -1; }
int test_func_to_call_2(int a, const char *b) { printf("%s: Called with %d, %s\n", __func__, a, b); return -2; }
int test_func2 (const char *a, int b);
int test_func2_marshaler (const char *a, int b, decltype(&test_func2) target) {
printf("%s: Called with %s, %d, %p\n", __func__, a, b, target);
return target(a, b);
}
int test_func2_to_call_1(const char *b, int a) { printf("%s: Called with %d, %s\n", __func__, a, b); return -1; }
int test_func2_to_call_2(const char *b, int a) { printf("%s: Called with %d, %s\n", __func__, a, b); return -2; }
int main (void)
{
auto fn1 = make_one_name_instance(&test_func_to_call_1, &test_func_marshaler);
auto fn2 = make_one_name_instance(&test_func_to_call_2, &test_func_marshaler);
auto fn3 = make_two_name_instance(&test_func2_to_call_2, &test_func2_marshaler);
auto fn4 = make_two_name_instance(&test_func2_to_call_2, &test_func2_marshaler);
auto rv1 = fn1(1, "test stuff here 1");
printf("rv1: %d\n", rv1);
auto rv2 = fn2(2, "test stuff here 2");
printf("rv2: %d\n", rv2);
auto rv3 = fn3("test stuff here 1", 3);
printf("rv3: %d\n", rv1);
auto rv4 = fn4("test stuff here 2", 4);
printf("rv4: %d\n", rv2);
return 0;
}
//More details: https://github.com/FEX-Emu/fex-assorted-tests-bins/tree/main/src/copyable-functions
template<typename Fn>
struct CopyableTrampoline;
template<typename R, typename... Args>
struct CopyableTrampoline<R(Args...)> {
using rv_t = R;
using target_t = R(Args...);
using marshaler_t = R(Args..., target_t *target);
struct instance_info_t {
target_t *target;
marshaler_t *marshaler;
};
static R trampoline_canonical(Args... args) {
//instance_info *info;
//asm("movabs $0xAABBCCDDEEFF0011, %0" : "=r" (info));
auto info = (instance_info_t*)&instance_info_canonical;
return info->marshaler(args..., info->target);
}
static __attribute__((aligned(16), naked)) void instance_info_canonical() {
__asm__(".quad 0");
__asm__(".quad 0");
}
static target_t *make_instance(target_t *target, marshaler_t *marshaler, char *section_start, char *section_end) {
auto section_size = section_end - section_start;
printf ("Section len: %d\n", (int)section_size);
// alloc
auto instance = (uint8_t*)mmap(0, section_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// copy
memcpy(instance, section_start, section_size);
// bind
auto code_offset = (uintptr_t)&trampoline_canonical - (uintptr_t)section_start;
auto info_offset = (uintptr_t)&instance_info_canonical - (uintptr_t)section_start;
printf("code_offset offset: %d\n", (int)code_offset);
printf("info_offset offset: %d\n", (int)info_offset);
auto info = (instance_info_t*)(instance + info_offset);
#if defined(ADDR_PATCH)
auto patch_offset = (uintptr_t)&test_func_canonical_patch - (uintptr_t)__start_test_func_section;
printf("patch_offset offset: %d\n", (int)patch_offset);
auto patch = (instance_info_t**)(instance + patch_offset + 2); // +2 is architecture specific
printf("Patching: %p from %p to %p\n", patch, *patch, info);
*patch = info;
#endif
info->target = target;
info->marshaler = marshaler;
return (target_t *)(instance + code_offset);
}
};
#define DECL_COPYABLE_TRAMPLOLINE(name, rv, ...) \
template __attribute__((section(#name "_copy_section"))) void CopyableTrampoline<rv(__VA_ARGS__)>::instance_info_canonical(); \
template __attribute__((section(#name "_copy_section"))) auto CopyableTrampoline<rv(__VA_ARGS__)>:: \
trampoline_canonical(__VA_ARGS__) -> rv_t; \
template auto CopyableTrampoline<rv(__VA_ARGS__)>:: \
make_instance(target_t *target, marshaler_t *marshaler, char *section_start, char *section_end) -> target_t *; \
CopyableTrampoline<rv(__VA_ARGS__)>::target_t *make_##name##_instance(CopyableTrampoline<rv(__VA_ARGS__)>::target_t *target, CopyableTrampoline<rv(__VA_ARGS__)>::marshaler_t *marshal) { \
extern char __start_##name##_copy_section[]; \
extern char __stop_##name##_copy_section[]; \
return CopyableTrampoline<rv(__VA_ARGS__)>::make_instance(target, marshal, __start_##name##_copy_section, __stop_##name##_copy_section); \
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment