undefined reference to `libiconv


今天編譯代碼突然發現報錯如下:

undefined reference to `libiconv'

查詢網上資料好多都是說sphinx編譯的問題。

這部分代碼之前是可以編譯通過沒有問題的。而我正好前幾天在機器上面嘗試sphinx,重新安裝了libiconv庫。

懷疑跟此有關系。

但是那些都解決不了我的問題了。知道看到下面這個文章,才恍然大悟

http://tonybai.com/2013/04/25/a-libiconv-linkage-problem/

看了自己機器上面iconv.h也有兩份,分別在/usr/include 和/usr/local/include下面。因此確認就是這部分的問題了。

定位到問題之后,馬上決定卸載之前源碼安裝的iconv,(進入源碼目錄 make uninstall)

發現還是編譯錯誤,不過問題編程了/usr/bin/ld cannot find –liconv

這是個好消息,其會自動尋找iconv了(這時我的編譯文件中沒有指定-liconv了)

我就用源碼的那部分生成so文件,然后mv到我的/usr/lib目錄下面

再編譯,ok 問題解決了。 So happy

不過奇怪的是看同事的目錄下都沒有對應的so,不知道怎么回事(奇怪啊。。希望能找到答案)

 

 

與在Solaris系統上不同,Linux的libc庫中包含了libiconv庫中函數的定義,因此在Linux上使用libiconv庫相關函數,編譯時是不需要顯式-liconv的。但最近我的一位同事在某redhat enterprise server 5.6機器上編譯程序時卻遇到了找不到iconv庫函數符號的鏈接問題,到底是怎樣一回事呢?這里分享一下問題查找過程。

一、現場重現

這里借用一下這位同事的測試程序以及那台機器,重現一下問題過程:
/*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了。的確如那位同事所說出現了怪異的情況。

二、現場取證

慣性思維讓我首先提出疑問:難道是這台機器上的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怎么會鏈接到這里了呢?

三、大偵探的分析^_^

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就不足為奇了。

四、問題解決

我們不想顯式的加上-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卸載掉!那就這么做吧^_^!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM