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

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


Here is a simple example project to illustrate the use of -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 testLDEXTRAFLAGS ?=all: progbar.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 -ldlclean:    rm -f *.o *.so progtest: prog    ./$<

Here, bar.c becomes a shared library libbar.so and main.c becomesa program that dlopens libbar and calls bar() from that library.bar() calls foo(), which is external in bar.c and defined in main.c.

So, without -rdynamic:

$ make testgcc -c -Wall -o main.o main.cgcc -c -Wall -fpic -o bar.o bar.cgcc -shared -o libbar.so bar.ogcc  -o prog main.o -L. -lbar -ldl./prog./libbar.so: undefined symbol: fooMakefile:23: recipe for target 'test' failed

And with -rdynamic:

$ make cleanrm -f *.o *.so prog$ make test LDEXTRAFLAGS=-rdynamicgcc -c -Wall -o main.o main.cgcc -c -Wall -fpic -o bar.o bar.cgcc -shared -o libbar.so bar.ogcc -rdynamic -o prog main.o -L. -lbar -ldl./progHello world


I use rdynamic to print out backtraces using the backtrace()/backtrace_symbols() of Glibc.

Without -rdynamic, you cannot get function names.

To know more about the backtrace() read it over here.


-rdynamic exports 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.

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. */voidprint_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. */voiddummy_function (void){  print_trace (); }intmain (void){  dummy_function ();   return 0;}

compile the program: gcc main.c and run it, the output:

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:

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):

without -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:

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!