gcc 庫的鏈接順序問題


前言

最近MIPS上開發一個程序,需要用到浮點運算。

寫好bootloader,main函數,在main函數調用log浮點運算,包含math庫。

然后再寫好makefile,ld腳本。

gcc的參數用到了:

CFLAGS= -c -march=3081 -msoft-float -fno-inline  $(ENDIAN) -G0

ld的參數用到了:

LDFLAGS= -march=3081 -msoft-float -nostartfiles -lgcc -lm -lc  -Wl,-Map,rlx_test.map

庫的鏈接順序

本文的重點是講述gcc庫的鏈接順序。

剛開始的時候,在鏈接參數部分,我的順序是這么安排的: -lc -lgcc -lm。

結果compile正常,但是在ld的時候,遇到問題了,總是報log函數找不到errno變量。

經過仔細分析,發現我的程序本身是有問題的,運算表達式可以總結為:log(a)。

結果a為0了,log函數會報錯,會將錯誤代碼賦給__errno這個全局變量。而__errno變量是聲明在errno.h頭文件中,但是定義在libc的某個file中。

gcc庫的鏈接順序,官方是這么解釋的:

https://gcc.gnu.org/onlinedocs/gcc-5.2.0/gcc/Link-Options.html#Link-Options

gcc -l 解釋如下:
       -l library
           Search the library named library when linking.  (The second alter-
           native with the library as a separate argument is only for POSIX
           compliance and is not recommended.)

           It makes a difference where in the command you write this option;
           the linker searches and processes libraries and object files in the
           order they are specified.  Thus, foo.o -lz bar.o searches library z
           after file foo.o but before bar.o.  If bar.o refers to functions in
           z, those functions may not be loaded.

這句話翻譯過來的意思就是說,如果你的庫在鏈接時安排的順序是:foo.o -lz bar.o。那么gcc的鏈接器先搜索庫foo,然后是z庫,然后是bar庫。

這樣就帶來一個問題,如果庫bar調用了庫z里面的函數,但是鏈接器是先搜索的庫z,這時候並沒有發現庫bar調用庫z啊,所以就不會把庫z中的這部分函數體挑出來進行鏈接。

而是只把庫z中,被foo庫調用的函數體挑出來。

 

回到我們之前的描述。

由於__errno全局變量是定義在庫libc中,而庫libm中的log函數會訪問__errno全局變量。這就要求鏈接時,libm在libc的前面。所以我們應該這樣安排庫的鏈接順序:

 -lm -lgcc -lc 

一句話,越是被別人調用的越底層的庫,就越放在后面;越是調用別人的越上層的庫,就越放在前面。

庫的說明

下面我們來講講libm,libgcc, libc分別是做什么用的:

libm庫,是數學運算函數的庫,里面包含了各種基本的數學函數實現,例如sin,cos,平方根,log等。

libgcc庫,要想用gcc編譯代碼就需要調用的庫,里面包含了一些基本的函數。參考https://gcc.gnu.org/onlinedocs/gccint/Libgcc.html。例如部分整形/浮點運算,異常處理,一些雜項函數。

libc庫,是c的標准庫,里面包含了基本輸入輸出函數,memcpy之類,string copy之類的函數。

從這些庫的用途,我們就知道為什么要按照-lm -lgcc -lc 的順序調用了。

當然在編譯內核和一些庫時,我們可能會看到nostdlib,這個選項,意思是不調用標准庫libc和libgcc,而是要我們自己去實現這些基本的庫函數被內核函數調用。

一些編譯和鏈接參數的說明

-march=3081,是指定cpu架構是MIPS 3081。

-msoft-float,是針對沒有FPU單元的CPU,編譯浮點運算代碼需要的,也叫軟浮點。

-nostartfiles,是指不用默認的bootload代碼,而要用我們自己寫啟動引導代碼。

 


免責聲明!

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



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