如何找出是否一个指针是PC /的Visual C堆栈上++
-
19-08-2019 - |
题
[这是为PC / VISUAL C ++特别(尽管任何其他的答案将是相当照明:))]
您如何判断一个指针来自于堆栈的对象?例如:
int g_n = 0;
void F()
{
int *pA = &s_n;
ASSERT_IS_POINTER_ON_STACK(pA);
int i = 0;
int *pB = &i;
ASSERT_IS_POINTER_ON_STACK(pB);
}
所以仅第二断言(PB)应该跳闸。我使用了一些内联汇编弄清楚,如果是SS段寄存器或类似的东西中的思维。有谁知道,如果有任何内置的功能,这一点,或者一个简单的方法来做到这一点?
谢谢! RC
解决方案
从技术上讲,在移植的C你可以不知道。一种参数堆栈是很多,但不是所有的编译器荣幸硬件细节。一些编译器将使用寄存器参数时,他们可以(即,快速调用)。
如果您使用的是Windows NT具体工作,你想从调用NtCurrentTeb抢线程执行块()。 乔·达菲的博客对信息这并从中你可以得到堆栈范围。您在范围检查指针,你应该是好去。
其他提示
不管你做什么,这将是非常特定于平台的和非便携式。假设你确定这一说法,请继续阅读。如果一个指针指向堆栈中的某处,它将位于当前堆栈指针%esp
并且堆叠的顶部之间。
获取堆栈顶部的方法之一是在main()
的开始读入。然而,这有几个问题:
- 该堆栈的顶部实际上略高,因为C运行时进入main()
之前初始化堆栈
- 在C ++中,全局对象的构造函数main()
之前调用
- 如果您的应用程序的多线程,每个线程都有自己独立的堆栈。在这种情况下,你需要说明堆的底部一个线程局部变量
来得到当前的堆栈指针的一种方法是使用内联组件:
uint32_t GetESP(void)
{
uint32_t ret;
asm
{
mov esp, ret
}
return ret;
}
谨防内联和优化!优化器可能会打破这种代码。
我第二个问题 - 为什么你需要知道什么?没有什么好可能来自这一点。
我觉得这种方法可能会奏效,如果编译器合理的事情指针比较和堆栈向下增长:
static void * markerTop = NULL;
int main()
{
char topOfStack;
markerTop = &topOfStack;
...
}
bool IsOnStack(void * p)
{
char bottomOfStack;
void * markerBottom = &bottomOfStack;
return (p > markerBottom) && (p < markerTop);
}
由于您指定的Visual C,并声称,我会假设你可以使用一个调试版本。 在这种情况下,可以采取该特定编译器把用于存储检查fenceposts的优点:
#define IS_POINTER_TO_STACK(vp) (*((int*)(vp)-1)==0xCCCCCCCC)
在所有这些情况下正确地工作在调试版本:
#define ASSERT(v) printf("assert: %d\n", v); //so it doesn't really quit
int g_n = 0;
void test_indirectly(void* vp) {
ASSERT(IS_POINTER_TO_STACK(vp));
}
void F() {
int *pA = &g_n;
ASSERT(IS_POINTER_TO_STACK(pA)); //0
int i = 0;
int j = 0;
int *pB = &i;
ASSERT(IS_POINTER_TO_STACK(pB)); //1
ASSERT(IS_POINTER_TO_STACK(&j)); //1
int *pC = (int*)malloc(sizeof(int));
ASSERT(IS_POINTER_TO_STACK(pC)); //0
free(pC);
ASSERT(IS_POINTER_TO_STACK(pC)); //0
pC = new int;
ASSERT(IS_POINTER_TO_STACK(pC)); //0
delete pC;
char* s = "HelloSO";
char w[6];
ASSERT(IS_POINTER_TO_STACK("CONSTANT")); //0
ASSERT(IS_POINTER_TO_STACK(s)); //0
ASSERT(IS_POINTER_TO_STACK(&w[0])); //1
test_indirectly(&s); //1
int* pD; //uninit
ASSERT(IS_POINTER_TO_STACK(pD)); //runtime error check
}
(除了最后一个因未初始化的存储器造成了运行时错误 - 但仍然供应验证你指针的目的)
这只能在调试版本的作品 - 发布版本报告虚假它们全部
忽略的问题“为什么” ......如果一个简单的方法,你有顶部栈帧的控制将设置一个全局变量是一个栈对象的地址,然后用如果来检查功能目标指针是这个地址和它创建的堆栈上的变量的地址之间。
void* TopOfStack; // someone must populate this in the first stack frame
bool IsOnTheStack(void* p)
{
int x;
return (size_t) p < (size_t) TopOfTheStack &&
(size_t) p > (size_t) &x;
}
这不,当然多线程工作,除非你TopOfTheStack线程本地。
由编译器堆栈的优化也可能导致问题。
是的,我知道这是非常不可移植的,但是这是一个内部应用程序,以模仿其他硬件设施这样做。看来线程执行块可能是要走的路。
没有办法,我相信,Visual C ++或几乎任何现代的Windows(或古老的32位OS / 2)的平台上运行,因为在这些平台上的堆栈动态增长,也就是堆栈的一个新的页面被分配只有当你的程序试图在堆栈的当前分配块的顶部(反正32位Windows)进入所谓的保护页,4 KB块特制的内存。当你的程序试图访问该保护页和(1)正常,有效栈的一个新的页面映射到位的话,上面的当前分配堆栈的顶部及(2)创建另一个操作系统拦截所产生的异常防护页面正上方的顶新,所以它可以为以后需要增加堆栈。所述OS执行此,直到堆栈达到其限制,并且通常这种限制设置非常高。
如果你的程序试图访问属于堆的未分配的一部分,但位于保护页面上方的任何地址,你的程序会崩溃,因为操作系统没有办法解读为此这一点。你的程序只是试图它的地址空间以外的存储器访问中,即使指针所属理论上,到任务的堆栈段。
不过,如果你需要一种方法来找到某地址是否属于堆栈的分配部分(即“堆栈上的对象”)中提及的乔·达菲的博客还是不错的。只要不使用StackLimit描述那里,使用其他,已经在这个线程描述,方法获取堆栈目前上方,让你在栈上,而不是整个的分配的部分工作,可能未分配部分,一个
我在与谁说,这是相当困难的,其中会不断调整堆栈大小的环境中可靠地做人民的协议。
但是,随着对“为什么呢?”人 - 有趣的是我想做一个小的嵌入式平台上今天这个。我有一个函数,它接受一个指向对象,然后保持该指针的保持为在函数返回后的一段时间(因为它的处理,其中指针指向的数据)。
我不希望我的函数的调用者在自动变量的地址传递,因为我不希望,同时它还是正在处理的数据得到践踏别。呼叫者必须通过在静态数据或const数据的地址,和一个不错的“ASSERT_IS_ON_STACK()”宏可能是一个有用的提醒。
便携式?一点也不。可怕的糖果机界面?绝对
这样是小型嵌入式系统的性质 - 好断言可以帮助
。确定,作为对 “为什么”:
一些处理器存储器控制器也不能执行的DMA或图存储器向/从堆栈段(一个或多个);所以在跨平台的世界,以确保我没有从那里发送数据,一个跨平台的断言是非常有用的。