aus dem Speicher löschen?

Lesezeit: 8 Minuten

Ich suche nach einer Möglichkeit, generierten Objektcode direkt aus dem Speicher zu laden.

Ich verstehe, dass ich, wenn ich es in eine Datei schreibe, dlopen aufrufen kann, um seine Symbole dynamisch zu laden und sie zu verknüpfen. Dies scheint jedoch ein wenig umständlich zu sein, wenn man bedenkt, dass es im Speicher beginnt, auf die Festplatte geschrieben und dann von dlopen erneut in den Speicher geladen wird. Ich frage mich, ob es eine Möglichkeit gibt, Objektcode, der im Speicher vorhanden ist, dynamisch zu verknüpfen. Soweit ich das beurteilen kann, gibt es verschiedene Möglichkeiten, dies zu tun:

  1. Bringen Sie dlopen dazu, zu denken, dass Ihr Speicherort eine Datei ist, obwohl sie den Speicher nie verlässt.

  2. Finden Sie einen anderen Systemaufruf, der das tut, wonach ich suche (ich glaube nicht, dass dies existiert).

  3. Finden Sie eine dynamische Verknüpfungsbibliothek, die Code direkt im Speicher verknüpfen kann. Offensichtlich ist es etwas schwierig, danach zu googeln, da “Dynamic Linking Library” Informationen zum dynamischen Linken von Bibliotheken aufdeckt, nicht zu Bibliotheken, die die Aufgabe des dynamischen Linkens übernehmen.

  4. Abstrahieren Sie einige APIs von einem Linker und erstellen Sie eine neue Bibliothek aus ihrer Codebasis. (Offensichtlich ist dies die am wenigsten wünschenswerte Option für mich).

Welche davon sind also möglich? machbar? Können Sie mich auf eines der Dinge hinweisen, von denen ich angenommen habe, dass sie existieren? Gibt es eine andere Möglichkeit, an die ich noch gar nicht gedacht habe?

Benutzeravatar von Parakleta
Parakleta

Ich brauchte eine Lösung dafür, weil ich ein skriptfähiges System habe, das kein Dateisystem hat (unter Verwendung von Blobs aus einer Datenbank) und binäre Plugins laden muss, um einige Skripte zu unterstützen. Dies ist die Lösung, die ich mir ausgedacht habe, die unter FreeBSD funktioniert, aber möglicherweise nicht portabel ist.

void *dlblob(const void *blob, size_t len) {
    /* Create shared-memory file descriptor */
    int fd = shm_open(SHM_ANON, O_RDWR, 0);
    ftruncate(fd, len);
    /* MemMap file descriptor, and load data */
    void *mem = mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0);
    memcpy(mem, blob, len);
    munmap(mem, len);
    /* Open Dynamic Library from SHM file descriptor */
    void *so = fdlopen(fd,RTLD_LAZY);
    close(fd);
    return so;
}

Offensichtlich fehlt dem Code jegliche Art von Fehlerprüfung usw., aber dies ist die Kernfunktionalität.

ETA: Meine anfängliche Annahme, dass fdlopen Ist POSIX falsch, scheint dies ein FreeBSD-ismus zu sein.

  • Die Leute scheinen mit einfachem dlopen davonzukommen hier.

    – jugr

    25. Februar 2017 um 9:54 Uhr

  • @yugr dein Vorschlag ist genau der triviale Fall, den der Fragesteller bereits verworfen hat.

    – Parakleta

    26. Februar 2017 um 12:04 Uhr

  • Nicht ganz, mit /run/shm Die Datei wird nie auf die Festplatte geschrieben.

    – jugr

    27. Februar 2017 um 8:34 Uhr

  • @yugr /run/shm ist nicht POSIX, es ist ein Linux-ism, und ohne es fällt die Funktion darauf zurück, nur zu schreiben /tmp. Unabhängig davon, ob die Datei es auf die Festplatte schafft (/tmp könnte auf einigen Systemen eine Ramdisk sein) müssen Sie immer noch mit dem Dateisystem interagieren, die Berechtigungen haben, es zu erstellen, kontrollieren, ob andere Leute darauf zugreifen können, sicherstellen, dass Sie die Verknüpfung ordnungsgemäß aufheben, wenn Sie fertig sind (oder abstürzen). Warum posten Sie nicht eine Antwort mit Ihrem Vorschlag und lassen die Leute kommentieren und darüber abstimmen?

    – Parakleta

    27. Februar 2017 um 21:54 Uhr

  • Nun, ich denke nicht, dass diese kleine Ergänzung wirklich eine separate Antwort verdient. Stimmen Sie dem Linuxismus zu, aber OP hat nicht ausdrücklich erwähnt, dass er eine POSIX-kompatible Lösung benötigt. Was das Dateisystem betrifft – wieder ein guter Punkt, aber ich denke, das OP kümmerte sich mehr um den tatsächlichen Festplattenzugriff (“auf die Festplatte geschrieben und dann von dlopen neu in den Speicher geladen”).

    – jugr

    27. Februar 2017 um 22:03 Uhr


