2

我正在编写一个极其优化的叶函数并使其运行得更快,我想将 R13 用作通用寄存器。在使用它之前,我通过将 R13 移动到 VFP 寄存器之一来保留 R13,在从函数返回之前,我通过将其移回来恢复它。它看起来像这样:

/* Start of the function */
push { r4 - r12, r14 }
vmov s0, r13
/* Body of the function. Here I use R13
 * as a general purpose register */
vmov r13, s0
pop { r4 - r12, r14 }
bx lr

它有效。但是我读过一些操作系统假定 R13 总是用作堆栈指针,将它用作通用寄存器可能会导致崩溃。我还应该说这个函数只在 Android (Linux) 上运行。谢谢!

4

2 回答 2

5

显然,如果您已经在使用所有其他 GP 寄存器,包括lr,并且不能将您的一些工作转移到 NEON 寄存器,例如使用压缩整数,即使您只关心低 32 位,您应该只考虑这一点。

(对于更多的标量整数,使用 SIMD regs 通常只有在有一组孤立的值不与算法中的其他值交互时才有用,并且您不需要在它们上分支或将它们用作指针。在int 和 SIMD 在某些 ARM CPU 上很慢。)

这是非常不标准的,甚至可能只在用户空间安全,而不是内核


如果您安装了任何信号处理程序,则当这些信号之一到达时,您的堆栈指针必须有效。(这是异步的。)

除了信号处理程序之外,Linux 中没有其他异步使用用户空间堆栈指针。 (除非您使用 GDB 进行调试并使用print foo(123)where foo 是目标进程中的函数。)

正如对Can I use rsp as a general purpose register的评论中提到的(这个问题的 x86-64 等效项),即使是信号也有一种解决方法:

用于sigaltstack设置备用堆栈,并在安装处理程序时SA_ONSTACK的标志中指定。sigaction

正如@Timothy 指出的那样,如果您的 SP 的临时值可能是恰好“指向”到 alt 堆栈中的整数,则信号调度机制将假定这是一个嵌套信号并且不会修改 SP(因为在实际的嵌套-将覆盖第一个信号处理程序仍在使用的堆栈的信号情况)。因此,您可能会push远离 SP 进入未映射的页面,除非您分配的数量是您需要的两倍,并且只将上半部分传递给sigaltstack. (对于在没有做太多事情后返回的简单信号处理程序来说,可能只有 2k 或 4k)。

即使使用嵌套信号,这也应该是安全的:只有最外层的信号处理程序可以从 alt 堆栈的底部附近开始,并使用实际 altstack 之外的一些分配空间。如果 SP 仍在 altstack 内,另一个信号将使用低于该空间的空间。或者,如果 SP 已超出 altstack,它将使用 altstack 的顶部。

或者,如果您的任何 GP 寄存器需要成为指针,您可以通过使用 SP 来保存指向绝对不是 alt 堆栈的其他东西的指针,从而避免这种过度分配的需要。 如果调试器将当前 SP 用于某事,或者如果您弄错了 altstack 机制,则让它成为一个有效指针会使您面临损坏而不是错误。但这只是故障模式的不同:两者都是灾难性的。


硬件中断将状态保存在内核堆栈上,而不是用户空间堆栈上。如果他们使用用户堆栈:

  1. 用户空间可能会因 SP 无效而使操作系统崩溃。
  2. 用户空间可以通过让另一个用户空间线程修改内核的堆栈数据(包括返回地址)来获得内核权限。

(一个进程的所有用户空间线程共享同一个页表,并且可以读/写彼此的堆栈映射。)

Linux/Android 与没有虚拟内存或严格执行权限分离的轻量级 RTOS 非常不同。

于 2019-03-06T12:27:29.810 回答
1

当你的代码执行时上下文切换/中断触发时,OS/hw 可能会假设 R13 是 TOS,因此它会将其保存在它可以在恢复执行时恢复 TOS 的想法中。

在您的情况下,这可能是一个问题。

一个明智的方法是使这段代码变得关键,并以某种方式强制系统滴答/中断直到例程完成/R13 恢复。

如果你真的需要额外的寄存器,你可能最好使用 LR (R14)。

于 2019-03-06T14:46:41.387 回答