Skip to content

Instantly share code, notes, and snippets.

@slembcke
Created May 13, 2010 07:12
Show Gist options
  • Save slembcke/399586 to your computer and use it in GitHub Desktop.
Save slembcke/399586 to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
// NOTE: double precision floats count as two arguments
// givenArgc - count of arguments generated function should accept
// largeStruct - true if the function returns a struct larger than 4bytes
// func - function to wrap
// partialArgc - count of arguments passed by the trampoline to func
// ... - The partial arguments to pass
//
// trampoline(n, 0, func, m, p1, ..., pm) returns a function f such that
// when f(g1, ..., gn) is called it calls func(p1, ..., pm, g1, ..., gn)
void *trampoline(int givenArgc, int largeStruct, void *func, int partialArgc, ...){
size_t size = 15 + 7*givenArgc + 8*partialArgc + (largeStruct ? 7 : 0);
uint8_t *buff = malloc(size);
size_t i = 0;
// mov %esp,%ecx - copy stack pointer
buff[i++] = 0x89;
buff[i++] = 0xe1;
// sub $0x10,%esp - extend stack
buff[i++] = 0x83;
buff[i++] = 0xec;
buff[i++] = (partialArgc + 0xf)&(~0xf);
if(largeStruct){
// mov 0x4(%ecx),%eax - copy struct return address
buff[i++] = 0x8b;
buff[i++] = 0x41;
buff[i++] = 0x04;
// mov %eax,0x4(%esp) - write struct return address
buff[i++] = 0x89;
buff[i++] = 0x44;
buff[i++] = 0x24;
buff[i++] = 0x04;
}
// mov (%ecx),%eax - copy ret
buff[i++] = 0x8b;
buff[i++] = 0x01;
// mov %eax,(%esp) - write ret
buff[i++] = 0x89;
buff[i++] = 0x04;
buff[i++] = 0x24;
int offset = (largeStruct ? 2 : 1);
for(int index=0; index<givenArgc; index++){
// mov 0x4(%ecx),%eax - copy param
buff[i++] = 0x8b;
buff[i++] = 0x41;
buff[i++] = (index + offset)*4;
// mov %eax,0xc(%esp) - write param
buff[i++] = 0x89;
buff[i++] = 0x44;
buff[i++] = 0x24;
buff[i++] = (index + partialArgc + offset)*4;
}
va_list params;
va_start(params, partialArgc);
for(int index=0; index<partialArgc; index++){
uint32_t param = va_arg(params, uint32_t);
// movl param,0x8(%esp) - write closure 1
buff[i++] = 0xc7;
buff[i++] = 0x44;
buff[i++] = 0x24;
buff[i++] = (index + offset)*4;
buff[i++] = param>>0 & 0xff;
buff[i++] = param>>8 & 0xff;
buff[i++] = param>>16 & 0xff;
buff[i++] = param>>24 & 0xff;
}
va_end(params);
uint32_t rel = (uint32_t)func - (uint32_t)buff - size;
// jmp to func
buff[i++] = 0xe9;
buff[i++] = rel>>0 & 0xff;
buff[i++] = rel>>8 & 0xff;
buff[i++] = rel>>16 & 0xff;
buff[i++] = rel>>24 & 0xff;
return buff;
}
void func(char a, short b, int c, float d){
printf("%d %d %d %f\n", a, b, c, d);
}
int add(int a, int b){return a+b;}
typedef struct packshort {short a, b;} packshort;
packshort makePackshort(short a, short b){return (packshort){a, b};}
typedef struct packint {int a, b, c;} packint;
packint makePackint(int a, int b, int c){return (packint){a, b, c};}
double doubleadd(double a, double b){return a+b;}
typedef struct shortvec3 {short a, b, c;} shortvec3;
shortvec3 sv3add(shortvec3 u, shortvec3 v){return (shortvec3){u.a+v.a, u.b+v.b, u.c+v.c};}
int main(void){
void (*f1)(float) = trampoline(1, 0, func, 3, 1, 2, 3);
f1(4.0);
int (*f2)(void) = trampoline(0, 0, add, 2, 5, 6);
printf("%d\n", f2());
packshort (*f3)(short b) = trampoline(1, 0, makePackshort, 1, 8);
packshort ps = f3(9);
printf("%d %d\n", ps.a, ps.b);
packint (*f4)(int b) = trampoline(1, 1, makePackint, 2, 7, 8);
packint pi = f4(9);
printf("%d %d %d\n", pi.a, pi.b, pi.c);
// doubles count as two args!
double (*f5)(double b) = trampoline(2, 0, doubleadd, 2, 5.0);
printf("%f\n", f5(6.0));
// 6 byte structs get rounded up to 8 bytes (two arguments)
// returned as a large struct
shortvec3 (*f6)(shortvec3 v) = trampoline(2, 1, sv3add, 2, (shortvec3){4,5,6});
shortvec3 v = f6((shortvec3){1, 2, 3});
printf("%d %d %d\n", v.a, v.b, v.c);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment