为什么我必须在运行程序之前设置 LD_LIBRARY_PATH,即使我已经在编译阶段链接了库位置?

为什么我必须在运行程序之前设置 LD_LIBRARY_PATH,即使我已经在编译阶段链接了库位置?

我正在使用 编译模型make。该模型有一个Makefile通过类似于 的标志将源代码与依赖库连接起来-L/lib1 -L/lib2。但是当我尝试运行该模型时,它会失败,除非我还确保环境变量

export LD_LIBRARY_PATH=/lib1:/lib2

并指向完全相同的库。这对我来说似乎多余。

这里到底发生了什么?为什么我必须在编译之前和执行之前指定库的位置?

这可能是一个愚蠢的问题;我对编译机器代码不太有经验,通常只使用脚本语言。

答案1

尽管每个人都在通俗意义上使用编译,将源代码转换为可执行文件,但从技术上讲,它只是相当长的管道中的一个步骤:

  1. 输入文件通过预处理器运行,产生单个翻译单元。
  2. 预处理器的输出被编译为程序集。
  3. 汇编器将其作为输入并输出目标文件。
  4. 链接器将目标文件拼接在一起以生成可执行文件。

[ 严格地说,没有必要将这些步骤分开,现代编译器通常将它们结合起来以提高效率。]

我们关心的是链接步骤,它将您的代码与标准系统库结合起来。链接器将对象从静态库直接复制到可执行文件中。然而,对于共享库,它仅提供对该库的引用。

共享库有很多优点。您可以更新它们而无需重新编译程序,并且它们使用更少的内存,因为程序可以共享通用代码。它们还有一个明显的缺点,即代码不在可执行文件中。

解决方案是动态加载器,它负责在运行时解析所有共享库引用。装载机自动运行;这样做的说明是链接器包含在可执行文件中的一件事。当然,这前提是加载器可以找到这些库。

系统库位于标准目录中,这很简单。如果不是这种情况,加载程序将搜索 LD_LIBRARY_PATH。为什么链接器不直接将路径放入可执行文件中?因为那样你就无法移动或更改库。

实际上,您也无法真正移动可执行文件,因为该库位于系统搜索路径之外。如果它仅在库位于 ~luke/lib 中时运行,那么您不能将其交给 joe,除非他可以读取您的文件。如果你换了一份新工作,乔就很糟糕。

仅供参考,它还会以多种其他方式吸收。如果您只能在编译时指定库位置等,那么调试将成为一场永恒的噩梦。

相关内容