Linux 如何解析用作扩展的库的未解析符号

How does linux resolve unresolved symbols for libraries that are used as extensions

本文关键字:符号 扩展 何解析 Linux      更新时间:2023-10-16

有一个谜,我试图理解:

我制作了一个可以使用动态库扩展的应用程序,这些动态库包含一些代码,但是需要访问应用程序本身中定义的一些函数。为了明确这一点:

我有应用程序,我们称之为APP,然后我有扩展EXT.APP扩展了一些在EXT中实现的功能,但EXT需要调用APP中定义的一些函数才能"钩住"它(例如在APP布局中注册新项目等(。在 MS Windows 中,由于未解析的符号,我将无法编译 EXT——这是有道理的——我将如何调用 APP 中的函数而实际上没有任何可以链接这些函数的内容,所以我创建了一个 dll 库的 APP,它基本上是 APP 只是构建为 DLL,其中包含我需要访问的所有这些函数,使用 __declspec(dllexport( 导出(我们称之为 LIB(, 所以它的工作原理是这样的:

APP

加载EXT和EXT通过LIB调用APP函数。在某些时候,这是一个令人讨厌的解决方案,但我想不出更好的解决方案。最重要的是 - 它完美地工作。

现在让我发疯的是,为什么这一切都可以在 Linux 上运行而无需创建 LIB?这个 windows 的事情很讨厌,但它非常有意义,但是在 linux 上,即使不必构建 APP 或 LIB,我也可以构建 EXT,它只是以某种方式忽略这些未解析的符号并无论如何都链接它。整个库都包含它们,我可以通过调用来验证:

ld: warning: cannot find entry symbol _start; not setting start address
libhuggle_md.so: undefined reference to `Huggle::Query::NetworkManager'
libhuggle_md.so: undefined reference to `Huggle::Syslog::HuggleLogs'
libhuggle_md.so: undefined reference to `Huggle::Core::HuggleCore'
libhuggle_md.so: undefined reference to `Huggle::QueryPool::HugglePool'
libhuggle_md.so: undefined reference to `Huggle::Localizations::HuggleLocalizations'
libhuggle_md.so: undefined reference to `Huggle::Configuration::HuggleConfiguration'
libhuggle_md.so: undefined reference to `Huggle::GC::gc'
libhuggle_md.so: undefined reference to `Huggle::WikiUser::WikiUser(QString)'
libhuggle_md.so: undefined reference to `Huggle::WikiUtil::MessageUser(Huggle::WikiUser*, QString, QString, QString, bool, Huggle::Query*, bool, bool, bool, QString, bool, bool)'

因此,您可以看到EXT引用了APP的某些功能,但它从未链接到任何可以实现它们的库。他们只是没有解决。

当我在APP中加载EXT时,内核内部的一些魔术不知何故发生了,并且都神奇地工作了。为什么Linux上的APP不需要LIB,而Windows确实需要它?为什么可以在 linux 上将某些内容与未解析的外部符号链接起来?它如何知道我指的是哪些符号?它会在APP中找到它们并解决它们运行时吗?

对于任何感兴趣的人,这里有一个完整的来源:https://github.com/huggle/huggle3-qt-lx 如果你在 linux 上克隆它并运行./configure --extension然后让你看到它首先构建一个扩展(即使没有什么可以链接的(,然后它会创建应用程序,如果你运行make install然后尝试运行它, 您将看到它加载得很好,并使用一些魔法在运行时修复了库中未解析的符号。这是如何工作的?为什么它在Windows中不起作用?

我认为它与用于linux(以及许多其他* NIX(和动态链接器中的可执行文件和库的ELF格式有关。

启动动态链接

程序(创建进程(时,动态链接器将准备此进程的地址空间。Linux 库使用 PIC(位置独立代码(进行编译,因此它们可以放置在进程地址空间中的任何位置。运行时来自不同模块的函数之间的链接通过 PLT(过程查找(和 GOT(全局偏移(表解析。PLT(只读,可执行部分(保存对GOT(读写,不可执行部分(表中地址的间接跳转指令。通过 PLT 首次调用函数会导致跳转到某个运行时链接器函数,该函数更新 GOT 条目(并跳转到真实地址(。对同一函数的后续调用直接跳转到该函数。

据我了解,编译器有足够的信息(函数原型和头文件中的其他数据(来正确构建库。但是要构建可执行文件,您必须提供所有必需的库(但在运行时,只要它们提供所有使用的函数,您就可以更改使用的库(。

我假设动态链接的工作方式类似于其他使用 ELF 格式的 UNIX,如操作系统。

我对 Windows 可执行文件格式不是很熟悉,所以我无法评论为什么类似的技巧在那里不起作用。