C 静态库不能相互识别 - 为什么我得到 "undefined reference to `function`"? (链接器错误)
C static libraries do not recognize each other - why do I get "undefined reference to `function`"? (linker error)
所以,我做了一个这样的静态库:
liba.h
-----
#ifndef LIBA_H
#define LIBA_H
void do_something();
#endif
和
liba.c
-----
#include "liba.h"
#include <stdio.h>
void do_something() {
printf("liba\n");
}
还有一个类似的静态库:
libb.h
-----
#ifndef LIBB_H
#define LIBB_H
void do_something_else();
#endif
和
libb.c
-----
#include "liba.h"
#include "libb.h"
#include <stdio.h>
void do_something_else() {
do_something(); /* this is supposed to execute from liba */
printf("libb\n");
}
还有一个简单的主文件:
main.c
-----
#include "liba.h"
#include "libb.h"
int main() {
do_something_else();
}
静态库编译为:
gcc -c liba.c
gcc -c libb.c -I<path_to_liba.h>
ar rcs liba.a liba.o
ar rcs libb.a libb.o
主程序编译为:
gcc main.c -I<path_to_liba.h> -I<path_to_libb.h>
-L<path_to_liba.a> -L<path_to_libb.a> -la -lb
当我尝试编译主程序时出现 link 错误:
libb/libb.a(libb.o): In function `do_something_else':
libb.c:(.text+0x14): undefined reference to `do_something'
collect2: error: ld returned 1 exit status
出于某些奇怪的原因,将我的 main.c
文件更改为此文件时它起作用了:
main.c
-----
#include "liba.h"
#include "libb.h"
int main() {
do_something();
do_something_else();
}
这不仅编译,而且给出了预期的输出:
Output:
liba
liba
libb
如果不从主文件中调用 do_something
,我该如何 运行 do_something_else
?我显然做错了什么。
与 static 库链接时 - 位置很重要!在你的情况下 libb
依赖于 liba
,因此,依赖库应该放在使用它的人之后。
所以,试试这个方法:
gcc main.c -I<path_to_liba.h> -I<path_to_libb.h>
-L<path_to_liba.a> -L<path_to_libb.a> -lb -la
或者甚至这样,如果你不想关心顺序:
gcc main.c -I<path_to_liba.h> -I<path_to_libb.h>
-L<path_to_liba.a> -L<path_to_libb.a>
-Wl,--whole-archive -la -lb -Wl,--no-whole-archive
然而,在这种情况下,整个库都将被链接,即使它根本没有被使用。
快速解决
更改指定静态库的顺序。
说明
对于静态库,指定库的顺序很重要。原因是如果目标文件解析了以前未解析的符号,链接器将仅包含静态库中的目标文件(请记住,这些基本上只是目标文件的存档)。
以你的例子和顺序liba.a libb.a
:
编译main.c
给出一个带有未解析符号do_something_else
的目标文件。现在,链接器打开 liba.a
,查看包含在其中的单个目标文件 liba.o
中导出的符号,发现 do_something_else
没有匹配项(因为只有 do_something
从 liba.o
).因此,仍然需要解析 do_something_else
,链接器查看下一个库 libb.a
,查看从 libb.o
导出的符号,找到 do_something_else
并因此添加 libb.o
到将被链接的目标文件。 libb.o
包含符号 do_something
的未解析引用,因此链接器会尝试解析它。首先,它在从中获取目标文件 libb.o
的库中查找是否有另一个解析该符号的目标文件。然后它尝试使用下一个库/目标文件之一解析该符号。由于库 libb.a
中既没有另一个目标文件,也没有要查看的其他库/目标文件,因此链接器失败并显示未解析的符号 do_something
.
当您使用订单libb.a liba.a
时:
来自 main.o
的未解析符号 do_something_else
通过包含来自 libb.a
的 libb.o
来解析。这会添加未解析的符号 do_something
,然后可以通过包含 liba.a
中的 liba.o
来解析它。至此,链接成功。
理由
为什么不让链接器在所有库中搜索新的未解析符号?我在这里只能猜测这部分,但也提供一个合理的用例:
按照指定的顺序在库中搜索一次很简单。既要执行,又要理解。另外,考虑一下:
// main.c
#include <stdio.h>
int calculate(int value);
int main() {
printf("%d\n", calculate(42));
}
现在有一个库 libsuperfast.a
,其中包含(在其他代码中)calculate
的实现,它依赖于某些特定于平台的东西,并且仅在该平台上有条件地编译。但是您也想支持其他平台。因此,您手动实现 calculate
并将其放入 libslowashell.a
。链接喜欢
$CC -o app main.o libsuperfast.a libslowashell.a
如果可用,将包括快速代码,否则将包括慢速代码。为了便携性做了类似的事情,例如在 gnulib.
其他解决方案
- 无条件包含整个静态库,使用
-Wl,--whole-archive
。
- 使用libtool。允许您创建静态库和共享库,而不必过多担心平台细节。并为您处理库之间的依赖关系。
在创建 libb.a
时手动解决对 liba.a
的依赖。为此,首先从必须解析其对 liba.a
的引用的文件创建一个可重定位目标文件,并与 liba.a
链接,然后从该部分链接的文件(可能还有其他文件)创建库:
ld -r libb.o liba.a -o libb-partial.o
ar rcs libb.a libb-partial.o # possibly others
- 使用
-Wl,--start-group libb.a liba.a -Wl,--end-group
。这会导致链接器重复搜索库,直到不再有未解析的符号或不再有可以从库中解析的符号。
所以,我做了一个这样的静态库:
liba.h
-----
#ifndef LIBA_H
#define LIBA_H
void do_something();
#endif
和
liba.c
-----
#include "liba.h"
#include <stdio.h>
void do_something() {
printf("liba\n");
}
还有一个类似的静态库:
libb.h
-----
#ifndef LIBB_H
#define LIBB_H
void do_something_else();
#endif
和
libb.c
-----
#include "liba.h"
#include "libb.h"
#include <stdio.h>
void do_something_else() {
do_something(); /* this is supposed to execute from liba */
printf("libb\n");
}
还有一个简单的主文件:
main.c
-----
#include "liba.h"
#include "libb.h"
int main() {
do_something_else();
}
静态库编译为:
gcc -c liba.c
gcc -c libb.c -I<path_to_liba.h>
ar rcs liba.a liba.o
ar rcs libb.a libb.o
主程序编译为:
gcc main.c -I<path_to_liba.h> -I<path_to_libb.h>
-L<path_to_liba.a> -L<path_to_libb.a> -la -lb
当我尝试编译主程序时出现 link 错误:
libb/libb.a(libb.o): In function `do_something_else':
libb.c:(.text+0x14): undefined reference to `do_something'
collect2: error: ld returned 1 exit status
出于某些奇怪的原因,将我的 main.c
文件更改为此文件时它起作用了:
main.c
-----
#include "liba.h"
#include "libb.h"
int main() {
do_something();
do_something_else();
}
这不仅编译,而且给出了预期的输出:
Output:
liba
liba
libb
如果不从主文件中调用 do_something
,我该如何 运行 do_something_else
?我显然做错了什么。
与 static 库链接时 - 位置很重要!在你的情况下 libb
依赖于 liba
,因此,依赖库应该放在使用它的人之后。
所以,试试这个方法:
gcc main.c -I<path_to_liba.h> -I<path_to_libb.h>
-L<path_to_liba.a> -L<path_to_libb.a> -lb -la
或者甚至这样,如果你不想关心顺序:
gcc main.c -I<path_to_liba.h> -I<path_to_libb.h>
-L<path_to_liba.a> -L<path_to_libb.a>
-Wl,--whole-archive -la -lb -Wl,--no-whole-archive
然而,在这种情况下,整个库都将被链接,即使它根本没有被使用。
快速解决
更改指定静态库的顺序。
说明
对于静态库,指定库的顺序很重要。原因是如果目标文件解析了以前未解析的符号,链接器将仅包含静态库中的目标文件(请记住,这些基本上只是目标文件的存档)。
以你的例子和顺序liba.a libb.a
:
编译main.c
给出一个带有未解析符号do_something_else
的目标文件。现在,链接器打开 liba.a
,查看包含在其中的单个目标文件 liba.o
中导出的符号,发现 do_something_else
没有匹配项(因为只有 do_something
从 liba.o
).因此,仍然需要解析 do_something_else
,链接器查看下一个库 libb.a
,查看从 libb.o
导出的符号,找到 do_something_else
并因此添加 libb.o
到将被链接的目标文件。 libb.o
包含符号 do_something
的未解析引用,因此链接器会尝试解析它。首先,它在从中获取目标文件 libb.o
的库中查找是否有另一个解析该符号的目标文件。然后它尝试使用下一个库/目标文件之一解析该符号。由于库 libb.a
中既没有另一个目标文件,也没有要查看的其他库/目标文件,因此链接器失败并显示未解析的符号 do_something
.
当您使用订单libb.a liba.a
时:
来自 main.o
的未解析符号 do_something_else
通过包含来自 libb.a
的 libb.o
来解析。这会添加未解析的符号 do_something
,然后可以通过包含 liba.a
中的 liba.o
来解析它。至此,链接成功。
理由
为什么不让链接器在所有库中搜索新的未解析符号?我在这里只能猜测这部分,但也提供一个合理的用例:
按照指定的顺序在库中搜索一次很简单。既要执行,又要理解。另外,考虑一下:
// main.c
#include <stdio.h>
int calculate(int value);
int main() {
printf("%d\n", calculate(42));
}
现在有一个库 libsuperfast.a
,其中包含(在其他代码中)calculate
的实现,它依赖于某些特定于平台的东西,并且仅在该平台上有条件地编译。但是您也想支持其他平台。因此,您手动实现 calculate
并将其放入 libslowashell.a
。链接喜欢
$CC -o app main.o libsuperfast.a libslowashell.a
如果可用,将包括快速代码,否则将包括慢速代码。为了便携性做了类似的事情,例如在 gnulib.
其他解决方案
- 无条件包含整个静态库,使用
-Wl,--whole-archive
。 - 使用libtool。允许您创建静态库和共享库,而不必过多担心平台细节。并为您处理库之间的依赖关系。
在创建
libb.a
时手动解决对liba.a
的依赖。为此,首先从必须解析其对liba.a
的引用的文件创建一个可重定位目标文件,并与liba.a
链接,然后从该部分链接的文件(可能还有其他文件)创建库:ld -r libb.o liba.a -o libb-partial.o ar rcs libb.a libb-partial.o # possibly others
- 使用
-Wl,--start-group libb.a liba.a -Wl,--end-group
。这会导致链接器重复搜索库,直到不再有未解析的符号或不再有可以从库中解析的符号。