Tam*_*lei 28 language-agnostic linker loader
如果它们都包含已编译的代码,为什么我们不能在运行时加载"静态"文件,为什么我们不能在编译时链接动态库?为什么需要单独的格式来包含"独立"代码?什么需要存储哪个保证差异?
Mar*_*age 22
静态库是真正的库,因为它们包含目标文件库.每个目标文件通常是从单个源文件创建的,包含机器代码以及有关代码所需数据的信息.在链接步骤中,链接器将选择必要的目标文件并将它们组合成可执行文件.
机器代码的一个重要部分是跳转,调用和数据指针必须包含实际的内存地址.但是,如果目标文件需要在另一个目标文件中调用另一个函数,则它只能使用符号引用该函数.当链接器将目标文件组合成可执行代码时,符号引用将被解析并转换为实际内存地址.
动态库是可执行代码,可以加载到内存中并立即执行.在某些操作系统上,可能存在另外的步骤,其中通过将可执行代码移动到另一个位置来重新设置代码,并且这要求代码内的所有绝对地址被移位固定量.此操作仍然比组合目标文件和解析链接器完成的符号快得多.
把它们加起来:
如果你曾试图链接一个合理大小的项目,你会注意到它需要花费很多时间,可能比你想要等待启动一个应用程序的时间长.这就解释了为什么你不能执行静态库.并且动态库已经过优化和剥离,除了可执行代码之外不包含任何内容,这使得它们不适合用作静态库.
目标文件中的代码未链接.它包含对尚未解析的外部函数的隐式引用.
链接目标文件以创建DLL时,链接器会查看所有这些外部引用,并查找可满足它们的其他库(静态或动态).通过将该函数的主体(或其他)包含到DLL中来解析对静态库中的名称的引用.如果它引用动态库,则DLL和引用函数的名称都包含在DLL中.
最终,没有理由,这将有是事实.从理论上讲,每次加载文件时都可以编写加载程序来完成所有这些操作.它基本上只是一个优化:链接器执行相对较慢的工作部分.剩下对DLL的引用,但它们被解决到加载器查找和加载目标文件(如果需要)并解析引用的函数相当快的程度.当链接器正在完成它的工作时,它会通过扫描很长的定义列表来找到你关心的那些,这会慢得多.
注意:以下答案不是平台无关的,而是特定于基于ELF的系统和其他一些类似的系统。其他人可以填写其他系统的详细信息。
什么是静态库?
静态库是*.o
归档中文件的集合。每个文件都可以包含对未定义符号的引用,链接器必须对此文件进行解析,例如,您的库可能具有对的引用printf
。该库未提供有关在哪里printf
可以找到任何指示的信息,预计链接器会在要求链接的其他一个库中找到它。
假设您的库包含以下代码:
read_png.o
write_png.o
read_jpg.o
write_jpg.o
resize_image.o
handle_error.o
Run Code Online (Sandbox Code Playgroud)
如果应用程序仅使用read_png
和write_png
,则其他代码段将不会加载到可执行文件中(handle_error
从read_png
和调用的除外write_png
)。
我们无法在运行时加载静态库,因为:
链接器不知道在哪里可以找到外部对象printf
。
会很慢。动态库已针对快速加载进行了优化。
静态库没有名称空间的概念。我无法定义自己的名称,handle_error
因为它会与库的定义冲突。
什么是动态库?
ELF系统上的动态库是与可执行文件相同类型的对象。它还会导出更多符号,可执行文件只需要导出即可_start
。动态库经过优化,因此可以将整个对象直接映射到内存中。
如果printf
在动态库中有对的调用,则除了静态库的要求之外,还有一些其他要求:
您必须指定具有的库printf
。
您必须以一种特殊的方式调用该函数,以使链接程序为插入地址printf
。在静态库中,链接器只能修改您的代码并直接插入地址,但是共享库则无法实现。
我们不想使用动态库静态链接,因为:
我们不能仅链接动态库的一部分。即使我们的可执行文件从不调用read_jpg
,它也会被包含在内,因为动态库是全有还是全无。
即使很小,函数调用的额外开销也是浪费的。
摘要
编译看起来像这样:
Source ==compile==> Object ==link==> Executable / Shared Library
Run Code Online (Sandbox Code Playgroud)
静态库是一个充满了尚未链接对象的档案。还有很多工作要做。
共享库是链接的最终产品,可以随时加载到内存中。
静态库是最早发明的。如果两者是同时发明的,那么它们可能会更加相似。