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: [.]
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章