打印出堆栈指针的值
- 2024-11-14 08:29:00
- admin 原创
- 56
问题描述:
如何在 Linux(Debian 和 Ubuntu)中用 C 打印出堆栈指针的当前值?
我尝试过谷歌,但没有找到结果。
解决方案 1:
有一个技巧,但它不可移植,甚至不能保证能起作用,那就是简单地将本地地址作为指针打印出来。
void print_stack_pointer() {
void* p = NULL;
printf("%p", (void*)&p);
}
p
这实际上会打印出当前堆栈指针的近似地址
解决方案 2:
没有可移植的方法可以做到这一点。
在 GNU C 中,这可能适用于具有名为 SP 的寄存器的目标 ISA,包括 x86,其中 gcc 将“SP”识别为 ESP 或 RSP 的缩写。
// broken with clang, but usually works with GCC
register void *sp asm ("sp");
printf("%p", sp);
GCC 现在已不推荐使用本地寄存器变量:
此功能唯一支持的用途是在调用扩展 asm时指定输入和输出操作数的寄存器
定义寄存器变量不会保留寄存器。除了调用扩展 asm 时,指定寄存器的内容无法保证。因此,明确不支持以下用途。如果它们似乎有效,那只是偶然事件,并且可能会由于周围代码中(看似)不相关的更改,甚至未来版本的 gcc 优化中的微小更改而停止按预期工作。...
在实践中,它也被 clang 破坏,sp
像任何其他未初始化的变量一样被处理。
解决方案 3:
除了duedl0r关于GCC的具体回答之外,您还可以使用__builtin_frame_address(0)
GCC 特定的(但不是x86特定的)。
这也应该适用于Clang(但它存在一些错误)。
采用本地地址(正如JaredPar 回答的那样)也是一个解决方案。
请注意,据我所知,C 标准理论上不需要任何调用堆栈。
记住 Appel 的论文:垃圾收集可以比堆栈分配更快;一个非常奇怪的 C 实现可以使用这种技术!但据我所知,它从未用于 C。
我们可以梦想其他技术。而且,你可以拆分堆栈(至少在最近的 GCC 上),在这种情况下,堆栈指针的概念就没有什么意义了(因为堆栈不是连续的,可能由许多段组成,每个段包含几个调用框架)。
解决方案 4:
Linux
您可以使用伪文件系统proc
来打印堆栈指针。
查看这里的/proc/your-pid/stat 伪文件,其中的字段28
,29
。
startstack %lu
堆栈的起始(即底部)地址。kstkesp %lu
ESP(堆栈指针)的当前值,可以在进程的内核堆栈页中找到。
您只需解析这两个值!
解决方案 5:
您还可以使用扩展的汇编指令,例如:
#include <stdint.h>
uint64_t getsp( void )
{
uint64_t sp;
asm( "mov %%rsp, %0" : "=rm" ( sp ));
return sp;
}
对于 32 位系统,必须将 64 替换为 32,并将 rsp 替换为 esp。
解决方案 6:
您可以使用 setjmp。具体细节取决于实现,请查看头文件。
#include <setjmp.h>
jmp_buf jmp;
setjmp(jmp);
printf("%08x
", jmp[0].j_esp);
在执行未知代码时,这也很方便。您可以检查前后的 sp 并执行longjmp
清理。
解决方案 7:
如果您使用的是 msvc,则可以使用提供的函数_AddressOfReturnAddress()
它将返回返回地址的地址,该地址保证是函数入口处的 RSP 值。从该函数返回后,由于返回地址被弹出,RSP 值将增加 8。使用该信息,您可以编写一个简单的函数来返回堆栈指针的当前地址,如下所示:
uintptr_t GetStackPointer() {
return (uintptr_t)_AddressOfReturnAddress() + 0x8;
}
int main(int argc, const char argv[]) {
uintptr_t rsp = GetStackPointer();
printf("Stack pointer: %p
", rsp);
}
展示
解决方案 8:
您可以在文件中找到该信息/proc/<your-process-id>/maps
,与字符串出现的行相同[stack]
(因此它与编译器或机器无关)。这种方法的唯一缺点是,要读取该文件,需要 root 权限。
解决方案 9:
尝试 lldb 或 gdb。例如,我们可以在 lldb 中设置回溯格式。
settings set frame-format "frame #${frame.index}: ${ansi.fg.yellow}${frame.pc}: {pc:${frame.pc},fp:${frame.fp},sp:${frame.sp}} ${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}
"
因此我们可以在调试中打印 bp 、 sp ,例如
frame #10: 0x208895c4: pc:0x208895c4,fp:0x01f7d458,sp:0x01f7d414 UIKit`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 376
更多信息请见https://lldb.llvm.org/use/formatting.html
解决方案 10:
您可以使用以下内容:
uint32_t msp_value = __get_MSP(); // Read Main Stack pointer
如果你想获取PSP值,方法如下:
uint32_t psp_value = __get_PSP(); // Read Process Stack pointer
如果要使用汇编语言,也可以使用MSP和PSP流程:
MRS R0, MSP // Read Main Stack pointer to R0
MRS R0, PSP // Read Process Stack pointer to R0
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件