GNU/Linux >> Tutoriales Linux >  >> Linux

¿Por qué y cómo se pueden ejecutar algunas bibliotecas compartidas, como si fueran ejecutables?

Esa biblioteca tiene un main() función o punto de entrada equivalente, y se compiló de tal manera que es útil como ejecutable y como objeto compartido.

Aquí hay una sugerencia sobre cómo hacer esto, aunque no funciona para mí.

Aquí hay otra respuesta a una pregunta similar sobre S.O, que descaradamente plagiaré, modificaré y agregaré un poco de explicación.

Primero, la fuente de nuestra biblioteca de ejemplo, test.c :

#include <stdio.h>                  

void sayHello (char *tag) {         
    printf("%s: Hello!\n", tag);    
}                                   

int main (int argc, char *argv[]) { 
    sayHello(argv[0]);              
    return 0;                       
}                   

Compile eso:

gcc -fPIC -pie -o libtest.so test.c -Wl,-E

Aquí, estamos compilando una biblioteca compartida (-fPIC ), pero diciéndole al enlazador que es un ejecutable normal (-pie ) y hacer exportable su tabla de símbolos (-Wl,-E ), de modo que se pueda vincular de forma útil.

Y, aunque file dirá que es un objeto compartido, funciona como un ejecutable:

> ./libtest.so 
./libtest.so: Hello!

Ahora tenemos que ver si realmente se puede vincular dinámicamente. Un programa de ejemplo, program.c :

#include <stdio.h>

extern void sayHello (char*);

int main (int argc, char *argv[]) {
    puts("Test program.");
    sayHello(argv[0]);
    return 0;
}

Usando extern nos ahorra tener que crear un encabezado. Ahora compila eso:

gcc program.c -L. -ltest

Antes de que podamos ejecutarlo, necesitamos agregar la ruta de libtest.so para el cargador dinámico:

export LD_LIBRARY_PATH=./

Ahora:

> ./a.out
Test program.
./a.out: Hello!

Y ldd a.out mostrará el enlace a libtest.so .

Tenga en cuenta que dudo que así sea como se compila realmente glibc, ya que probablemente no sea tan portátil como glibc en sí (ver man gcc con respecto al -fPIC y -pie interruptores), pero demuestra el mecanismo básico. Para conocer los detalles reales, tendría que consultar el archivo MAKE de origen.


Busquemos una respuesta en un repositorio glibc aleatorio en GitHub. Esta versión proporciona un "banner" en el archivo version.c .

En el mismo archivo hay algunos puntos interesantes:el __libc_print_version función que imprime el texto en stdout y el __libc_main (void) símbolo que está documentado como el punto de entrada. Entonces, este símbolo se llama cuando se ejecuta la biblioteca.

Entonces, ¿cómo sabe exactamente el enlazador o el compilador que esta es la función de punto de entrada?

Sumerjámonos en el archivo MAKE. En las banderas del enlazador hay una interesante:

# Give libc.so an entry point and make it directly runnable itself.
LDFLAGS-c.so += -e __libc_main

Este es el indicador del enlazador para establecer el punto de entrada a la biblioteca. Al crear una biblioteca, puede proporcionar el -e function_name marca para que el enlazador habilite un comportamiento ejecutable. ¿Qué hace realmente? Miremos el manual (algo desactualizado pero aún válido):

El lenguaje de comandos del enlazador incluye un comando específicamente para definir la primera instrucción ejecutable en un archivo de salida (su punto de entrada). Su argumento es un nombre de símbolo:

ENTRADA(símbolo)

Al igual que las asignaciones de símbolos, el comando ENTRADA se puede colocar como un comando independiente en el archivo de comandos o entre las definiciones de sección dentro del comando SECCIONES, lo que tenga más sentido para su diseño.

ENTRADA es solo una de varias formas de elegir el punto de entrada. Puede indicarlo de cualquiera de las siguientes formas (mostradas en orden descendente de prioridad:los métodos que se encuentran más arriba en la lista anulan los métodos que se encuentran más abajo).

the `-e' entry command-line option;
the ENTRY(symbol) command in a linker control script;
the value of the symbol start, if present;
the address of the first byte of the .text section, if present;
The address 0. 

Por ejemplo, puede usar estas reglas para generar un punto de entrada con una declaración de asignación:si no se define un inicio de símbolo dentro de sus archivos de entrada, simplemente puede definirlo, asignándole un valor apropiado ---

inicio =0x2020;

El ejemplo muestra una dirección absoluta, pero puede usar cualquier expresión. Por ejemplo, si sus archivos de objetos de entrada usan alguna otra convención de nombre de símbolo para el punto de entrada, puede simplemente asignar el valor de cualquier símbolo que contenga la dirección de inicio para comenzar:

inicio =otro_símbolo;

(la documentación actual se puede encontrar aquí)

El ld linker en realidad crea un ejecutable con una función de punto de entrada si proporciona la opción de línea de comando -e (que es la solución más popular), proporcione un símbolo de función start , o especifique una dirección de símbolo para el ensamblador.

Sin embargo, tenga en cuenta que no se garantiza que funcione con otros enlazadores (no sé si el lld de llvm tiene el mismo indicador). No sé por qué esto debería ser útil para fines distintos a proporcionar información sobre el archivo SO.


Linux
  1. ¿Por qué Scp es tan lento y cómo hacerlo más rápido?

  2. ¿Por qué algunos puertos informados por Nmap están filtrados y no los demás?

  3. Introducción a las bibliotecas compartidas de Linux (Cómo crear bibliotecas compartidas)

  4. ¿Cómo mostrar todas las bibliotecas compartidas utilizadas por los ejecutables en Linux?

  5. ¿Cómo verificar qué bibliotecas compartidas se cargan en tiempo de ejecución para un proceso determinado?

Sistemas de archivos virtuales en Linux:por qué los necesitamos y cómo funcionan

Cómo enumerar las bibliotecas compartidas utilizadas por los ejecutables en Linux

Linux:¿por qué son tan grandes los verdaderos y los falsos?

¿Por qué algunos emoji en blanco y negro y otros son demasiado grandes?

En un entorno vacío, ¿cómo se encuentran los ejecutables?

¿Cómo verificar si una biblioteca compartida está instalada?