什么是’-rDynamic’做什么,什么时候需要它?

-rdynamic(或者链接器级别的 --export-dynamic)到底是做什么的? 它与 -fvisibility*标志或者 pragma__attribute__所定义的符号可见性有什么关系?

对于 --export-dynamicLd (1)提到:

如果你使用“ dlopen”来加载一个动态对象,这个对象需要引用 程序定义的符号,而不是其他动态符号 对象,那么你可能需要 在链接程序本身时使用此选项。 ..。

我不确定我是否完全理解。你能不能提供一个例子,不工作没有 -rdynamic,但与它呢?

编辑 : 我实际上尝试编译了几个虚拟库(单文件、多文件、各种 O 级别、一些函数间调用、一些隐藏符号、一些可见) ,有的使用了 -rdynamic,有的没有使用。到目前为止,我已经得到了 字节相同输出(当然是在保持所有其他标志不变的情况下) ,这非常令人困惑。

50191 次浏览

下面是一个简单的示例项目,说明了 -rdynamic的用法。

Barc

extern void foo(void);


void bar(void)
{
foo();
}

总机

#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;
}

马克菲尔

.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
./$<

在这里,bar.c变成共享库 libbar.so,而 main.c变成 从该库调用 bar()dlopen程序。 bar()调用 foo()foo()bar.c中是外部的,在 main.c中定义。

因此,没有 -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

-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

我使用 rDynamic 打印出 Glibc 的 backtrace()/backtrace_symbols()回溯轨迹。

如果没有 -rdynamic,就不能得到函数名。

要了解更多关于 backtrace()阅读它通过 给你

-rdynamic导出可执行文件的符号,这主要解决了 Mike Kinghan 的答案中描述的场景,但也有助于例如 Glibc 的 backtrace_symbols()符号化回溯。

下面是一个小实验(从 给你拷贝的测试程序)

#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;
}

编译程序: 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]

现在,使用 -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]

如您所见,我们现在得到了正确的堆栈跟踪!

现在,如果我们研究 ELF 的符号表条目(readelf --dyn-syms a.out) :

没有 -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__

对于 -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

我希望这能有所帮助!

来自 Linux 编程接口:

42.1.6

访问主程序中的符号

假设我们使用 dlopen()动态加载共享库, 使用 dlsym()获取函数 x()的地址 然后调用 x()。如果 x()依次调用函数 y(), 那么通常会在其中一个共享库中查找 y() 由程序加载。

有时,最好让 x()调用 在主程序中实现 y()。(这类似于 为了做到这一点,我们必须使 (全局范围)主程序中可用于动态的符号 链接器,通过使用 --export-dynamic链接器链接程序 选择:

$ gcc -Wl,--export-dynamic main.c(加上进一步的选择和 争论)

同样地,我们可以写下:

$ gcc -export-dynamic main.c

使用这两个选项中的任何一个都允许动态加载库 在主程序中访问全局符号。

gcc -rdynamic选项和 gcc -Wl,-E选项更进一步

-Wl,--export-dynamic的同义词。

我猜这只适用于动态加载的共享库,用 dlopen()打开。如果我错了请纠正我。