[英]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
當我嘗試編譯主程序時,我收到一個鏈接錯誤:
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
? 我顯然做錯了什么。
更改指定靜態庫的順序。
對於靜態庫,指定庫的順序很重要。 原因是鏈接器將只包含靜態庫中的目標文件(記住,那些基本上只是目標文件的存檔),如果該目標文件解析了以前未解析的符號。
以你的例子和命令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
:
未解決的符號do_something_else
從main.o
由包括解決libb.o
從libb.a
。 這會添加未解析的符號do_something
,然后可以通過包含liba.o
來liba.a
。 因此,鏈接成功。
為什么不讓鏈接器在所有庫中搜索新的未解析符號? 我只能在這里猜測這一部分,但也提供了一個明智的用例:
按照指定的順序只搜索一次庫是很簡單的。 既要實施也要理解。 另外,考慮一下:
// 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
。 在創建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
。 這會導致鏈接器重復搜索庫,直到沒有更多未解析的符號或者無法從庫中解析出更多符號。 與靜態庫鏈接時 - 位置很重要! 在你的情況下, 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
但是,在這種情況下,整個庫將被鏈接,即使它根本不被使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.