gcc選項-g與-rdynamic的異同


https://blog.csdn.net/bobbypollo/article/details/79888753
注意,這是
鏈接選項,而不是編譯選項。

在將c文件編譯成.o的過程中,使用rdynamic是沒有任何效果的。

只有將.o鏈接成elf時,才有效果。

這主要是對可執行程序而言的,而編譯動態庫時,即使沒有rdynamic選項,默認也會將非靜態函數放入動態符號表中(刻意隱藏的函數除外)。

一個驗證方法

顯示可執行程序文件內的動態符號(注意,僅僅是動態符號):

readelf -Ds a.out

默認情況下,可執行程序(非動態庫)文件內我們定義的非靜態函數,是不放到動態符號表中的,鏈接時只有加上-rdynamic才能將所有非靜態函數加到動態符號表中。

但qin_dev中編譯生成的resource_manager,不加rdynamic的情況下,里面依然會有大量動態符號,why?什么情況下,即使沒有-rdynamic選項,可執行程序內的很多函數也放到動態符號表中???

在《深入理解計算機系統》7.11節中,有如下描述:

“dlopen函數加載和鏈接共享庫filename。用已用帶RTLD_GLOBAL選項打開了的庫解析filename中的外部符號。

如果當前可執行文件是帶-rdynamic選項編譯的,那么對於符號解析而言,它的全局符號也是可用的。”

1.這段話中“用已用帶RTLD_GLOBAL選項打開了的庫”一句,感覺非常拗口,請問如何理解?

2.外部符號的定義是:由其他模塊定義並被模塊m引用的全局符號。我不理解在什么情況下,一個可執行文件會定義一些在動態庫中被引用的全局變量?能否舉個例子?

3.按照我目前的理解:若可執行文件A調用了動態庫B,而A與B均引用新的動態庫C,那么在庫C中的全局變量t,在linux環境下,A與B應將t視為同一份,那么-rdynamic是否在linux下是無用的呢?
https://www.cnblogs.com/lidabo/p/6203397.html
遇到如下情況,主程序通過dlopen來打開.so文件,但是.so用到了主程序的log函數。

編譯so時,通過引用主程序頭文件來編譯通過,頭文件有log函數聲明:

extern "C" {
void print()
}

在主程序的.c文件里有函數的具體實現。

但是dlopen后運行so中函數時,出現找不到相應的symbol。

這時候就需要在編譯主程序ld時加上參數-rdynamic,該參數的作用是:將指示連接器把所有符號(而不僅僅只是程序已使用到的外部符號,但不包括靜態符號,比如被static修飾的函數)都添加到動態符號表(即.dynsym表)里,以便那些通過dlopen()或backtrace()(這一系列函數使用.dynsym表內符號)這樣的函數使用。

-rdynamic
Pass the flag ‘-export-dynamic’ to the ELF linker, on targets that support
it. This instructs the linker to add all symbols, not only used ones, to the
dynamic symbol table. This option is needed for some uses of dlopen or to
allow obtaining backtraces from within a program.

-g是編譯選項,而-rdynamic是鏈接選項

參考:http://www.lenky.info/archives/2013/01/2190

小例子:

a.cc

[cpp] view plain copy

include "stdio.h"

include <dlfcn.h>

extern "C" {
void print()
{
printf("I am in so file!\n");
}

void fun()
{
void * err = dlopen("./libtmp.so", RTLD_LAZY);
printf("dlopen = %p\n", err);
if (err == NULL) {
printf("err=%s\n", dlerror());
}
}
}

a.h

[cpp] view plain copy

extern "C" void print();
extern "C" void fun(); // 函數聲明和定義都要有extern “C”,或者都沒有,否則調用時出現undefined symbol fun

define NODE_MODULE \

extern "C" { \
static void print_main() attribute((constructor)) // dlopen時會自動調用該contructor函數
static void print_main() { \
print(); \
} \
}

so.cc

[cpp] view plain copy

include "a.h"

include "stdio.h"

NODE_MODULE

foo.h

[python] view plain copy

void foo();

foo.cc

[cpp] view plain copy

include "stdio.h"

void foo()
{
printf("foo === \n");
}

main.cc

[cpp] view plain copy

include "a.h"

int main(void)
{
fun();
return 0;
}
Makefile

[cpp] view plain copy

all:dynamic

libtmp.so:so.cc
g++ -fPIC -shared -o $@ $^

a.o:
g++ -c a.cc -fPIC

liba.a:a.o
ar -r $@ $^

