代码之家  ›  专栏  ›  技术社区  ›  B.Gen.Jack.O.Neill

在Windows下运行的C二进制文件中初始化数据段

  •  3
  • B.Gen.Jack.O.Neill  · 技术社区  · 14 年前

    到目前为止,我知道(主要是由于stackoverflow用户)局部变量存储在堆栈上。现在我也终于明白为什么了。所以没关系。

    所以,我的想法是,全局变量位于程序代码的末尾。在最后一个指令之后。为什么我会这么想?因为这样,您就不需要浪费任何额外的内存和CPU时间。因为变量及其默认值在执行时会被OS复制到RAM中。

    为什么我认为这是可能的?因为,如果我没有错的话,在现代x86操作系统上,每个程序从0开始都有自己的地址空间。这样,编译就很容易知道全局变量的地址。因为它知道程序的长度,所以可以计算它在地址空间中的位置。

    为什么我认为这一切都错了?因为我已经在想为什么局部变量是以同样的方式在堆栈上创建的。当你有一些ELF格式的例程时,你有一些预编译的例程,只是带有变量的未解析地址。

    另外,在一些文章中,我读到使用malloc分配内存会扩展堆。因为堆的东西是程序代码后面的空间,所以会有错误,因为它会增长到堆栈中。否则,堆栈将需要位于进程地址空间的末尾,但taht将严重浪费内存。

    2 回复  |  直到 14 年前
        1
  •  5
  •   Billy ONeal IS4    14 年前

    一个程序的内存使用量不是该程序所用语言的函数。您可以在C#中编写占用大量内存的代码,在C中也可以这样做。

    也就是说,我将试着回答你的一些问题:

    如何存储和处理全局变量

    全局变量位于程序代码的末尾

    在某些架构上可能是这样,但不一定是这样。在Windows上(使用可移植可执行文件格式),它们没有任何关联,可以映射到完全不同的任意位置。事实上,最新的体系结构很可能不是这种情况,它不鼓励将代码放在数据所在的位置(出于安全目的,您不希望允许缓冲区溢出来覆盖程序代码)

    如果我没有错的话,在现代x86操作系统上,每个程序从0开始都有自己的地址空间

    null 不变。然而,问题通常是在实际加载代码之前,就有动态库或其他项占用了进程的地址空间。(例如,对代码所在文件的引用,或包含传递给程序的命令行的内存块)

    我读到使用malloc分配内存会扩展堆


    在现代处理器中,内存和地址空间混淆了。事物的地址位置通常与它们的物理存储位置关系不大。维基百科关于 Virtual Memory 可能会让事情对你更有意义。祝你好运!


    PE exe文件实际上包含有关全局变量的信息,这些信息可以与其他数据区分开来

    该操作系统实际上将它们映射到“最佳”可用空间

    现代处理器使用平面内存模型。访问任何一个地址和访问任何其他地址一样容易。

    事实并非如此(好吧,在很大程度上,它可能会改变的原因是一整罐蠕虫本身)。要访问全局,代码本身需要知道 基址

    首先,exe是否包含有关所需堆栈大小的信息?

    是的,存在控制堆栈的保留大小和提交大小的设置。因为在Windows上可以安全地处理堆栈溢出,所以通常堆栈只有1mb;在*nix机器上通常是8MB或更多。

    堆栈大小是否有限制?

    您可以阅读PE规范: http://www.microsoft.com/whdc/system/platform/firmware/pecoff.mspx


        2
  •  0
  •   ninjalj    14 年前

    在典型的虚拟内存现代系统中,程序的地址空间通常由以下部分组成:

    • 代码:标记为只读。通常不是从0开始,而是从更高的地址开始。如果不想在地址0上或附近找到任何内容,请考虑空指针。
    • 只读数据:通常紧跟在代码后面,因为它是只读的。
    • 读/写数据:通常在rodata之后,但从新页开始,因为它必须是读/写的。
    • 单位化静态变量:通常在数据之后。这个部分的内容不是来自可执行文件,可执行文件只是告诉大小,加载程序用零初始化它。
    • Heap:通常在bss之后,在Unix上可以通过调用brk()/sbrk()进行扩展。
    • 堆栈:通常从一些高内存区域开始,远离堆,然后向下增长。

    动态库有自己的代码和数据段,动态链接器(GOT,PLT,…),调试器。。。