共享库:Windows与Linux方法

QAH*_*QAH 23 linux windows shared-libraries

我有一个关于Windows共享库(DLL)与Linux共享库(SO)的快速问题.

为什么在创建Windows DLL时它需要客户端程序也链接到静态库(.lib文件),但在Linux中创建的应用程序不需要任何链接来防止这种静态库.

它与代码重定位等有什么关系吗?谢谢.

dat*_*olf 34

为什么在创建Windows DLL时它需要客户端程序也链接到静态库(.lib文件),但在Linux中创建的应用程序不需要任何链接来防止这种静态库.

这是Microsoft做出的历史性设计决策,因此链接器可以将DLL引用添加到可执行文件中,而不会在链接时出现DLL的特定版本.原因是,总有不同版本的Windows,不同版本的DLL.当时微软还在OS/2上与IBM合作,计划是,Windows程序也可以在OS/2上执行.好吧,微软决定通过推出基于NT内核的专业级操作系统来"反弹"OS/2.但这意味着,对于开发,您希望开发人员能够链接到系统DLL,而无需提供DLL的所有不同变体.相反,动态链接"模板"将用于创建DLL和可执行文件(都是PE格式),这些是特定.lib文件,根本不是库,而只是符号和序数表(这是一个鲜为人知的事实,但PE二进制符号不仅可以通过字符串标识符加载,还可以加载整数,即所谓的序数.

序数的副作用是,它们允许隐藏人类可读的符号,这样只有在您知道序数←→函数关系时才能使用DLL.

在Unix中,传统是"你在将要运行它的系统上构建它",或者"你有所有目标系统文件".所以从来没有激励这么独立的图书馆和联系信息.从技术上讲,同样适用于DLL也是如此.PE可以导出DLL所执行的符号和重定位表,并且链接器可以从中获取所需的所有信息,就好了.

如果要使用Unix共享对象隐藏符号,通常使用包含struct其中所有函数指针的单个符号来执行此操作,并且仅按名称导出此结构的全局常量实例,其中包含大量未明确命名的指针.但是,您可以对Windows DLL执行完全相同的操作.

TL; DR:其原因不是技术问题,而是营销和分销决策.


rod*_*igo 23

实际上不是代码重定位,这是一个完全不同的问题.它与建筑有所不同:

  • 在Windows中,DLL就像可执行文件(EXE).EXE和DLL之间的主要区别在于EXE具有入口点(main/WinMain函数),因此它可用于启动进程,而DLL只能加载到预先存在的进程中.但是看(1)

  • 在Linux中,.so的工作类似于静态库(.a).主要区别在于.so文件可以与正在运行的程序链接,而.a文件只能在编译程序时链接.

这种方法的结果是在linux中可以使用相同的文件来构建和运行程序.但是在Windows中,您需要一个适当的库(LIB)来链接程序.实际上,对应于DLL的lib通常只有函数的名称,以满足链接器,以及执行重定位的存根.但见(2)

(1)嗯,DLL也有入口点,但它不用作主函数,就像某种初始化/终结钩子一样.

(2)一些链接器足够智能,在某些简单的情况下,能够通过使用DLL本身链接到DLL,而无需额外的LIB文件.我认为至少MinGW链接器可以做到这一点.

  • 它们的行为甚至不同.`.so`是一个ELF对象; 它看起来通常像没有入口点的可执行文件.`.a`是一个包含多个`.o`文件的存档文件; 它的外观和行为更像是tar文件而不是库!链接一个`.a`文件只需将所有成员`.o`文件取消归档并将其拉入; 这与链接`.so`的方式完全不同. (9认同)
  • `.so`与静态库(`.a`)完全*完全不同. (8认同)
  • @rodrigo:你的答案在这两方面都是错误的.首先就像在Windows中一样,在Linux中,共享对象的文件格式与常规可执行文件的格式相同:ELF(在Windows中它是PE).那么在Windows中完全可以编写只提供DLL文件的链接器,链接器从DLL本身提取符号和重定位信息.使用sepratate .lib主要是出于历史原因,因此您可以在没有实际库存在的情况下链接到库; 因为不同版本的Windows具有不同的DLL. (6认同)
  • @duskwuff:我不同意:这些只是实现细节。看看编译器可观察到的行为:它们都定义了外部符号,它们都可以链接(但一个在链接时另一个在运行时),它们都可以有未定义的引用,它们都使用几乎完全相同的算法处理符号解析。主要区别(除了静态与动态之外)是 .so 不是由多个 .o 组成,因此它实际上更像是 .o 而不是 .a 或者更像是带有单个 .o 的 .a。当然,.so 文件具有更多功能(RPATH、NEEDED)等,但这些是动态需求。 (2认同)