臂链接寄存器r14如何工作

时间:2014-08-27 22:20:48

标签: c arm

我想了解arm链接寄存器的工作原理以及它在调试中的作用。 我从写一个简单的函数开始。

#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
}

事件日志打印以下内容:  返回地址:0x0000ABAB

我的问题是为什么do_print_r14函数中的打印件打印相同的值。 如果我只是登录行号和功能名称,那会不会更有帮助 将指向代码的确切位置。为什么开发人员在调试中使用r14?

这个问题对你们所有人来说听起来都很基本,但我完全不确定为什么我们需要r14注册。

3 个答案:

答案 0 :(得分:3)

行号和函数名称告诉您您的位置,但返回地址告诉您如何到达 - 这在更复杂的代码中非常有用。在调试纯汇编,高度优化的代码或其他可能没有可理解堆栈帧的情况下,有时检查链接寄存器是了解该地址的唯一方法。

当然,这总是相对于当前的函数,因此将“打印返回地址”包含在自己的小函数中是弄巧成拙的,因为它只会告诉你在哪里从<假设编译器还没有决定内联它来调用 函数,在这种情况下你确实可以使用__LINE__代替调用。

现在是微妙但重要的一点:__return_address()内在的意图是“当前函数的返回地址”,这与“R14的当前内容”绝不相同 - 如果编译器在输入时保存了返回地址,然后可以随意使用R14。 do_print_r14()中的两行都打印0x823194BB,因为这是调用do_print_r14()的地方,并且在该调用期间没有任何内容会改变它。如果你想要查看C环境的抽象级别,看看在执行期间R14发生了什么实际,你需要使用内联汇编技巧,或者使用调试器逐步完成。

答案 1 :(得分:0)

r14是CPU从函数返回时将跳转到的地址。因此,对__return_address的调用将产生相同的值。

或许可以更好地证明这一点:

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();
}

此处将打印两个不同的值(对应于调用的两个不同位置do_print_r14)。

答案 2 :(得分:0)

LR的用法由ARM体系结构和ARM AAPCS定义。链接寄存器的帮助仅仅是副作用而不是功能。

来自AAPCS:

  

5.3子程序调用

     

ARM和Thumb指令集都包含一个原始子程序调用指令BL,它执行分支链接操作。执行BL的效果是将程序计数器的顺序下一个值 - 返回地址 - 传送到链接寄存器(LR),将目标地址传送到程序计数器(PC)。