ARM寄存器r9在Linux内核中的应用

8
“ARM架构过程调用标准”(AAPCS/EABI)规定(5.1.1):
"The role of register r9 is platform specific."

但是
"A virtual platform [...] may designate r9 as an additional callee-saved
 variable register, v6."

问题是:Linux内核是否将r9用于某些特殊目的?还是将其用作普通的非易失性寄存器?
3个回答

1
找出内核如何使用它的简单方法是构建内核(CROSS_COMPILE=... ARCH=arm make vmlinux),然后反汇编整个内核。
${CROSS_COMPILE}objdump -d vmlinux.o | grep 'sb|r9'

要检查(使用r9sb名称,因为它取决于您的objdump输出的确切内容)。

如果您在序言/尾声代码中发现它被使用(在指令如push {..., r9, ...}stmfd sp!, {..., r9, ...}或其相应的pop/ldmfd中),那么它是被调用者保存的。否则,它只是另一个临时寄存器。结果可能取决于您的工具链、内核配置选项或ARM目标。

话虽如此,如果您编译了一个Thumb-2内核,它将不会被调用者保存。这是因为Thumb-2的push/pop仅操作较低的寄存器集(并以互补方式处理lr/pcpush lrpop pc配对)。


谢谢提示!我不会为vmlinux这样的文件做这个操作,因为它包含了调度程序的部分。但是,一个objdump -DS -M reg-names-std net/core/dev.o | grep push | grep r9(非架构代码)显示了相当多的条目,所以可以安全地假设r9是一个非易失性寄存器。 - Mircea
如果您在objdump中使用“-S”,它会将代码与源代码交替显示;对于Linux树,这将使输出变得非常长...再加上“-D”,它还会反汇编数据段(不仅仅是像“-d”一样的代码),使其更长。另一方面,“-d”在Linux内核上是实用/有用的。 - FrankH.
我知道。但是我只在一个对象文件上进行操作 :) - Mircea
Thumb2的信息是错误的。 Thumb1(最初在ARMv4和ARMv5上)在Thumb模式下对寄存器使用有限制。Thumb2(一个模糊的概念)在ARMv6及以后版本中发现,具有混合的16/32位指令流,并且可以保存/恢复r9。此外,问题说明了AAPCS / EABI,这适用于非Thumb1代码。 - artless noise
此外,OP对这些信息的使用是错误的。仅在grep中命中r9是不够的。您需要查看堆栈操作。正确的答案由编译器和libc确定;Linux本身可以支持任何一种。在一个系统上,您可以看到两者都有。仅因为一个二进制文件具有r9的堆栈使用并不意味着您构建的程序不会使用它。应该在工具输出上执行objdump/grep组合。 - artless noise

0

简而言之 - 这取决于libc和编译器选项。对于大多数ARM Linux发行版,答案是它是被调用者保存的寄存器。

只有深度嵌入式的libc和工具可能会使用静态基址

Linux内核本身不使用静态基址,r9可供使用。

不清楚问题是针对用户进程还是内核开发人员。


注册表sb代表静态基础。它是静态数据(包括程序全局变量)的引用。对于某些系统,您可能有多个共享相同代码但需要不同静态基础的进程。例如,网络网关可能会为每个端口创建一个包含该端口状态的静态基础进程。
由于Linux具有VM可以将物理存储重新映射到所需位置,因此它是静态基础的替代方案。如果在NOMMU系统上运行,例如Cortex-M版本的Linux,则可能会使用sb。这不是实际操作系统和Linux的责任。它只会在上下文切换时保存/恢复r9。因此,实际答案取决于libc

可以使用GOT和其他RAM thunk。但在深度嵌入式设备中,使用NOR闪存或ROM使用静态基址可能是最有效的。对于像Raspberry Pi、Beagle bone、Android手机等设备,它们都有MMU,没有必要进行静态基址的操作。

您可以使用objdump -d my_code | grep -E '(sb|r9)'来确定r9的堆栈使用情况。仅在grep中命中r9是不够的。您需要查看堆栈操作。在一个系统上,您可以同时看到两者。仅因为一个二进制文件具有r9的堆栈使用并不意味着您构建的程序不会使用它。应该在工具输出上执行objdump/grep组合。

正确答案由编译器和libc确定;Linux本身可以支持任何一种。您应该查看libc文档。事实上,gcc通过使用不同的选项(如sb)来支持使用。

  • -mpic-register=
  • -mpic-data-is-text-relative
  • -msingle-pic-base
  • -mpic-register=
  • -mpic-data-is-text-relative
  • -msingle-pic-base

https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html


U-boot使用 静态基址 作为实例。一个静态二进制文件可以使用静态基址,而同一系统上的其他程序可以使用通用 r9。Linux有一些关于ABI使用的命名惯例,但主要与系统调用机制有关,而不是寄存器使用。

可以确定的是,Linux内核不会管理sb/r9寄存器。它需要由libc加载器进行管理和设置,该加载器将调用 mapsbrk等来设置初始的 sb,然后在进程的 main() 运行之前或在静态二进制文件中,在程序启动代码中设置。

更多技术细节请参见:静态共享库


0

我认为寄存器定义存储在include/asm-arm/ptrace.h中。

不过我不确定100%...


是的,它们在那里。我并没有看到任何特别之处,但这并不是一个保证。以 PowerPC 为例:ptrace.h 中有带有所有 GPR 的 pt_regs 数组。但是 r2 是进程上下文中的“current()”任务 :) - Mircea
1
奇怪,我正在阅读一篇关于它的文章,ARM似乎说ptrace.h有所有寄存器定义。很抱歉我没能提供帮助,但http://infocenter.arm.com可能会包含这些信息。这也可能取决于您使用的ARM处理器。 - Trevor Arjeski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接