查找数据段的地址范围

Posted

技术标签:

【中文标题】查找数据段的地址范围【英文标题】:Finding the address range of the data segment 【发布时间】:2011-05-17 13:51:27 【问题描述】:

作为一个编程练习,我正在用 C 编写一个标记和清除垃圾收集器。我希望扫描数据段(全局变量等)以查找指向已分配内存的指针,但我不知道如何获取该段的地址范围。我怎么能这样做?

【问题讨论】:

我同意,但是有什么办法可以在程序中得到这个,比如系统调用? 如果你不告诉我们系统是什么,我们怎么回答? 我正在运行最新版本的 Ubuntu Linux。但我认为系统调用是一种接口(即它们可能以不同的方式实现但它们仍然存在)? 没有。基本上。您运行的操作系统是最重要的问题。 Windows vs POSIX 是最重要的问题,据我所知,所有 Linux 变体都是 POSIX。一旦你说“我在 Windows”或“我在 POSIX”,那么是的,你在谈论一个已知的接口,但如果你不知道系统,你就不知道接口。跨度> 【参考方案1】:

如果您使用的是 Windows,那么有一些 Windows API 可以帮助您。

//store the base address the loaded Module
dllImageBase = (char*)hModule; //suppose hModule is the handle to the loaded Module (.exe or .dll)

//get the address of NT Header
IMAGE_NT_HEADERS *pNtHdr = ImageNtHeader(hModule);

//after Nt headers comes the table of section, so get the addess of section table
IMAGE_SECTION_HEADER *pSectionHdr = (IMAGE_SECTION_HEADER *) (pNtHdr + 1);

ImageSectionInfo *pSectionInfo = NULL;

//iterate through the list of all sections, and check the section name in the if conditon. etc
for ( int i = 0 ; i < pNtHdr->FileHeader.NumberOfSections ; i++ )

     char *name = (char*) pSectionHdr->Name;
     if ( memcmp(name, ".data", 5) == 0 )
     
          pSectionInfo = new ImageSectionInfo(".data");
          pSectionInfo->SectionAddress = dllImageBase + pSectionHdr->VirtualAddress;

          **//range of the data segment - something you're looking for**
          pSectionInfo->SectionSize = pSectionHdr->Misc.VirtualSize;
          break;
      
      pSectionHdr++;

将 ImageSectionInfo 定义为,

struct ImageSectionInfo

      char SectionName[IMAGE_SIZEOF_SHORT_NAME];//the macro is defined WinNT.h
      char *SectionAddress;
      int SectionSize;
      ImageSectionInfo(const char* name)
      
            strcpy(SectioName, name); 
       
;

这是一个完整的、最小的 WIN32 控制台程序,您可以在 Visual Studio 中运行,它演示了 Windows API 的使用:

#include <stdio.h>
#include <Windows.h>
#include <DbgHelp.h>
#pragma comment( lib, "dbghelp.lib" )

void print_PE_section_info(HANDLE hModule) // hModule is the handle to a loaded Module (.exe or .dll)

   // get the location of the module's IMAGE_NT_HEADERS structure
   IMAGE_NT_HEADERS *pNtHdr = ImageNtHeader(hModule);

   // section table immediately follows the IMAGE_NT_HEADERS
   IMAGE_SECTION_HEADER *pSectionHdr = (IMAGE_SECTION_HEADER *)(pNtHdr + 1);

   const char* imageBase = (const char*)hModule;
   char scnName[sizeof(pSectionHdr->Name) + 1];
   scnName[sizeof(scnName) - 1] = '\0'; // enforce nul-termination for scn names that are the whole length of pSectionHdr->Name[]

   for (int scn = 0; scn < pNtHdr->FileHeader.NumberOfSections; ++scn)
   
      // Note: pSectionHdr->Name[] is 8 bytes long. If the scn name is 8 bytes long, ->Name[] will
      // not be nul-terminated. For this reason, copy it to a local buffer that's nul-terminated
      // to be sure we only print the real scn name, and no extra garbage beyond it.
      strncpy(scnName, (const char*)pSectionHdr->Name, sizeof(pSectionHdr->Name));

      printf("  Section %3d: %p...%p %-10s (%u bytes)\n",
         scn,
         imageBase + pSectionHdr->VirtualAddress,
         imageBase + pSectionHdr->VirtualAddress + pSectionHdr->Misc.VirtualSize - 1,
         scnName,
         pSectionHdr->Misc.VirtualSize);
      ++pSectionHdr;
   


