注意:
.LIB
的所有情况均指的是 link.exe
导入库,而不是目标文件的存档。
我花了很长时间弄清楚如何适应 Windows 中
.DLL
文件之间的循环依赖关系。这是因为我正在使用一个 makefile
,它在循环依赖的共享库中具有未解析的外部。我认为我需要更多地了解链接器......
我要回顾一下,因为它很容易忘记。不仅如此,我对 Windows 中链接的理解很可能存在问题。任何更正都会有帮助。
为了这个问题,我有3个文件:exec.obj、one.obj和two.obj。
场景:
one.obj
包含在 two.obj
中定义的符号,并通过 link.exe
链接到 .DLL
。two.obj
包含在 one.obj 中定义的符号,并通过 link.exe
链接到另一个 .DLL
。exec.obj
包含在 one.obj
和 two.obj
中定义的符号,并通过 link.exe
链接到 .EXE
。常见的循环依赖问题:
当
one.obj
被link.exe
处理时,会创建one.dll
和one.lib
(其导入库)。 one.lib
的目的是描述在 one.dll
中找到的导出符号。
在当前时间点,
one.dll
无法从two.obj
导入符号,因为two.obj
尚未被link.exe
处理,因此two.lib
(导入库)尚不存在。因此,one.dll
包含对 two.obj
中定义的符号的未解析引用。
就像
one.obj
一样,当two.obj
被link.exe
处理时,它会创建一个导入库(two.lib
)和two.dll
。然而,two.obj
实际上可以通过查看one.lib
内部来找到它自己未解析的引用。此时,one.dll
具有未解析的引用,而 two.dll
则没有。无论哪种方式,exec.obj
在处理时都会有未解析的引用。
常见的循环依赖解决方案:
问题是,每当您尝试将
one.obj
链接到 .DLL
时,它都会搜索未解析的引用,将 one.obj
转换为 .DLL 并创建相应的 one.lib
(导入库),所有这些都在同一步骤中进行。该步骤完成后,再搜索任何其他未解析的引用就为时已晚,因为 one.obj
现在是 one.dll
。
解决方案是分解步骤。创建相应的导入库(
one.lib
),而不将one.obj
变成one.dll
。这将允许 one.obj
稍后由 link.exe
处理,从而在最终链接 two.lib
时不会禁用它使用 two.obj
的能力。
问题:
使用
one.lib
在其自己的单步中创建相应的导入库 (link.exe
) 时,会创建另一个名为 one.exp
的文件。我不明白为什么有必要。如果one.obj
仍然存在,则在处理two.lib
时可以在two.obj
中找到未解析的引用。但是,根据我的阅读,有必要在最后一步中包含 one.exp
。 不仅要处理 one.obj
并让它找到 two.lib
,还必须将 one.exp
添加到输入 link.exe
的文件列表中。为什么? two.lib
已经描述了 one.obj
所需的符号,而 two.exp
从来不需要处理 two.obj
。
您必须将时钟倒回到 20 世纪 80 年代末,那时使用链接器的 /DEF 或 /EXPORT 选项指定导出。他们使生成 one.dll 的第二个链接步骤变得脆弱,当您不使用“完全相同”的相同选项时,您会被射断。通过链接 one.exp 并省略选项来避免,现在不再可能出错,并且链接器不会重写 one.lib 不再那么相关了,现在几乎每个人都使用
__declspec(dllexport)
属性来指定导出。在这种情况下,当您不链接 one.exp 时,除了 one.lib 文件日期比 Two.dll 更新并导致不必要的重建之外,我没有看到明显的失败情况。不太确定,我从来没有让自己陷入这样的困境。