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();
}
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Oct 26, 2015 at 10:42
\$\endgroup\$
6
  • \$\begingroup\$ It doesn't handle indirect calls, does it? \$\endgroup\$ Commented Oct 26, 2015 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\$ Commented Oct 26, 2015 at 16:09
  • \$\begingroup\$ After googling for 'indirect call'... No, it does not handle it. It sees 'through' it. \$\endgroup\$ Commented Oct 26, 2015 at 16:20
  • \$\begingroup\$ When foo was called via register (pointer). \$\endgroup\$ Commented Oct 26, 2015 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\$ Commented Oct 26, 2015 at 16:24

1 Answer 1

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.

answered Oct 27, 2015 at 20:08
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.