undefined reference問題分析

1.簡述

嵌入式Linux軟件開發(fā)過程中,需要開發(fā)者自己編寫makefile腳本編譯程序,在編譯程序時,經(jīng)常會遇到“undefined reference to xxx”的 編譯報錯,下面是簡單分析。

2.常見錯誤

  1. 鏈接時缺少相關(guān)的目標(biāo)文件
~/linux$ cat main.c 
int main()
{
    test();
}
~/linux$ 
~/linux$ cat test.c 
#include<stdio.h>
void test()
{
    printf("test undefined reference");
}
~/linux$ 
~/linux$ gcc -c test.c 
~/linux$ gcc -c main.c 
main.c: In function ‘main’:
main.c:3:5: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
     test();
     ^
~/linux$ 
~/linux$ gcc -o main main.o
main.o: In function `main':
main.c:(.text+0xa): undefined reference to `test'
collect2: error: ld returned 1 exit status
~/linux$ gcc -o main main.o test.o
~/linux$

比較常見的“undefined reference to”問題,首先編譯main.o的時候已經(jīng)出現(xiàn)告警,此告警一般由下面兩種情況造成的:

  • 沒有把函數(shù)所在的c文件生成.o目標(biāo)文件
  • 在函數(shù)所在的c文件中定義了,但是沒有在與之相關(guān)聯(lián)的.h文件中聲明

在大型的編譯工程中,很容易忽視這種告警問題,到鏈接的時候,出現(xiàn)“undefined reference to”問題。因為,在鏈接的時候發(fā)現(xiàn)找不到某個函數(shù)的實現(xiàn)文件。

  1. 鏈接時缺少相關(guān)的庫文件
~/linux$ cat test.h 
void test();
~/linux$ gcc -c test.c 
~/linux$ ar -rc test.a test.o
~/linux$ gcc -c main.c
main.c: In function ‘main’:
main.c:3:5: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
     test();
     ^
~/linux$ gcc -o main main.o
main.o: In function `main':
main.c:(.text+0xa): undefined reference to `test'
collect2: error: ld returned 1 exit status
~/linux$ gcc -o main main.o ./test.a
~/linux$ 

出現(xiàn)“undefined reference to”問題,主要也是因為找不到test函數(shù)的實現(xiàn),而test函數(shù)是在庫中的,因此鏈接的時候添加庫就能編譯通過

  1. 鏈接的庫文件中又使用了另一個庫文件
~/linux$ ls
func.c  func.h  main.c  test.c  test.h
~/linux$ cat func.c 
#include <stdio.h>

int func()
{
    printf("enter func");
    return 0;
} 
~/linux$ cat func.h
int func();
~/linux$ cat main.c 
#include "test.h"

int main()
{
    test();
}
~/linux$ cat test.c 
#include<stdio.h>
#include"func.h"

void test()
{
    printf("enter test");
    func();
}
~/linux$ 
~/linux$ cat test.h
void test();
~/linux$ 
~/linux$ gcc -c func.c
~/linux$ gcc -c test.c 
~/linux$ gcc -c main.c 
~/linux$ ar -rc func.a func.o
~/linux$ ar -rc test.a test.o
~/linux$ gcc -o main main.o test.a
test.a(test.o): In function `test':
test.c:(.text+0x19): undefined reference to `func'
collect2: error: ld returned 1 exit status
~/linux$ 
~/linux$ gcc -o main main.o test.a func.a

比較隱蔽的一類問題,鏈接的時候,發(fā)現(xiàn)test.a調(diào)用了func()函數(shù),但卻找不到對應(yīng)的實現(xiàn)文件。將func()的實現(xiàn)庫文件添加后編譯可以通過
4.多個庫文件鏈接順序問題

~/linux$ gcc -o main main.o func.a test.a
test.a(test.o): In function `test':
test.c:(.text+0x19): undefined reference to `func'
collect2: error: ld returned 1 exit status

鏈接的時候需要注意庫之間的依賴順序:依賴其它庫的庫,一定要放在被依賴庫的前面

  1. 在C++代碼中連接c語言的庫
~/linux$ cat test.c 
#include<stdio.h>

void test()
{
    printf("enter test");
}
~/linux$ cat test.h 
void test();
~/linux$ cat main.cpp 
#include "test.h"

int main()
{
    test();
    return 1;
}
~/linux$ gcc -c test.c 
~/linux$ ar -rc test.a test.o
~/linux$ g++ -o main main.cpp test.a
/tmp/cced7bc4.o: In function `main':
main.cpp:(.text+0x5): undefined reference to `test()'
collect2: error: ld returned 1 exit status
~/linux$ 
~/linux$ cat main.cpp 
extern "C"
{
#include "test.h"
}

