Gibt es eine Option für GNU ld, um -dynamic-linker (PT_INTERP) vollständig wegzulassen?

Lesezeit: 11 Minuten

Benutzer-Avatar
R.. GitHub HÖREN SIE AUF, ICE ZU HELFEN

Ich experimentiere mit dem Konzept rein statisch verknüpfter ausführbarer PIE-Dateien unter Linux, stoße jedoch auf das Problem, dass der GNU-Binutils-Linker darauf besteht, einen PT_INTERP-Header zur Ausgabebinärdatei hinzuzufügen, wenn -pie verwendet wird, auch wenn es auch gegeben ist -static. Gibt es eine Möglichkeit, dieses Verhalten zu unterbinden? Das heißt, gibt es eine Möglichkeit, GNU ld ausdrücklich anzuweisen, bestimmte Header nicht in die Ausgabedatei zu schreiben? Vielleicht mit einem Linker-Skript?

(Bitte antworten Sie nicht mit Behauptungen, dass es nicht funktionieren wird; ich bin mir bewusst, dass das Programm noch eine Verschiebungsverarbeitung benötigt – adressenbezogene Verschiebungen nur aufgrund meiner Verwendung von -Bsymbolic – und ich habe einen speziellen Startcode anstelle des Standards Scrt1.o damit umzugehen. Aber ich kann es nicht aufrufen, ohne dass der dynamische Linker bereits eingreift und die Arbeit erledigt, es sei denn, hexedit the PT_INTERP Header aus der Binärdatei.)

  • Lassen Sie mich sehen, ob ich das gerade habe. Sie geben Ihren eigenen Einstiegspunkt an, der wiederum eine benutzerdefinierte Verschiebung durchführt, und Sie möchten nicht, dass der Kernel in den Standardinterpreter geladen wird? Was ist, wenn Sie mit Bibliotheken verknüpfen, die einen Initialisierungslauf über .init benötigen? Meiner Erfahrung nach ist es keine gute Idee, wenn Sie etwas mit Ihren ausführbaren Dateien tun möchten, es aber keine Möglichkeit gibt, es mit einer Permutation von LDFLAGS zu generieren.

    – Max Deliso

    9. Mai 2012 um 9:23 Uhr

  • Wenn ich versuchen würde, dies in das Build-System einer Anwendung zu integrieren, würde ich Ihnen zu 100 % zustimmen. Es ist ein schrecklicher Hack, dass eine Anwendung mit Linker-Optionen wie dieser herumspielt, und es würde sowieso nicht funktionieren, weil es alles erfordert .a Bibliotheken als PIC gebaut werden. Woran ich jedoch arbeite, ist eine neue Toolchain-Option, die für den Einsatz in sicherheitsorientierten Distributionen gedacht ist, wo das Ausführen des dynamischen Linkers für setuid-Binärdateien ein inakzeptables Risiko darstellt. Es ist viel einfacher bereitzustellen, wenn keine Änderungen an der erforderlich sind ld Ebene, nur bei der gcc-Spezifikation und crt eben.

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

    9. Mai 2012 um 13:39 Uhr

  • Es sieht so aus, als müssten Sie einen Patch für ld schreiben und sich dann mit ihnen darüber streiten, warum er auf trunk angewendet werden sollte. das klingt auch nach einer sehr interessanten arbeit.

    – Max Deliso

    11. Mai 2012 um 1:56 Uhr

  • Wenn es mit einem Linker-Skript möglich ist, wäre das weniger ideal als nur eine Befehlszeilenoption, aber viel besser als internes Patchen. Eine Antwort von jemandem, der sich mit Linker-Skripten auskennt, wäre sehr willkommen.

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

    11. Mai 2012 um 2:33 Uhr

  • Muss man das überhaupt weglassen? Das heißt, es wird kein Post-Build-Make-Schritt (oder was auch immer Sie bevorzugen) durchgeführt, um das zu entfernen PT_INTERP Überschrift reicht?

    – Mahmoud Al-Qudsi

    11. Mai 2012 um 3:22 Uhr

Benutzer-Avatar
rodrigo

Vielleicht bin ich naiv, aber … würde nicht ausreichen, nach dem Standard-Linker-Skript zu suchen, es zu bearbeiten und die Zeile zu entfernen, die in die verlinkt .interp Sektion?

Zum Beispiel sind in meinem Computer die Skripte drin /usr/lib/ldscripts und die fragliche Zeile ist interp : { *(.interp) } in dem SECTIONS Sektion.

