¿Qué hace exactamente `-rdynamic` y cuándo exactamente se necesita?

8 minutos de lectura

avatar de usuario
PSkocik

que hace exactamente -rdynamic (o --export-dynamic en el nivel del enlazador) y cómo se relaciona con la visibilidad del símbolo según lo definido por el -fvisibility* banderas o visibilidad pragmaarena __attribute__¿s?

Para --export-dynamic, ld(1) menciona:

… Si usa “dlopen” para cargar un objeto dinámico que necesita hacer referencia a los símbolos definidos por el programa, en lugar de algún otro objeto dinámico, entonces probablemente necesitará usar esta opción cuando vincule el programa mismo. …

No estoy seguro de entender completamente esto. ¿Podría proporcionar un ejemplo que no funcione sin -rdynamic pero hace con eso?

Editar: De hecho, intenté compilar un par de bibliotecas ficticias (un solo archivo, varios archivos, varios niveles -O, algunas llamadas entre funciones, algunos símbolos ocultos, algunos visibles), con y sin -rdynamicy hasta ahora he estado recibiendo byte idéntico salidas (cuando se mantienen constantes todas las demás banderas, por supuesto), lo cual es bastante desconcertante.

avatar de usuario
mike reyhan

Aquí hay un proyecto de ejemplo simple para ilustrar el uso de -rdynamic.

bar.c

extern void foo(void);

void bar(void)
{
    foo();
}

C Principal

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

Aquí, bar.c se convierte en una biblioteca compartida libbar.so y main.c se convierte en un programa que dlopens libbar y llamadas bar() de esa biblioteca.
bar() llamadas foo()que es externo en bar.c y definido en main.c.

Entonces, sin -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

Y con -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

  • Su ejemplo deja perfectamente claro lo que significa la página de manual. ¡Muchas gracias!

    – PSkocik

    18/04/2016 a las 18:42

  • Me preguntaba por qué está rdynamic en el ejecutable y no en el objeto compartido. Según esta respuesta: stackoverflow.com/questions/50418941/…, un resumen conciso de esta respuesta es: Los símbolos solo se exportan de forma predeterminada desde bibliotecas compartidas. -rdynamic le dice al enlazador que haga lo mismo con los ejecutables.

    – thejinx0r

    12 de noviembre de 2018 a las 18:54

  • Además de usar -rdynamictambién verifique que su sistema de compilación no agregue -fvisibility=hidden ¡opción! (ya que descartará por completo el efecto de -rdynamic)

    – Dima Litvinov

    13 de mayo de 2020 a las 0:44

  • Buen ejemplo, pero el -L. -lbar no es necesario durante la compilación del programa, ¿verdad? solo son necesarios para la vinculación de bibliotecas estáticas. La biblioteca dinámica se encuentra mediante LD_LIBRARY_PATH.

    -Chan Kim

    8 de junio de 2021 a las 8:28

  • Estoy de acuerdo con @ChanKim. -L. -lbar no es necesario ya que estamos descargando la librería a mano. También debería funcionar bien sin tener que modificar LD_LIBRARY_PATH ya que estamos abriendo la lib con una ruta ("./libbar.so" en lugar de "libbar.so") así que está bien irse LD_LIBRARY_PATH solo o tal cual.

    – mchiasson

    26 de julio de 2021 a las 22:33


-rdynamic exporta los símbolos de un ejecutable, esto aborda principalmente escenarios como se describe en la respuesta de Mike Kinghan, pero también ayuda, por ejemplo, a Glibc backtrace_symbols() simbolizando el retroceso.

Aquí hay un pequeño experimento (programa de prueba copiado de aquí)

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

compilar el programa: gcc main.c y ejecutarlo, la salida:

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]

Ahora compila con -rdynamices decir gcc -rdynamic main.cy ejecuta de nuevo:

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]

Como puede ver, ¡obtenemos un seguimiento de pila adecuado ahora!

Ahora, si investigamos la entrada de la tabla de símbolos de ELF (readelf --dyn-syms a.out):

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

con -rdynamictenemos más símbolos, incluidos los ejecutables:

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

¡Espero que eso ayude!

avatar de usuario
alfombras_profundas

Yo uso rdynamic para imprimir backtraces usando el backtrace()/backtrace_symbols() de Glibc.

Sin -rdynamicno puede obtener nombres de funciones.

Para saber más sobre el backtrace() léelo aquí.

  • Una solución mucho mejor es usar un desbobinador normal que pueda acceder a la información de depuración.

    – yugr

    18 mayo 2018 a las 21:15


  • @yugr, ¿puede proporcionar alguna referencia a lo que se refiere?

    – f3xy

    26 de junio de 2019 a las 21:47

  • @f3xy Ver por ejemplo esta Flameeyes publica sobre las desventajas de agregar símbolos adicionales a la tabulación dinámica. Los desenrolladores dedicados, como libbacktrace o libunwind, pueden simbolizar la pila sin sobrecarga mediante el uso de la información de depuración del programa.

    – yugr

    27 de junio de 2019 a las 1:41

  • @yugr La información de depuración agrega mucho más volumen al ejecutable (piense en los sistemas integrados con pequeñas particiones flash) y puede no ser apropiado si está enviando software propietario. Ya -rdynamic agrega mucha información útil para alguien que realiza ingeniería inversa del binario. -rdynamic es un buen truco: el binario aún se puede eliminar, pero respetará esos símbolos porque son dinámicos.

    – Kaz

    3 de junio de 2020 a las 17:44


  • @Kaz “La información de depuración agrega mucho más volumen al ejecutable”: los desenrolladores basados ​​​​en depuración solo necesitan -gline-tables-only que es significativamente más pequeño que lleno -g información de depuración. “sistemas integrados con pequeñas particiones flash”: estos sistemas generalmente solo imprimen direcciones de todos modos (que luego se simbolizan en el host). “puede no ser apropiado si está enviando software propietario”: no recomendaría ningún software propietario para imprimir trazas inversas simbolizadas en versiones de lanzamiento, ya sea con debuginfo o -rdynamic.

    – yugr

    3 de junio de 2020 a las 19:44


Desde La interfaz de programación de Linux:

42.1.6

Acceso a símbolos en el programa principal

Supongamos que usamos dlopen() para cargar dinámicamente una biblioteca compartidautilizar dlsym() para obtener la dirección de una función x() de esa biblioteca, y luego llamar x(). Si x() a su vez llama a una función y()luego y() normalmente se buscaría en una de las bibliotecas compartidas cargadas por el programa.

A veces, es deseable en lugar de tener x() invocar una implementación de y() en el programa principal. (Esto es similar a un mecanismo de devolución de llamada). Para hacer esto, debemos hacer que los símbolos (de alcance global) en el programa principal estén disponibles para el enlazador dinámico, vinculando el programa usando el --export-dynamic opción del enlazador:

$ gcc -Wl,--export-dynamic main.c (además de otras opciones y argumentos)

De manera equivalente, podemos escribir lo siguiente:

$ gcc -export-dynamic main.c

El uso de cualquiera de estas opciones permite que una biblioteca cargada dinámicamente acceda a símbolos globales en el programa principal.

los gcc -rdynamic opción y la gcc -Wl,-E la opción está más lejos

sinónimos de -Wl,--export-dynamic.

Supongo que esto solo funciona para la biblioteca compartida cargada dinámicamente, abierta con dlopen(). Corrígeme si estoy equivocado.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad