Perché e come sono eseguibili alcune librerie condivise, come se fossero eseguibili?


56

Su sistemi Linux a 32 bit, invocando questo

$ /lib/libc.so.6

e su sistemi a 64 bit questo

$ /lib/x86_64-linux-gnu/libc.so.6

in una shell, fornisce un output come questo:

GNU C Library stable release version 2.10.1, by Roland McGrath et al.
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.0 20090506 (Red Hat 4.4.0-4).
Compiled on a Linux >>2.6.18-128.4.1.el5<< system on 2009-08-19.
Available extensions:
    The C stubs add-on version 2.1.2.
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
    RT using linux kernel aio
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

Perché e come ciò accade e come è possibile fare lo stesso in altre librerie condivise?

Ho cercato i /usr/libfile eseguibili e ho trovato /usr/lib/libvlc.so.5.5.0. L'esecuzione ha portato a un errore di segmentazione . : - /


Oltre a tutte le seguenti risposte, il mio richiamo è se si imposta il bit x su una libreria condivisa, è stato (forse lo è ancora) caricarlo da un eseguibile anche con il bit r libero. Una volta era considerata una buona pratica di sicurezza bandire dal mondo file eseguibili e librerie di sistema. A causa dell'open source diffuso, questo si applica davvero solo alla / ft / ls della directory ftp anonima. Per me, lasciare l'x bit set sembra un volto di quella vecchia pratica.
Giosuè,

Risposte:


53

Tale libreria ha una main()funzione o un punto di ingresso equivalente ed è stata compilata in modo tale da risultare utile sia come eseguibile che come oggetto condiviso.

Ecco un suggerimento su come farlo, anche se non funziona per me.

Ecco un altro in una risposta a una domanda simile su SO , che plagiarò spudoratamente, ottimizzerò e aggiungerò un po 'di spiegazione.

In primo luogo, fonte per la nostra biblioteca esempio 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;                       
}                   

Compilare che:

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

Qui stiamo compilando una libreria condivisa ( -fPIC), ma diciamo al linker che è un eseguibile regolare ( -pie), e per rendere esportabile la sua tabella dei simboli ( -Wl,-E), in modo che possa essere utilmente collegata.

E, sebbene filedirà che è un oggetto condiviso, funziona come un eseguibile:

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

Ora dobbiamo vedere se può davvero essere collegato dinamicamente. Un programma di esempio, program.c:

#include <stdio.h>

extern void sayHello (char*);

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

L'uso externci consente di evitare di creare un'intestazione. Ora compila che:

gcc program.c -L. -ltest

Prima di poterlo eseguire, è necessario aggiungere il percorso di libtest.soper il caricatore dinamico:

export LD_LIBRARY_PATH=./

Adesso:

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

E ldd a.outmostrerà il collegamento a libtest.so.

Nota che dubito che sia così compilato glibc, poiché probabilmente non è portatile come glibc stesso (vedi man gccper quanto riguarda gli switch -fPICe -pie), ma dimostra il meccanismo di base. Per i dettagli reali dovresti guardare il makefile di origine.


1
Ottima risposta, grazie! :-) Ho provato a utilizzare nmla libreria condivisa, ma non era una versione di debug. Quindi, perché libvlce altri si schiantano?
Ho1,

1
Poiché la maggior parte delle librerie condivise non è pensata per essere eseguibile, GNU libcè un'eccezione.
Riccioli d'oro

Ne ho trovati altri due: lde libpthread.
Ho1,

@ Ho1 ld.soè speciale in altri modi. In una certa misura, è più un eseguibile reale che un normale eseguibile collegato dinamicamente.
Casuale 832

1
Le opzioni di cui sopra, anche se creano la libreria eseguibile-condivisa, ma sono incomplete nel senso che segnala l'errore, quando alcuni eseguibili tentano di collegarsi a questo. Esempio di riferimento dettagliato aggiunto qui: unix.stackexchange.com/a/479334/152034
parasrish

21

Tuffiamoci per una risposta nel repository glibc casuale in github. Questa versione fornisce un "banner" nel file version.c.

Nello stesso file ci sono alcuni punti interessanti: __libc_print_versionla funzione che fornisce la stampa allo stdin stesso testo e simbolo __libc_main (void)che è documentato come punto di ingresso. Quindi questo simbolo viene chiamato quando si esegue la libreria.

Quindi, come fa il linker / compilatore a sapere che questa è esattamente la funzione del punto di ingresso?

Tuffiamoci nel makefile . Nelle bandiere dei linker c'è una bandiera interessante:

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

Quindi questo è un flag linker per impostare il punto di ingresso nella libreria. Quando si -e function_namecrea una libreria è possibile fornire al linker la creazione di comportamenti eseguibili. Cosa fa veramente? Diamo un'occhiata al manuale (un po 'datato ma ancora valido) :

Il linguaggio dei comandi del linker include un comando specifico per la definizione della prima istruzione eseguibile in un file di output (il suo punto di ingresso). Il suo argomento è un nome simbolo:

ENTRY (simbolo)

Come le assegnazioni di simboli, il comando ENTRY può essere inserito come comando indipendente nel file di comando o tra le definizioni di sezione all'interno del comando SECTIONS - qualunque cosa abbia più senso per il layout.

ENTRY è solo uno dei tanti modi per scegliere il punto di ingresso. Puoi indicarlo in uno dei seguenti modi (mostrato in ordine decrescente di priorità: i metodi più in alto nella lista hanno la precedenza sui metodi in basso).

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. 

Ad esempio, puoi utilizzare queste regole per generare un punto di ingresso con un'istruzione di assegnazione: se nei file di input non è definito alcun inizio di simbolo, puoi semplicemente definirlo, assegnandogli un valore appropriato ---

inizio = 0x2020;

L'esempio mostra un indirizzo assoluto, ma puoi usare qualsiasi espressione. Ad esempio, se i file degli oggetti di input utilizzano qualche altra convenzione nome-simbolo per il punto di ingresso, è possibile semplicemente assegnare il valore di qualunque simbolo contenga l'indirizzo iniziale per iniziare:

start = altro_simbolo;

(la documentazione attuale è disponibile qui )

Davvero il ldlinker crea un eseguibile con la funzione del punto di ingresso se si fornisce l'opzione della riga di comando -e(la soluzione più pratica), si fornisce il simbolo della funzione starto si immette l'indirizzo del simbolo nell'assemblatore.

Tuttavia, tieni presente che chiaramente non è garantito il funzionamento con altri linker (non so se lvm's lld abbia la stessa bandiera). Perché questo dovrebbe essere utile per scopi diversi dal fornire informazioni su tale file, non ne sono a conoscenza.


1
Se fosse Python fornirebbe test unitari.
Erik Aronesty,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.