// For demo purpopses, create an extra constant data section whose name is exactly 8 bytes long (the max)
#pragma const_seg(".t_const") // begin allocating const data in a new section whose name is 8 bytes long (the max)
const char const_string1[] = "This string is allocated in a special const data segment named \".t_const\".";
#pragma const_seg() // resume allocating const data in the normal .rdata section

int main(int argc, const char* argv[])

   print_PE_section_info(GetModuleHandle(NULL)); // print section info for "this process's .exe file" (NULL)

如果您对 DbgHelp 库的其他用途感兴趣,This page 可能会有所帮助。

你可以在这里阅读PE图像格式,详细了解它。一旦你理解了 PE 格式,你就可以使用上面的代码,甚至可以修改它以满足你的需要。

PE 格式

Peering Inside the PE: A Tour of the Win32 Portable Executable File Format

An In-Depth Look into the Win32 Portable Executable File Format, Part 1

An In-Depth Look into the Win32 Portable Executable File Format, Part 2

Windows API 和结构

IMAGE_SECTION_HEADER Structure

ImageNtHeader Function

IMAGE_NT_HEADERS Structure

我认为这将在很大程度上帮助你,其余的你可以自己研究:-)

顺便说一句,你也可以看到这个帖子,因为所有这些都与这个有关:

Scenario: Global variables in DLL which is used by Multi-threaded Application

【讨论】:

完美!!!!!!!!!!!!!!!,应该被标记为答案而不是被接受的垃圾答案。 @SSpoke:很高兴它对您有所帮助,即使在十年之后! :O【参考方案2】:

linux(和其他unix)的文本(程序代码)和数据的界限:

#include <stdio.h>
#include <stdlib.h>

/* these are in no header file, and on some
systems they have a _ prepended 
These symbols have to be typed to keep the compiler happy
Also check out brk() and sbrk() for information
about heap */

extern char  etext, edata, end; 

int
main(int argc, char **argv)

    printf("First address beyond:\n");
    printf("    program text segment(etext)      %10p\n", &etext);
    printf("    initialized data segment(edata)  %10p\n", &edata);
    printf("    uninitialized data segment (end) %10p\n", &end);

    return EXIT_SUCCESS;

这些符号的来源:Where are the symbols etext ,edata and end defined?

【讨论】:

brk() 和 sbrk() 在设计上无处不在,但不是 POSIX。那是因为参数是实现定义的。查看您的手册页了解详情。 因此,由于这些代表三个段中每个段的结尾,因此要搜索数据段,我们将从 &etext 搜索到 &eddata,对吧? 如何扩展到共享对象?如果我加载一个so,它也有一个数据和bss段。这些符号在这种情况下不起作用。还是他们?你能启发我吗?【参考方案3】:

由于您可能必须将垃圾收集器设置为程序运行的环境,因此您可以直接从 elf 文件中获取它。

【讨论】:

【参考方案4】:

加载可执行文件来自的文件并解析 PE 标头,用于 Win32。我不知道在其他操作系统上。请记住,如果您的程序包含多个文件(例如 DLL),您可能有多个数据段。

【讨论】:

【参考方案5】:

对于 ios,您可以使用 this solution。它显示了如何查找文本段范围,但您可以轻松更改它以找到您喜欢的任何段。

【讨论】:

以上是关于查找数据段的地址范围的主要内容,如果未能解决你的问题,请参考以下文章

进程的虚拟地址范围

如何查找IP地址范围

如何表示地理位置查找的街道地址范围?

在放大代码段的范围内找不到类型“AnyCancellable”

在半径范围内查找位置

CCNA-8.IPv6