4

I've got a C function that's supposed to get the value of the caller's lr register into a local variable.

I've tried the following code:

volatile long lr;
asm(
    "str %0, [sp, #4]\n" :
    "=r", (lr)
);

However, this doesn't change anything. It's not that I get the wrong value, it's just that the value of the lr local variable doesn't change at all ( contains garbage ).

Any ideas?

Thanks!

Niv
  • 2,294
  • 5
  • 29
  • 41

2 Answers2

6

To answer the question directly, there are two solutions.

    long lr;
    asm(" mov %0,lr\n" :: "=r" (lr));

This will put the lr value in the lr variable. You may wish to do this to analyse something. If you are assuming that the lr contains a return address, then this is untrue as Igor explains. In a non-leaf function, the compiler may use the lr as a temporary when computing a function argument as it is going to be clobbered.

    register long lr asm("lr");

This form allows you to reference the variable lr anywhere and the compiler will use the live lr value. The value may change through-out the function. For instance, when you call another function, it will usually be set to the return point in the currently active function.

As Igor explains whether getting the lr may not give you exactly what you expect. And the mechanism's we are using are gcc specific. You can not do this in a portable way; but accessing an lr is intrinsically non-portable.

See: ARM link register and frame pointer question for a little more on the use of lr.

artless noise
  • 21,212
  • 6
  • 68
  • 105
  • 1
    As you are trying to access the APCS's *stack frame's* saved `lr`, I guess what you really want is the `__built_in_return_address()`; You would be better off to use the `fp` in this case, as it is always active. The `sp` will be adjusted for local variables and will never be setup properly to get the saved `lr`. You don't have to understand this at all if you use the `__built_in_return_address()`. – artless noise Aug 16 '13 at 13:13
  • Also the newer AAPCS does not mandate a frame pointer be set up and so you need to use ancillary tables in ELF/object files to do a call tree walk (and for object clean up). It is better to let `__built_in_return_address()` do this for you. See [`-mabi`](https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html) values which may affect `lr` use. – artless noise Feb 27 '17 at 14:14
4

Your code will get the value at the address SP+4. Whether that location contains the initial LR depends on the compiler, optimization settings, specific function, or the place in the function where you put this code. In short, it would probably work only by accident.

EDIT: your code is not even reading anything relevant, it's just storing a value of uninitialized variable on the stack (hint: naming your variable lr doesn't make it magically take the value of the register LR). In the best case it won't do anything, in the worst you'll overwrite something important and it will crash.

I'll assume that you actually want to get the function's return address, not LR specifically. There exist compiler-specific options for that.

  1. GCC offers the __builtin_return_address() intrinsic which returns the return address of the current function (if called with 0) or, possibly, other functions in the call stack (though I wouldn't rely on the latter).

  2. Visual C++ has _ReturnAddress() and even _AddressOfReturnAddress() intrinsics.

I would recommend using one of the above (assuming you're using GCC or VC) and not rely on tricks which may stop working at any time.

Igor Skochinsky
  • 24,629
  • 2
  • 72
  • 109