6
\$\begingroup\$

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();
}
\$\endgroup\$
6
  • \$\begingroup\$ It doesn't handle indirect calls, does it? \$\endgroup\$
    – vnp
    Oct 26 '15 at 16:07
  • \$\begingroup\$ 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? \$\endgroup\$
    – coderodde
    Oct 26 '15 at 16:09
  • \$\begingroup\$ After googling for 'indirect call'... No, it does not handle it. It sees 'through' it. \$\endgroup\$
    – coderodde
    Oct 26 '15 at 16:20
  • \$\begingroup\$ When foo was called via register (pointer). \$\endgroup\$
    – vnp
    Oct 26 '15 at 16:22
  • \$\begingroup\$ If called_from is being executed (topmost stack frame \$n\$), it tampers with the stack frame \$n - 2\$. Oh, man, I dunno. \$\endgroup\$
    – coderodde
    Oct 26 '15 at 16:24
2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.