GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi et comment certaines bibliothèques partagées sont-elles exécutables, comme si elles étaient des exécutables ?

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

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

En voici une autre dans une réponse à une question similaire sur S.O, que je vais sans vergogne plagier, modifier et ajouter un peu d'explication.

Tout d'abord, la source de 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 en disant à l'éditeur de liens qu'il s'agit d'un exécutable normal (-pie ), et de rendre sa table de symboles exportable (-Wl,-E ), de sorte qu'il peut être utilement lié.

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

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

Maintenant, nous devons voir si cela 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;
}

Utilisation de extern nous évite d'avoir à créer un en-tête. Compilez maintenant :

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 vers libtest.so .

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


Plongeons pour une réponse dans un dépôt glibc aléatoire sur GitHub. Cette version fournit une "bannière" dans le fichier version.c .

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

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

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

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

C'est donc le drapeau de l'éditeur de liens pour définir le point d'entrée de la bibliothèque. Lors de la construction d'une bibliothèque, vous pouvez fournir le -e function_name flag pour que l'éditeur de liens active un comportement exécutable. Que fait-il vraiment ? Examinons le manuel (quelque peu obsolète 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 - selon ce qui a le plus de sens pour votre mise en page.

ENTRY n'est qu'une des nombreuses manières 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 situées plus haut dans la liste remplacent les méthodes situées 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 attribuant une valeur appropriée ---

début =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 attribuer la valeur de tout symbole contenant l'adresse de début pour commencer :

début =autre_symbole;

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

Le ld l'éditeur de liens crée en fait un exécutable avec une fonction de point d'entrée si vous fournissez l'option de ligne de commande -e (qui est la solution la plus populaire), fournissez un symbole de fonction start , ou spécifiez une adresse de symbole pour l'assembleur.

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


Linux
  1. Dans un environnement vide, comment trouve-t-on les exécutables ?

  2. Introduction aux bibliothèques partagées Linux (comment créer des bibliothèques partagées)

  3. Comment afficher toutes les bibliothèques partagées utilisées par les exécutables sous Linux ?

  4. Comment vérifier quelles bibliothèques partagées sont chargées au moment de l'exécution pour un processus donné ?

  5. Comment fonctionne la liaison dynamique, son utilisation et comment et pourquoi créer une dylib

Systèmes de fichiers virtuels sous Linux :pourquoi nous en avons besoin et comment ils fonctionnent

Comment répertorier les bibliothèques partagées utilisées par les exécutables sous Linux

Linux – Pourquoi le vrai et le faux sont-ils si grands ?

Pourquoi certains Emoji N&B et d'autres sont-ils trop gros ?

Comment vérifier si une bibliothèque partagée est installée ?

Pourquoi il y a `/lib` et `/lib64` mais seulement `/bin` ?