eine .so erstellen, die auch eine ausführbare Datei ist

Lesezeit: 6 Minuten

Benutzeravatar von user175104
Benutzer175104

Jeder kennt also wahrscheinlich die glibc /lib/libc.so.6 kann in der Shell wie eine normale ausführbare Datei ausgeführt werden, in welchen Fällen es seine Versionsinformationen ausgibt und beendet wird. Dies erfolgt über die Definition eines Einstiegspunkts in der .so. In einigen Fällen könnte es interessant sein, dies auch für andere Projekte zu verwenden. Leider ist der Einstiegspunkt auf niedriger Ebene, den Sie mit der Option -e von ld festlegen können, etwas zu niedrig: Der dynamische Loader ist nicht verfügbar, sodass Sie keine richtigen Bibliotheksfunktionen aufrufen können. glibc implementiert aus diesem Grund den Systemaufruf write() über einen nackten Systemaufruf in diesem Einstiegspunkt.

Meine Frage ist jetzt, kann sich jemand einen netten Weg vorstellen, wie man einen vollständigen dynamischen Linker von diesem Einstiegspunkt booten könnte, damit man auf Funktionen von anderen .so’s zugreifen könnte?

Benutzeravatar des beschäftigten Russen
Angestellter Russe

Aktualisierung 2: siehe die etwas kompliziertere Lösung von Andrew G Morgan, die für jedes GLIBC funktioniert (diese Lösung wird auch in verwendet libc.so.6 selbst (seit Ewigkeiten), weshalb man es so ausführen kann ./libc.so.6 (es gibt Versionsinformationen aus, wenn es auf diese Weise aufgerufen wird)).

Aktualisierung 1: dies funktioniert mit neueren GLIBC-Versionen nicht mehr:

./a.out: error while loading shared libraries: ./pie.so: cannot dynamically load position-independent executable

Ursprüngliche Antwort von 2009:

Erstellen Sie Ihre gemeinsame Bibliothek mit -pie Option erscheint, um Ihnen alles zu geben, was Sie wollen:

/* pie.c */
#include <stdio.h>
int foo()
{
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return 42; 
}
int main() 
{ 
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return foo(); 
}


/* main.c */
#include <stdio.h>

extern int foo(void);
int main() 
{ 
  printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
  return foo(); 
}


$ gcc -fPIC -pie -o pie.so pie.c -Wl,-E
$ gcc main.c ./pie.so


$ ./pie.so
in main pie.c:9
in foo pie.c:4
$ ./a.out
in main main.c:6
in foo pie.c:4
$

PS glibc implementiert write(3) per Systemaufruf, weil es nirgendwo anders anrufen muss (es ist die am niedrigsten bereits Niveau). Das hat nichts mit Ausführungsmöglichkeit zu tun libc.so.6.

  • Das fand ich -shared Option verhindert möglicherweise, dass dies funktioniert, und gibt Ihnen einen Segmentierungsfehler. Sie müssen sicherstellen, dass -shared Option ist nicht da oder setzen Sie es vor -pie wird also ignoriert.

    – David Grayson

    6. Juli 2015 um 5:11 Uhr

  • Das ist also so einfach wie das Hinzufügen von main() und -pie zu meiner gemeinsam genutzten Bibliothek?

    – Ich liebe Mathe

    9. April 2020 um 4:54 Uhr

  • Dies funktioniert nicht mehr mit neueren GLIBC: ./a.out: error while loading shared libraries: ./pie.so: cannot dynamically load position-independent executable.

    – Angestellter Russe

    23. März 2021 um 4:20 Uhr

  • Es sieht so aus, als gäbe es eine Antwort, die jetzt funktioniert.

    – Tüftler

    12. Juli 2021 um 4:47 Uhr

Benutzeravatar von Andrew G. Morgan
Andrew G Morgan

Ich habe gesucht, um Unterstützung dafür hinzuzufügen pam_cap.so, und fand diese Frage. Wie @EmployedRussian in einem Follow-up zu ihrem eigenen Beitrag feststellt, funktionierte die akzeptierte Antwort irgendwann nicht mehr. Es hat eine Weile gedauert, bis ich herausgefunden habe, wie man das wieder zum Laufen bringt, also ist hier ein funktionierendes Beispiel.

