关于ARMv8架构下函数栈
本文是原「蜗窝讨论区」的历史存档(2016-05-31),来自版块「Linux kernel技术问答」,共 5 帖。讨论区已停止服务,此处仅供查阅。
Hi All,
Q群里问问题不过瘾,还是来这边了
是一个有关v8架构下函数栈的问题,先帖代码:
[== Undefined ==]
/*
* AArch64 PCS assigns the frame pointer to x29.
*
* A simple function prologue looks like this:
* sub sp, sp, #0x10
* stp x29, x30,
* mov x29, sp
*
* A simple function epilogue looks like this:
* mov sp, x29
* ldp x29, x30,
* add sp, sp, #0x10
*/
int notrace unwind_frame(struct stackframe *frame)
{
unsigned long high, low;
unsigned long fp = frame->fp;
low = frame->sp;
high = ALIGN(low, THREAD_SIZE);
if (fp < low || fp > high || fp & 0xf)
return -EINVAL;
frame->sp = fp + 0x10;
frame->fp = *(unsigned long *)(fp);
/*
* -4 here because we care about the PC at time of bl,
* not where the return will go.
*/
frame->pc = *(unsigned long *)(fp + 8) - 4;
return 0;
}
Q: 注释部分:
[== Undefined ==]
* A simple function prologue looks like this:
* sub sp, sp, #0x10
* stp x29, x30,
* mov x29, sp
大概做了以下几件事: 1. sp指针自减0x10 2. 将0x29(即FP)入栈,将0x30(即LR)入栈,地址为SP 3. 将FP寄存器的值赋给SP 那么是不是意味着,对于单个函数,函数栈的大小只能是0x10?如果函数中临时变量较多超出0x10怎么办?
之前我在x86架构下,确定一个函数的栈范围需要ESP和EBP两个指针,似乎ARM下是用FP和SP,并且FP的相邻地址就保存了LR 按照以上的说法,如果出现了系统崩溃的bug,似乎只需要知道FP指针,就可以推导函数调用的整个流程(因为相当于同时知道了LR)
这个例子是哪个场景呢?看着有些奇怪。不过:
showstopper 写道: 那么是不是意味着,对于单个函数,函数栈的大小只能是0x10?如果函数中临时变量较多超出0x10怎么办?
肯定不会有问题啊: SP自减0x10,然后将FP和LR入栈(意味着SP又减了0x8),之后再把SP赋值给FP(意味着当前函数的frame pointer是从这里开始的),于是当前函数的栈帧,就基于现在的FP,欢快的递减了。 哦……我明白了,你是不是把mov指令理解反了?是把SP给到FP,而不是把FP给到SP。
弯曲的旅行 写道: 这个例子是哪个场景呢?看着有些奇怪。不过:
showstopper 写道: 那么是不是意味着,对于单个函数,函数栈的大小只能是0x10?如果函数中临时变量较多超出0x10怎么办?
肯定不会有问题啊: SP自减0x10,然后将FP和LR入栈(意味着SP又减了0x8),之后再把SP赋值给FP(意味着当前函数的frame pointer是从这里开始的),于是当前函数的栈帧,就基于现在的FP,欢快的递减了。 哦……我明白了,你是不是把mov指令理解反了?是把SP给到FP,而不是把FP给到SP。
额....这边是我打错了,sorry。 我想表达的意思是:是不是每个函数的栈的大小是固定的0x10,看unwind_frame函数中的 frame->sp = fp + 0x10; 好像是这个意思。 另外我在一篇博文中看到 “ Q6:调用栈是动态生长的还是静态划分的 从系统内存管理角度来会所,函数调用栈肯定是动态生长的; 而从一个函数的调用栈本身来说,动态或静态两种方式都存在。如MIPS的堆栈一般都在函数入口处一次性开辟:addiu sp sp –x; 而ARM 32的函数堆栈,则在函数内部也是动态生长的;到了ARM 64,函数调用栈又变成静态规划的了。 “
也就是说在v8架构下,每个函数的栈的大小是静态划分,并且大小是0x10吗?
另外Q群里的维尼熊同学给了我一个patch链接: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=1b15ec7a7427d4188ba91b9bbac696250a059d22 这个patch对sp的范围作了一些检查,但是是在task->stack~task->stack THREAD_SIZE 范围内的检查,这是整个进程的栈,我想知道的单个函数调用时的栈的大小是不是固定的?如果是固定的,那么是根据什么样的原理划分的0x10.
更正一下上面的表述(把stp和push搞混了)。 SP自减0x10(16bytes),然后执行stp x29, x30, ,也就是说: x29(8bytes)保存到了SP,x30(8bytes)保存到了SP+8,自减的这16个bytes,就是为了保存这两个寄存器而已啊。 ARM中有限制函数栈的大小的概念吗?没有吧?
啊 我明白了 我感觉自己问的好蠢..... 非常感谢你的解答
