专业编程基础技术教程

网站首页 > 基础教程 正文

flutter之dartsdk--StackFrame栈帧

ccvgpt 2024-08-04 12:05:18 基础教程 15 ℃


一,StackFrame类

flutter之dartsdk--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));


}

Tags:

最近发表
标签列表