The link register in ARM processors is a special register that stores the return address when a function call is made. It enables linking between different parts of a program when jumping between functions and returning back. Understanding the link register is key to low-level programming on ARM devices.
Function Calls and the Link Register
When a program calls a function, the address of the next instruction after the call needs to be stored somewhere so that the program can return to that point after the function finishes executing. This return address is stored in the link register (LR).
For example, consider the following C code:
int sum(int a, int b) {
return a + b;
}
int main() {
int result = sum(3, 4);
// do something with result
}
When main() calls sum(), the address of the instruction after the call (where result is used) needs to be stored. The LR register is used to store this return address. When sum() finishes executing, it returns, and the program jumps back to the instruction stored in the LR. This enables linking between different parts of the program.
ARM Calling Conventions and the Link Register
The ARM procedure call standard defines conventions and requirements for how function calls work on ARM processors. This includes the use of the link register.
Key points in the ARM calling convention related to LR:
- When a BL (branch with link) instruction performs a function call, it stores the return address in the LR register.
- It is the caller’s responsibility to save LR if the callee needs to use it. This is often done by pushing LR onto the stack.
- The LR register does not need to be preserved across function calls. The callee can modify it freely.
- To return, the Ret instruction pops the PC (program counter) from the stack which was previously saved in LR.
Therefore, the general flow when calling and returning from a function is:
- Caller calls BL to branch to function, return address stored in LR
- Caller pushes LR to stack to preserve it
- Callee does work, can modify LR
- Callee pops PC from stack to return, loading return address from LR
- Execution resumes in caller at instruction after original BL
Using Link Register in ARM Assembly
In ARM assembly code, the link register is referred to as LR or R14. Here is an example of using it:
// Function to add two integers
ADD func:
ADD R0, R0, R1 // R0 = R0 + R1
BX LR // Return
// Call add function
MOV R0, #3 // R0 = 3
MOV R1, #4 // R1 = 4
BL ADD // Call add(), return address in LR
// LR is now return address
The BL instruction branches to the label ADD, which is the start of the add function. BL both branches and saves the return address to the add instruction in LR. When the function returns, it returns to the instruction after the BL.
The caller does not need to preserve LR since the callee is not using it. The function returns using BX LR which branches to the address stored in LR, returning control back to the caller.
Preserving LR on the Stack
If the callee function needs to use LR within its execution, the caller must preserve it first before branching. This can be done by pushing LR onto the stack:
// Caller function
PUSH {LR} // Push LR onto stack
BL func // Branch and link to func
POP {LR} // Restore LR
// Function call
func:
// Use LR
BX LR // Return
This pushes LR onto the stack before calling func, and pops it back after. The func code is free to use LR as needed without corrupting the return address.
Uses of the Link Register
Beyond storing return addresses, the link register in ARM can be used for several other purposes:
- Branch table dispatch: The LR can contain a branch table index used for switch/case statements.
- Indirect branches: BX LR can branch to an address dynamically computed in LR.
- Position independence: LR can be used to get the address of the instruction after a BL for position independent code.
- Subroutine linkage: LR is used to pass arguments and linkage data between subroutines.
So while the link register is most commonly associated with return addresses, it has utility for other tasks where linking between different code segments is required.
Link Register vs Program Counter
The program counter (PC) and link register (LR) work closely together. Their key differences are:
- PC contains the address of the currently executing instruction.
- LR contains the address to return to after a function call.
- PC is updated automatically by the processor as instructions are executed.
- LR must be explicitly set by branch instructions like BL.
- PC is saved to LR for function calls, LR is loaded back into PC to return.
In general, PC holds the current execution address, while LR holds the future return address. LR acts as a kind of secondary program counter used explicitly for branches and function calls.
Typical Uses of the Link Register
To summarize, the link register is most commonly used for:
- Storing return addresses for function calls
- Preserving return addresses across function calls
- Returning back to the calling code after functions complete
- Indirect branches (computed go to location)
- Position independent code references
- Passing arguments and data between routines
Anytime code needs to branch away and then return back to where it left, the link register provides the linking mechanism to make this possible. It is an indispensable register for low-level ARM programming.
Saving State on Context Switches
When the ARM processor needs to context switch between processes or handle an interrupt, the complete processor state must be saved so it can be restored later. This includes saving the link register.
Typically the LR is pushed onto the current process’s stack along with other general purpose registers, the program counter, and status registers. This preserves enough state for the original process to resume where it left off once the interrupt or other process completes.
Link Register Exceptions
If the link register contains an invalid return address, the processor will branch to a bad location when returning from a function call or interrupt. This causes an exception called prefetch abort. The operating system then handles the case to either terminate the process safely or take corrective action.
Storing an invalid address in the link register when returning to non-executable memory is one common cause of prefetch abort exceptions. Defensive programming techniques should be used to avoid corrupting the LR contents.
Summary
The link register in ARM processors plays a vital role in branching between functions and returning to the proper instruction address. It temporary stores the return address across calls, links disjoint code segments together, and facilitates critical context switching. Mastering use of the LR is required for robust application and system-level programming on ARM architectures.