Comment rendre un object partagé Linux (bibliothèque) exécutable seul?

Remarquant que gcc -shared crée un fichier exécutable, j’ai juste eu l’idée bizarre de vérifier ce qui se passait quand j’essayais de l’exécuter … eh bien le résultat était un segfault pour ma propre lib. Donc, étant curieux à ce sujet, j’ai essayé de “lancer” la glibc ( /lib/x86_64-linux-gnu/libc.so.6 sur mon système). Bien sûr, il ne s’est pas écrasé mais m’a fourni des résultats:

 GNU C Library (Debian GLIBC 2.19-18) stable release version 2.19, by Roland McGrath et al. Copyright (C) 2014 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. Comstackd by GNU CC version 4.8.4. Comstackd on a Linux 3.16.7 system on 2015-04-14. Available extensions: 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 libc ABIs: UNIQUE IFUNC For bug reporting instructions, please see: . 

Donc, ma question est la suivante: quelle est la magie derrière cela? Je ne peux pas simplement définir un symbole main dans une bibliothèque – ou puis-je?

J’ai écrit un article de blog sur ce sujet où je vais plus en profondeur parce que je l’ai trouvé insortinggant. Vous pouvez trouver ma réponse originale ci-dessous.


Vous pouvez spécifier un point d’entrée personnalisé à l’éditeur de liens avec l’ -Wl,-e,entry_point à gcc, où entry_point est le nom de la fonction “principale” de la bibliothèque.

 void entry_point() { printf("Hello, world!\n"); } 

L’éditeur de liens ne s’attend pas à ce que quelque chose lié à -shared soit exécuté en tant qu’exécutable, et il doit recevoir plus d’informations pour que le programme soit exécutable. Si vous essayez d’exécuter la bibliothèque maintenant, vous rencontrerez une erreur de segmentation.

La section .interp fait partie du fichier binary résultant requirejs par le système d’exploitation pour exécuter l’application. Il est défini automatiquement par l’éditeur de liens si l’ -shared n’est pas utilisée. Vous devez définir cette section manuellement dans le code C si vous créez une bibliothèque partagée que vous souhaitez exécuter par elle-même. Voir cette question .

Le travail de l’interprète consiste à rechercher et à charger les bibliothèques partagées nécessaires à un programme, à préparer le programme à exécuter, puis à l’exécuter. Pour le format ELF (omniprésent pour modern * nix) sous Linux, le programme ld-linux.so est utilisé. Voir sa page de manuel pour plus d’informations.

La ligne ci-dessous met une chaîne dans la section .interp en utilisant les atsortingbuts GCC . Placez ceci dans la scope globale de votre bibliothèque pour indiquer explicitement à l’éditeur de liens que vous souhaitez inclure un chemin de l’éditeur de liens dynamic dans votre fichier binary.

 const char interp_section[] __atsortingbute__((section(".interp"))) = "/path/to/ld-linux"; 

La manière la plus simple de trouver le chemin d’access à ld-linux.so est d’exécuter ldd sur n’importe quelle application normale. Exemple de sortie de mon système:

 jacwah@jacob-mint17 ~ $ ldd $(which gcc) linux-vdso.so.1 => (0x00007fff259fe000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007faec5939000) /lib64/ld-linux-x86-64.so.2 (0x00007faec5d23000) 

Une fois que vous avez spécifié l’interpréteur, votre bibliothèque devrait être exécutable! Il y a juste un léger défaut: il va entry_point quand entry_point retourne.

Lorsque vous comstackz un programme avec main , ce n’est pas la première fonction à appeler lors de son exécution. main est en fait appelée par une autre fonction appelée _start . Cette fonction est chargée de configurer argv et argc et d’autres initialisations. Il appelle alors main . Lorsque main retourne, _start appelle exit avec la valeur de retour de main .

Il n’y a pas d’adresse de retour sur la stack dans _start car c’est la première fonction à appeler. Si elle essaie de revenir, une lecture non valide se produit (provoquant finalement une erreur de segmentation). C’est exactement ce qui se passe dans notre fonction de point d’entrée. Ajoutez un appel pour exit tant que dernière ligne de votre fonction d’entrée pour nettoyer correctement et ne pas planter.

exemple.c

 #include  #include  const char interp_section[] __atsortingbute__((section(".interp"))) = "/path/to/ld-linux"; void entry_point() { printf("Hello, world!\n"); exit(0); } 

Comstackr avec gcc example.c -shared -fPIC -Wl,-e,entry_point .