int main()
{
    test();
    return 1;
}
~/linux$ g++ -o main main.cpp test.a

庫文件由c編譯器生成的,在c++代碼中鏈接時,產(chǎn)生“undefined reference to xxx”的問題。解決方法是在c相關(guān)的庫文件的頭文件添加extern “C”的聲明

3.分析

一般而言,在Linux下編譯程序分為以下4個階段:

  1. 預(yù)處理。處理宏定義等宏命令(如#define等)——生成后綴為“.i”的文件;
  2. 編譯。將預(yù)處理后的文件轉(zhuǎn)換成匯編語言——生成后綴為“.s”的文件;
  3. 匯編。由匯編生成的文件翻譯為二進(jìn)制目標(biāo)文件——生成后綴為“.o”的文件;
  4. 鏈接。多個目標(biāo)文件(二進(jìn)制)結(jié)合庫函數(shù)等綜合成的能直接獨立執(zhí)行的執(zhí)行文件——生成后綴為“.out”的文件。

鏈接的查找順序:

  1. -L 指定的路徑, 從左到右依次查找
  2. 由環(huán)境變量LIBRARY_PATH指定的路徑,使用":"分割從左到右依次查找
  3. /etc/ld.so.conf 指定的路徑順序
  4. /lib 和 /usr/lib (64位下是/lib64和/usr/lib64)

動態(tài)庫調(diào)用的查找順序:

  1. ld的-rpath參數(shù)指定的路徑, 寫死在代碼中
  2. ld腳本指定的路徑
  3. LD_LIBRARY_PATH 指定的路徑
  4. /etc/ld.so.conf 指定的路徑
  5. /lib和/usr/lib(64位下是/lib64和/usr/lib64)

對于動態(tài)鏈接庫,實際的符號定位是在運行期進(jìn)行的。在編譯.so的時候,如果沒有把它需要的庫和他一起進(jìn)行聯(lián)編,比如libx.so 需要使用uldict, 但是忘記在編譯libx.so的時候加上-luldict的話,在編譯libx.so的時候不會報錯。因為這個時候libx.so被認(rèn)為是一個庫,它里面存在一些不知道具體實現(xiàn)的符號是合法的,是可以在運行期指定或者編譯另外的二進(jìn)制程序的時候指定。

如果是采用g++ -L path -lx 的方式進(jìn)行編譯,鏈接器會發(fā)現(xiàn)所需要的uldict的符號表找不到而報錯;但是如果是程序采用dlopen的方式載入,由于是運行期,這個程序在這個地方就直接運行報錯了;另外還有一種情況就是一個對外的接口在動態(tài)庫中已經(jīng)聲明定義了,但是忘記實現(xiàn)了,這個時候也會產(chǎn)生類似的錯誤。如果在運行期報出這樣的錯誤,就要注意是否是由于某些庫沒有鏈接進(jìn)來或者某些接口沒有實現(xiàn)的原因產(chǎn)生。

因此,undefined reference error錯誤的原因是:

  • 沒有指定對應(yīng)的庫(.o/.a/.so)。 使用了鏈接庫中定義的實體,但沒有鏈接庫(-lXXX)或者沒有指定庫路徑(-LYYY);
  • 鏈接庫的參數(shù)順序不對。越是基礎(chǔ)的庫,越要寫在后面,無論是靜態(tài)還動態(tài);
  • gcc/ld 版本不匹配。gcc/ld的版本的兼容性問題,當(dāng)在高版本機器上使用低版本的機器編譯的庫,就會導(dǎo)致這樣的錯誤。這個問題比較常見在32位的環(huán)境上,使用了64位的庫,或者反過來64位環(huán)境使用了32位的庫;
  • C/C++相互依賴。后綴為.c的,gcc把它當(dāng)作是C程序(cc/cpp兩個編譯器均判定為c++程序),而g++當(dāng)作是c++程序。使用extern "C"保證g++編譯的C++代碼能夠調(diào)用gcc編譯的c代碼 ;而在我們的64位環(huán)境中g(shù)cc鏈接g++的庫還需要加上 -lstdc++;
  • 運行期報錯。由于程序使用dlopen的方式載入.so庫, 但.so沒有把所有需要的庫都鏈接上。

參考

https://segmentfault.com/a/1190000006049907?utm_source=tuicool&utm_medium=referral

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容