Suppose you are given two functions, foo
and bar
, and neither of them are inlined. Now, if foo
calls for bar
and bar
calls for called_from
, bar
receives the address of the caller, that is, the address of foo
. I know that there is no real need for such computation as it is always possible to simply pass an enumeration that indicates the caller, yet I was in the mood for hacking the stack.
My code compiles on Visual Studio.
main.cpp:
#include <stdio.h>
/************************************************************************
* Suppose that foo() calls for bar() and bar() calls for called_from(). *
* Now if neither bar() and foo() are inlined functions, bar() receives *
* the address of foo() from the function called_from(). *
************************************************************************/
void* called_from() {
__asm {
mov eax, [ebp]
mov eax, [eax] // foo's ebp
mov eax, [eax + 4] // foo's return address
mov edx, eax; // we might need a copy of
// the foo's return address
sub eax, 5; // the adress of the instruction 'call foo'
mov bl, [eax] // the opcode of the call instruction
inc eax // [eax] points to the address of foo
mov eax, [eax]; // load the caller's address
cmp bl, 0xE8
je RelationalAddress // the address is relational
cmp bl, 0xFF
je Exit // the adress is absolute
jmp Abort
RelationalAddress:
add eax, edx // convert relational address
// to an abolute one
jmp Exit
Abort:
mov eax, 0 // failure, return null pointer
Exit:
}
}
// We need the macro as otherwise it will obscure the actual caller.
#define is_called_from(n) (called_from() == (void*) n)
Here are a few tests:
void foo(); void bar(); void baz(); int main() { printf( "main:\t0x%p\n" "foo: \t0x%p\n" "bar: \t0x%p\n" "baz: \t0x%p\n" "\n", main, foo, bar, baz); foo(); bar(); baz(); getchar(); return 0; } void foo() { printf("This is foo:\n" "called from %p\n" "that is, from ", called_from()); if (is_called_from(main)) { puts("main.\n"); } else if (is_called_from(bar)) { puts("bar.\n"); } else { puts("somewhere else.\n"); } } void bar() { printf("This is bar:\n" "called from 0x%p\n" "\n", called_from()); foo(); } void baz() { printf("This is baz:\n" "called from 0x%p\n" "\n", called_from()); foo(); }
1 Answer 1
Didn't totally work
After porting the code to gnu C style, it only "sort of" worked for me. I was able to have foo()
print that it was called from bar()
or baz()
. However, if foo()
was called directly from main()
, then it crashed when trying to find its caller. That's probably because on my system (Cygwin 32-bit, using Gnu C compiler), main()
doesn't have a caller on the stack.
Clobbered register
It's not safe to write to ebx
without pushing it on the stack and popping it off later. ebx
is considered non-volatile, which means that a function must preserve its value. If you don't, then the calling function will be affected because its local variable may be stored in ebx
, for example. You can safely use eax
, ecx
, and edx
though, because they are all considered volatile registers (i.e. you are allowed to clobber them). So you should change your ebx
accesses to ecx
instead.
Not portable
Your asm statement assumes that the compiler will insert these lines at the top of the function:
pushl %ebp
movl %esp, %ebp
That may be what your compiler does, but it's not a certainty. For example, with my GNU C compiler, it normally outputs the above code. But with the -O2 option, it does this instead:
subl 28,ドル %esp
If you want to make sure that you get the function prologue you need, you should write the whole thing in assembly and include the prologue/epilogue you require.
bar
is actuall assigned to a function pointer and then called? \$\endgroup\$foo
was called via register (pointer). \$\endgroup\$called_from
is being executed (topmost stack frame \$n\$), it tampers with the stack frame \$n - 2\$. Oh, man, I dunno. \$\endgroup\$