[英]What exactly does `-rdynamic` do and when exactly is it needed?
What exactly does -rdynamic
(or --export-dynamic
at the linker level) do and how does it relate to symbol visibility as defined by the -fvisibility*
flags or visibility pragma
s and __attribute__
s? -rdynamic
(或--export-dynamic
器级别的--export-dynamic
)究竟做了什么,它与-fvisibility*
标志或可见性pragma
s 和__attribute__
s 定义的符号可见性有什么关系?
For --export-dynamic
, ld(1) mentions:对于--export-dynamic
, ld(1)提到:
... If you use "dlopen" to load a dynamic object which needs to refer back to the symbols defined by the program, rather than some other dynamic object, then you will probably need to use this option when linking the program itself. ...如果您使用“dlopen”加载一个动态对象,该对象需要引用回由程序定义的符号,而不是某个其他动态对象,那么您可能需要在链接程序本身时使用此选项。 ... ...
I'm not sure I completely understand this.我不确定我是否完全理解这一点。 Could you please provide an example that doesn't work without -rdynamic
but does with it?您能否提供一个示例,该示例在没有-rdynamic
情况下不起作用,但可以使用它?
Edit : I actually tried compiling a couple of dummy libraries (single file, multi-file, various -O levels, some inter-function calls, some hidden symbols, some visible), with and without -rdynamic
, and so far I've been getting byte-identical outputs (when keeping all other flags constant of course), which is quite puzzling.编辑:我实际上尝试编译几个虚拟库(单个文件,多文件,各种 -O 级别,一些函数间调用,一些隐藏符号,一些可见),有和没有-rdynamic
,到目前为止我已经一直在获得字节相同的输出(当然,当所有其他标志保持不变时),这很令人费解。
Here is a simple example project to illustrate the use of -rdynamic
.这里有一个简单的示例项目来说明-rdynamic
的使用。
bar.c酒吧
extern void foo(void);
void bar(void)
{
foo();
}
main.c主文件
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
void foo(void)
{
puts("Hello world");
}
int main(void)
{
void * dlh = dlopen("./libbar.so", RTLD_NOW);
if (!dlh) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
void (*bar)(void) = dlsym(dlh,"bar");
if (!bar) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
bar();
return 0;
}
Makefile生成文件
.PHONY: all clean test
LDEXTRAFLAGS ?=
all: prog
bar.o: bar.c
gcc -c -Wall -fpic -o $@ $<
libbar.so: bar.o
gcc -shared -o $@ $<
main.o: main.c
gcc -c -Wall -o $@ $<
prog: main.o | libbar.so
gcc $(LDEXTRAFLAGS) -o $@ $< -L. -lbar -ldl
clean:
rm -f *.o *.so prog
test: prog
./$<
Here, bar.c
becomes a shared library libbar.so
and main.c
becomes a program that dlopen
s libbar
and calls bar()
from that library.在这里, bar.c
成为共享库libbar.so
而main.c
成为dlopen
的libbar
并从该库调用bar()
的程序。 bar()
calls foo()
, which is external in bar.c
and defined in main.c
. bar()
调用foo()
,它在bar.c
中是外部的并在main.c
定义。
So, without -rdynamic
:所以,没有-rdynamic
:
$ make test
gcc -c -Wall -o main.o main.c
gcc -c -Wall -fpic -o bar.o bar.c
gcc -shared -o libbar.so bar.o
gcc -o prog main.o -L. -lbar -ldl
./prog
./libbar.so: undefined symbol: foo
Makefile:23: recipe for target 'test' failed
And with -rdynamic
:并使用-rdynamic
:
$ make clean
rm -f *.o *.so prog
$ make test LDEXTRAFLAGS=-rdynamic
gcc -c -Wall -o main.o main.c
gcc -c -Wall -fpic -o bar.o bar.c
gcc -shared -o libbar.so bar.o
gcc -rdynamic -o prog main.o -L. -lbar -ldl
./prog
Hello world
I use rdynamic to print out backtraces using the backtrace()
/ backtrace_symbols()
of Glibc.我使用 rdynamic 使用 Glibc 的backtrace()
/ backtrace_symbols()
打印出回溯。
Without -rdynamic
, you cannot get function names.没有-rdynamic
,您将无法获得函数名称。
To know more about the backtrace()
read it over here .要了解有关backtrace()
更多信息,请阅读此处。
-rdynamic
exports the symbols of an executable, this mainly addresses scenarios as described in Mike Kinghan's answer, but also it helps eg Glibc's backtrace_symbols()
symbolizing the backtrace. -rdynamic
导出可执行文件的符号,这主要解决了 Mike Kinghan 的回答中描述的场景,但它也有助于例如 Glibc 的backtrace_symbols()
符号化回溯。
Here is a small experiment (test program copied from here )这是一个小实验(从这里复制的测试程序)
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
/* Obtain a backtrace and print it to stdout. */
void
print_trace (void)
{
void *array[10];
size_t size;
char **strings;
size_t i;
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
printf ("Obtained %zd stack frames.\n", size);
for (i = 0; i < size; i++)
printf ("%s\n", strings[i]);
free (strings);
}
/* A dummy function to make the backtrace more interesting. */
void
dummy_function (void)
{
print_trace ();
}
int
main (void)
{
dummy_function ();
return 0;
}
compile the program: gcc main.c
and run it, the output:编译程序: gcc main.c
并运行它,输出:
Obtained 5 stack frames.
./a.out() [0x4006ca]
./a.out() [0x400761]
./a.out() [0x40076d]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f026597f830]
./a.out() [0x4005f9]
Now, compile with -rdynamic
, ie gcc -rdynamic main.c
, and run again:现在,使用-rdynamic
编译,即gcc -rdynamic main.c
,然后再次运行:
Obtained 5 stack frames.
./a.out(print_trace+0x28) [0x40094a]
./a.out(dummy_function+0x9) [0x4009e1]
./a.out(main+0x9) [0x4009ed]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f85b23f2830]
./a.out(_start+0x29) [0x400879]
As you can see, we get a proper stack trace now!如您所见,我们现在获得了正确的堆栈跟踪!
Now, if we investigate ELF's symbol table entry ( readelf --dyn-syms a.out
):现在,如果我们调查 ELF 的符号表条目( readelf --dyn-syms a.out
):
without -rdynamic
没有-rdynamic
Symbol table '.dynsym' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND free@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND backtrace_symbols@GLIBC_2.2.5 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND backtrace@GLIBC_2.2.5 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (3)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
8: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
with -rdynamic
, we have more symbols, including the executable's:使用-rdynamic
,我们有更多符号,包括可执行文件:
Symbol table '.dynsym' contains 25 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND free@GLIBC_2.2.5 (2)
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND backtrace_symbols@GLIBC_2.2.5 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND backtrace@GLIBC_2.2.5 (2)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (3)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
9: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
10: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
11: 0000000000601060 0 NOTYPE GLOBAL DEFAULT 24 _edata
12: 0000000000601050 0 NOTYPE GLOBAL DEFAULT 24 __data_start
13: 0000000000601068 0 NOTYPE GLOBAL DEFAULT 25 _end
14: 00000000004009d8 12 FUNC GLOBAL DEFAULT 14 dummy_function
15: 0000000000601050 0 NOTYPE WEAK DEFAULT 24 data_start
16: 0000000000400a80 4 OBJECT GLOBAL DEFAULT 16 _IO_stdin_used
17: 0000000000400a00 101 FUNC GLOBAL DEFAULT 14 __libc_csu_init
18: 0000000000400850 42 FUNC GLOBAL DEFAULT 14 _start
19: 0000000000601060 0 NOTYPE GLOBAL DEFAULT 25 __bss_start
20: 00000000004009e4 16 FUNC GLOBAL DEFAULT 14 main
21: 00000000004007a0 0 FUNC GLOBAL DEFAULT 11 _init
22: 0000000000400a70 2 FUNC GLOBAL DEFAULT 14 __libc_csu_fini
23: 0000000000400a74 0 FUNC GLOBAL DEFAULT 15 _fini
24: 0000000000400922 182 FUNC GLOBAL DEFAULT 14 print_trace
I hope that helps!我希望这有帮助!
From The Linux Programming Interface :从Linux 编程接口:
42.1.6 42.1.6
Accessing Symbols in the Main Program在主程序中访问符号
Suppose that we use
dlopen()
to dynamically load a shared library , usedlsym()
to obtain the address of a functionx()
from that library, and then callx()
.假设我们使用dlopen()
动态加载共享库,使用dlsym()
x()
从该库中获取函数x()
的地址,然后调用x()
。 Ifx()
in turn calls a functiony()
, theny()
would normally be sought in one of the shared libraries loaded by the program.如果x()
依次调用函数y()
,则y()
通常会在程序加载的共享库之一中查找。Sometimes, it is desirable instead to have
x()
invoke an implementation ofy()
in the main program.有时,最好让x()
y()
在主程序中调用y()
的实现。 (This is similar to a callback mechanism.) In order to do this, we must make the (global-scope) symbols in the main program available to the dynamic linker, by linking the program using the--export-dynamic
linker option: (这类似于回调机制。)为了做到这一点,我们必须通过使用--export-dynamic
链接器选项链接程序,使主程序中的(全局范围)符号可用于动态链接器:
$ gcc -Wl,--export-dynamic main.c
(plus further options and arguments)$ gcc -Wl,--export-dynamic main.c
(加上更多的选项和参数)Equivalently, we can write the following:等价地,我们可以写出以下内容:
$ gcc -export-dynamic main.c
Using either of these options allows a dynamically loaded library to access global symbols in the main program.使用这些选项中的任何一个都允许动态加载的库访问主程序中的全局符号。
The
gcc -rdynamic
option and thegcc -Wl,-E
option are furthergcc -rdynamic
选项和gcc -Wl,-E
选项更进一步synonyms for
-Wl,--export-dynamic
.-Wl,--export-dynamic
同义词。
I guess this only works for dynamically loaded shared library, opened with dlopen()
.我想这仅适用于动态加载的共享库,用dlopen()
打开。 Correct me if I am wrong.如果我错了,请纠正我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.