我想要了解一个谜:
我已经创建了一个可以使用动态库扩展的应用程序,其中包含一些代码,但需要访问应用程序本身定义的某些函数。说清楚:
我有应用程序让它称之为APP,然后我有扩展EXT。 APP扩展了一些在EXT中实现的功能,但是EXT需要调用APP中定义的一些功能以便“挂钩”它(例如在APP布局中注册新项目等)。在MS Windows中,由于未解析的符号,我无法编译EXT - 这是有意义的 - 如何调用APP中的函数而不实际有任何链接这些,所以我创建了一个基本的APP的DLL库APP只是构建为一个DLL,其中包含我需要使用__declspec(dllexport)导出的所有这些函数(让我们称它为LIB),所以它的工作原理如下:
APP加载EXT,EXT通过LIB调用APP功能。这在某种程度上是一个讨厌的解决方案,但我想不出更好。最重要的是 - 它非常完美。
现在让我发疯的是,如果不必创建LIB,这一切都能在linux上运行吗?这个windows的东西很讨厌,但它很有道理,但是在linux上我甚至可以构建EXT而无需构建APP或LIB,它只是忽略了这些未解析的符号并将其链接起来。整个库包含它们,我可以通过调用验证:
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中不起作用?
答案 0 :(得分:2)
我认为它与用于Linux中的可执行文件和库(以及许多其他* NIXes)和动态链接器的 ELF 格式有关。
当启动动态链接程序(创建它的进程)时,动态链接器会准备此进程的地址空间。 Linux库使用PIC(位置无关代码)编译,因此它们可以放在进程地址空间的任何位置。运行时来自不同模块的函数之间的链接通过 PLT (过程查找)和 GOT (全局偏移)表来解决。 PLT(只读,可执行部分)将间接跳转指令保存到GOT(读写,非可执行部分)表中的地址。首先通过PLT调用函数导致跳转到某些运行时链接器函数,该函数更新GOT条目(并跳转到实际地址)。对同一函数的后续调用直接跳转到它。
据我所知,编译器有足够的信息(函数原型和头文件中的其他数据)来正确构建库。但是要构建可执行文件,您必须提供所有必需的库(但在运行时您可以更改使用的库,只要它们提供所有使用的函数)。
我认为动态链接的工作方式与其他UNIX操作系统类似,使用ELF格式。
我对Windows可执行格式不太熟悉,所以我不能评论为什么类似的技巧在那里不起作用。