2

I have been doing some research into inline assembly in C and how the call stack works but I have been unable to figure out if it's at all possible to retrieve the address of a variable that is requesting the return value of a function, from WITHIN the function.

int hypothetical_func(){

    /*...
    .. some assembly to get the address of 'int a' from the call stack?
    ...*/

    return 5;
}

int main(){
    int a = hypothetical_func();
}

Is this at all possible?

Jack Avante
  • 1,405
  • 1
  • 15
  • 32
  • 1
    No. :) . Sorry, it's a simple answer. :) . The only way to do it would be to write code that analyzes the binary in memory and locates where its return value ends up being assigned after the call return. – David Hoelzer Oct 03 '19 at 12:19
  • 3
    Who says that variable even has a memory address ? – Sander De Dycker Oct 03 '19 at 12:25
  • Oh well :( I did find some code that actually looks through the memory for that frame pointer, but I bet that'd take a long time, and it's also a lot of code that I barely understand. Anyone want to do the honors of making "No" an official answer? xD – Jack Avante Oct 03 '19 at 12:35

3 Answers3

4

NO. int is returned in a register, and the callee has no involvement in what the caller does with that register after it returns. It might never be stored in memory.

If the return-type wasn't int, but instead something large enough that the calling convention returned it by value, then hypothetical_func would have an address for an output. (Or a hypothetical (terrible) calling convention might return even int via hidden pointer instead of a register. Assuming the machine is a register machine like all real CPUs.)

But that might just be a return-value temporary, not the actual LHS of an assignment. (Or initialization, which is close enough to the same thing in C, if not C++). Especially if the assignment is to a global or something. See What prevents the usage of a function argument as hidden pointer? for the case of *out = foo(); where T *out is a function arg. It's highly non-trivial to prove if/when it's safe to pass that function arg along as the return-value object for foo().

And some compilers don't even try to optimize, and just make space on the stack for the return-value temporary and copy from there into the final object.

And as @prl points out, the return-value might not even be the initializer for a variable. e.g. printf("%d\n", foo()); just passes on the return value to a function arg. Or foo(); discards the return value, not assigning it anywhere. (But if the calling convention specifies that the function returns by hidden pointer, the caller must pass a pointer to enough scratch space. The callee is still going to write its return value and needs to not segfault from a bad pointer or overwrite something else. That's an asm / calling-convention detail separate from the operation of the C abstract machine. Or I guess you could say the return-value object still exists, it's just not assigned anywhere.)


Plus with inline assembly, you don't even have access to that. Unless you count writing a __attribute__((naked)) function where you still write the whole function inside an asm statement, and the compiler doesn't handle anything except the name-mangling of the function name. No prologue or epilogue, or abstracting away the calling convention with C variables for args and one that you return. (/grumble that C compilers can't create functions that return multiple separate values in multiple registers like you can in hand-written asm.)

But even with hand-written asm, there's no way to do this for normal calling conventions on normal ISAs like x86 and ARM. The return-value object for int is just a register.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
2

It’s not possible in any practical sense. The return value may not be assigned to a variable, and if it is, the variable may not have a memory address. Even if neither of those is the case, and the return value is assigned to a variable in memory, the address of that variable isn’t available to the function.

An impractical way to figure this out (if needed for debugging, for example) is to get the return address from the stack and disassemble instructions looking for one that writes the return value register to memory. Such an instruction, if it exists, will typically be within a few instructions after the return point.

prl
  • 11,716
  • 2
  • 13
  • 31
-1

If int a = hypothetical_func(); is inside another function, it should be on the stackframe of that function, thus using backtrace() you could find that functions stackframe and locate that variable.

For more extraordinary information: https://www.linuxjournal.com/article/6391

Motomotes
  • 4,111
  • 1
  • 25
  • 24
  • If you're doing this programmatically, probably you'd look at GNU C `__builtin_return_address(0)` to get your own return address (i.e. the instruction after the `call` that jumped to your function, if you ignore optimized tailcalls or pretend they were the ultimate caller.) – Peter Cordes Oct 03 '19 at 13:37
  • Assuming that int a is in main(), and based on the other answers, if it was first declared and uninitialized, and then assigned a value using the function, would it be possible to perform a backtrace on main to find the address of a? – Jack Avante Oct 03 '19 at 13:37
  • @TiboroJacko: No, not without human reverse engineering. Or in a debug build (where even local variables have addresses), maybe you could also parse debug symbols. But the showstopper for using debug symbols is that you'd have to already know the variable name `a`; your function doesn't get that. – Peter Cordes Oct 03 '19 at 13:39