ARM汇编。将r13(堆栈指针)用作通用寄存器是否安全?

时间:2019-03-06 12:20:36

标签: android linux assembly arm stack-pointer

我正在编写一个非常优化的叶子函数,为了使其运行更快,我想将R13用作通用寄存器。在使用R13并将其从功能返回之前,我通过将R13移至VFP寄存器之一来保留它,方法是将其移回。看起来像这样:

/* 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)上运行。谢谢!

2 个答案:

答案 0 :(得分:5)

很明显,仅当您已经在使用其他所有GP寄存器(包括lr )并且不能转移某些GP寄存器时,才应考虑这一点。对NEON寄存器起作用,例如即使只关心低32位,也可以使用压缩整数。

(使用SIMD规则获取更大的标量整数通常仅在以下情况下才有用:一组孤立的值不与算法中的其他值交互,并且您无需在它们上分支或将它们用作指针。在某些ARM CPU上,int和SIMD之间的传输很慢。)

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


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

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

如对Can I use rsp as a general purpose register的评论(此问题的x86-64等效)中所述,甚至对于信号也有一种解决方法:

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

@Timothy指出,如果您的SP暂存值可能是恰好“指向”备用栈的整数,则信号分配机制将假定这是一个嵌套信号,并且不会修改SP(因为在实际的嵌套信号情况下将覆盖第一个信号处理程序仍在使用的堆栈)。因此,除非您根据需要分配两倍的内存,并且仅将上半部分传递给push ,否则您可能离SP sigaltstack到一个未映射的页面。 (对于简单的信号处理程序,可能只有2k或4k,这些信号处理程序在执行不多之后便会返回)。

即使对于嵌套信号,这也应该是安全的:只有最外面的信号处理程序才能在alt堆栈的底部附近开始,并使用超出实际altstack的部分分配空间。如果SP仍位于altstack中,则另一个信号将使用该空间以下的空间。否则,如果SP超出了altstack,它将使用altstack的顶部。

或者,如果您的任何GP寄存器都需要为指针,则可以使用SP保留指向绝对不是alt堆栈的其他对象的指针,从而避免这种过度分配的需要。如果它是有效的指针,则在调试程序将当前SP用于某些内容,或者您​​错改了altstack机制的情况下,您很容易遭受破坏,而不会出错。但这只是失败模式的不同:两者都是灾难性的。


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

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

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

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

答案 1 :(得分:1)

当执行代码时触发上下文切换/ irq时,OS /硬件可能会假定R13为TOS,因此将其保存为可以恢复R13的想法。 恢复执行时的TOS。

这可能是您遇到的问题。

明智的方法是使代码段变得关键,并以某种方式强制系统滴答/ irq挂起,直到例程完成/ R13恢复为止。

如果确实需要额外的寄存器,则最好使用LR(R14)。