Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 pragmas and __attribute__s?

For --export-dynamic, ld(1) mentions:

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

I'm not sure I completely understand this. Could you please provide an example that doesn't work without -rdynamic but does with it?

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.

like image 295
PSkocik Avatar asked Apr 18 '16 11:04

PSkocik


People also ask

What is Rdynamic?

-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. -s.

How do you use dynamic linking?

Dynamic linking is a two-step process that relies on accessing the addresses of code. The first step occurs at compilation. When a file is compiled with a dynamic library, instead of copying the actual object code contained in the library, the linker simply scans the code contained and checks for missing symbols.

Which is bigger in process memory a statically linked binary or a dynamically linked binary?

Statically linked files are significantly larger in size because external programs are built into the executable files. In dynamic linking only one copy of shared library is kept in memory.


2 Answers

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

$ 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 
like image 154
Mike Kinghan Avatar answered Sep 30 '22 20:09

Mike Kinghan


-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. */ 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:

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!

like image 41
Y.H. Avatar answered Sep 30 '22 20:09

Y.H.