Ich verstehe nicht, warum Sie darüber nachdenken dlopen, da dies viel mehr nicht-portablen Code erfordert, um das richtige Objektformat auf der Festplatte (z. B. ELF) zum Laden zu generieren. Wenn Sie bereits wissen, wie Sie Maschinencode für Ihre Architektur generieren, einfach mmap Erinnerung mit PROT_READ|PROT_WRITE|PROT_EXEC und fügen Sie Ihren Code dort ein, weisen Sie dann die Adresse einem Funktionszeiger zu und rufen Sie ihn auf. Sehr einfach.

  • Dies scheint kein sehr schöner Weg zu sein, wenn mehr als ein paar Leute sich entwickeln. Muss Ihr eingefügter Code nicht auch seine eigenen Funktionszeiger auflösen und PIC usw. sein? Es scheint nur so, als würde man eine .so kompilieren und dann in der Lage sein dlopen es wäre viel schöner.

    – mrduclaw

    19. Februar 2011 um 22:49 Uhr

  • Ich denke, es hängt davon ab, welche Art von Code Sie generieren. Ich dachte an JIT-Code für eine virtuelle Maschine/Dynrec für einen Emulator, wo es keine willkürlichen Aufrufe und keinen Zugriff auf Daten innerhalb des aufrufenden Programms geben würde.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    19. Februar 2011 um 23:25 Uhr

  • Dies ist in der Tat eine nette Möglichkeit, mit relativ einfachem, in sich geschlossenem Code umzugehen (auch: Wie oft möchten Sie am Ende des Tages wirklich, dass dynamisch generierter Code beliebige Aufrufe durchführen kann?)

    – Stefan Kanon

    20. Februar 2011 um 0:43 Uhr


  • R.. Ich habe dies sicherlich in Betracht gezogen, aber dies würde auch einen Linker erfordern, da die Ausgabe des Compilers, mit dem ich arbeite, Objektcode ist, kein Maschinencode. Aus diesem Grund habe ich dort oben die Vorschläge 3 und 4: Wenn ich dies tun würde, müsste ich eine Art plattformübergreifende Bibliothek zum dynamischen Verknüpfen im Speicher finden. Aber wenn das nicht existiert, dann ist das überhaupt keine Lösung.

    – Jeremy Salwen

    20. Februar 2011 um 2:01 Uhr

  • @Stephen Canon, tatsächlich ist dies in einigen Geschäftsbereichen eine ziemlich regelmäßige Anforderung und kommt unter Windows ziemlich häufig vor. Es ist jedoch die Art von Dingen, die Sie einmal schreiben und einfach immer wieder verwenden.

    – mrduclaw

    23. Februar 2011 um 16:59 Uhr

Es gibt keinen anderen Standardweg, als die Datei zu schreiben und sie dann wieder mit zu laden dlopen().

Möglicherweise finden Sie auf Ihrer aktuellen spezifischen Plattform eine alternative Methode. Es liegt an Ihnen zu entscheiden, ob dies besser ist als der Ansatz „Standard und (relativ) portabel“.

