Pourquoi et comment certaines bibliothèques partagées peuvent-elles être exécutées, comme si elles étaient exécutables?

56

Sur les systèmes Linux 32 bits, l'appel de cette

$ /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 la même chose dans d'autres bibliothèques partagées?

J'ai regardé /usr/libpour trouver des exécutables, et j'ai trouvé /usr/lib/libvlc.so.5.5.0. L'exécuter a conduit à une erreur de segmentation . : - /

Ho1
la source
En plus de toutes les réponses suivantes, je me souviens que si vous définissez le bit x sur une bibliothèque partagée, il était (peut-être encore) possible de le charger à partir d’un fichier exécutable même avec le bit r vide. Auparavant, il était considéré comme une bonne pratique de sécurité de bannir le monde des exécutables système et des bibliothèques. En raison de l'open source généralisé, cela ne s'applique plus vraiment que dans le répertoire / bin / ls du répertoire ftp anonyme. Pour moi, quitter le jeu de bits x ressemble à un visage de cette ancienne pratique.
Josué

Réponses:

53

Cette bibliothèque a une main()fonction ou un point d’entrée équivalent et a été compilée de telle sorte qu’elle soit 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.

Voici une autre réponse à une question similaire sur SO , que je vais plagier sans vergogne, peaufiner et ajouter 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;                       
}                   

Compiler que:

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

Ici, nous compilons une bibliothèque partagée ( -fPIC), mais nous disons à l'éditeur de liens qu'il s'agit d'un exécutable ( -pie) régulier et nous rendons sa table de symboles exportable ( -Wl,-E), de manière à ce qu'elle puisse être liée utilement.

Et, bien que filedira que c'est un objet partagé, cela 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;
}

L'utilisation externnous évite d'avoir à créer un en-tête. Maintenant compilez ça:

gcc program.c -L. -ltest

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

export LD_LIBRARY_PATH=./

Maintenant:

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

Et ldd a.outmontrera le lien vers libtest.so.

Notez que je doute que c’est ainsi que glibc soit réellement compilée, car elle n’est probablement pas aussi portable que glibc elle-même (voir man gccen ce qui concerne les commutateurs -fPICet -pie), mais elle illustre le mécanisme de base. Pour plus de détails, vous devriez regarder le fichier makefile source.

boucle d'or
la source
1
Super réponse, merci! :-) J'ai essayé d'utiliser nmsur la bibliothèque partagée, mais ce n'était pas une version de débogage. Alors, pourquoi libvlcet d'autres crash?
Ho1
1
Comme la plupart des bibliothèques partagées ne sont pas destinées à être exécutables, GNU libcest une exception.
goldilocks
J'ai trouvé deux autres: ldet libpthread.
Ho1
@ Ho1 ld.soest spécial à d'autres égards. Dans une certaine mesure, il s’agit plus d’un véritable exécutable que d’un exécutable normal lié dynamiquement.
Random832
1
Les options ci-dessus, même si vous créez la bibliothèque exécutable-partagée, mais sont incomplètes en ce sens qu'elles signalent une erreur, lorsque certains exécutables essaient de créer un lien avec cela. Exemple de
parasrish
21

Plongeons-nous pour une réponse dans un dépôt aléatoire 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_versionla fonction qui permet d’imprimer sur stdin le même texte et le même symbole, __libc_main (void)documentés comme point d’entrée. Donc, ce symbole est 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?

Nous allons plonger 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 d’éditeur de liens pour la définition du point d’entrée dans la bibliothèque. Lors de la construction d'une bibliothèque, vous pouvez fournir -e function_nameà l'éditeur de liens un comportement exécutable. Qu'est-ce que ça fait vraiment? Regardons le manuel (un peu daté mais toujours valable) :

Le langage de commande de l'éditeur de liens comprend une commande permettant de 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 pour les affectations de symboles, la commande ENTRY peut être placée en tant que commande indépendante dans le fichier de commandes ou dans les définitions de section de la commande SECTIONS - selon vos préférences.

L'ENTRÉE n'est qu'un moyen parmi d'autres de choisir le point d'entrée. Vous pouvez l'indiquer de l'une des façons suivantes (en ordre de priorité décroissant: les méthodes situées plus haut dans la liste remplacent les méthodes plus basses).

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'objet d'entrée utilisent une autre convention de nom de symbole pour le point d'entrée, vous pouvez simplement affecter la valeur de tout symbole contenant l'adresse de début à démarrer:

start = autre_symbole;

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

Vraiment, l' ldéditeur de liens crée un exécutable avec une fonction de point d'entrée si vous lui fournissez une option de ligne de commande -e(solution la plus pratique), un symbole de fonction startou une adresse de symbole dans l'assembleur.

Cependant, veuillez noter qu'il n'est clairement pas garanti de travailler avec d'autres linkers (je ne sais pas si llvm's lld a le même drapeau). Pourquoi cela devrait-il être utile à des fins autres que la fourniture d'informations sur un fichier, je ne le sais pas.

IBr
la source
1
S'il s'agissait de python, il fournirait des tests unitaires.
Erik Aronesty