歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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代碼,而要用我們自己寫啟動引導代碼。

Linux升級GCC 4.8.1清晰簡明教程(Ubuntu 12.04 64位版為例)  http://www.linuxidc.com/Linux/2014-04/99583.htm

在CentOS 6.4中編譯安裝GCC 4.8.1 + GDB 7.6.1 + Eclipse 在CentOS 6.4中編譯安裝GCC 4.8.1 + GDB 7.6.1 + Eclipse

Ubuntu下Vim+GCC+GDB安裝及使用 http://www.linuxidc.com/Linux/2013-01/78159.htm

Ubuntu下兩個GCC版本切換 http://www.linuxidc.com/Linux/2012-10/72284.htm

CentOS6.5升級手動安裝GCC4.8.2  http://www.linuxidc.com/Linux/2015-01/112595.htm

GCC 的詳細介紹:請點這裡
GCC 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved