# A C function for returning the address of the calling function

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

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

cmp bl, 0xE8

cmp bl, 0xFF
je Exit              // the adress is absolute
jmp Abort

// 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();
}

• It doesn't handle indirect calls, does it?
– vnp
Oct 26 '15 at 16:07
• In the terminology described in the comment above, do you mean the case where bar is actuall assigned to a function pointer and then called? Oct 26 '15 at 16:09
• After googling for 'indirect call'... No, it does not handle it. It sees 'through' it. Oct 26 '15 at 16:20
• When foo was called via register (pointer).
– vnp
Oct 26 '15 at 16:22
• If called_from is being executed (topmost stack frame $n$), it tampers with the stack frame $n - 2$. Oh, man, I dunno. Oct 26 '15 at 16:24

# 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.