这是我最近一直在思考的重要内容:
我知道我的程序的虚拟地址空间包含堆栈(每个线程)和堆以及一些静态分配的内存以及所有这些。但它是否也包含程序的图像和所有指令?并且有可能以某种方式(无论平台如何依赖于技巧)找出我自己的图像的地址范围?内存是只读的吗?
简而言之:我可以创建一个自己打印的程序吗?
如果无法做到这一点,那么问题是,我可以打印自己的堆栈吗?我在想这样的事情:
const char * BASE;
void print_stack();
int main(int argc, char * argv[]) {
BASE = &argc;
/* do stuff */
print_stack();
return 0;
}
void print_stack() {
int sentinel;
const char * bottom = &sentinel;
while (bottom < BASE)
printf("%02X ", *bottom++);
}
答案 0 :(得分:1)
要回答您的第一个问题,当然它包含您的程序说明:您只能执行您可以访问的内容。要获取说明的地址,您可以获取功能的地址并从那里开始打印。然后,您可以使用udis86之类的库来反汇编它们。但请注意,您的编译器不需要以任何特定方式对函数进行排序,因此从main
开始并且从那里读取并不能保证获得所有内容,可能会在未分配的内存上进行操作。
要获得整个指令存储器范围(您正在寻找.text
段),您可以从操作系统中查找地址+大小(在Linux中,该信息将位于{{1在OS X中,您可以使用/proc/[pid]/maps
或通过vmmap
内核陷阱请求内核),然后直接读取内存。您还可以使用mach_vm_region()
转储程序的符号,将所有指向nm
段(它们应在.text
输出中标记为T
)并转储那些。这不是一个好方法,因为你必须反汇编所有内容以确定它们之间的填充位置。
可以访问所有映射内存,但并非所有映射内存都可写(nm
段不可写)。需要注意的一点是,如果您的操作系统实现ASLR,地址可能不会稳定地调用调用。
要解决您的第二个问题,是的,您可以打印自己的堆栈并在第三方库的帮助下对其进行符号化,但不是您尝试这样做的方式。堆栈通常会增长 down (即从高地址开始向低地址移动。作为读者的练习,通过.text
或其他反汇编程序反汇编您的一个函数,看看内存是如何堆栈在你的函数prolog期间被分配),因此你的for循环将永远不会运行,因为gdb
可能总是大于BASE
的地址。
答案 1 :(得分:1)
是的,代码字节(通常在此上下文中称为程序“text”)是虚拟地址空间的一部分。
您可以确定某个功能的地址,例如main()
,并使用它来确定一系列文本页面中的单个有效地址。然后,您必须调用特定于虚拟内存的API以确定该地址的映射范围。
共享库(.so文件)将其文本映射到不连续的VM区域。