1

I wanted to understand how arm link register works and how is it helpful in debugging. I started by writing a simple function.

#define MACRO_TEST()  (event_log__add_args(MACRO_TEST,__return_address()))         

static void do_print_r14(void) {
    printf_all("return address 0x%08X \n",__return_address()); //prints 0x823194BB
    MACRO_TEST();
    printf_all("return address 0x%08X \n",__return_address()); //prints 0x823194BB
}

The event log prints the following : Return Address: 0x0000ABAB

My question is why the prints in the do_print_r14 function prints the same value. Wouldn't it be more helpful if I just login the Line number and function name , that will point to the exact location of the code. Why do developers use r14 in debugging?

This question might sound very basic to you all but I am not at all sure why we need r14 register.

user968000
  • 1,765
  • 3
  • 22
  • 31

3 Answers3

4

The line number and function name tell you where you are, but the return address tells you how you got there - that can be very useful in more complex code. When debugging pure assembly, heavily optimised code, or other situations where you might not have an intelligible stack frame, sometimes inspecting the link register is the only way to know that address.

Of course, this is always relative to the current function, thus having "print the return address" wrapped up in its own little function is self-defeating since it will then only tell you where you called that function from (assuming the compiler hasn't decided to inline it), in which case you indeed may as well have used __LINE__ instead of the call.

Now the subtle but important point: the intent of the __return_address() intrinsic is "the return address of the current function", which is by no means the same thing as "the current contents of R14" - if the compiler has saved the return address on entry it's then free to use R14 for whatever it wants. The two lines in do_print_r14() both print 0x823194BB because that is where do_print_r14() was called from, and during that call nothing is going to change that. If you want to look below the abstraction level of the C environment and see what actually happens to R14 during execution, you'll need to use inline assembly tricks, or step through with a debugger.

Notlikethat
  • 20,095
  • 3
  • 40
  • 77
  • 1
    See also: [ARM Link and frame pointer](http://stackoverflow.com/questions/15752188/arm-link-register-and-frame-pointer) and [ARM sp and lr](http://stackoverflow.com/questions/8236959/what-are-sp-stack-and-lr-in-arm). The OP has some concept issues. The `__return_address()` maybe the actual `lr` or in a stack frame for the case of non-leaf functions; that is a good point. – artless noise Aug 30 '14 at 01:01
0

r14 is the address that the CPU will jump to when returning from the function. Thus, calls to __return_address will yield the same value.

Perhaps a better demonstration of this would be something like:

static void do_print_r14(void) {
    printf_all("return address 0x%08X \n",__return_address());
}

static void test_r14(void) {
    printf_all("first call...\n");
    do_print_r14();
    printf_all("second call...\n");
    do_print_r14();
}

Here, two different values will be printed (corresponding to the two different places do_print_r14 is called).

Drew McGowen
  • 11,471
  • 1
  • 31
  • 57
0

Usage of LR is defined by ARM Architecture and ARM AAPCS. Link register's help on is merely a side effect rather than a feature.

From AAPCS:

5.3 Subroutine Calls

Both the ARM and Thumb instruction sets contain a primitive subroutine call instruction, BL, which performs a branch-with-link operation. The effect of executing BL is to transfer the sequentially next value of the program counter—the return address—into the link register (LR) and the destination address into the program counter (PC).

auselen
  • 27,577
  • 7
  • 73
  • 114