一,StackFrame类
1,基础概念
fp叫做frame pointer寄存器,即栈帧指针寄存器;sp叫做stack pointer寄存器,即栈指针寄存器。那么它们具体的作用是什么 呢?
首先,大家知道每个进程都有自己独立的栈空间,进程中有千千万万的函数调用,这些函数共享进程的这个栈空间,那么问题就 来了,函数运行过程中会有非常多的入栈出栈的过程,当函数返回backtrace的时候怎样能精确定位到返回地址呢?还有子函数 所保存的一些寄存器的内容?这样就有了栈帧的概念,即每个函数所使用的栈空间是一个栈帧,所有的栈帧就组成了这个进程完 整的栈。而fp就是栈基址寄存器,指向当前函数栈帧的栈底,sp则指向当前函数栈帧的栈顶。通过sp和fp所指出的栈帧可以恢 复出母函数的栈帧,以此类推就可以backtrace出所有函数的调用顺序。
上图描述的是ARM的栈帧布局方式,main stack frame为调用函数的栈帧,func1 stack frame为当前函数(被调用者)的栈帧, 栈底在高地址,栈向下增长。此图是网上的图,理论上应该是上图的格式,fp、sp、lr和pc这四个寄存器是非常特殊的寄存 器,它们记录了当前正在运行的函数一些重要信息,在刚进入一个新的函数开始执行的时候,它们保存的是上个函数的信息,需 要将它们入栈保存起来,这很重要!这些并没有定义在ATPCS中,ATPCS规定的是函数调用的时候参数如何传递,以及函数返 回值 的保存等。上面的这些个人觉得是一种默契,定义函数现场的保存及恢复,这些默契包括ATPCS都是人为的一种约束,目 的是为了保证程序运行中不会出错,具体怎样实现应该是不同的编译器不尽相同
参考博客https://blog.csdn.net/zwl1584671413/article/details/108379520
参考博客:https://www.freesion.com/article/1446954374/(推荐)
参考博客:https://www.cnblogs.com/shuisanya/p/16693651.html
二,FrameLayout
1,FrameLayout结构捕获运行时系统和编译器使用的框架布局的配置特定属性。
运行时系统使用stack_frame.h中定义的Runtime_frame_layout。
编译器使用runtime_api.h中定义的编译器::target::frame_layout
struct FrameLayout {
从FP到第一个对象的偏移量(以word字节为单位)
int first_object_from_fp;
// 从FP到最后一个固定对象的偏移量(以word字节为单位)
int last_fixed_object_from_fp;
// 从FP到slot最后一个参数的偏移量(以word字节为单位)
int param_end_from_fp;
// 从SP的入口(帧设置之前)到最后一个参数的偏移量
int last_param_from_entry_sp;
// 从FP到第一个局部的偏移量
int first_local_from_fp;
// frame的固定大小
int dart_fixed_frame_size;
// The offset (in words) from FP to the saved pool (if applicable).
int saved_caller_pp_from_fp;
//从FP到代码对象(如果适用)的偏移量 .
int code_from_fp;
// Entry and exit frame layout.//(Entry/exit是dartvm 与dart之间的转化)
int exit_link_slot_from_entry_fp;
// 保存pc下的固定slots的数量
int saved_below_pc() const { return -first_local_from_fp; }
// 返回可以找到[变量]的FP相对索引(假设未捕获[变量]),以word字节表示
int FrameSlotForVariable(const LocalVariable* variable) const;
//返回可以找到[variable_index]的FP相对索引(假定[variaable_index]]来自[LocalVariable::index()],该索引未被捕获)
int FrameSlotForVariableIndex(int index) const;
// 从FP相对索引返回变量索引
intptr_t VariableIndexForFrameSlot(intptr_t frame_slot) const {
if (frame_slot <= first_local_from_fp) {
return frame_slot - first_local_from_fp;
} else {
ASSERT(frame_slot > param_end_from_fp);
return frame_slot - param_end_from_fp;
}
}
// 调用以在启动期间初始化堆栈帧布局
static void Init();
};
2,Slot(变量槽)就是存储局部变量
三,xcode打印栈帧信息
1,thread backtrace:
结果:
2,frame select 1
结果:
3,register read
结果:
4,Linux/AndroidABI和iOS ABI在帧指针的选择、R9的处理以及过程间堆栈对齐方面有所不同
//EABI(Linux、Android)
//请参阅“ARM架构的过程调用标准”。
// R0-R1: Argument / result / volatile
// R2-R3: Argument / volatile
// R4-R10: Preserved
// R11: Frame pointer
// R12: Volatile
// R13: 是 sp 寄存器,也是栈顶指针,保存着栈顶的位置。
// R14: 为链接寄存器 lr,用于保存返回地址,用于执行流的跳转返回
// R15: 为程序计数器 pc,保存下一条取指指令的地址。
堆栈对齐:始终为4字节,公共接口为8字节
Linux(Debian armhf)和Android在浮点寄存器中是否传递浮点参数方面也有所不同。Linux使用hardfp,Android使用softfp。请参见TargetCPUFeatures::hardfp_supported()。
备注:参阅“ARM-thumb 过程调用标准”:
R0-R3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。
被调用函数在返回之前不必恢复 r0-r3。如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。
R4-R11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。
R12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。
参考博客https://zhuanlan.zhihu.com/p/362668607
5,iOS ABI
//请参阅“iOS ABI函数调用指南”
// R0-R1: Argument / result / volatile
// R2-R3: Argument / volatile
// R4-R6: Preserved
// R7: Frame pointer
// R8-R11: Preserved
// R12: Volatile
// R13: 是 sp 寄存器,也是栈顶指针,保存着栈顶的位置。
// R14: 为链接寄存器 lr,用于保存返回地址,用于执行流的跳转返回
// R15: 为程序计数器 pc,保存下一条取指指令的地址。
堆栈对齐:始终为4字节,公共接口为4字节
//iOS在整数寄存器(softfp)中传递浮点参数
6,Instr类允许访问ARM架构指令集编码中定义的各个字段,如图A3-1所示。
//示例:测试ptr处的指令是否设置了条件代码位。
bool InstructionSetsConditionCodes(byte* ptr) {
Instr* instr = Instr::At(ptr);
int type = instr->TypeField();
return ((type == 0) || (type == 1)) && instr->HasS();
}
参考constants_arm.h
《Writing ARMv6 code for iOS》https://developer.apple.com/documentation/xcode/writing-armv6-code-for-ios
四,KBC Frame Layout
重要提示:KBC堆栈正在向上增长,这与其他所有堆栈不同
架构。这使得能够通过无符号索引对本地变量进行有效寻址
| | <- TOS(Top Of Stack)
Callee frame | ... |
| saved FP | (FP of current frame)
| saved PC | (PC of current frame)
| code object |
| function object |
+--------------------+
Current frame | ... T| <- SP of current frame
| ... T|
| first local T| <- FP of current frame
| caller's FP |
| caller's PC |
| code object T| (current frame's code object)
| function object T| (current frame's function object)
+--------------------+
Caller frame | last parameter | <- SP of caller frame
| ... |
四,代码分析
void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
RawObject** first = reinterpret_cast<RawObject**>(sp());
RawObject** last = reinterpret_cast<RawObject**>(
fp() + (runtime_frame_layout.first_local_from_fp * kWordSize));
}