Dieses ausgearbeitete Beispiel umfasst 5 Dateien, um zu zeigen, wie die Dinge mit einigen entsprechenden Tests funktionieren.

Betrachten Sie zunächst dieses triviale Programm (nennen Sie es empty.c):

int main(int argc, char **argv) { return 0; }

Beim Kompilieren können wir sehen, wie es die dynamischen Symbole auf meinem System wie folgt auflöst:

$ gcc -o empty empty.c
$ objcopy --dump-section .interp=/dev/stdout empty ; echo
/lib64/ld-linux-x86-64.so.2
$ DL_LOADER=/lib64/ld-linux-x86-64.so.2

Diese letzte Zeile setzt eine Shell-Variable zur späteren Verwendung.

Hier sind die beiden Dateien, die meine gemeinsam genutzte Beispielbibliothek erstellen:

/* multi.h */
void multi_main(void);
void multi(const char *caller);

und

/* multi.c */
#include <stdio.h>
#include <stdlib.h>
#include "multi.h"

void multi(const char *caller) {
    printf("called from %s\n", caller);
}

__attribute__((force_align_arg_pointer))
void multi_main(void) {
    multi(__FILE__);
    exit(42);
}

const char dl_loader[] __attribute__((section(".interp"))) =
    DL_LOADER ;

(Update 13.11.2021: Die Zwangsausrichtung soll helfen __i386__ Code SSE-kompatibel sein – ohne ihn werden wir schwer zu debuggen glibc SIGSEGV stürzt ab.)

Wir können es wie folgt kompilieren und ausführen:

$ gcc -fPIC -shared -o multi.so -DDL_LOADER="\"${DL_LOADER}\"" multi.c -Wl,-e,multi_main
$ ./multi.so
called from multi.c
$ echo $?
42

Das ist also ein .so die als eigenständige Binärdatei ausgeführt werden kann. Als nächstes validieren wir, dass es als gemeinsam genutztes Objekt geladen werden kann.

/* opener.c */
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    void *handle = dlopen("./multi.so", RTLD_NOW);
    if (handle == NULL) {
        perror("no multi.so load");
        exit(1);
    }
    void (*multi)(const char *) = dlsym(handle, "multi");
    multi(__FILE__);
}

Das heißt, wir laden das gemeinsam genutzte Objekt dynamisch und führen eine Funktion daraus aus:

$ gcc -o opener opener.c -ldl
$ ./opener
called from opener.c

Schließlich verlinken wir mit diesem gemeinsamen Objekt:

/* main.c */
#include "multi.h"

int main(int argc, char **argv) {
    multi(__FILE__);
}

Wo wir es wie folgt kompilieren und ausführen:

$ gcc main.c -o main multi.so
$ LD_LIBRARY_PATH=./ ./main
called from main.c

(Anmerkung, weil multi.so sich nicht an einem standardmäßigen Systembibliotheksspeicherort befindet, müssen wir den Ort, an dem die Laufzeitumgebung nach der gemeinsam genutzten Objektdatei sucht, mit der überschreiben LD_LIBRARY_PATH Umgebungsvariable.)

Ich nehme an, du hättest deine ld -e zeigen Sie auf einen Einstiegspunkt, der dann die verwenden würde dlopen() Funktionsfamilie, um den Rest des dynamischen Linkers zu finden und zu bootstrappen. Dafür müsstest du natürlich sorgen dlopen() selbst war entweder statisch gelinkt oder Sie müssen möglicherweise genug von Ihrem eigenen Linker-Stub implementieren, um darauf zuzugreifen (unter Verwendung von Systemaufrufschnittstellen wie z mmap() genauso wie es die libc selbst tut.

Nichts davon klingt für mich “nett”. Eigentlich nur der Gedanke, die Glibc-Quellen (und die ld-linux Quellcode, als ein Beispiel) ausreicht, um die Größe des Auftrags einzuschätzen, klingt für mich ziemlich altmodisch. Es könnte auch ein Albtraum der Portabilität sein. Es kann große Unterschiede zwischen der Implementierung von Linux geben ld-linux und wie die Verknüpfungen unter OpenSolaris, FreeBSD usw. durchgeführt werden. (Ich weiß nicht).

1411380cookie-checkeine .so erstellen, die auch eine ausführbare Datei ist

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy