C 手臂链接寄存器r14的工作原理
我想了解arm链接寄存器是如何工作的,以及它对调试有何帮助。 我从编写一个简单的函数开始C 手臂链接寄存器r14的工作原理,c,arm,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
#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注册
r14
是CPU从函数返回时将跳转到的地址。因此,调用\uuu return\u 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\u print\u r14
)r14
是CPU从函数返回时将跳转到的地址。因此,调用\uuu return\u 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\u print\u r14
)行号和函数名告诉您在哪里,但返回地址告诉您如何到达那里-这在更复杂的代码中非常有用。当调试纯汇编、高度优化的代码或其他可能没有可理解堆栈帧的情况时,有时检查链接寄存器是了解该地址的唯一方法
当然,这始终是相对于当前函数的,因此将“打印返回地址”包装在自己的小函数中是自欺欺人的,因为它只会告诉您从何处调用该函数(假设编译器没有决定内联它),在这种情况下,您可能确实使用了\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
现在,微妙但重要的一点是:\uuu return\u address()
内在的意图是“当前函数的返回地址”,这与“R14的当前内容”绝不是一回事——如果编译器在条目上保存了返回地址,那么它就可以自由地使用R14来实现它想要的任何目的。do\u print\u r14()
中的两行都是print 0x823194BB,因为在那里调用了do\u print\u r14()
,在调用过程中,没有任何东西会改变这一点。如果您想在C环境的抽象级别下查看R14在执行过程中的实际情况,则需要使用内联汇编技巧,或使用调试器进行调试。行号和函数名告诉您所在位置,但是返回地址告诉您是如何到达那里的——这在更复杂的代码中非常有用。当调试纯汇编、高度优化的代码或其他可能没有可理解堆栈帧的情况时,有时检查链接寄存器是了解该地址的唯一方法
当然,这始终是相对于当前函数的,因此将“打印返回地址”包装在自己的小函数中是自欺欺人的,因为它只会告诉您从何处调用该函数(假设编译器没有决定内联它),在这种情况下,您可能确实使用了\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
现在,微妙但重要的一点是:\uuu return\u address()
内在的意图是“当前函数的返回地址”,这与“R14的当前内容”绝不是一回事——如果编译器在条目上保存了返回地址,那么它就可以自由地使用R14来实现它想要的任何目的。do\u print\u r14()
中的两行都是print 0x823194BB,因为在那里调用了do\u print\u r14()
,在调用过程中,没有任何东西会改变这一点。如果您想查看C环境的抽象级别下面,并了解R14在执行过程中实际发生了什么,您需要使用内联汇编技巧,或者使用调试器进行调试。LR的使用由ARM体系结构和ARM定义。linkregister在这方面的帮助只是一个副作用,而不是一个特性
来自AAPCS:
5.3子例程调用
ARM和Thumb指令集都包含一个原语子例程调用指令BL,它执行带链接的分支操作。执行BL的效果是将程序计数器的下一个值依次从返回地址传输到链接寄存器(LR),并将目标地址传输到程序计数器(PC)
LR
的使用由ARM架构和ARM定义。linkregister在这方面的帮助只是一个副作用,而不是一个特性
来自AAPCS:
5.3子例程调用
ARM和Thumb指令集都包含一个原语子例程调用指令BL,它执行带链接的分支操作。执行BL的效果是将程序计数器的下一个值依次从返回地址传输到链接寄存器(LR),并将目标地址传输到程序计数器(PC)
它正在打印相同的值。我刚试过。可能是由于编译器优化?它正在打印相同的值。我刚试过。可能是由于编译器优化?另请参见:和。OP有一些概念问题。\uuu return\u address()
可能是实际的lr
,对于非叶函数,可能是在堆栈帧中;这是一个很好的观点。另请参见:和。OP有一些概念问题。\uuu return\u address()
可能是实际的lr
,对于非叶函数,可能是在堆栈帧中;这是一个很好的观点。