web-dev-qa-db-fra.com

Pourquoi et comment certaines bibliothèques partagées sont-elles exécutables, comme s'il s'agissait d'exécutables?

Sur les systèmes Linux 32 bits, l'invocation de ceci

$ /lib/libc.so.6

et sur les systèmes 64 bits cette

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

dans un shell, fournit une sortie comme celle-ci:

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

Pourquoi et comment cela se produit-il, et comment est-il possible de faire de même dans d'autres bibliothèques partagées?

J'ai regardé /usr/lib pour trouver des exécutables, et j'ai trouvé /usr/lib/libvlc.so.5.5.0. Son exécution a conduit à un défaut de segmentation . : - /

57
Ho1

Cette bibliothèque a une fonction main() ou un point d'entrée équivalent, et a été compilée de telle manière qu'elle soit utile à la fois comme exécutable et comme objet partagé.

Voici ne suggestion sur la façon de procéder, bien que cela ne fonctionne pas pour moi.

Voici un autre dans ne réponse à une question similaire sur S.O , que je plagierai sans vergogne, Tweak, et ajouterai un peu d'explication.

Tout d'abord, source pour notre exemple de bibliothèque, 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;                       
}                   

Compilez cela:

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

Ici, nous compilons une bibliothèque partagée (-fPIC), mais indiquant à l'éditeur de liens qu'il s'agit d'un exécutable standard (-pie), et pour rendre sa table de symboles exportable (-Wl,-E), de sorte qu'il peut être utilement lié à.

Et, bien que file dise que c'est un objet partagé, il fonctionne comme un exécutable:

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

Maintenant, nous devons voir s'il peut vraiment être lié dynamiquement. Un exemple de programme, program.c:

#include <stdio.h>

extern void sayHello (char*);

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

L'utilisation de extern nous évite d'avoir à créer un en-tête. Maintenant, compilez cela:

gcc program.c -L. -ltest

Avant de pouvoir l'exécuter, nous devons ajouter le chemin de libtest.so pour le chargeur dynamique:

export LD_LIBRARY_PATH=./

Maintenant:

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

Et ldd a.out montrera le lien avec libtest.so.

Notez que je doute que c'est ainsi que la glibc est réellement compilée, car elle n'est probablement pas aussi portable que la glibc elle-même (voir man gcc en ce qui concerne la -fPIC et -pie commutateurs), mais il montre le mécanisme de base. Pour les vrais détails, vous devriez regarder le makefile source.

54
goldilocks

Plongeons-nous pour une réponse dans le dépôt aléatoire de glibc dans github. Cette version fournit une "bannière" dans le fichier version.c .

Dans le même fichier, il y a quelques points intéressants: __libc_print_version La fonction qui permet d'imprimer au même texte et symbole __libc_main (void) qui est documentée comme un point d'entrée. Ce symbole est donc appelé lors de l'exécution de la bibliothèque.

Alors, comment l'éditeur de liens/compilateur sait-il qu'il s'agit exactement d'une fonction de point d'entrée?

Plongeons-nous dans le makefile . Dans les drapeaux de l'éditeur de liens, il y a un drapeau intéressant:

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

Il s'agit donc d'un indicateur de l'éditeur de liens pour définir le point d'entrée dans la bibliothèque. Lors de la construction d'une bibliothèque, vous pouvez fournir -e function_name Pour que l'éditeur de liens crée un comportement exécutable. Que fait-il vraiment? Regardons dans le manuel (un peu daté mais toujours valide) :

Le langage de commande de l'éditeur de liens comprend une commande spécifiquement pour définir la première instruction exécutable dans un fichier de sortie (son point d'entrée). Son argument est un nom de symbole:

ENTRÉE (symbole)

Comme les affectations de symboles, la commande ENTRY peut être placée soit en tant que commande indépendante dans le fichier de commandes, soit parmi les définitions de section dans la commande SECTIONS - ce qui a le plus de sens pour votre mise en page.

L'ENTRÉE n'est qu'une des nombreuses façons de choisir le point d'entrée. Vous pouvez l'indiquer de l'une des manières suivantes (affichées par ordre décroissant de priorité: les méthodes plus haut dans la liste remplacent les méthodes plus bas).

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. 

Par exemple, vous pouvez utiliser ces règles pour générer un point d'entrée avec une instruction d'affectation: si aucun début de symbole n'est défini dans vos fichiers d'entrée, vous pouvez simplement le définir en lui affectant une valeur appropriée ---

start = 0x2020;

L'exemple montre une adresse absolue, mais vous pouvez utiliser n'importe quelle expression. Par exemple, si vos fichiers d'objets d'entrée utilisent une autre convention de nom de symbole pour le point d'entrée, vous pouvez simplement affecter la valeur du symbole contenant l'adresse de début à démarrer:

start = autre_symbole;

(la documentation actuelle peut être trouvée ici )

Vraiment ld l'éditeur de liens crée un exécutable avec une fonction de point d'entrée si vous lui fournissez l'option de ligne de commande -e (Solution la plus pratique), fournissez le symbole de fonction start ou injectez l'adresse du symbole en assembleur.

Cependant, veuillez noter qu'il n'est clairement pas garanti de fonctionner avec d'autres éditeurs de liens (je ne sais pas si le lld de llvm a le même indicateur). Pourquoi cela devrait être utile à des fins autres que la fourniture d'informations sur ce fichier, je ne sais pas.

21
IBr