簡體   English   中英

C靜態庫不能互相識別 - 為什么我得到“未定義的對`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

當我嘗試編譯主程序時,我收到一個鏈接錯誤:

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_somethingliba.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_elsemain.o由包括解決libb.olibb.a 這會添加未解析的符號do_something ,然后可以通過包含liba.oliba.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
  • 使用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 這會導致鏈接器重復搜索庫,直到沒有更多未解析的符號或者無法從庫中解析出更多符號。

靜態庫鏈接時 - 位置很重要! 在你的情況下, 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM