LD_PRELOAD interpolation and variadic functions
As part of my work on OpenMP debugging, I had to implement interpolation functions to capture the beginning and end of some library functions. In general, that's easy:
// library function we want to intercept:
// int test(int a, int b);
int test(int a, int b) {
static fct_t real_test = dlsym(RTLD_NEXT, "test");
//before test
real_test(a, b);
//after test
}
I didn't test this code, but that should work. Now, what it the function you want to intercept looks like that:
void myprintf_real(const char *fmt, ...);
Portable Answer
you can't intercept it!
ASM Hardcore Answer
//tested and certainly only working on x86-64
void myprintf_asm_interpo(const char *fmt, ...) {
//before myprintf
// unroll the frame
asm volatile("mov -0xb8(%rbp),%rdi\n\t"
"mov -0xa8(%rbp),%rsi\n\t"
"mov -0xa0(%rbp),%rdx\n\t"
"mov -0x98(%rbp),%rcx\n\t"
"mov -0x90(%rbp),%r8\n\t"
"mov -0x88(%rbp),%r9\n\t"
"add $0xc0,%rsp\n\t");
//jump to myprintf_real;
volatile register void ** rsp asm ("rsp");
//movq $[addrs], -8(%%rsp) // I can't compile that ...
*(rsp-1) = myprintf_real;
asm volatile(
"mov %rbp,%rsp\n\t"
"pop %rbp\n\t"
"mov (%rax), %rax\n\t"
"jmpq *%rax");
}
I disassembled the function prolog with GDB, then reversed it and put it before jumping to myprintf_real. With that technique I can't insert code after the function execution (because it returns directly to the caller frame), but that was already a good start.
GCC-Specific Clean Answer
With __builtin_apply_args() and __builtin_apply()! Easy peasy! I prefer my code to be gcc-specific than architecture specific, especially with such a hardcoded blob of assembly!
int myprintf_gcc_interpo(char *fmt, ...) {
// before
void *arg = __builtin_apply_args();
void *ret = __builtin_apply((void*)printf, arg, 100);
//after
__builtin_return(ret);
}
From stackoverflow with love ;-) Test it with this file.
(gdb) break *0x972