C语言 `-rdynamic` 到底是做什么的,什么时候需要它?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36692315/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 10:23:48  来源:igfitidea点击:

What exactly does `-rdynamic` do and when exactly is it needed?

cgccshared-librarieselfdynamic-loading

提问by PSkocik

What exactly does -rdynamic(or --export-dynamicat the linker level) do and how does it relate to symbol visibility as defined by the -fvisibility*flags or visibility pragmas and __attribute__s?

究竟做什么-rdynamic(或--export-dynamic在链接器级别)做什么以及它与-fvisibility*标志或可见性pragmas 和__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 -rdynamicbut 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-identicaloutputs (when keeping all other flags constant of course), which is quite puzzling.

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

回答by Mike Kinghan

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.cbecomes a shared library libbar.soand main.cbecomes a program that dlopens libbarand calls bar()from that library. bar()calls foo(), which is external in bar.cand defined in main.c.

这里,bar.c将成为共享库libbar.so,并main.c成为一个程序,dlopenš libbar,并呼吁bar()该库。 bar()调用foo(),它是外部 inbar.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

回答by deep_rugs

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()阅读这里

回答by Y.H.

-rdynamicexports the symbols of an executable, this mainly addresses scenarios as described in Mike Kinghan's answer, but also it helps e.g. Glibc's backtrace_symbols()symbolizing the backtrace.

-rdynamic导出可执行文件的符号,这主要解决了 Mike Kinghan 的回答中描述的场景,但也有助于例如 Glibcbacktrace_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.cand 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, i.e. gcc -rdynamic main.c, and run again:

现在,使用-rdynamic, ie 进行编译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!

我希望这有帮助!

回答by Rick

From The Linux Programming Interface:

Linux 编程接口

42.1.6

Accessing Symbols in the Main Program

Suppose that we use dlopen()to dynamically load a shared library, use dlsym()to obtain the address of a function x()from that library, and then call x(). If x()in turn calls a function y(), then y()would normally be sought in one of the shared libraries loaded by the program.

Sometimes, it is desirable instead to have x()invoke an implementation of y()in the main program. (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-dynamiclinker option:

$ gcc -Wl,--export-dynamic main.c(plus further options and arguments)

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 -rdynamicoption and the gcc -Wl,-Eoption are further

synonyms for -Wl,--export-dynamic.

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

I guess this only works for dynamically loaded shared library, opened with dlopen(). Correct me if I am wrong.

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