Was genau macht `-rdynamic` und wann genau wird es benötigt?

Lesezeit: 8 Minuten

Benutzeravatar von PSkocik
PSkocik

Was genau macht -rdynamic (oder --export-dynamic auf der Linker-Ebene) tun und in welcher Beziehung es zur Sichtbarkeit von Symbolen steht, wie sie von definiert werden -fvisibility* Flaggen oder Sichtbarkeit pragmas und __attribute__s?

Zum --export-dynamic, ld(1) erwähnt:

… Wenn Sie “dlopen” verwenden, um ein dynamisches Objekt zu laden, das auf die vom Programm definierten Symbole zurückgreifen muss, und nicht auf ein anderes dynamisches Objekt, dann müssen Sie diese Option wahrscheinlich beim Linken des Programms selbst verwenden. …

Ich bin mir nicht sicher, ob ich das vollständig verstehe. Könnten Sie bitte ein Beispiel geben, das ohne nicht funktioniert? -rdynamic aber tut es damit?

BearbeitenHinweis: Ich habe tatsächlich versucht, ein paar Dummy-Bibliotheken zu kompilieren (einzelne Datei, mehrere Dateien, verschiedene -O-Ebenen, einige Interfunktionsaufrufe, einige versteckte Symbole, einige sichtbar), mit und ohne -rdynamicund bisher habe ich bekommen Byte-identisch Ausgänge (wenn alle anderen Flags natürlich konstant gehalten werden), was ziemlich verwirrend ist.

Benutzeravatar von Mike Kinghan
Mike Kinghan

Hier ist ein einfaches Beispielprojekt, um die Verwendung zu veranschaulichen -rdynamic.

bar.c

extern void foo(void);

void bar(void)
{
    foo();
}

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

Hier, bar.c wird eine gemeinsame Bibliothek libbar.so und main.c wird ein Programm, das dlopens libbar und ruft bar() aus dieser Bibliothek.
bar() Anrufe foo()das ist extern in bar.c und definiert in main.c.

Also ohne -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

Und mit -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

  • Ihr Beispiel macht deutlich, was die Manpage bedeutet. Danke vielmals!

    – PSkočik

    18. April 2016 um 18:42 Uhr

  • Ich habe mich gefragt, warum rdynamic auf der ausführbaren Datei und nicht auf dem gemeinsam genutzten Objekt ist. Gemäß dieser Antwort: stackoverflow.com/questions/50418941/… lautet eine kurze Zusammenfassung dieser Antwort: Symbole werden standardmäßig nur aus gemeinsam genutzten Bibliotheken exportiert. -rdynamic weist den Linker an, dasselbe für ausführbare Dateien zu tun.

    – thejinx0r

    12. November 2018 um 18:54 Uhr

  • Neben der Verwendung -rdynamicüberprüfen Sie auch, ob Ihr Build-System nicht hinzufügt -fvisibility=hidden Möglichkeit! (da es die Wirkung von vollständig verwirft -rdynamic)

    – Dima Litwinow

    13. Mai 2020 um 0:44 Uhr

  • Gutes Beispiel, aber die -L. -lbar ist während der Prog-Kompilierung nicht erforderlich, oder? Sie sind nur für das statische Linken von Bibliotheken erforderlich. Die dynamische Bibliothek wird von LD_LIBRARY_PATH gefunden.

    – Chan-Kim

    8. Juni 2021 um 8:28 Uhr

  • Ich stimme @ChanKim zu. -L. -lbar ist nicht notwendig, da wir die Lib von Hand löschen. Es sollte auch gut funktionieren, ohne Änderungen vornehmen zu müssen LD_LIBRARY_PATH da wir die Bibliothek mit einem Pfad öffnen ("./libbar.so" Anstatt von "libbar.so") also ist es in Ordnung zu gehen LD_LIBRARY_PATH allein oder so wie es ist.

    – mchiasson

    26. Juli 2021 um 22:33 Uhr


-rdynamic exportiert die Symbole einer ausführbaren Datei, dies adressiert hauptsächlich Szenarien, wie in Mike Kinghans Antwort beschrieben, aber es hilft auch zB Glibcs backtrace_symbols() symbolisiert die Rückverfolgung.

Hier ein kleines Experiment (Testprogramm kopiert von hier)

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

Kompilieren Sie das Programm: gcc main.c und führe es aus, die Ausgabe:

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]

Jetzt kompilieren Sie mit -rdynamicdh gcc -rdynamic main.cund erneut ausführen:

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]

Wie Sie sehen können, erhalten wir jetzt einen ordnungsgemäßen Stack-Trace!

Wenn wir nun den Symboltabelleneintrag von ELF untersuchen (readelf --dyn-syms a.out):

ohne -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__

mit -rdynamichaben wir mehr Symbole, einschließlich der ausführbaren:

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

Ich hoffe das hilft!

Benutzeravatar von deep_rugs
deep_rugs

Ich verwende rdynamic, um Backtraces mit dem auszudrucken backtrace()/backtrace_symbols() von Glibc.

Ohne -rdynamickönnen Sie keine Funktionsnamen erhalten.

Um mehr über die zu erfahren backtrace() überlesen hier.

  • Eine viel bessere Lösung ist die Verwendung eines normalen Unwinders, der auf Debuginfo zugreifen kann.

    – jugr

    18. Mai 2018 um 21:15 Uhr


  • @yugr können Sie einen Hinweis darauf geben, worauf Sie sich beziehen?

    – f3xy

    26. Juni 2019 um 21:47 Uhr

  • @f3xy Siehe zB Dies Flameeyes-Post über die Nachteile des Hinzufügens zusätzlicher Symbole zu dynamischen Symtabs. Dedizierte Unwinder wie libbacktrace oder libunwind können Stapel ohne Overhead symbolisieren, indem sie die Debug-Informationen des Programms verwenden.

    – jugr

    27. Juni 2019 um 1:41 Uhr

  • @yugr Debug-Info fügt der ausführbaren Datei viel mehr Volumen hinzu (denken Sie an eingebettete Systeme mit kleinen Flash-Partitionen) und ist möglicherweise nicht geeignet, wenn Sie proprietäre Software versenden. Schon -rdynamic Fügt eine Menge Informationen hinzu, die für jemanden hilfreich sind, der die Binärdatei zurückentwickelt. -rdynamic ist ein netter Trick: Die Binärdatei kann immer noch entfernt werden, aber sie respektiert diese Symbole, weil sie dynamisch sind.

    – Kas

    3. Juni 2020 um 17:44 Uhr


  • @Kaz “Debug-Informationen fügen der ausführbaren Datei viel mehr Masse hinzu” – Debuginfo-basierte Unwinder müssen nur -gline-tables-only das ist deutlich kleiner als voll -g Debug-Informationen. “eingebettete Systeme mit kleinen Flash-Partitionen” – solche Systeme drucken normalerweise sowieso nur Adressen (die dann auf dem Host symbolisiert werden). “Möglicherweise nicht angemessen, wenn Sie proprietäre Software versenden” – Ich würde keine proprietäre Software empfehlen, symbolisierte Backtraces in Release-Builds zu drucken, sei es mit Debuginfo oder -rdynamic.

    – jugr

    3. Juni 2020 um 19:44 Uhr


Aus Die Linux-Programmierschnittstelle:

42.1.6

Zugriff auf Symbole im Hauptprogramm

Angenommen, wir verwenden dlopen() zu dynamisch eine gemeinsam genutzte Bibliothek ladenverwenden dlsym() um die Adresse einer Funktion zu erhalten x() aus dieser Bibliothek und rufen Sie dann an x(). Wenn x() wiederum ruft eine Funktion auf y()dann y() normalerweise in einer der vom Programm geladenen gemeinsam genutzten Bibliotheken gesucht werden.

Manchmal ist es wünschenswert, stattdessen zu haben x() Rufen Sie eine Implementierung von auf y() im Hauptprogramm. (Dies ähnelt einem Rückrufmechanismus.) Um dies zu tun, müssen wir die (globalen) Symbole im Hauptprogramm dem dynamischen Linker zur Verfügung stellen, indem wir das Programm mit der verknüpfen --export-dynamic Linker-Option:

$ gcc -Wl,--export-dynamic main.c (plus weitere Optionen und Argumente)

Äquivalent können wir Folgendes schreiben:

$ gcc -export-dynamic main.c

Die Verwendung einer dieser Optionen ermöglicht es einer dynamisch geladenen Bibliothek, auf globale Symbole im Hauptprogramm zuzugreifen.

Das gcc -rdynamic Möglichkeit und die gcc -Wl,-E Option sind weiter

Synonyme für -Wl,--export-dynamic.

Ich denke, das funktioniert nur für dynamisch geladene gemeinsam genutzte Bibliotheken, die mit geöffnet wurden dlopen(). Korrigiere mich, wenn ich falsch liege.

1418140cookie-checkWas genau macht `-rdynamic` und wann genau wird es benötigt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy