Linux嵌入式開發日常技術總結(2) 編譯鏈接

編譯鏈接

gcc的CFLAGS/CXXFLAGS編譯選項

更多信息參考: man gcc.

-Wl,option1,option2,option3,value3,option4=value4[:value5:value6],option5

將選項從gcc傳遞給linker,例如:

-Wl,-Map,output.map 
將 -Map output.map 傳遞給linker。

當時用GNU的linker的時候,你也可以使用 -Wl,-Map=output.map

-std=<standard>

決定選擇的語言標準,例如:

=std=gnu++0x=

這個與 std=c++11 類似,2011 ISO 標準C++修改案。包含一些特定的語法,如: nullptr, 或 enum class {...};.

-fno-rtti

取消C++運行時動態類型診斷特性(rtti),(i.e. dynamic_cast 以及 typeid)

-fno-exceptions

取消C++對異常處理的支持。(i.e. 類似 try{...} catch {...} 的語法)

-l<libname>

鏈接時,搜索庫名稱 libname 。以庫文件 liba.so.1 爲例,

  • libname 是庫的linker name。這裏是: a

  • soname 是運行時搜索的動態庫,這裏是: liba.so

  • realname 是庫對應的實際庫文件,這裏是: liba.so.1

    因爲linker name是 a, 主要用於編譯之後的鏈接,所以我們在編譯傳遞選項的時候,使用linkername, 即: -la

    我們也需要注意 -l 選項在 CFLAGS/CXXFLAGS 中的位置,例如:如果 foo.o 引用了 z 中的一個函數,如果選項位置不對,那些函數可能不會被加載。

    foo.o -lz bar.o
    

    因爲, z 庫的搜索是在 bar.o 之前,但是卻在 foo.o 之後。

-L<libdir>

指定編譯結束前的鏈接階段時,依賴庫的搜索路徑。即: 將 libdir 目錄添加至 -l 所指定的依賴庫文件的搜索路徑。

例如:

-L./lib/ -L./

將會吧 ./lib./ 添加到待鏈接的庫的搜索路徑。

-g

生成調試信息。生成調試信息之後, gdb 可以利用調試信息進行調試,這些調試信息也可以通過 strip 命令移除。

-W<warnname>

產生 <warn> 的警告。

例如:

-Wunused-macros
-Werror
-Wall

這裏,

  • -Wunused-macros 會在一些宏沒有使用的情況下,在編譯期間產生警告。
  • -Werror 會將所有的警告信息看做編譯錯誤。
  • -Wall 會打開一些用戶覺得有問題並且容易避免的 <warname> 產生警告,但是它並不是將所有的 <warnname> 打開。

庫文件的鏈接

更多的信息參考: man ld, 尤其是對 -rpath-rpath-link 選項的相關部分。

搜索規則

當加載庫的時候(例如 libm.so ,可能是動態加載庫,或者通過 dlopen() 函數加載庫)

將使用如下的過程對這個 libm.so 庫進行搜索:

假設:
libm.so被libn.so加載(即,libn.so->libm.so)。
libn-1.so被libn-2.so加載(即,libn-2.so->libn-1.so)。
......
lib1.so被可執行文件a.out加載(即,a.out->lib1.so),
或者lib1.so被另外一個二進制庫liba.so加載,而liba.so是通過dlopen("liba.so")加載的(即,liba.so->lib1.so)。

1. 如果libn.so具有RUNPATH值,(通過readelf -d可以檢查), 轉至步驟8。
2. 否則(即libn.so無RUNPATH),搜索libn.so的RPATH值所表示的路徑。
3. 如果加載libn.so的加載者(即libn-1.so)具有RUNPATH值,則轉至步驟5。
4. 否則(即libn-1.so無RUNPATH),搜索libn-1.so的RPATH值所表示的路徑。
5. 重複步驟3~4,搜索libn-2.so,libn-3.so,...(如果無RUNPATH)...的RPATH值所表示的路徑,直至到達lib1.so(即搜索完lib1.so的RPATH)
6. 如果lib1.so的加載者(a.out或liba.so)具有RUNPATH,轉至步驟8。
7. 否則(無RUNPATH)搜索lib1.so加載者(即,a.out或liba.so)的RPATH值鎖表示的路徑。

8. 搜索 LD_LIBRARY_PATH 表示的路徑(環境變量)。
9. 搜索 libn.so的RUNPATH所表示的路徑(可通過readelf命令手動查看)。
10. 搜索默認目錄(如:/usr/lib, /lib, 等,這些路徑取決於在生成編譯器時對編譯器的配置選項是如何指定的,可以通過gcc -v來查看)
11. 搜索ld.so.conf所指定的路徑(每次更新需通過ldconfig將其值存於ld.so.cache,可通過strings命令對ld.so.cache進行檢查)

注意,當尋找到 libm.so 的時候,整個的搜索過程將會停止。

我們可以看到,

  1. 期間涉及的所有層級的庫文件的RUNPATH將會導致該庫的RPATH被禁用。
  2. 每次都儘可能的搜索完最底層庫(最接近libm.so)的所有可能路徑,再搜索更高層次。

LD_LIBRARY_PATH

LD_LIBRARY_PATH,是用於搜索庫文件(即lib*.so)的環境變量,在動態加載或 dlopen() 的時候會使用到它。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GSTDIR/lib

-rpath, -rpath-link, --enable-new-dtags

鏈接其用於操作 RPATHRUNPATH 的選項

  • -rpath 用於庫的 DT_RPATH

  • -rpath... --enable-new-dtags 用於庫的 DT_RUNPATH

如果 DT_RUNPATH 存在,那麼 DT_RPATH 會被忽略。

-rpath-link-rpath 一樣,不同在於 -rpath-link 是用於鏈接期間,而 -rpath 適用於運行時的鏈接。

如果 -rpath-rpath-link 沒有被指定,那麼可以通過 LD_RUN_PATH 環境變量來指定其值。

這個選項可以指定一系列的目錄名稱,通過冒號分割的名稱列表或者通過多次選項指定都可以。

LIBS    += -Wl,-rpath,/data/3rd/widevine_eme:/data/3rd/browser_engine:/3rd/widevine_eme:/3rd/browser_engine
LIBS    += -Wl,-rpath-link=$(BROWSER_ENG_PATH),-rpath-link=/data/3rd/browser_engine:/3rd/browser_engine

舉例

假設有如下依賴關係

a.out -> liba.so
liba.so -> libb.so

path:
./a.out
./liba.so
./libb.so
/usr/lib/libb.so

默認情況,取決於 gcc -v 看到的編譯器配置選項(使用 /usr/lib/libb.so)

$gcc main.c

如有有 LD_LIBRARY_PATH 將優先採用之。

使用 RPATH, 指定 ./libb.so (使用 ./libb.so)

$gcc main.c -Wl,--rpath=.

這樣, RPATH 的搜索會先於 LD_LIBRARY_PATH (和默認搜索), 可能導致 LD_LIBRARY_PATH (和默認搜索)被覆蓋。

使用 RUNPATH

$gcc main.c -ldl -Wl,--rpath=.,--enable-new-dtags
$export LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH

這會導致 RPATH 的搜索被忽略,進而使得 LD_LIBRARY_PATH 的搜索(或默認搜索)起作用( /usr/lib/libb.so)。

binutils

objdump

-x

顯示所有可用頭部信息,包括符號表,或者重定位位置。 (需要打開 gcc 的 -g

$objdump -x browser
./browser: file format elf32-little
architecture: UNKNOWN!, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x000100d8

Program Header:
0x70000001 off    0x000312f0 vaddr 0x000392f0 paddr 0x000392f0 align 2**2
         filesz 0x00000790 memsz 0x00000790 flags r--
  ...
Dynamic Section:
  NEEDED               libAvodIpc.so
  NEEDED               libAvodQos.so
  NEEDED               libSSPK.so
  ...
  RPATH                ../browser_engine:/3rd/widevine_eme
  ...
SYMBOL TABLE:
...
00032ee8 l     O .rodata        00000012              _ZZ17prof_get_key_infoE12__FUNCTION__
000105b8 l       .text  00000000              $a
00010a90 l       .text  00000000              $d
00032efc l     O .rodata        00000012              _ZZ17prof_load_profileE12__FUNCTION__
...

-T

打印動態符號表位置中的信息. 這個一般被動態鏈接對象使用,與 nm -D 的輸出類似。(可能需要打開 gcc 的 -g

$objdump -T libbrowser_engine.so
libbrowser_engine.so:     file format elf32-little

DYNAMIC SYMBOL TABLE:
00010d98 l    d  .init  00000000              .init
000fd50c l    d  .jcr   00000000              .jcr
00000000      DF *UND*  00000000  GLIBC_2.4   signal
...
00041824 g    DF .text  00000330  BWS_ENGINE  _Z24x_browser_set_default_fsh
00000000      D  *UND*  00000000              x_sema_delete
0022a7d4 g    DO .bss   00000004  BWS_ENGINE  iBasePiStrmDbgEnable
00000000      D  *UND*  00000000              _ZN5opera5OperaC1ERKS0_
...

-t

打印文件符號表所在位置包含的信息,類似 nm 。(可能需要打開 gcc 的 -g

$objdump -t browser
browser:     file format elf32-little

SYMBOL TABLE:
00008134 l    d  .interp        00000000              .interp
00008148 l    d  .note.ABI-tag  00000000              .note.ABI-tag
00008168 l    d  .hash  00000000              .hash
...
00032ee8 l     O .rodata        00000012              _ZZ17prof_get_key_infoE12__FUNCTION__
000105b8 l       .text  00000000              $a
00010a90 l       .text  00000000              $d
00032efc l     O .rodata        00000012              _ZZ17prof_load_profileE12__FUNCTION__
...

nm

參見前面部分解釋,這個命令主要檢查elf格式文件的符號定義。

ldd

主要用於檢查文件所依賴的庫,交叉編譯中需使用 arm-xxx-ldd 類似的命令。

strip

可以將elf文件中所有調試信息(這些信息是在用 -ggcc 命令編譯時生成的)去掉。

readelf

查看 RPATHRUNPATH

$ readelf -d a.out
  1. 沒有 RPATH&RUNPATH的情況

    Dynamic section at offset 0xf20 contains 21 entries:
      Tag        Type                         Name/Value
     0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
     0x00000001 (NEEDED)                     Shared library: [libc.so.6]
     0x0000000c (INIT)                       0x8048360
    ...
    
  2. 有RPATH的情況

    Dynamic section at offset 0xf18 contains 22 entries:
      Tag        Type                         Name/Value
     0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
     0x00000001 (NEEDED)                     Shared library: [libc.so.6]
     0x0000000f (RPATH)                      Library rpath: [.]
     0x0000000c (INIT)                       0x8048360
    ....
    
  3. 有RUNPATH的情況

    Dynamic section at offset 0xf10 contains 23 entries:
      Tag        Type                         Name/Value
     0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
     0x00000001 (NEEDED)                     Shared library: [libc.so.6]
     0x0000000f (RPATH)                      Library rpath: [.]
     0x0000001d (RUNPATH)                    Library runpath: [.]
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章