Da die Generierung des Objektcodes in erster Linie eher plattformspezifisch ist, sind zusätzliche plattformspezifische Techniken für Sie möglicherweise nicht von Bedeutung. Aber es ist eine Ermessensentscheidung – und hängt in jedem Fall davon ab, dass es sich um eine nicht standardmäßige Technik handelt, was relativ unwahrscheinlich ist.

  • Zählt eine Pipe auch als Filedesktriptor? Kannst du es also nicht in dlopen() leiten?

    – Imakuchen

    31. März 2012 um 15:41 Uhr

  • @imacake – es ist ein Dateideskriptor, aber keiner, den Sie suchen oder mmapping können.

    – Flexo

    15. Dezember 2012 um 10:01 Uhr

  • “Es gibt keinen anderen Standardweg, als die Datei zu schreiben und sie dann erneut zu laden” sollte in etwas wie “Sie können die Datei schreiben und laden” korrigiert werden, siehe R.. Antwort.

    – Simon

    14. April 2014 um 9:56 Uhr

  • @Simon: Wenn der zu ladende Code keine anderen Funktionen aufrufen muss (vollständig eigenständig ist), können Sie verwenden mmap() direkt und es wird wahrscheinlich funktionieren. Wenn der zu ladende Code andere Funktionen aufruft, müssen Sie die Adressen dieser Symbole mit irgendeiner Methode auflösen. Dies geschieht normalerweise durch dlopen() für dich. Wenn Sie kurzschließen dlopen()dann liegt es an Ihnen als Code-Ersteller sicherzustellen, dass Sie beispielsweise ASLR berücksichtigt haben und die richtigen Funktionsadressen an den richtigen Stellen im Code haben.

    – Jonathan Leffler

    14. April 2014 um 13:31 Uhr

  • Ein kleines Problem, das Sie beachten sollten: Unter Linux habe ich festgestellt, dass, wenn ich möchte, dass ein Programm eine .so-Datei schreibt, dlopen es, dlsym daraus, und dann ein anderes .so schreibe, dlopen es und dlsym von it, dann müssen sich die beiden .so-Dateinamen unterscheiden.

    – Mike Speer

    20. April 2019 um 0:56 Uhr

Wir haben bei Google eine Möglichkeit implementiert, dies zu tun. Leider hat die ursprüngliche glibc die Notwendigkeit nicht verstanden, so dass sie nie akzeptiert wurde. Das Featureanfrage mit Patches ist ins Stocken geraten. Es ist bekannt als dlopen_from_offset.

Das dlopen_with_offset glibc-Code ist in den glibc google/grte* Branches verfügbar. Aber niemand sollte Viel Spaß ihre eigene glibc modifizieren.

Benutzeravatar von Basile Starynkevitch
Basile Starynkevitch

Das müssen Sie nicht Belastung der im Speicher generierte Code, da er sich bereits im Speicher befindet!

Sie können jedoch – auf nicht portable Weise – Maschinencode im Speicher generieren (vorausgesetzt, er befindet sich in einem Speichersegment mmmap-ed mit PROT_EXEC Flagge).

(in diesem Fall ist kein “Verknüpfen” oder Verschieben erforderlich, da Sie Maschinencode mit eindeutigen absoluten oder relativen Adressen generieren, insbesondere zum Aufrufen externer Funktionen)

Es gibt einige Bibliotheken, die das tun: Auf GNU/Linux unter x86 oder x86-64Ich weiß von GNU-Blitz (der schnell Maschinencode generiert, der langsam läuft), DotGNU LibJIT (der Code mittlerer Qualität generiert) und LLVM & GCCJIT (der in der Lage ist, ziemlich optimierten Code im Speicher zu generieren, aber Zeit braucht, um ihn auszugeben). Und LuaJit hat auch eine ähnliche Einrichtung. Seit 2015 hat GCC 5 eine gccjit Bibliothek.

Und natürlich können Sie immer noch C-Code in einer Datei generieren, einen Compiler forken, um ihn in ein gemeinsames Objekt zu kompilieren, und diese gemeinsame Objektdatei öffnen. Ich mache das in GCC-SCHMELZ , eine domänenspezifische Sprache zur Erweiterung von GCC. In der Praxis funktioniert es ganz gut.

Nachträge

Wenn die Leistung beim Schreiben der generierten C-Datei ein Problem darstellt (dies sollte nicht der Fall sein, da das Kompilieren einer C-Datei viel langsamer ist als das Schreiben), sollten Sie einige verwenden tmpfs Dateisystem dafür (vielleicht in /tmp/ das ist oft a tmpfs Dateisystem unter Linux)

  • Diese Antwort verdient keine Abstimmung. Es interpretiert die Idee des Fragestellers völlig falsch.

    – Krypton

    3. September 2014 um 8:26 Uhr

  • Diese Antwort verdient keine Abstimmung. Es interpretiert die Idee des Fragestellers völlig falsch.

    – Krypton

    3. September 2014 um 8:26 Uhr

1392420cookie-checkaus dem Speicher löschen?

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

Privacy policy