Sie können das verwendete Standardskript mit dem folgenden Befehl ausgeben:

$ ld --verbose ${YOUR_LD_FLAGS} | \
    gawk 'BEGIN { s = 0 } { if ($0 ~ /^=/) s = !s; else if (s == 1) print; }'

Sie können die ändern gawk Skript leicht, um die zu entfernen interp line (oder einfach verwenden grep -v und verwenden Sie dieses Skript, um Ihr Programm zu verknüpfen.

  • Bisher ist dies wahrscheinlich der beste Ansatz. Leider müssen für jedes System neue Versionen der Linker-Skripte erstellt werden, und es kann nicht einfach auf ein vorhandenes funktionierendes Linker-Skript zurückgegriffen werden. Aber wenn ich den Proof of Concept zum Laufen bringen kann, kann ich ihn vielleicht in Upstream-Binutils bekommen.

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

    18. Mai 2012 um 2:13 Uhr

  • @R.. – Nun, mein Trick der Verwendung strace funktioniert nicht, weil das Skript tatsächlich hineinkompiliert wird ld und nicht von der Festplatte gelesen. Aber man bekommt es mit ld --verbose und ein bisschen Magie in der Ausgabe (siehe die aktualisierte Antwort.

    – rodrigo

    18. Mai 2012 um 12:59 Uhr

  • Aus diesem Code können Sie den ld-Wrapper erstellen, so wie es manche für tun gcc.

    – Alexander

    18. Mai 2012 um 18:48 Uhr

Benutzer-Avatar
R.. GitHub HÖREN SIE AUF, ICE ZU HELFEN

Ich denke, ich habe eine Lösung gefunden: einfach verwenden -shared Anstatt von -pie um Kuchenbinärdateien zu erstellen. Sie benötigen ein paar zusätzliche Linker-Optionen, um das Verhalten zu korrigieren, aber es scheint die Notwendigkeit eines benutzerdefinierten Linker-Skripts zu vermeiden. Oder anders gesagt, die -shared Das Linker-Skript ist bereits im Wesentlichen korrekt, um statische Pie-Binärdateien zu verknüpfen.

Wenn ich es damit zum Laufen bekomme, aktualisiere ich die Antwort mit der genauen Befehlszeile, die ich verwende.

Aktualisieren: Es klappt! Hier ist die Befehlszeile:

gcc -shared -static-libgcc -Wl,-static -Wl,-Bsymbolic \
    -nostartfiles -fPIE Zcrt1.s Zcrt2.c /usr/lib/crti.o hello.c /usr/lib/crtn.o

wobei Zcrt1.s eine modifizierte Version von Scrt1.s ist, die eine Funktion in Zcrt2.c aufruft, bevor sie ihre normale Arbeit erledigt, und der Code in Zcrt2.c den Aux-Vektor direkt hinter den Arrays argv und environment verarbeitet, um den Abschnitt DYNAMIC zu finden, durchläuft dann die Verschiebungstabellen und wendet alle relativen Verschiebungen an (die einzigen, die existieren sollten).

Jetzt kann all dies (mit ein wenig Arbeit) in ein Skript oder eine gcc-Spezifikationsdatei verpackt werden …

  • Netter Trick. Sie bauen im Wesentlichen auf das auf, was Sie brauchen, anstatt es wegzuziehen. +1 und viel Glück.

    – Mahmoud Al-Qudsi

    28. Mai 2012 um 3:56 Uhr

Benutzer-Avatar
Mahmoud Al-Qudsi

Um meine frühere Anmerkung zu erweitern, da dies nicht in diese winzige Schachtel passt (und dies nur als Idee oder Diskussion dient, fühlen Sie sich bitte nicht verpflichtet, ein Kopfgeld anzunehmen oder zu belohnen), vielleicht ist der einfachste und sauberste Weg, dies zu tun, zu ragen Fügen Sie einen Post-Build-Schritt hinzu, um die zu entfernen PT_INTERP Header aus der resultierenden Binärdatei?

Noch einfacher als die Kopfzeilen manuell zu bearbeiten und möglicherweise alles verschieben zu müssen, ist es, sie einfach zu ersetzen PT_INTERP mit PT_NULL. Ich weiß nicht, ob Sie einen Weg finden können, die Datei einfach über vorhandene Tools (eine Art skriptfähiges Hex-Suchen und Ersetzen) zu patchen, oder ob Sie dafür ein kleines Programm schreiben müssen. Ich weiß, dass libbfd (die GNU Binary File Descriptor Library) im letzteren Fall Ihr Freund sein könnte, da es das ganze Geschäft viel einfacher machen wird.

Ich glaube, ich verstehe einfach nicht, warum es wichtig ist, dass dies über eine durchgeführt wird ld Möglichkeit. Wenn verfügbar, kann ich sehen, warum es vorzuziehen wäre; aber wie etwas (zugegebenermaßen leichtes) Googeln darauf hinweist, dass es eine solche Funktion nicht gibt, ist es möglicherweise weniger mühsam, sie einfach separat und im Nachhinein zu tun. (Vielleicht das Hinzufügen der Flagge zu ld ist einfacher als das Ersetzen von zu scripten PT_INTERP mit PT_NULLaber die Entwickler davon zu überzeugen, es hochzuladen, ist eine andere Sache.)


Anscheinend (und bitte korrigieren Sie mich, wenn Sie das schon gesehen haben) können Sie das Verhalten von überschreiben ld in Bezug auf einen der ELF-Header in Ihrem Linker-Skript mit dem PHDRS Befehlund verwenden :none um anzugeben, dass ein bestimmter Header-Typ in keinem Segment enthalten sein soll. Ich bin mir der Syntax nicht sicher, aber ich nehme an, es würde ungefähr so ​​​​aussehen:

PHDRS
{
  headers PT_PHDR PHDRS ;
  interp PT_INTERP ;
  text PT_LOAD FILEHDR PHDRS ;
  data PT_LOAD ;
  dynamic PT_DYNAMIC ;
}

SECTIONS
{
  . = SIZEOF_HEADERS;
  .interp : { } :none
  ...
}

Von dem ld docs Sie können das Linker-Skript mit überschreiben --library-path:

--library-path=searchdir

Fügen Sie den Pfad searchdir zur Liste der Pfade hinzu, die ld nach Archivbibliotheken und ld-Kontrollskripten durchsuchen wird. Sie können diese Option beliebig oft verwenden. Die Verzeichnisse werden in der Reihenfolge durchsucht, in der sie auf der Kommandozeile angegeben werden. Auf der Befehlszeile angegebene Verzeichnisse werden vor den Standardverzeichnissen durchsucht. Alle -L-Optionen gelten für alle -l-Optionen, unabhängig von der Reihenfolge, in der die Optionen angezeigt werden. Die standardmäßig durchsuchten Pfade (ohne Angabe mit »-L«) hängen davon ab, welchen Emulationsmodus ld verwendet, und in manchen Fällen auch davon, wie er konfiguriert wurde. Siehe Abschnitt Umgebungsvariablen. Die Pfade können auch in einem Link-Skript mit dem Befehl SEARCH_DIR angegeben werden. Auf diese Weise angegebene Verzeichnisse werden an der Stelle durchsucht, an der das Linker-Skript in der Befehlszeile erscheint.

Auch von den Abschnitt über implizite Linker-Skripte:

Wenn Sie eine Linker-Eingabedatei angeben, die der Linker nicht als Objektdatei oder Archivdatei erkennen kann, versucht er, die Datei als Linker-Skript zu lesen. Wenn die Datei nicht als Linker-Skript geparst werden kann, meldet der Linker einen Fehler.

Was scheinbar Werte in benutzerdefinierten Linker-Skripten impliziert, im Gegensatz zu implizit definierten Linker-Skripten, Wille Ersetzen Sie Werte in den Standardskripten.

  • Dies ist zum Experimentieren einfach, und tatsächlich habe ich es zum Experimentieren getan. Aber es hilft überhaupt nicht, wenn das Ziel darin besteht, eine Toolchain aufzubauen, in die Sie einige zusätzliche Optionen einfügen können CFLAGS und LDFLAGS und erhalten Sie ein beliebiges Programm, um es als statischen Kuchen zu erstellen.

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

    11. Mai 2012 um 3:32 Uhr

  • Sperren eines Upstream-Patches von ld, um das Weglassen zu unterstützen PT_INTERP (vorausgesetzt, es existiert noch keine), ich glaube nicht, dass es eine Alternative gibt, die eine generische Toolchain erstellen kann.

    – Mahmoud Al-Qudsi

    11. Mai 2012 um 3:35 Uhr

  • Ich habe einige Informationen zum Überschreiben des Standardverhaltens für alle ELF-Header gefunden und meine Antwort aktualisiert. Gilt dies für Sie?

    – Mahmoud Al-Qudsi

    11. Mai 2012 um 3:52 Uhr

  • Das sieht vielversprechend aus. Gibt es eine Möglichkeit, dies mit vorhandenen Linker-Skripten zu verketten, die ld bereits verwendet, ohne das gesamte bereits darin enthaltene Material zu kopieren/duplizieren?

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

    11. Mai 2012 um 4:14 Uhr

  • Es gibt sowohl benutzerdefinierte als auch implizit definierte Linker-Skripts. Ich denke, das eine oder andere wird das tun, was Sie brauchen, weil ich mir nicht sicher bin, was die Bedeutung von “override” in den ld-Dokumenten eigentlich ist. Siehe aktualisierten Beitrag.

    – Mahmoud Al-Qudsi

    11. Mai 2012 um 4:24 Uhr

Benutzer-Avatar
Alexander

Ich bin kein Experte für GNU ld, aber ich habe die folgenden Informationen in der gefunden Dokumentation:

Der spezielle Sekname `/DISCARD/’ kann verwendet werden, um Eingabeabschnitte zu verwerfen. Alle Abschnitte, die einem Ausgabeabschnitt namens `/DISCARD/’ zugewiesen sind, werden nicht in die endgültige Linkausgabe aufgenommen.

Ich hoffe, dies wird dir helfen.

AKTUALISIEREN:

(Dies ist die erste Version der Lösung, die nicht funktioniert, da der INTERP-Abschnitt zusammen mit dem Header PT_INTERP gelöscht wird.)

Haupt c:

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

main.x:

SECTIONS {                                                                                                                                                    
    /DISCARD/ : { *(.interp) }                                                                                                                                
}

Baubefehl:

$ gcc -nostdlib -pie -static -Wl,-T,main.x main.c
$ readelf -S a.out | grep .interp

Build-Befehl ohne Option -Wl,-T,main.x:

$ gcc -nostdlib -pie -static main.c 
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000000218
$ readelf -S a.out | grep .interp
  [ 1] .interp           PROGBITS        00000134 000134 000013 00   A  0   0  1

UPDATE 2:

Die Idee dieser Lösung besteht darin, dass der ursprüngliche Abschnitt „INTERP“ (. interp in der Linker-Skriptdatei) in .interp1 umbenannt wird. Mit anderen Worten, der gesamte Inhalt des Abschnitts wird im Abschnitt .interp1 abgelegt. Daher können wir den INTERP-Abschnitt (jetzt leer) sicher entfernen, ohne befürchten zu müssen, dass die standardmäßigen Linker-Skripteinstellungen verloren gehen, und daher wird auch der Header INTERP_PT entfernt.

SECTIONS {
    .interp1 : { *(.interp); } : NONE
    /DISCARD/ : { *(.interp) }
}

Um zu zeigen, dass der Inhalt des Abschnitts INTERP in der Datei vorhanden ist (als .interp1), aber der INTERP_PT-Header entfernt wurde, verwende ich eine Kombination aus readelf + grep.

$ gcc -nostdlib -pie -Wl,-T,main.x main.c
$ readelf -l a.out | grep interp
   00     .note.gnu.build-id .text .interp1 .dynstr .hash .gnu.hash .dynamic .got.plt 
$ readelf -S a.out | grep interp
  [ 3] .interp1          PROGBITS        0000002e 00102e 000013 00   A  0   0  1

Benutzer-Avatar
Elliot

Die Option -Wl,--no-dynamic-linker löst die Probleme mit binutils 2.26 oder höher.

  • Ja, ich habe den Patch geschrieben, der es hinzugefügt hat. 🙂 Aber trotzdem +1 und willkommen auf der Seite.

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

    7. März 2019 um 18:20 Uhr


  • Funktioniert nicht in binutils 2.25, unrecognized option '--no-dynamic-linker'. Diese Option ist nur in Version 2.26 oder höher verfügbar.

    – Geringer Strom

    15. Mai 2019 um 8:00 Uhr

  • Ja, ich habe den Patch geschrieben, der es hinzugefügt hat. 🙂 Aber trotzdem +1 und willkommen auf der Seite.

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

    7. März 2019 um 18:20 Uhr


  • Funktioniert nicht in binutils 2.25, unrecognized option '--no-dynamic-linker'. Diese Option ist nur in Version 2.26 oder höher verfügbar.

    – Geringer Strom

    15. Mai 2019 um 8:00 Uhr

1245060cookie-checkGibt es eine Option für GNU ld, um -dynamic-linker (PT_INTERP) vollständig wegzulassen?

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

Privacy policy