我正在学习elf文件。我读了一些关于它的东西,我开始对它知之甚少,但有些东西让我感到困惑。我想继续学习并掌握它,但首先我想确保我把最简单的事情做好。我会在下面陈述我所知道的,请纠正我犯的错误。
当你编写一个C应用程序并编译它(比如用gcc)时,它会被翻译成表示代码和数据的机器指令。调用编译器的输出是一个elf文件。elf文件包含(除其他外)一个节头,该节头基本上是一系列Elf64_Shdr,每个节对应于编译后的应用程序包含的每个节。所以基本上,一个节只是一些机器指令,它们代表代码或数据,以及关于它的一些信息,比如它(第一条指令的地址)在哪里,它有多长(大小),它是可写的还是可读的(一些标志),等等。我对节和节头的理解正确吗?
当我们运行make命令并将elf文件传递给它时,链接器就会发挥作用,并查看编译器创建的所有部分,根据ld脚本文件的规则,将它们分组为"段",并创建我们可以运行的可执行文件因此,基本上,段只不过是具有相同属性的部分,它们分组在一个具有特定名称的公共部分中这样正确吗?
然后,当我们实际运行创建的可执行文件时,加载程序开始发挥作用,查看链接器创建的这些段,并通过读取这些段所包含的信息,将机器指令映射到各个内存位置,以便进程可以运行。这就是所谓的(在我看来)记忆图像这样正确吗?
谢谢你的阅读并帮助我消除疑虑谢谢你。
您的描述中有多处不准确,不清楚您是在理解所涉及的处理方面不准确,还是在描述方面不准确。
当你编写一个C应用程序并编译它(比如用gcc)时,它会被翻译成表示代码和数据的机器指令。
这并不完全准确:"机器指令"one_answers"机器代码"之间存在差异。
当编译.c
文件时,一些编译器会将其翻译成机器指令(汇编),然后将其传递给汇编程序以生成机器代码(GCC就是这样做的)。其他编译器集成了汇编程序,并有效地跳过了汇编程序生成步骤(Clang就是这么做的)。
调用编译器的输出是一个elf文件。
在一些但不是所有系统上,编译的结果是一个可重定位的ELF文件。其他系统生成不同格式的对象文件,如XCOFF或Mach-O。
elf文件包含(除其他外)一个节头,该节头基本上是一系列Elf64_Shdr,每个Elf64_Shdr用于编译的应用程序包含的每个节。
应用程序尚未构建,因此这是不准确的。此外,Elf64_Shdr
仅适用于64位ELF平台;在32位机器上,它是Elf32_Ehdr
。
当我们运行make命令时
make
命令与任何事情都没有任何关系。它只是根据需要调用编译器和链接器(或其他工具)。您可以用shell脚本替换它,也可以手动键入命令。
并将其传递给elf文件
链接步骤涉及一个或多个(通常是多个)可重定位的ELF
对象文件、存档库和动态库。
链接器开始发挥作用,查看编译器创建的所有部分的名称和属性,并按照ld脚本文件的规则将它们分组为"段">
要了解链接器的作用,您可以阅读本系列博客文章。
您的描述淡化了链接器的作用。链接器要复杂得多,它执行您没有提到的重新定位解析和许多其他任务。
并创建我们可以运行的可执行文件。
通常为true。
您可以要求链接器将几个可重定位的对象文件组合成组合的对象文件(使用ld -r foo.o bar.o -o combined.o
),在这种情况下,结果将而不是可执行文件。
您也可以要求链接器链接共享库,而不是链接可执行文件。
因此,基本上,分段只不过是具有相同属性的分段,它们被分组在一个具有特定名称的公共分段中。
错误。链接比将部分分组在一起要多得多。
然后,当我们实际运行创建的可执行文件时,加载程序开始发挥的作用
加载程序仅用于动态链接的可执行文件。完全静态的可执行文件没有加载程序,而是由内核本身直接启动。
并查看链接器创建的这些段,并通过读取它们包含的信息将机器指令映射到各个内存位置,以便进程可以运行。这就是所谓的(在我看来)记忆图像。
基本正确。内存映像的某些部分根本不来自磁盘(例如线程本地存储和组合.bss
部分的内容)