在ARM Linux中,每个线程在内核堆栈“底部”保留的少数字节的目的是什么
问题 :
为什么在创建内核堆栈的“底部”时保留8个字节?
背景 :
我们知道struct pt_regs
和thread_info
共享相同的2个连续页面(8192个字节),其中pt_reg
位于较高端, thread_info
位于较低端。 但是,我注意到这两个页面的最高地址保留了8个字节:
在arch / arm / include / asm / threadinfo.h中
#define THREAD_START_SP (THREAD_SIZE - 8)
为什么在创建内核堆栈的“底部”时保留8个字节?
- 如果我们在堆栈上保留任何内容,则它必须是8的倍数。
- 如果我们在堆栈上方查看,我们希望确保它已映射。
8的倍数
堆栈和用户寄存器需要对齐到8个字节。 这使事情变得更有效率,因为许多ARM具有64位总线,并且内核堆栈上的操作(例如ldrd
和ldrd
)可能具有这些要求。 您可以在usr_entry
宏中看到保护 。 特别,
#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7) #error "sizeof(struct pt_regs) must be a multiple of 8" #endif
ARMv5(体系结构版本5)添加了ldrd
和ldrd
指令。 它也是内核的EABI版本的要求(与OABI相比)。 因此,如果我们在堆栈上保留任何内容,则它必须是8的倍数。
偷看堆栈
对于最顶层的框架,我们可能想要查看以前的数据。 为了不经常检查堆栈是否在8K范围内,保留了额外的条目。 具体来说,我认为信号需要窥视堆栈。
这样,您只需读取堆栈指针并屏蔽掉THREAD_SIZE
位即可访问thread_info
结构(否则SP
最初将位于下一个THREAD_SIZE
块上)。
static inline struct thread_info *current_thread_info(void) { register unsigned long sp asm ("sp"); return (struct thread_info *)(sp & ~(THREAD_SIZE - 1)); }
八个字节来自ARM调用约定, SP
需要8字节对齐。
更新: AAPCS 5.2.1.1规定:
进程只能访问(用于读取或写入)由[SP,stack-base-1]定义的整个堆栈的闭合间隔(其中SP是寄存器r13的值)。
由于堆栈是全降序的
THREAD_START_SP (THREAD_SIZE - 8)
可能会通过非法访问下一页(分段错误)来强制执行此要求。