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@18:a0fde14b6c39, 2016-04-17 (annotated)
- Committer:
- Christopher Haster
- Date:
- Sun Apr 17 23:38:04 2016 -0500
- Revision:
- 18:a0fde14b6c39
- Parent:
- 17:079c5ad807fb
Increase to 5 args
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Christopher Haster |
12:a005f8ca5d36 | 1 | /* Platform specific trampoline generation |
Christopher Haster |
12:a005f8ca5d36 | 2 | */ |
Christopher Haster |
12:a005f8ca5d36 | 3 | #ifndef TRAMPOLINE_H |
Christopher Haster |
12:a005f8ca5d36 | 4 | #define TRAMPOLINE_H |
Christopher Haster |
12:a005f8ca5d36 | 5 | |
Christopher Haster |
12:a005f8ca5d36 | 6 | |
Christopher Haster |
12:a005f8ca5d36 | 7 | /* Type of function available from trampolines |
Christopher Haster |
12:a005f8ca5d36 | 8 | */ |
Christopher Haster |
12:a005f8ca5d36 | 9 | typedef void entry_t(); |
Christopher Haster |
12:a005f8ca5d36 | 10 | |
Christopher Haster |
12:a005f8ca5d36 | 11 | /* Trampoline storage |
Christopher Haster |
12:a005f8ca5d36 | 12 | */ |
Christopher Haster |
12:a005f8ca5d36 | 13 | typedef struct trampoline trampoline_t; |
Christopher Haster |
12:a005f8ca5d36 | 14 | |
Christopher Haster |
12:a005f8ca5d36 | 15 | /* Trampoline setup |
Christopher Haster |
12:a005f8ca5d36 | 16 | * @param trampoline Storage for trampoline |
Christopher Haster |
12:a005f8ca5d36 | 17 | * @param callback Function to call with context |
Christopher Haster |
12:a005f8ca5d36 | 18 | * @param context Context to pass to function |
Christopher Haster |
12:a005f8ca5d36 | 19 | * @return Entry point into trampoline |
Christopher Haster |
12:a005f8ca5d36 | 20 | */ |
Christopher Haster |
12:a005f8ca5d36 | 21 | static inline entry_t *trampoline_init(trampoline_t *trampoline, |
Christopher Haster |
12:a005f8ca5d36 | 22 | void (*callback)(void *), void *context); |
Christopher Haster |
12:a005f8ca5d36 | 23 | |
Christopher Haster |
12:a005f8ca5d36 | 24 | |
Christopher Haster |
12:a005f8ca5d36 | 25 | /* Platform specific trampoline generation |
Christopher Haster |
12:a005f8ca5d36 | 26 | */ |
Christopher Haster |
12:a005f8ca5d36 | 27 | #if defined(__CORTEX_M3) || defined(__CORTEX_M4) || defined(__thumb2__) |
Christopher Haster |
12:a005f8ca5d36 | 28 | struct trampoline { |
Christopher Haster |
12:a005f8ca5d36 | 29 | volatile uint32_t code[1]; |
Christopher Haster |
12:a005f8ca5d36 | 30 | volatile uint32_t context; |
Christopher Haster |
12:a005f8ca5d36 | 31 | volatile uint32_t callback; |
Christopher Haster |
12:a005f8ca5d36 | 32 | }; |
Christopher Haster |
12:a005f8ca5d36 | 33 | |
Christopher Haster |
12:a005f8ca5d36 | 34 | static inline entry_t *trampoline_init(trampoline_t *trampoline, |
Christopher Haster |
12:a005f8ca5d36 | 35 | void (*callback)(void *), void *context) { |
Christopher Haster |
12:a005f8ca5d36 | 36 | |
Christopher Haster |
12:a005f8ca5d36 | 37 | // Loads context into r0 and branches to callback |
Christopher Haster |
12:a005f8ca5d36 | 38 | // Only touches scratch registers saved by caller |
Christopher Haster |
12:a005f8ca5d36 | 39 | trampoline->code[0] = 0x8001e89f; // ldm pc, {r0, pc} |
Christopher Haster |
12:a005f8ca5d36 | 40 | trampoline->context = (uint32_t)context; |
Christopher Haster |
12:a005f8ca5d36 | 41 | trampoline->callback = (uint32_t)callback; |
Christopher Haster |
12:a005f8ca5d36 | 42 | |
Christopher Haster |
12:a005f8ca5d36 | 43 | // Flush caches |
Christopher Haster |
12:a005f8ca5d36 | 44 | #if defined(__GNUC__) |
Christopher Haster |
12:a005f8ca5d36 | 45 | __asm__ volatile ("isb 0xf":::"memory"); |
Christopher Haster |
12:a005f8ca5d36 | 46 | __asm__ volatile ("dsb 0xf":::"memory"); |
Christopher Haster |
12:a005f8ca5d36 | 47 | #else |
Christopher Haster |
12:a005f8ca5d36 | 48 | __ISB() |
Christopher Haster |
12:a005f8ca5d36 | 49 | __DSB() |
Christopher Haster |
12:a005f8ca5d36 | 50 | #endif |
Christopher Haster |
12:a005f8ca5d36 | 51 | |
Christopher Haster |
12:a005f8ca5d36 | 52 | // Set thumb bit |
Christopher Haster |
12:a005f8ca5d36 | 53 | return (entry_t *)((uint32_t)trampoline->code + 1); |
Christopher Haster |
12:a005f8ca5d36 | 54 | } |
Christopher Haster |
12:a005f8ca5d36 | 55 | |
Christopher Haster |
12:a005f8ca5d36 | 56 | #elif defined(__CORTEX_M0PLUS) || defined(__CORTEX_M0) || defined(__thumb__) |
Christopher Haster |
12:a005f8ca5d36 | 57 | struct trampoline { |
Christopher Haster |
12:a005f8ca5d36 | 58 | volatile uint16_t code[4]; |
Christopher Haster |
12:a005f8ca5d36 | 59 | volatile uint32_t context; |
Christopher Haster |
12:a005f8ca5d36 | 60 | volatile uint32_t callback; |
Christopher Haster |
12:a005f8ca5d36 | 61 | }; |
Christopher Haster |
12:a005f8ca5d36 | 62 | |
Christopher Haster |
12:a005f8ca5d36 | 63 | static inline entry_t *trampoline_init(trampoline_t *trampoline, |
Christopher Haster |
12:a005f8ca5d36 | 64 | void (*callback)(void *), void *context) { |
Christopher Haster |
12:a005f8ca5d36 | 65 | |
Christopher Haster |
12:a005f8ca5d36 | 66 | // Loads context into r0, callback to r1, and branches |
Christopher Haster |
12:a005f8ca5d36 | 67 | // Only touches scratch registers saved by caller |
Christopher Haster |
12:a005f8ca5d36 | 68 | trampoline->code[0] = 0x2202; // movs r2, #2 |
Christopher Haster |
12:a005f8ca5d36 | 69 | trampoline->code[1] = 0x447a; // add r2, pc |
Christopher Haster |
12:a005f8ca5d36 | 70 | trampoline->code[2] = 0xca03; // ldm r2!, {r0, r1} |
Christopher Haster |
12:a005f8ca5d36 | 71 | trampoline->code[3] = 0x4708; // bx r1 |
Christopher Haster |
12:a005f8ca5d36 | 72 | trampoline->context = (uint32_t)context; |
Christopher Haster |
12:a005f8ca5d36 | 73 | trampoline->callback = (uint32_t)callback; |
Christopher Haster |
12:a005f8ca5d36 | 74 | |
Christopher Haster |
12:a005f8ca5d36 | 75 | // Flush caches |
Christopher Haster |
12:a005f8ca5d36 | 76 | #if defined(__GNUC__) |
Christopher Haster |
12:a005f8ca5d36 | 77 | __asm__ volatile ("isb 0xf":::"memory"); |
Christopher Haster |
12:a005f8ca5d36 | 78 | __asm__ volatile ("dsb 0xf":::"memory"); |
Christopher Haster |
12:a005f8ca5d36 | 79 | #else |
Christopher Haster |
12:a005f8ca5d36 | 80 | __ISB() |
Christopher Haster |
12:a005f8ca5d36 | 81 | __DSB() |
Christopher Haster |
12:a005f8ca5d36 | 82 | #endif |
Christopher Haster |
12:a005f8ca5d36 | 83 | |
Christopher Haster |
12:a005f8ca5d36 | 84 | // Set thumb bit |
Christopher Haster |
12:a005f8ca5d36 | 85 | return (entry_t *)((uint32_t)trampoline->code + 1); |
Christopher Haster |
12:a005f8ca5d36 | 86 | } |
Christopher Haster |
12:a005f8ca5d36 | 87 | |
Christopher Haster |
12:a005f8ca5d36 | 88 | #elif defined(__i386__) |
Christopher Haster |
16:c88d591ae0b4 | 89 | #include <sys/mman.h> |
Christopher Haster |
16:c88d591ae0b4 | 90 | #include <unistd.h> |
Christopher Haster |
16:c88d591ae0b4 | 91 | |
Christopher Haster |
12:a005f8ca5d36 | 92 | struct trampoline { |
Christopher Haster |
12:a005f8ca5d36 | 93 | volatile uint8_t code[24]; |
Christopher Haster |
12:a005f8ca5d36 | 94 | volatile uint32_t context; |
Christopher Haster |
12:a005f8ca5d36 | 95 | volatile uint32_t callback; |
Christopher Haster |
12:a005f8ca5d36 | 96 | }; |
Christopher Haster |
12:a005f8ca5d36 | 97 | |
Christopher Haster |
12:a005f8ca5d36 | 98 | static inline entry_t *trampoline_init(trampoline_t *trampoline, |
Christopher Haster |
12:a005f8ca5d36 | 99 | void (*callback)(void *), void *context) { |
Christopher Haster |
12:a005f8ca5d36 | 100 | |
Christopher Haster |
12:a005f8ca5d36 | 101 | // Pushes context and calls callback |
Christopher Haster |
12:a005f8ca5d36 | 102 | // Relative call is used to access pc-relative addresses |
Christopher Haster |
12:a005f8ca5d36 | 103 | // Only touches scratch registers saved by caller |
Christopher Haster |
12:a005f8ca5d36 | 104 | volatile uint8_t *c = trampoline->code; |
Christopher Haster |
12:a005f8ca5d36 | 105 | c[ 0]=0x55; // push %ebp |
Christopher Haster |
12:a005f8ca5d36 | 106 | c[ 1]=0x89; c[2]=0xe5; // mov %esp, %ebp |
Christopher Haster |
12:a005f8ca5d36 | 107 | c[ 3]=0xe8; c[4]=0; c[5]=0; c[6]=0; c[7]=0; // call 0 |
Christopher Haster |
12:a005f8ca5d36 | 108 | c[ 8]=0x5a; // pop %edx |
Christopher Haster |
12:a005f8ca5d36 | 109 | c[ 9]=0x8b; c[10]=0x42; c[11]=16; // mov 16(%edx), %eax |
Christopher Haster |
12:a005f8ca5d36 | 110 | c[12]=0x50; // push %eax |
Christopher Haster |
12:a005f8ca5d36 | 111 | c[13]=0x8b; c[14]=0x42; c[15]=20; // mov 20(%edx), %eax |
Christopher Haster |
12:a005f8ca5d36 | 112 | c[16]=0xff; c[17]=0xd0; // call *%eax |
Christopher Haster |
12:a005f8ca5d36 | 113 | c[18]=0x89; c[19]=0xec; // mov %ebp, %esp |
Christopher Haster |
12:a005f8ca5d36 | 114 | c[20]=0x5d; // pop %ebp |
Christopher Haster |
12:a005f8ca5d36 | 115 | c[21]=0xc3; // ret |
Christopher Haster |
12:a005f8ca5d36 | 116 | trampoline->context = (uint32_t)context; |
Christopher Haster |
12:a005f8ca5d36 | 117 | trampoline->callback = (uint32_t)callback; |
Christopher Haster |
12:a005f8ca5d36 | 118 | |
Christopher Haster |
16:c88d591ae0b4 | 119 | // Mark region as executable |
Christopher Haster |
16:c88d591ae0b4 | 120 | uintptr_t pagesize = sysconf(_SC_PAGESIZE); |
Christopher Haster |
16:c88d591ae0b4 | 121 | uintptr_t start = (uintptr_t)trampoline & ~(pagesize-1); |
Christopher Haster |
16:c88d591ae0b4 | 122 | uintptr_t size = ((uintptr_t)trampoline - start) + sizeof(trampoline_t); |
Christopher Haster |
16:c88d591ae0b4 | 123 | int err = mprotect((void*)start, size, PROT_READ | PROT_WRITE | PROT_EXEC); |
Christopher Haster |
16:c88d591ae0b4 | 124 | if (err) { |
Christopher Haster |
16:c88d591ae0b4 | 125 | return 0; |
Christopher Haster |
16:c88d591ae0b4 | 126 | } |
Christopher Haster |
16:c88d591ae0b4 | 127 | |
Christopher Haster |
12:a005f8ca5d36 | 128 | // Return entry point |
Christopher Haster |
12:a005f8ca5d36 | 129 | return (entry_t *)trampoline->code; |
Christopher Haster |
12:a005f8ca5d36 | 130 | } |
Christopher Haster |
12:a005f8ca5d36 | 131 | |
Christopher Haster |
12:a005f8ca5d36 | 132 | #elif defined(__amd64__) |
Christopher Haster |
17:079c5ad807fb | 133 | #include <sys/mman.h> |
Christopher Haster |
17:079c5ad807fb | 134 | #include <unistd.h> |
Christopher Haster |
17:079c5ad807fb | 135 | |
Christopher Haster |
17:079c5ad807fb | 136 | struct trampoline { |
Christopher Haster |
17:079c5ad807fb | 137 | volatile uint8_t code[24]; |
Christopher Haster |
17:079c5ad807fb | 138 | volatile uint64_t context; |
Christopher Haster |
17:079c5ad807fb | 139 | volatile uint64_t callback; |
Christopher Haster |
17:079c5ad807fb | 140 | }; |
Christopher Haster |
17:079c5ad807fb | 141 | |
Christopher Haster |
17:079c5ad807fb | 142 | static inline entry_t *trampoline_init(trampoline_t *trampoline, |
Christopher Haster |
17:079c5ad807fb | 143 | void (*callback)(void *), void *context) { |
Christopher Haster |
17:079c5ad807fb | 144 | |
Christopher Haster |
17:079c5ad807fb | 145 | // Pushes context and calls callback |
Christopher Haster |
17:079c5ad807fb | 146 | // Relative call is used to access pc-relative addresses |
Christopher Haster |
17:079c5ad807fb | 147 | // Only touches scratch registers saved by caller |
Christopher Haster |
17:079c5ad807fb | 148 | volatile uint8_t *c = trampoline->code; |
Christopher Haster |
17:079c5ad807fb | 149 | c[ 0]=0x55; // push %rbp |
Christopher Haster |
17:079c5ad807fb | 150 | c[ 1]=0x48; c[ 2]=0x89; c[ 3]=0xe5; // mov %rsp, %rbp |
Christopher Haster |
17:079c5ad807fb | 151 | // mov 13(%rip), %rdi |
Christopher Haster |
17:079c5ad807fb | 152 | c[ 4]=0x48; c[ 5]=0x8b; c[ 6]=0x3d; c[ 7]=13; c[ 8]=0; c[ 9]=0; c[10]=0; |
Christopher Haster |
17:079c5ad807fb | 153 | // mov 14(%rip), %rax |
Christopher Haster |
17:079c5ad807fb | 154 | c[11]=0x48; c[12]=0x8b; c[13]=0x05; c[14]=14; c[15]=0; c[16]=0; c[17]=0; |
Christopher Haster |
17:079c5ad807fb | 155 | c[18]=0xff; c[19]=0xd0; // callq *%rax |
Christopher Haster |
17:079c5ad807fb | 156 | c[20]=0x5d; // pop %rbp |
Christopher Haster |
17:079c5ad807fb | 157 | c[21]=0xc3; // retq |
Christopher Haster |
17:079c5ad807fb | 158 | trampoline->context = (uint64_t)context; |
Christopher Haster |
17:079c5ad807fb | 159 | trampoline->callback = (uint64_t)callback; |
Christopher Haster |
17:079c5ad807fb | 160 | |
Christopher Haster |
17:079c5ad807fb | 161 | // Mark region as executable |
Christopher Haster |
17:079c5ad807fb | 162 | uintptr_t pagesize = sysconf(_SC_PAGESIZE); |
Christopher Haster |
17:079c5ad807fb | 163 | uintptr_t start = (uintptr_t)trampoline & ~(pagesize-1); |
Christopher Haster |
17:079c5ad807fb | 164 | uintptr_t size = ((uintptr_t)trampoline - start) + sizeof(trampoline_t); |
Christopher Haster |
17:079c5ad807fb | 165 | int err = mprotect((void*)start, size, PROT_READ | PROT_WRITE | PROT_EXEC); |
Christopher Haster |
17:079c5ad807fb | 166 | if (err) { |
Christopher Haster |
17:079c5ad807fb | 167 | return 0; |
Christopher Haster |
17:079c5ad807fb | 168 | } |
Christopher Haster |
17:079c5ad807fb | 169 | |
Christopher Haster |
17:079c5ad807fb | 170 | // Return entry point |
Christopher Haster |
17:079c5ad807fb | 171 | return (entry_t *)trampoline->code; |
Christopher Haster |
17:079c5ad807fb | 172 | } |
Christopher Haster |
17:079c5ad807fb | 173 | |
Christopher Haster |
12:a005f8ca5d36 | 174 | #else |
Christopher Haster |
12:a005f8ca5d36 | 175 | #error "Target is currently not supported" |
Christopher Haster |
12:a005f8ca5d36 | 176 | #endif |
Christopher Haster |
12:a005f8ca5d36 | 177 | |
Christopher Haster |
12:a005f8ca5d36 | 178 | |
Christopher Haster |
12:a005f8ca5d36 | 179 | #endif |