【问题标题】:compiled c program memory addresses编译的c程序内存地址
【发布时间】:2012-08-06 14:05:30
【问题描述】:

我正在尝试理解以下内容:

给定一个 C 语言的小 Hello World 程序

#include <stdio.h>

int main()
{
    int i;
    for(i=0; i < 10; i++)
    {
        printf("Hello, world!\n");
    }
}

当您使用 gcc 编译它,然后使用 objdump 检查生成的 .out 文件时,您会得到如下内容:

08048374 <main>:
8048374:       55                      push   ebp
8048375:       89 e5                   mov    ebp,esp
8048377:       83 ec 08                sub    esp,0x8
804837a:       83 e4 f0                and    esp,0xfffffff0
804837d:       b8 00 00 00 00          mov    eax,0x0
8048382:       29 c4                   sub    esp,eax
8048384:       c7 45 fc 00 00 00 00    mov    DWORD PTR [ebp-4],0x0
804838b:       83 7d fc 09             cmp    DWORD PTR [ebp-4],0x9
804838f:       7e 02                   jle    8048393 <main+0x1f>
8048391:       eb 13                   jmp    80483a6 <main+0x32>
8048393:       c7 04 24 84 84 04 08    mov    DWORD PTR [esp],0x8048484
804839a:       e8 01 ff ff ff          call   80482a0 <printf@plt>
804839f:       8d 45 fc                lea    eax,[ebp-4]
80483a2:       ff 00                   inc    DWORD PTR [eax]
80483a4:       eb e5                   jmp    804838b <main+0x17>
80483a6:       c9                      leave  
80483a7:       c3                      ret    
80483a8:       90                      nop    
80483a9:       90                      nop    
80483aa:       90                      nop    

生成的 .out 文件中的第一列值是内存地址,如果我理解正确,这些地址包含其他列中的指令。

现在我的问题:如果您将文件复制到另一台机器(或者甚至在同一台机器上的不同位置)并再次转储文件,这些地址应该更改为其他地址,因为程序将位于内存中的不同位置, 正确的?但如果我这样做,我会得到完全相同的输出,相同的地址值。这是为什么?我显然误解了第一列的含义,有人可以向我解释一下这些地址到底是什么吗?提前致谢!

更新: 据我所知,由于 Paul R 的回答和一些进一步的维基百科阅读,这些地址引用了一个虚拟地址空间,其中代码由运行它的机器的操作系统执行。这些虚拟地址被其操作系统映射到实际机器上的绝对地址。

【问题讨论】:

  • 我想你误解了内存和磁盘的概念。该地址是 memory(也称为 RAM,为简单起见)中的地址,而在 disk 上这些地址不被使用。建议大家多了解一下计算机的基本架构,比如磁盘和内存的区别。
  • “这些地址应该更改为其他地址,因为程序将位于内存中的不同位置,对吗?” -- 你不是在处理内存中的程序,你是在处理一个最终将被加载到内存中的目标文件。
  • 我确实了解内存和磁盘之间的区别,我的困惑来自于不知道这些地址是虚拟的

标签: c memory gcc assembly gdb


【解决方案1】:

左列中的地址是(虚拟)地址,您的代码在运行时将在该地址处为loaded。除非代码是position-independent,否则需要在这些地址加载才能正确运行。

【讨论】:

  • 我同意,但它们是相对地址,不是吗?
  • 一般来说,在标准操作系统中,它们不应该是相对地址吗?由于无法保证内存可用,因此将它们设为绝对地址不会使事情复杂化吗?
  • @Razvan 你知道什么是虚拟地址空间吗?
  • 好吧,虚拟内存子系统本身确实是一个完全独立的主题,但是是的,在典型的桌面或服务器操作系统中,每个进程通常都有自己的虚拟地址空间,你甚至不需要考虑物理地址或两者之间的映射。加载器在系统的这方面并没有真正发挥任何作用——它只是将代码加载到所需的虚拟地址区域。
  • 感谢 Paul R,很好的解释
【解决方案2】:

32 位操作系统中的每个进程都在其自己的 4GB 虚拟内存区域中运行。该区域在内核和您的进程之间共享,通常以 3GB/1GB 内存分割的形式进行,其中从 0x00000000 开始的较低 3GB 内存区域由应用程序使用,而较高的 1GB 内存区域由内核使用。

现在如果我们考虑应用程序较低的 3GB 用户空间区域,该区域进一步分为不同的段,例如文本段、已初始化数据段、未初始化数据段等。

因此,您编写的代码放置在该文本区域中,在您的示例中恰好从 08048374 开始。

因此,整个汇编代码都放置在此虚拟地址中,而与您用来运行它的任何机器无关,因为这是在链接阶段预先定义的。因此,这个地址不会改变。希望这会有所帮助。

【讨论】:

  • 您应该澄清您在答案中提到的 4 GB、3 GB 等大小仅特定于典型的 32 位虚拟内存操作系统,或者可能使答案更普遍适用。
  • 是的。编辑上面的答案以反映 32 位操作系统参考,同时描述每个进程的 4GB VM 可用性。谢谢保罗。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-22
  • 2020-12-20
  • 1970-01-01
  • 2015-01-03
  • 2013-06-17
相关资源
最近更新 更多