使用外部库时,通常必须确定是使用静态版本还是动态版本。通常,您不能交换它们:如果该库是作为动态库构建的,则无法对其进行静态链接。
为什么会这样?
示例:我正在Windows上构建C ++程序,并使用一个库,该库为链接器提供一个小的.lib
文件,而在运行可执行文件时必须提供一个大的.dll
文件。如果.dll
中的库代码可以在运行时解析,为什么不能在编译时解析并将其直接放入我的可执行文件中?
答案 0 :(得分:2)
为什么会这样?
大多数接头(AIX接头是一个显着的例外)丢弃在连接的过程中的信息。
例如,假设您有foo.o
和foo
,其中bar.o
和bar
。假设foo
呼叫bar
。
在链接foo.o
和bar.o
在一起成为一个共享库,链接器合并代码和数据段,并解析的引用。从foo
到bar
的呼叫变成CALL $relative_offset
。完成此操作后,您将无法再分辨来自foo.o
的代码与来自bar.o
的代码之间的边界,也无法分辨CALL $relative_offset
在foo.o
中使用的名称。 - 重定位条目已被丢弃
假设现在您想将foobar.so
与您的main.o
静态链接,并假设main.o
已经定义了自己的bar
。
如果您有libfoobar.a
,那将是微不足道的:链接器将从存档中提取foo.o
,将不使用存档中的bar.o
,并且解决从foo.o
到bar
到main.o
的呼叫。
但是应该清楚,foobar.so
不能满足以上要求-呼叫已经解决了 other bar
,您不能放弃从来到代码bar.o
,因为你不知道在哪里的代码是
在AIX上,有可能(或者至少在十年前曾经有过这种可能性)“取消链接”共享库,然后将其转回归档文件,然后可以将其静态链接到另一个共享库或主可执行文件中。
如果将
foo.o
和bar.o
链接到foobar.so
中,那么从foo
到bar
的调用总是解析为bar.o
中的那个?
这是UNIX共享库与Windows DLL有很大不同的地方。在UNIX上(在通常情况下),从foo
到bar
的调用将解析为主要可执行文件中的bar
。
这允许一个例如在主要malloc
中实现free
和a.out
,并且对malloc
的 all 调用使用 one 堆实现始终如一。在Windows上,您必须始终跟踪“此内存来自哪个堆实现”。
在UNIX模型是不是没有缺点虽然,作为共享库不是自包含的大部分密封单元(不同于一个Windows DLL)。
您为什么要将其解析为
bar
中的另一个main.o
?
如果您不解决对main.o
的调用,则与针对libfoobar.a
的链接相比,您将得到一个完全不同的程序。