sphinx undefined reference to libiconv

今天編譯代碼忽然發現報錯以下: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卸載掉!那就這麼作吧^_^!

相關文章
相關標籤/搜索