Flexible templated function class and related utilities that avoid dynamic memory allocation without limiting functionality
FuncPtr provides a flexible templated function class and related utilities while avoiding dynamic memory allocation and avoiding limited functionality.
FuncPtr provides an intuitive template interface:
FuncPtr<void(const char *)> func(puts); func("hello!\n"); // prints hello!
Several function types are supported by FuncPtr:
// Simple C functions void func(); FuncPtr<void()> fp(func); // C++ Methods struct Thing { void func(); }; Thing thing; FuncPtr<void()> fp(&thing, &Thing::func); // C functions with context void func(Thing *); FuncPtr<void()> fp(&thing, func); // Function objects struct Thing { void operator()(); }; Thing thing; FuncPtr<void()> fp(&thing);
There is no dynamic memory allocation, managing memory is placed entirely on the user. More advanced function manipulation can be accomplished with statically allocated classes:
// Function binding Binder<void(const char *), const char *> bind(putc, "hi!"); bind(); // prints hi! // Function composition Composer<int(const char *), const char *(int)> comp(puts, itoa); comp(10); // prints 10 // Function chaining Chainer<void(const char *), 2> chain; chain.attach(puts); chain.attach(puts); chain("hi!\n"); // prints hi! twice
FuncPtr allows easy support of a large range of function types in C++ APIs with very few lines of code:
class Thing { public: // The following two methods are sufficient for supporting // every supported function type void attach(FuncPtr<void()> func) { _func.attach(func); } template<typename T, typename M> void attach(T *obj, M method) { attach(FuncPtr<void()>(obj, method)); } private: FuncPtr<void()> _func; }
Additionally, FuncPtrs have several utilities for easy integration with C APIs:
// C style callbacks void register_callback(void (*callback)(void *), void *data); register_callback(&FuncPtr<void()>::thunk, &func); // C style functions without context void register_callback(void (*callback)()); Thunker thunk(func); register_callback(thunk); // mbed style callbacks void register_callback(T *obj, void (T::*M)()); register_callback(&func, &FuncPtr<void()>::call);
trampoline.h
- Committer:
- Christopher Haster
- Date:
- 2016-04-17
- Revision:
- 18:a0fde14b6c39
- Parent:
- 17:079c5ad807fb
File content as of revision 18:a0fde14b6c39:
/* Platform specific trampoline generation */ #ifndef TRAMPOLINE_H #define TRAMPOLINE_H /* Type of function available from trampolines */ typedef void entry_t(); /* Trampoline storage */ typedef struct trampoline trampoline_t; /* Trampoline setup * @param trampoline Storage for trampoline * @param callback Function to call with context * @param context Context to pass to function * @return Entry point into trampoline */ static inline entry_t *trampoline_init(trampoline_t *trampoline, void (*callback)(void *), void *context); /* Platform specific trampoline generation */ #if defined(__CORTEX_M3) || defined(__CORTEX_M4) || defined(__thumb2__) struct trampoline { volatile uint32_t code[1]; volatile uint32_t context; volatile uint32_t callback; }; static inline entry_t *trampoline_init(trampoline_t *trampoline, void (*callback)(void *), void *context) { // Loads context into r0 and branches to callback // Only touches scratch registers saved by caller trampoline->code[0] = 0x8001e89f; // ldm pc, {r0, pc} trampoline->context = (uint32_t)context; trampoline->callback = (uint32_t)callback; // Flush caches #if defined(__GNUC__) __asm__ volatile ("isb 0xf":::"memory"); __asm__ volatile ("dsb 0xf":::"memory"); #else __ISB() __DSB() #endif // Set thumb bit return (entry_t *)((uint32_t)trampoline->code + 1); } #elif defined(__CORTEX_M0PLUS) || defined(__CORTEX_M0) || defined(__thumb__) struct trampoline { volatile uint16_t code[4]; volatile uint32_t context; volatile uint32_t callback; }; static inline entry_t *trampoline_init(trampoline_t *trampoline, void (*callback)(void *), void *context) { // Loads context into r0, callback to r1, and branches // Only touches scratch registers saved by caller trampoline->code[0] = 0x2202; // movs r2, #2 trampoline->code[1] = 0x447a; // add r2, pc trampoline->code[2] = 0xca03; // ldm r2!, {r0, r1} trampoline->code[3] = 0x4708; // bx r1 trampoline->context = (uint32_t)context; trampoline->callback = (uint32_t)callback; // Flush caches #if defined(__GNUC__) __asm__ volatile ("isb 0xf":::"memory"); __asm__ volatile ("dsb 0xf":::"memory"); #else __ISB() __DSB() #endif // Set thumb bit return (entry_t *)((uint32_t)trampoline->code + 1); } #elif defined(__i386__) #include <sys/mman.h> #include <unistd.h> struct trampoline { volatile uint8_t code[24]; volatile uint32_t context; volatile uint32_t callback; }; static inline entry_t *trampoline_init(trampoline_t *trampoline, void (*callback)(void *), void *context) { // Pushes context and calls callback // Relative call is used to access pc-relative addresses // Only touches scratch registers saved by caller volatile uint8_t *c = trampoline->code; c[ 0]=0x55; // push %ebp c[ 1]=0x89; c[2]=0xe5; // mov %esp, %ebp c[ 3]=0xe8; c[4]=0; c[5]=0; c[6]=0; c[7]=0; // call 0 c[ 8]=0x5a; // pop %edx c[ 9]=0x8b; c[10]=0x42; c[11]=16; // mov 16(%edx), %eax c[12]=0x50; // push %eax c[13]=0x8b; c[14]=0x42; c[15]=20; // mov 20(%edx), %eax c[16]=0xff; c[17]=0xd0; // call *%eax c[18]=0x89; c[19]=0xec; // mov %ebp, %esp c[20]=0x5d; // pop %ebp c[21]=0xc3; // ret trampoline->context = (uint32_t)context; trampoline->callback = (uint32_t)callback; // Mark region as executable uintptr_t pagesize = sysconf(_SC_PAGESIZE); uintptr_t start = (uintptr_t)trampoline & ~(pagesize-1); uintptr_t size = ((uintptr_t)trampoline - start) + sizeof(trampoline_t); int err = mprotect((void*)start, size, PROT_READ | PROT_WRITE | PROT_EXEC); if (err) { return 0; } // Return entry point return (entry_t *)trampoline->code; } #elif defined(__amd64__) #include <sys/mman.h> #include <unistd.h> struct trampoline { volatile uint8_t code[24]; volatile uint64_t context; volatile uint64_t callback; }; static inline entry_t *trampoline_init(trampoline_t *trampoline, void (*callback)(void *), void *context) { // Pushes context and calls callback // Relative call is used to access pc-relative addresses // Only touches scratch registers saved by caller volatile uint8_t *c = trampoline->code; c[ 0]=0x55; // push %rbp c[ 1]=0x48; c[ 2]=0x89; c[ 3]=0xe5; // mov %rsp, %rbp // mov 13(%rip), %rdi c[ 4]=0x48; c[ 5]=0x8b; c[ 6]=0x3d; c[ 7]=13; c[ 8]=0; c[ 9]=0; c[10]=0; // mov 14(%rip), %rax c[11]=0x48; c[12]=0x8b; c[13]=0x05; c[14]=14; c[15]=0; c[16]=0; c[17]=0; c[18]=0xff; c[19]=0xd0; // callq *%rax c[20]=0x5d; // pop %rbp c[21]=0xc3; // retq trampoline->context = (uint64_t)context; trampoline->callback = (uint64_t)callback; // Mark region as executable uintptr_t pagesize = sysconf(_SC_PAGESIZE); uintptr_t start = (uintptr_t)trampoline & ~(pagesize-1); uintptr_t size = ((uintptr_t)trampoline - start) + sizeof(trampoline_t); int err = mprotect((void*)start, size, PROT_READ | PROT_WRITE | PROT_EXEC); if (err) { return 0; } // Return entry point return (entry_t *)trampoline->code; } #else #error "Target is currently not supported" #endif #endif