X86 glibc backtrace()如何确定哪些堆栈内存是返回地址?

X86 glibc backtrace()如何确定哪些堆栈内存是返回地址?,x86,arm,stack,glibc,backtrace,X86,Arm,Stack,Glibc,Backtrace,就程序而言,堆栈只是一堆内存。回溯函数如何确定哪些堆栈字节是指令指针返回地址,而哪些只是函数参数,等等 就程序而言,堆栈只是一堆内存 这是正确的。如果您对程序的结构一无所知,您将无法展开其堆栈 用于展开的最简单结构是保留单个“帧指针”寄存器以始终指向当前帧,并且帧包含已知偏移量处的前一帧指针 这是一种在i*86上使用了很长时间的机制,现在仍在许多RISC机器上使用。它使堆栈展开变得简单且非常快速,但在寄存器匮乏的机器上,例如i*x86,以及在较低程度上x86_64,它的效率不高,因为帧寄存器可以

就程序而言,堆栈只是一堆内存。回溯函数如何确定哪些堆栈字节是指令指针返回地址,而哪些只是函数参数,等等

就程序而言,堆栈只是一堆内存

这是正确的。如果您对程序的结构一无所知,您将无法展开其堆栈

用于展开的最简单结构是保留单个“帧指针”寄存器以始终指向当前帧,并且帧包含已知偏移量处的前一帧指针

这是一种在
i*86
上使用了很长时间的机制,现在仍在许多RISC机器上使用。它使堆栈展开变得简单且非常快速,但在寄存器匮乏的机器上,例如
i*x86
,以及在较低程度上
x86_64
,它的效率不高,因为帧寄存器可以在其他地方更好地使用

解决方案是创建增强数据,该数据描述了如何在给定寄存器和内存内容的当前集合中查找当前帧(例如,“当前帧位于
$rsp
的偏移量NN处”,或“位于
$rbp
的偏移量MM处”,“上一帧位于当前帧的偏移量J处”,等等)

这实际上就是
.eh_frame
的含义。从二进制文件中删除它,就不可能展开堆栈

这种机制也比简单的帧指针遍历慢得多,需要复杂的代码,并且容易出错(编译器不能保证发出正确的展开描述符,手工编码的程序集可能根本没有这些描述符)

有一些方法可以使退绕快速而简单,但它们需要不同的调用约定

就程序而言,堆栈只是一堆内存

这是正确的。如果您对程序的结构一无所知,您将无法展开其堆栈

用于展开的最简单结构是保留单个“帧指针”寄存器以始终指向当前帧,并且帧包含已知偏移量处的前一帧指针

这是一种在
i*86
上使用了很长时间的机制,现在仍在许多RISC机器上使用。它使堆栈展开变得简单且非常快速,但在寄存器匮乏的机器上,例如
i*x86
,以及在较低程度上
x86_64
,它的效率不高,因为帧寄存器可以在其他地方更好地使用

解决方案是创建增强数据,该数据描述了如何在给定寄存器和内存内容的当前集合中查找当前帧(例如,“当前帧位于
$rsp
的偏移量NN处”,或“位于
$rbp
的偏移量MM处”,“上一帧位于当前帧的偏移量J处”,等等)

这实际上就是
.eh_frame
的含义。从二进制文件中删除它,就不可能展开堆栈

这种机制也比简单的帧指针遍历慢得多,需要复杂的代码,并且容易出错(编译器不能保证发出正确的展开描述符,手工编码的程序集可能根本没有这些描述符)


有一些方法可以使展开快速而简单,但它们需要不同的调用约定。

可能使用
。eh_frame
堆栈展开元数据,以便即使没有帧指针也可以展开;希望这能让你在google上有所收获。@Peter我不认为backtrace()有那么聪明,我相信它只适用于传统的帧指针布局。似乎我错了,似乎它使用了eh_帧的东西,这意味着当支持的时候@PeterCordesRelated:假设使用
.eh\u frame
堆栈展开元数据,即使没有帧指针也允许展开;希望这能让你在google上有所收获。@Peter我不认为backtrace()有那么聪明,我相信它只适用于传统的帧指针布局。似乎我错了,似乎它使用了eh_帧的东西,这意味着当支持的时候@彼得·科尔德相关: