链接工作究竟如何?

ord*_*ary 5 c c++ assembly linker compilation

我理解编译过程的方式:

1)预处理:所有宏都替换为实际值,删除所有注释等.用您包含的文件的文字文本替换#include语句.

2)编译:这里不会深入挖掘,但结果是您所在的任何架构的汇编文件.

3)汇编:获取汇编文件并将其转换为二进制指令,即机器代码.

4)链接:这是我困惑的地方.此时你有一个可执行文件.但是,如果您实际运行该可执行文件会发生什 问题是你可能包含*.h文件,那些只包含函数原型?因此,如果您实际调用这些文件中的一个函数,它将没有定义,您的程序将崩溃?

如果是这样的话,那引信到底是什么的呢?它如何找到与您包含的.h相关联的.c文件,以及它如何将其注入到您的机器代码中?是否必须再次为该文件完成整个编译过程?

现在,我已经明白有两种类型的链接,动态和静态.当你为你创建的每个可执行文件重新编译库的源代码时是静态的吗?我不太明白动态链接是如何工作的.那么您编译一个可由您使用它的所有进程共享的可执行库?这怎么可能呢?它不会在尝试访问它的进程的地址空间之外吗?另外,对于动态链接,您是否仍需要在某个时刻编译库?是不是只是坐在那里等待使用?什么时候编译?

您是否可以通过上述内容清除所有误解,错误的假设并替换您正确的解释?

小智 12

此时你有一个可执行文件.

不.此时,您有目标文件,这些文件本身不可执行.

但是,如果您实际运行该可执行文件会发生什

像这样的东西:

h2co3-macbook:~ h2co3$ clang -Wall -o quirk.o quirk.c -c
h2co3-macbook:~ h2co3$ chmod +x quirk.o
h2co3-macbook:~ h2co3$ ./quirk.o
-bash: ./quirk.o: Malformed Mach-o file
Run Code Online (Sandbox Code Playgroud)

告诉过你这不是一个可执行文件.

问题是你可能包含*.h文件,那些只包含函数原型?

实际上非常接近.翻译单元(.c文件)(通常)转换为汇编/机器代码,代表它的作用.如果它调用一个函数,那么文件中会引用该函数,但没有定义.

因此,如果您实际调用这些文件中的一个函数,它将没有定义,您的程序将崩溃?

正如我所说,它甚至都不会运行.让我再说一遍:目标文件不可执行.

在引擎盖下,链接究竟做了什么?它是如何找到与.h相关联的.c文件[...]

它没有.它查找从.c文件生成的其他目标文件,最终查找库(基本上只是其他目标文件的集合).

它找到它们是因为你告诉它要寻找什么.假设你有一个由两个.c文件组成的项目,它们调用彼此的函数,这将不起作用:

gcc -c file1.c -o file1.o
gcc -c file2.c -o file2.o
gcc -o my_prog file1.o
Run Code Online (Sandbox Code Playgroud)

它将因链接器错误而失败:链接器将找不到file2.c(和file2.o)中实现的函数的定义.但这会奏效:

gcc -c file1.c -o file1.o
gcc -c file2.c -o file2.o
gcc -o my_prog file1.o file2.o
Run Code Online (Sandbox Code Playgroud)

[...]它如何将其注入您的机器代码?

对象文件包含它们调用的函数的存根引用(通常以函数入口点地址的形式或显式的,可读的名称).然后,链接器查看每个库和目标文件,找到引用(如果找不到函数定义则抛出错误),然后用实际的"调用此函数"机器代码指令替换存根引用.(是的,这在很大程度上已经简化了,但是如果没有您询问特定的体系结构和特定的编译器/链接器,很难准确地说出来......)

当你为你创建的每个可执行文件重新编译库的源代码时是静态的吗?

否.静态链接意味着库的目标文件的机器代码实际上被复制/合并到最终的可执行文件中.动态链接意味着将库加载到内存中一次,然后在启动可执行文件时由操作系统解析上述存根函数引用.库中的机器代码不会复制到最终的可执行文件中.(所以在这里,工具链中的链接器只完成部分工作.)

以下内容可以帮助您实现启发:如果您静态链接可执行文件,它将是自包含的.它将在任何地方运行(无论如何都在兼容的架构上).如果您动态链接它,它将仅在计算机上运行,​​如果该特定计算机具有程序引用的所有库.

那么您编译一个可由您使用它的所有进程共享的可执行库?这怎么可能呢?它不会在尝试访问它的进程的地址空间之外吗?

操作系统的动态链接器/加载程序组件负责所有这些.

另外,对于动态链接,您是否仍需要在某个时刻编译库?

正如我已经提到的:是的,它已经编译完毕.然后它会在某个时刻(通常在第一次使用时)加载到内存中.

什么时候编译?

可以使用它之前的一段时间.通常,编译库,然后安装到系统上的某个位置,以便操作系统和编译器/链接器知道它的存在,然后就可以开始编译(嗯,链接)使用该库的程序.不早点.