今天編譯代碼忽然發現報錯以下:java
undefined reference to `libiconv'linux
查詢網上資料好多都是說sphinx編譯的問題。c++
這部分代碼以前是能夠編譯經過沒有問題的。而我正好前幾天在機器上面嘗試sphinx,從新安裝了libiconv庫。編程
懷疑跟此有關係。ubuntu
可是那些都解決不了個人問題了。知道看到下面這個文章,才恍然大悟app
http://tonybai.com/2013/04/25/a-libiconv-linkage-problem/jvm
看了本身機器上面iconv.h也有兩份,分別在/usr/include 和/usr/local/include下面。所以確認就是這部分的問題了。函數
定位到問題以後,立刻決定卸載以前源碼安裝的iconv,(進入源碼目錄 make uninstall)測試
發現仍是編譯錯誤,不過問題編程了/usr/bin/ld cannot find –liconvui
這是個好消息,其會自動尋找iconv了(這時個人編譯文件中沒有指定-liconv了)
我就用源碼的那部分生成so文件,而後mv到個人/usr/lib目錄下面
再編譯,ok 問題解決了。 So happy
不過奇怪的是看同事的目錄下都沒有對應的so,不知道怎麼回事(奇怪啊。。但願能找到答案)
與在Solaris系統上不一樣,Linux的libc庫中包含了libiconv庫中函數的定義,所以在Linux上使用libiconv庫相關函數,編譯時是不須要顯式-liconv的。但最近個人一位同事在某redhat enterprise server 5.6機器上編譯程序時卻遇到了找不到iconv庫函數符號的連接問題,究竟是怎樣一回事呢?這裏分享一下問題查找過程。
1、現場重現
這裏借用一下這位同事的測試程序以及那臺機器,重現一下問題過程:
/*test.c */
…
#include <iconv.h>
int main(void)
{
int r;
char *sin, *sout;
size_t lenin, lenout;
char *src = "你好!";
char dst[256] = {0};
iconv_t c_pt;
sin = src;
lenin = strlen(src)+1;
sout = dst;
lenout = 256;
if ((c_pt = iconv_open("UTF-8", "GB2312")) == (iconv_t)(-1)){
printf("iconv_open error!. errno[%d].\n", errno);
return -1;
}
if ((r = iconv(c_pt, (char **)&sin, &lenin, &sout, &lenout)) != 0){
printf("iconv error!. errno[%d].\n", r);
return -1;
}
iconv_close(c_pt);
printf("SRC[%s], DST[%s].\n", src, dst);
return 0;
}
根據以前的經驗,咱們按以下命令編譯該程序:
$> gcc -g -o test test.c
/tmp/ccyQ5blC.o: In function `main':
/home/tonybai/tmp/test.c:28: undefined reference to `libiconv_open'
/home/tonybai/tmp/test.c:33: undefined reference to `libiconv'
/home/tonybai/tmp/test.c:38: undefined reference to `libiconv_close'
咦,這是咋搞的呢?怎麼找不到iconv庫的符號!!!顯式加上iconv的連接指示再試試。
$> gcc -g -o test test.c -liconv
這回編譯OK了。的確如那位同事所說出現了怪異的狀況。
2、現場取證
慣性思惟讓我首先提出疑問:難道是這臺機器上的libc版本有差別,檢查一下libc中是否認義了iconv相關符號。
$ nm /lib64/libc.so.6 |grep iconv
000000397141e040 T iconv
000000397141e1e0 T iconv_close
000000397141ddc0 T iconv_open
iconv的函數都定義了呀!怎麼會連接不到?
咱們再來看看已經編譯成功的那個test到底鏈接到哪一個iconv庫了。
$ ldd test
linux-vdso.so.1 => (0x00007fff77d6b000)
libiconv.so.2 => /usr/local/lib/libiconv.so.2 (0x00002abbeb09e000)
libc.so.6 => /lib64/libc.so.6 (0×0000003971400000)
/lib64/ld-linux-x86-64.so.2 (0×0000003971000000)
哦,系統裏竟然在/usr/local/lib下面單獨安裝了一份libiconv。gcc顯然是連接到這裏的libiconv了,但gcc怎麼會連接到這裏了呢?
3、大偵探的分析^_^
Gcc到底作了什麼呢?咱們看看其verbose的輸出結果。
$ gcc -g -o test test.c -liconv -v
使用內建 specs。
目標:x86_64-redhat-linux
配置爲:../configure –prefix=/usr –mandir=/usr/share/man –infodir=/usr/share/info –enable-shared –enable-threads=posix –enable- checking=release –with-system-zlib –enable-__cxa_atexit –disable-libunwind-exceptions –enable-libgcj-multifile –enable-languages=c,c++, objc,obj-c++,java,fortran,ada –enable-java-awt=gtk –disable-dssi –disable-plugin –with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre –with-cpu=generic –host=x86_64-redhat-linux
線程模型:posix
gcc 版本 4.1.2 20080704 (Red Hat 4.1.2-50)
/usr/libexec/gcc/x86_64-redhat-linux/4.1.2/cc1 -quiet -v test.c -quiet -dumpbase test.c -mtune=generic -auxbase test -g -version -o /tmp/ ccypZm0v.s
忽略不存在的目錄「/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../x86_64-redhat-linux/include」
#include "…" 搜索從這裏開始:
#include <…> 搜索從這裏開始:
/usr/local/include
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/include
/usr/include
搜索列表結束。
GNU C 版本 4.1.2 20080704 (Red Hat 4.1.2-50) (x86_64-redhat-linux)
由 GNU C 版本 4.1.2 20080704 (Red Hat 4.1.2-50) 編譯。
GGC 準則:–param ggc-min-expand=100 –param ggc-min-heapsize=131072
Compiler executable checksum: ef754737661c9c384f73674bd4e06594
as -V -Qy -o /tmp/ccaqvDgX.o /tmp/ccypZm0v.s
GNU assembler version 2.17.50.0.6-14.el5 (x86_64-redhat-linux) using BFD version 2.17.50.0.6-14.el5 20061020
/usr/libexec/gcc/x86_64-redhat-linux/4.1.2/collect2 –eh-frame-hdr -m elf_x86_64 –hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so. 2 -o test /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crti.o /usr/ lib/gcc/x86_64-redhat-linux/4.1.2/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2 -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2 -L/usr/lib/gcc/ x86_64-redhat-linux/4.1.2/../../../../lib64 -L/lib/../lib64
-L/usr/lib/../lib64 /tmp/ccaqvDgX.o -liconv -lgcc –as-needed -lgcc_s –no-as-needed -lc -lgcc –as-needed -lgcc_s –no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crtn.o
從這個結果來看,gcc在search iconv.h這個頭文件時,首先找到的是/usr/local/include/iconv.h,而不是/usr/include/iconv.h。這兩個文件有啥不一樣麼?
在/usr/local/include/iconv.h中,我找到以下代碼:
…
#ifndef LIBICONV_PLUG
#define iconv_open libiconv_open
#endif
extern iconv_t iconv_open (const char* tocode, const char* fromcode);
…
libiconv_open vs iconv_open,臥槽!!!再對比一下前面編譯時輸出的錯誤信息:
/tmp/ccyQ5blC.o: In function `main':
/home/tonybai/tmp/test.c:28: undefined reference to `libiconv_open'
/home/tonybai/tmp/test.c:33: undefined reference to `libiconv'
/home/tonybai/tmp/test.c:38: undefined reference to `libiconv_close'
大偵探醒悟了!大偵探帶你還原一下真實狀況。
咱們在執行gcc -g -o test test.c時, 根據gcc -v中include search dir的順序,gcc首先search到的是/usr/local/include/iconv.h,而這裏iconv_open等函數被預編譯器替換成 了libiconv_open等加上了lib前綴的函數,而這些函數符號顯然在libc中是沒法找到的,libc中只有不帶lib前綴的 iconv_open等函數的定義。大偵探也是一時眼拙了,沒有細緻查看gcc的編譯錯誤信息中的內容,這就是問題所在!
而gcc -g -o test test.c -liconv爲什麼能夠順利編譯經過呢?gcc是如何找到/usr/local/lib下的libiconv的呢?大偵探再次爲你們還原一下真相。
咱們在執行gcc -g -o test test.c -liconv時,gcc同 樣首先search到的是/usr/local/include/iconv.h,而後編譯test.c源碼,ok;接下來啓動ld程序進行連接;ld找 到了libiconv,ld是怎麼找到iconv的呢,libiconv在/usr/local/lib下,ld顯然是到這個目錄下search了。咱們 經過執行下面命令能夠知曉ld的默認搜索路徑:
$> ld -verbose|grep SEARCH
SEARCH_DIR("/usr/x86_64-redhat-linux/lib64"); SEARCH_DIR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/x86_64-redhat-linux/lib"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
ld的默認search路徑中有/usr/local/lib(我以前一直是覺得/usr/local/lib不是gcc/ld的默認搜索路徑的),所以找到libiconv就不足爲奇了。
4、問題解決
咱們不想顯式的加上-liconv,那如何解決這個問題呢?咱們是否能夠強制gcc先找到/usr/include/iconv.h呢?咱們先來作個試驗。
$ gcc -g -o test test.c -liconv -I ~/include -v
…
忽略不存在的目錄「/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../x86_64-redhat-linux/include」
#include "…" 搜索從這裏開始:
#include <…> 搜索從這裏開始:
/home/tonybai/include
/usr/local/include
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/include
/usr/include
搜索列表結束。
…
試驗結果彷佛讓咱們以爲可行,咱們經過-I指定的路徑被放在了第一的位置進行search。咱們來嘗試一下強制gcc先search /usr/include。
$ gcc -g -o test test.c -I ~/include -v
…
忽略不存在的目錄「/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../x86_64-redhat-linux/include」
忽略重複的目錄「/usr/include」
由於它是一個重複了系統目錄的非系統目錄
#include "…" 搜索從這裏開始:
#include <…> 搜索從這裏開始:
/usr/local/include
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/include
/usr/include
搜索列表結束。
…
糟糕!/usr/include被忽略了!仍是從/usr/local/include開始,方案失敗。
彷佛剩下的惟一方案就是將/usr/local/lib下的那份libiconv卸載掉!那就這麼作吧^_^!