libso.so: foo.cc liba.a
g++ -fPIC -shared -o $@ $< -L./ -la -Wl,--whole-archive -la -Wl,--no-whole-archive -ldl

dynamic:libso.so libtmp.so
g++ -o $@ main.cc -Wl,--rpath=. -L./ -lso rdynamic

clean:
rm dynamic liba.a a.o libtmp.so

運行dynamic后輸出為:

[python] view plain copy

I am in so file!
dlopen = 0xdeb030
如果沒有-rdynamic,則輸出為:

[python] view plain copy

dlopen = (nil)
err=./libtmp.so: undefined symbol: print
如果沒有-Wl,--whole-archive -la -Wl,--no-whole-archive,也會有錯誤:undefined symbol: print

--whole-archive 可以把 在其后面出現的靜態庫包含的函數和變量輸出到動態庫,--no-whole-archive 則關掉這個特性

使用readelf -s libso.so | grep fun來查看libso.so的符號表里是否有fun這個函數暴露出來。有--whole-archive的可以查到fun,而沒有--whole-archive的,則找不到fun

先理清一下code

可執行文件dynamic依賴與libso.so,而libso.so有包含liba.a,在liba.a的函數fun調用dlopen來打開libtmp.so

主函數調用liba.a的函數來打開libtmp.so

-fvisibility=hidden

  設置默認的ELF鏡像中符號的可見性為隱藏。使用這個特性可以非常充分的提高連接和加載共享庫的性能,生成更加優化的代碼,提供近乎完美的API輸出和防止符號碰撞。我們強烈建議你在編譯任何共享庫的時候使用該選項。

-fvisibility-inlines-hidden

    默認隱藏所有內聯函數,從而減小導出符號表的大小,既能縮減文件的大小,還能提高運行性能,我們強烈建議你在編譯任何共享庫的時候使用該選項

所以編譯的時候也不能有-fvisibility=hidden和-fvisibility-inlines-hidden。如果有,也會在dlopen時造成錯誤:undefined symbol

總結:

本實例雖小,但用到了不少編譯選項

a: attribute((constructor))
主程序main函數之前被執行或dlopen時被執行

b: -rdynamic

ld時將動態庫的的所有符號都輸出到符號表,以便dlopen和backtrace也能調用

c: --whole-archive -la -Wl,--no-whole-archive

靜態庫的符號導入到動態庫的符號表中,默認是hidden的

d: -fvisibility=hidden和-fvisibility-inlines-hidden

ELF鏡像中符號的可見性為隱藏(在實驗過程中不太好用,待研究)

在編譯nodejs第三方模塊時都會碰到這樣的問題,第三方模塊依賴與nodejs進行編譯,而第三方模塊又是通過dlopen來打開的,這就要求nodejs編譯時將一下第三方模塊需要的函數都暴露出來。

參考:

http://www.fx114.net/qa-225-106759.aspx

http://os.chinaunix.net/a2010/0112/1060/000001060902_3.shtml

https://www.cnblogs.com/LiuYanYGZ/p/5550544.html
https://www.it1352.com/358375.html
摘自http://www.tuicool.com/articles/EvIzUn

gcc選項-g與-rdynamic的異同
gcc 的 -g ,應該沒有人不知道它是一個調試選項,因此在一般需要進行程序調試的場景下,我們都會加上該選項,並且根據調試工具的不同,還能直接選擇更有針對性的說明,比如 -ggdb 。-g是一個編譯選項,即在源代碼編譯的過程中起作用,讓gcc把更多調試信息(也就包括符號信息)收集起來並將存放到最終的可執行文件內。
相比-g選項, -rdynamic 卻是一個 連接選項 ,它將指示連接器把所有符號(而不僅僅只是程序已使用到的外部符號)都添加到動態符號表(即.dynsym表)里,以便那些通過 dlopen() 或 backtrace() (這一系列函數使用.dynsym表內符號)這樣的函數使用。

看示例:

[root@www c]# cat t.c

include <stdio.h>

void bar() {}
void baz() {}
void foo() {}
int main() { foo(); printf("test"); return 0; }
對於上面的示例代碼,普通和加-g編譯:

[root@www c]# uname -a
Linux www.t1.com 2.6.38.8 #2 SMP Wed Nov 2 07:52:53 CST 2011 x86_64 x86_64 x86_64 GNU/Linux
[root@www c]# gcc -O0 -o t t.c
[root@www c]# gcc -O0 -g -o t.g t.c
[root@www c]# readelf -a t > t.elf
[root@www c]# readelf -a t.g > t.g.elf
[root@www c]# ls -lh *.elf t t.g
-rwxr-xr-x. 1 root root 6.6K Jul 24 06:50 t
-rw-r--r--. 1 root root 15K Jul 24 06:51 t.elf
-rwxr-xr-x. 1 root root 7.9K Jul 24 06:50 t.g
-rw-r--r--. 1 root root 16K Jul 24 06:51 t.g.elf
加-g編譯后,因為包含了debug信息,因此生成的可執行文件偏大(程序本身非常小,所以增加的調試信息不多)。
看-g編譯的符號表:

[root@www c]# readelf -s t

Symbol table '.dynsym' contains 4 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)

Symbol table '.symtab' contains 67 entries:
Num: Value Size Type Bind Vis Ndx Name
...
48: 00000000004003e0 0 FUNC GLOBAL DEFAULT 13 _start
49: 00000000004004c4 6 FUNC GLOBAL DEFAULT 13 bar
...
53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND putchar@@GLIBC_2.2.5
54: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _libc_start_main@@GLIBC
55: 00000000004005e8 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
56: 00000000004004d0 6 FUNC GLOBAL DEFAULT 13 foo
...
64: 00000000004004d6 31 FUNC GLOBAL DEFAULT 13 main
65: 0000000000400390 0 FUNC GLOBAL DEFAULT 11 _init
66: 00000000004004ca 6 FUNC GLOBAL DEFAULT 13 baz
注意.dynsym表,只有該程序用到的幾個外部動態符號存在。
加-rdynamic選項編譯,readelf查看:

[root@www c]# gcc -O0 -rdynamic -o t.rd t.c
[root@www c]# readelf -s t.rd

Symbol table '.dynsym' contains 20 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
5: 0000000000400724 6 FUNC GLOBAL DEFAULT 13 bar
6: 0000000000400730 6 FUNC GLOBAL DEFAULT 13 foo
7: 0000000000600b68 0 NOTYPE GLOBAL DEFAULT 24 __data_start
8: 0000000000600b80 0 NOTYPE GLOBAL DEFAULT ABS _end
9: 0000000000600b6c 0 NOTYPE GLOBAL DEFAULT ABS _edata
10: 0000000000600b68 0 NOTYPE WEAK DEFAULT 24 data_start
11: 0000000000400640 0 FUNC GLOBAL DEFAULT 13 _start
12: 0000000000400848 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
13: 0000000000400770 137 FUNC GLOBAL DEFAULT 13 __libc_csu_init
14: 0000000000600b6c 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
15: 0000000000400736 39 FUNC GLOBAL DEFAULT 13 main
16: 00000000004005f0 0 FUNC GLOBAL DEFAULT 11 _init
17: 0000000000400760 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
18: 0000000000400838 0 FUNC GLOBAL DEFAULT 14 _fini
19: 000000000040072a 6 FUNC GLOBAL DEFAULT 13 baz

Symbol table '.symtab' contains 67 entries:
Num: Value Size Type Bind Vis Ndx Name
...
50: 0000000000400640 0 FUNC GLOBAL DEFAULT 13 _start
51: 0000000000400724 6 FUNC GLOBAL DEFAULT 13 bar
...
55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND putchar@@GLIBC_2.2.5
56: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _libc_start_main@@GLIBC
57: 0000000000400848 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
58: 0000000000400730 6 FUNC GLOBAL DEFAULT 13 foo
...
64: 0000000000400736 31 FUNC GLOBAL DEFAULT 13 main
65: 00000000004005f0 0 FUNC GLOBAL DEFAULT 11 _init
66: 000000000040072a 6 FUNC GLOBAL DEFAULT 13 baz
[root@www c]#
可以看到添加-rdynamic選項后,.dynsym表就包含了所有的符號,不僅是已使用到的外部動態符號,還包括本程序內定義的符號,比如bar、foo、baz等。
.dynsym表里的數據並不能被strip掉:

[root@www c]# strip t.rd
[root@www c]# readelf -s t.rd

Symbol table '.dynsym' contains 20 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
5: 0000000000400724 6 FUNC GLOBAL DEFAULT 13 bar
6: 0000000000400730 6 FUNC GLOBAL DEFAULT 13 foo
7: 0000000000600b68 0 NOTYPE GLOBAL DEFAULT 24 __data_start
8: 0000000000600b80 0 NOTYPE GLOBAL DEFAULT ABS _end
9: 0000000000600b6c 0 NOTYPE GLOBAL DEFAULT ABS _edata
10: 0000000000600b68 0 NOTYPE WEAK DEFAULT 24 data_start
11: 0000000000400640 0 FUNC GLOBAL DEFAULT 13 _start
12: 0000000000400848 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
13: 0000000000400770 137 FUNC GLOBAL DEFAULT 13 __libc_csu_init
14: 0000000000600b6c 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
15: 0000000000400736 39 FUNC GLOBAL DEFAULT 13 main
16: 00000000004005f0 0 FUNC GLOBAL DEFAULT 11 _init
17: 0000000000400760 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
18: 0000000000400838 0 FUNC GLOBAL DEFAULT 14 _fini
19: 000000000040072a 6 FUNC GLOBAL DEFAULT 13 baz
簡單總結一下-g選項與-rdynamic選項的差別:
1,-g選項新添加的是調試信息(一系列.debug_xxx段),被相關調試工具,比如gdb使用,可以被strip掉。

2,-rdynamic選項新添加的是動態連接符號信息,用於動態連接功能,比如dlopen()系列函數、backtrace()系列函數使用,不能被strip掉,即強制strip將導致程序無法執行:

[root@www c]# ./t.rd
test[root@www c]# strip -R .dynsym t.rd
[root@www c]# ./t.rd
./t.rd: relocation error: ./t.rd: symbol , version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference
[root@www c]#
3,.symtab表在程序加載時會被加載器 丟棄 ,gdb等調試工具由於可以直接訪問到磁盤上的二進制程序文件:

[root@www c]# gdb t.g -q
Reading symbols from /home/work/dladdr/c/t.g...done.
(gdb)
因此可以使用所有的調試信息,這包括.symtab表;而backtrace()系列函數作為程序執行的邏輯功能,無法去讀取磁盤上的二進制程序文件,因此只能使用.dynsym表。
其它幾個工具可以動態指定查看,比如nm、objdump:

[root@www c]# nm t.rd
nm: t.rd: no symbols
[root@www c]# nm -D t.rd
0000000000400848 R _IO_stdin_used
w _Jv_RegisterClasses
0000000000600b6c A __bss_start
0000000000600b68 D __data_start
w gmon_start
0000000000400760 T __libc_csu_fini
0000000000400770 T __libc_csu_init
U __libc_start_main
0000000000600b6c A _edata
0000000000600b80 A _end
0000000000400838 T _fini
00000000004005f0 T _init
0000000000400640 T _start
0000000000400724 T bar
000000000040072a T baz
0000000000600b68 W data_start
0000000000400730 T foo
0000000000400736 T main
U printf
[root@www c]#
[root@www c]# objdump -T t.rd

t.rd: file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000 DF UND 0000000000000000 GLIBC_2.2.5 printf
0000000000000000 w D UND 0000000000000000 gmon_start
0000000000000000 w D UND 0000000000000000 _Jv_RegisterClasses
0000000000000000 DF UND 0000000000000000 GLIBC_2.2.5 __libc_start_main
0000000000400724 g DF .text 0000000000000006 Base bar
0000000000400730 g DF .text 0000000000000006 Base foo
0000000000600b68 g D .data 0000000000000000 Base __data_start
0000000000600b80 g D ABS 0000000000000000 Base _end
0000000000600b6c g D ABS 0000000000000000 Base _edata
0000000000600b68 w D .data 0000000000000000 Base data_start
0000000000400640 g DF .text 0000000000000000 Base _start
0000000000400848 g DO .rodata 0000000000000004 Base _IO_stdin_used
0000000000400770 g DF .text 0000000000000089 Base __libc_csu_init
0000000000600b6c g D ABS 0000000000000000 Base __bss_start
0000000000400736 g DF .text 0000000000000027 Base main
00000000004005f0 g DF .init 0000000000000000 Base _init
0000000000400760 g DF .text 0000000000000002 Base __libc_csu_fini
0000000000400838 g DF .fini 0000000000000000 Base _fini
000000000040072a g DF .text 0000000000000006 Base baz
4,-rdynamic選項不產生任何調試信息,因此在一般情況下,新增的附加信息比-g選項要少得多。除非是完全的靜態連接,否則即便是沒有加-rdynamic選項,程序使用到的外部動態符號,比如前面示例里的printf,也會被自動加入到.dynsym表。

完全參考:
http://stackoverflow.com/questions/8623884/gcc-debug-symbols-g-flag-vs-linkers-rdynamic-option


免責聲明!

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



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