1

我了解地址空间如何划分为:代码、数据、堆栈和堆。但是,我无法将给定 C 代码的内容映射到何处。

我知道:全局变量在数据部分。静态变量在数据部分。局部变量在堆栈部分。动态分配的空间在堆部分。我的问题是,当将库包含到程序中时,它在地址空间中的位置是什么?

我希望这个问题有意义..

4

2 回答 2

1

实际上,如果您有基于 linux 的 pc,您可以通过以下方式自行检查:

  1. 在 main() 中创建一个带有无限 while 循环的简单 c 应用程序。
  2. 编译

    $ gcc -o 主 ./main.c -g

  3. 发射

    $ gdb ./main

  4. 显示映射信息

    (gdb) r

    (gdb) info proc 映射

    映射地址空间:

          Start Addr           End Addr       Size     Offset objfile
            0x400000           0x401000     0x1000        0x0 /tmp/main
            0x600000           0x601000     0x1000        0x0 /tmp/main
            0x601000           0x602000     0x1000     0x1000 /tmp/main
            0x602000           0x623000    0x21000        0x0 [heap]
      0x7ffff7a0d000     0x7ffff7bcd000   0x1c0000        0x0 /lib/x86_64-linux-gnu/libc-2.23.so
      0x7ffff7bcd000     0x7ffff7dcd000   0x200000   0x1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
      0x7ffff7dcd000     0x7ffff7dd1000     0x4000   0x1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
      0x7ffff7dd1000     0x7ffff7dd3000     0x2000   0x1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
      0x7ffff7dd3000     0x7ffff7dd7000     0x4000        0x0
      0x7ffff7dd7000     0x7ffff7dfd000    0x26000        0x0 /lib/x86_64-linux-gnu/ld-2.23.so
      0x7ffff7fd4000     0x7ffff7fd7000     0x3000        0x0
      0x7ffff7ff6000     0x7ffff7ff8000     0x2000        0x0
      0x7ffff7ff8000     0x7ffff7ffa000     0x2000        0x0 [vvar]
      0x7ffff7ffa000     0x7ffff7ffc000     0x2000        0x0 [vdso]
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x25000 /lib/x86_64-linux-gnu/ld-2.23.so
      0x7ffff7ffd000     0x7ffff7ffe000     0x1000    0x26000 /lib/x86_64-linux-gnu/ld-2.23.so
      0x7ffff7ffe000     0x7ffff7fff000     0x1000        0x0
      0x7ffffffdd000     0x7ffffffff000    0x22000        0x0 [stack]
    

所以我们看到,ld-so已经把 c 库放到了地址中0x7ffff7bcd000 - 0x7ffff7dd5000。偏移量字段 - 是 ELF 文件本身的偏移量。我们可以使用 readelf 检查哪些部分对应于哪个偏移量:

$ readelf -a /lib/x86_64-linux-gnu/libc-2.23.so | less

敌人示例:

 [13] .text             PROGBITS         000000000001f8b0  0001f8b0
       0000000000153214  0000000000000000  AX       0     0     16

这意味着该.text部分有 offset 0x1f8b0。从上面的映射我们可以得出,主应用程序地址空间中.text部分开头的虚拟地址将是0x7ffff7bcd000 + 0x1f8b0

于 2018-05-23T08:28:06.950 回答
1

你是从一个糟糕的前提开始的:

我了解地址空间如何划分为:代码、数据、堆栈和堆。但是,我无法将给定 C 代码的内容映射到何处。

地址空间不是以这种方式划分的。地址空间包含内存。用于堆栈的内存与用于堆的内存无法区分。使堆栈成为堆栈的唯一原因是它在应用程序中使用堆栈指针进行分配。使堆成为堆的唯一原因是应用程序中有一个堆管理器。您可以从堆中分配内存并将其分配给硬件堆栈指针,并且您的内存既是堆又是堆栈。

这是另一个误解:

我知道:全局变量在数据部分。静态变量在数据部分。局部变量在堆栈部分。动态分配的空间在堆部分。

汇编器、链接器和系统之间的工作人员有何不同。然而,有理汇编器允许用户定义他们自己的命名节。在许多汇编程序中,我可以创建 Bobs_Data_Section、Freds_Data_Section 和 Sams_Data_section,并将全局变量放入其中。

对于大多数(但不是全部)编译器,程序员无法控制如何创建节。不能保证编译器会将全局变量放在称为“数据”的部分中。实际上,全局变量和静态局部变量可以在同一段中。

这样的“部分”通常只输入到链接器。链接器将汇编器和编译器定义的段组合到具有公共访问属性的内存区域中。例如,作为“数据”部分进入链接器的内容作为可执行文件中的指令从链接器中出来,以创建可读/写的页面。

我的问题是,当将库包含到程序中时,它在地址空间中的位置是什么?

因此,现在您遇到了如何尝试了解事物如何工作的问题。如果您将进程地址空间视为内存,则可以在任何地方加载库。程序加载器读取可执行文件中的指令,并在地址空间中任何可用的地方创建具有正确属性(例如,只读/不执行、读/写、只读/执行)的页面。

如果您将地址空间视为被划分为代码、数据等,那么加载库就会出现问题。这让我想知道为什么学校和书籍坚持使用这些荒谬的概念进行教学。

于 2018-05-23T14:47:38.537 回答