我想编写自己的堆栈步行器。我写了它,但是我遇到了引入从另一个线程读取堆栈寄存器的能力的问题。我的示例实现:
...
if (thrd) {
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_INTEGER;
GetThreadContext(thrd, &ctx);
rbp = ctx.Rbp;
} else {
register uintptr_t _rbp asm("rbp");
rbp = _rbp + 0x480; // добавление
}
...
如果我们从另一个流中读取,那么我们为上下文分配空间,然后我们读取它。但仅在添加此代码后,所有测试都崩溃并停止工作:根本没有显示跟踪,但它们是针对当前线程的(即我只是用 运行旧测试thrd == 0
)。最后,我得出的结论是,在设置寄存器的函数的序言中,MinGW 生成了一个跟踪。编码:
pushq %rbp
subq $1296, %rsp
leaq 128(%rsp), %rbp
这是意料之中的:
pushq %rbp
movq %rsp, %rbp
subq ..., %rsp
出于某种原因,他将第 3 行归因于,尽管他不应该这样做,结果 RBP 被扭曲了。在代码中,我添加到他的工作副本0x480
中,一切恢复正常。
第 3 行仅在在 下的堆栈上分配内存时出现CONTEXT
,否则不需要添加。
我认为这是某种另一种优化或类似的东西。以下是我编译时使用的选项:
-g -shared -fPIC -mwindows -march=x86-64
开启和关闭-g
,没有任何变化。如何摆脱它leaq
?
#include <windows.h>
#include <stdio.h>
void f() {
CONTEXT a;
CONTEXT b;
register uintptr_t rbp asm("rbp");
printf("rbp: %p *rbp: %p\n", rbp, *(void **)rbp);
}
int main() {
f();
}
这是同样的例子:你注释掉上下文堆栈上的内存分配行,你会得到不同的输出。
只有在两个上下文都被注释掉的情况下*rbp
才会位于堆栈区域,在其他情况下它等于某种垃圾。
结论
rbp: 000000000061FDF0 *rbp: 000000000061FE20 // закомменчены оба
rbp: 000000000061F980 *rbp: 0000000000702520 // закомменчен 1
rbp: 000000000061F4B0 *rbp: 0000000000060000 // ни один не закомменчен
你的期望就是你的问题 (c)
有关于“Windows x64 调用约定”和其他类似的文档。由此可见,帧指针(如果使用的话)应该指向固定分配的结束(即毕竟
auto
和之前),而不是alloca()
你想的地方。至于偏移量 128,这是一个可以接受的优化。允许代码生成器使用
[base+index+byte]
“真正的 modR/M 没有新奇的 SIB”风格的间接。