1.簡述
嵌入式Linux軟件開發(fā)過程中,需要開發(fā)者自己編寫makefile腳本編譯程序,在編譯程序時,經(jīng)常會遇到“undefined reference to xxx”的 編譯報錯,下面是簡單分析。
2.常見錯誤
- 鏈接時缺少相關(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)文件。
- 鏈接時缺少相關(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ù)是在庫中的,因此鏈接的時候添加庫就能編譯通過
- 鏈接的庫文件中又使用了另一個庫文件
~/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
鏈接的時候需要注意庫之間的依賴順序:依賴其它庫的庫,一定要放在被依賴庫的前面
- 在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個階段:
- 預(yù)處理。處理宏定義等宏命令(如#define等)——生成后綴為“.i”的文件;
- 編譯。將預(yù)處理后的文件轉(zhuǎn)換成匯編語言——生成后綴為“.s”的文件;
- 匯編。由匯編生成的文件翻譯為二進(jìn)制目標(biāo)文件——生成后綴為“.o”的文件;
- 鏈接。多個目標(biāo)文件(二進(jìn)制)結(jié)合庫函數(shù)等綜合成的能直接獨立執(zhí)行的執(zhí)行文件——生成后綴為“.out”的文件。
鏈接的查找順序:
- -L 指定的路徑, 從左到右依次查找
- 由環(huán)境變量LIBRARY_PATH指定的路徑,使用":"分割從左到右依次查找
- /etc/ld.so.conf 指定的路徑順序
- /lib 和 /usr/lib (64位下是/lib64和/usr/lib64)
動態(tài)庫調(diào)用的查找順序:
- ld的-rpath參數(shù)指定的路徑, 寫死在代碼中
- ld腳本指定的路徑
- LD_LIBRARY_PATH 指定的路徑
- /etc/ld.so.conf 指定的路徑
- /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