过go ,低级C/C++代码通过执行以下操作来识别指针ptr
是否指向只读存储器in Linux OS:
extern char etext, edata;
...
(ptr > &etext) && (ptr < &edata);
事实上,我仍然不时地看到这一点在行动中.
然而,在go 年(Why does subtracting the value of etext from edata not give me the correct size for my text segment)别人发布的一个问题的答案中,显示出如今传统的连续内存布局text -> data -> bss -> heap -> ... -> stack -> kernel
已经不再有保证.
这对我来说相当令人费解,因为正如我所说的,我看到上面的代码今天仍然在使用.还因为那个链接问题的操作员理所当然地给答案留下了 comments ,问他们现在在哪里可以找到关于记忆部分布局的信息--但答案的作者不知道.这是一个相当相关的技术信息,但我也找不到有关它的进一步信息,这些信息可以帮助我断言上面的代码是否仍然有用,或者如何根据现代内存节布局规则修改它.
This answer对于一个略有不同的问题,建议使用以下方法来判断指针是否在静态变量内存中--以解决如今内存段的顺序可能是随机的这一事实:
(void*)(x) <= (void*)&end || (void*)(x) <= (void*)&edata
其基本原理是,只需判断指针是否位于BSS和只读数据的结尾之前,即可确认静态内存中的指针.
因此,我的主要问题分为两部分:
-
据我所知,上面的后一种方法是默默地假设,尽管堆的位置可能是随机的,但它位于BBS and只读数据之后-从不介于两者之间.这是一个安全的假设吗?
-
如果是这样,并且知道文本代码段至少是第一位的,我们是否可以采用上面的方法来确保在modern Linux OSes中检测到指针特定于只读存储器数据(不包括BSS)中?我知道没有标准的通用解决方案,但目前我正试图仅为Linux系统解决这个问题(尽管Mac OS的解决方案可能非常相似,因为编译器也经常为这些操作系统提供与edata、etext和end类似功能).
我的try 是:
#include <iostream>
extern char etext, edata;
// the following is supposed to detect whether a pointer is in the Stack
// according to this answer:
https://stackoverflow.com/questions/35206343/is-it-possible-to-identify-whether-an-address-reference-belongs-to-static-heap-s
void *stack_bottom;
bool __attribute__((noinline)) inStack(void *x) {
void *stack_top = &stack_top;
return x <= stack_bottom && x >= stack_top;
}
// then my attempt at checking if a pointer is in read-only data,
// excluding the BSS segment:
bool roMem(void* c) {
if(&etext < &edata && &end > &edata)
{
return (c > &etext) && (c < &edata) && (!inStack((void*)c));
} else if(&etext > &edata && &end > &edata)
{
return (void*)(c) <= (void*)&edata && !inStack((void*)c);
} else if(&end < &edata)
{
return (void*)(c) > (void*)&end && (void*)(c) <= (void*)&edata && !inStack((void*)c);
}
}
class Test {
public:
const char* d;
Test(const char* c) { d = c; }
};
static const char* str5 = "short";
int main() {
const char* str1 = "short";
char* str2 = const_cast<char*>("short");
const char str3[6] = {'s', 'h', 'o', 'r', 't', 0};
const char* str4 = "longgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg";
std::cout << roMem((void*)str1) << std::endl; // prints 1
std::cout << roMem((void*)(const char*)str2) << std::endl; // prints 1
std::cout << roMem((void*)str3) << std::endl; // prints 0
std::cout << roMem((void*)str4) << std::endl; // prints 1
std::cout << roMem((void*)str5) << std::endl; // prints 1
std::cout << roMem((void*)"short") << std::endl; // prints 1
std::cout << roMem((void*)(const char*)2) << std::endl; // prints 0
Test x((const char*)3);
std::cout << roMem((void*)x.d) << std::endl; // prints 0
}
在我的Ubuntu 22.04机器上,上面的seems正在工作(用g++12和链接器ID或clang++13编译),但问题是,在判断(following this)时,它似乎也在生成紧跟在代码文本段之后的传统数据段序列-在这种情况下,旧的(ptr > &etext) && (ptr < &edata);
就足够了.问题是当机器不遵循紧跟在文本段之后的传统数据段序列时.