2012-06-01 4 views
6

Ich bin mir nicht sicher, was eine gute Betreffzeile für diese Frage ist, aber hier gehen wir ...x86_64: Ist es möglich PLT/GOT Referenzen "in-line" zu ersetzen?

Um Codelokalität/Kompaktheit für einen kritischen Abschnitt des Codes zu erzwingen, suche ich nach einem Weg um eine Funktion in einer externen (dynamisch geladenen) Bibliothek durch einen "Sprungschlitz" (eine ELF R_X86_64_JUMP_SLOT Verlagerung) direkt an der Aufrufstelle aufzurufen - was der Linker normalerweise in PLT/GOT eingibt, aber diese direkt an der Aufrufstelle inlined hat .

Wenn ich emulieren der Aufruf wie:

#include <stdio.h> 
int main(int argc, char **argv) 
{ 
     asm ("push $1f\n\t" 
      "jmp *0f\n\t" 
      "0: .quad %P0\n" 
      "1:\n\t" 
      : : "i"(printf), "D"("Hello, World!\n")); 
     return 0; 
}
den Platz für ein 64-Bit-Wort zu erhalten, wird der Anruf selbst funktioniert (bitte keine Kommentare zu dieser ist glücklicher Zufall, da dies bestimmte ABI Regeln bricht - das alles sind nicht Gegenstand dieser Frage ... und kann, für meinen Fall, auf andere Weise bearbeitet/angesprochen werden, versuche ich, dieses Beispiel kurz zu halten).

Es wird folgender Montage:

0000000000000000 <main>: 
    0: bf 00 00 00 00   mov $0x0,%edi 
         1: R_X86_64_32 .rodata.str1.1 
    5: 68 00 00 00 00   pushq $0x0 
         6: R_X86_64_32 .text+0x19 
    a: ff 24 25 00 00 00 00 jmpq *0x0 
         d: R_X86_64_32S .text+0x11 
     ... 
         11: R_X86_64_64 printf 
    19: 31 c0     xor %eax,%eax 
    1b: c3      retq
aber (wegen printf als unmittelbare zu verwenden, schätze ich ...?) Die Zieladresse ist hier noch, dass der PLT Haken - die gleiche R_X86_64_64 reloc. Verknüpfen der Objektdatei mit libc in einer tatsächlichen ausführbaren Datei ergibt:
0000000000400428 <[email protected]>: 
    400428:  ff 25 92 04 10 00  jmpq *1049746(%rip)  # 5008c0 <_GLOBAL_OFFSET_TABLE_+0x20> 
[ ... ] 
0000000000400500 <main>: 
    400500:  bf 0c 06 40 00   mov $0x40060c,%edi 
    400505:  68 19 05 40 00   pushq $0x400519 
    40050a:  ff 24 25 11 05 40 00 jmpq *0x400511 
    400511:  [ .quad 400428 ] 
    400519:  31 c0     xorl %eax, %eax 
    40051b:  c3      retq 
[ ... ] 
DYNAMIC RELOCATION RECORDS 
OFFSET   TYPE    VALUE 
[ ... ] 
00000000005008c0 R_X86_64_JUMP_SLOT printf
I.e. Dies gibt immer noch die zweistufige Umleitung, die erste Übertragungsausführung an den PLT-Hook, und springt dann in den Bibliothekseinstiegspunkt.

Gibt es eine Möglichkeit, wie ich den Compiler/Assembler/Linker anweisen kann - in diesem Beispiel - "Inline" das Sprungschlitzziel unter der Adresse 0x400511? I.e. Ersetze die "lokale" (aufgelöst zur Programmverbindungszeit durch ld) R_X86_64_64 verlagern mit der "remote" (aufgelöst zur Programmladezeit durch ld.so) R_X86_64_JUMP_SLOT eins (und erzwinge nicht-Lazy-Load für diesen Abschnitt des Codes)? Vielleicht können Linker-Mapfiles dies ermöglichen - wenn ja, wie?

Edit:
Um dies deutlich zu machen, ist die Frage, wie dieses Ziel zu erreichen in einem dynamisch verknüpften ausführbaren/für eine externe Funktion, die in einer dynamischen Bibliothek nur verfügbar ist. Ja, es ist wahr statische Linken dies auf einfachere Art und Weise löst, aber:

  • gibt es Systeme (wie Solaris), wo statische Bibliotheken werden in der Regel nicht vom Hersteller geliefert
  • es Bibliotheken, die als nicht verfügbar sind entweder Sourcecode oder statische Versionen

Daher statisch Linke sind hier nicht hilfreich :(

Edit2:
ich gefunden habe, dass In einigen Architekturen (SPARC, bemerkenswerterweise, siehe section on SPARC relocations in the GNU as manual) ist GNU as in der Lage, bestimmte Arten von Verschiebungsreferenzen für den Linker direkt zu erzeugen, indem Modifikatoren verwendet werden. Mit dem zitierten SPARC würde man %gdop(symbolname) verwenden, um den Assembler dazu zu bringen, Anweisungen an den Linker zu senden, in dem es heißt: "Lege diese Verlagerung genau hier an". Intels Assembler auf Itanium kennt die @fptr(symbol)link-relocation operator für die gleiche Sache (siehe auch Abschnitt 4 in der Itanium psABI). Aber gibt es einen äquivalenten Mechanismus - etwas, um den Assembler anzuweisen, einen spezifischen Linker-Verschiebungstyp an einer bestimmten Position im Code zu emittieren - existiert für x86_64?

Ich habe auch gefunden, dass der GNU Assembler eine .reloc Direktive hat, die angeblich für diesen Zweck verwendet werden soll; noch, wenn ich versuche:

#include <stdio.h> 
int main(int argc, char **argv) 
{ 
     asm ("push %%rax\n\t" 
      "lea 1f(%%rip), %%rax\n\t" 
      "xchg %%rax, (%rsp)\n\t" 
      "jmp *0f\n\t" 
      ".reloc 0f, R_X86_64_JUMP_SLOT, printf\n\t" 
      "0: .quad 0\n" 
      "1:\n\t" 
      : : "D"("Hello, World!\n")); 
     return 0; 
}

ich einen Fehler von dem Linker erhalten (beachten Sie, dass 7 == R_X86_64_JUMP_SLOT):

error: /tmp/cc6BUEZh.o: unexpected reloc 7 in object file
Der Assembler eine Objektdatei erzeugt, für die readelf sagt:
Relocation section '.rela.text.startup' at offset 0x5e8 contains 2 entries: 
    Offset    Info    Type    Symbol's Value Symbol's Name + Addend 
0000000000000001 000000050000000a R_X86_64_32   0000000000000000 .rodata.str1.1 + 0 
0000000000000017 0000000b00000007 R_X86_64_JUMP_SLOT  0000000000000000 printf + 0 
Das ist, was ich will - aber den Linker nimm es nicht.
Der Linker tut akzeptieren nur mit R_X86_64_64 statt oben; Das erzeugt die gleiche Art von Binärdatei wie im ersten Fall ... Umleiten an [email protected] nicht die "aufgelöste" 1 ...

+0

was ist mit Vorverbinden? http://en.wikipedia.org/wiki/Prelink – JohnTortugo

+0

Siehe auch: [versucht PLT-Indirection für Anrufe zu vermeiden * innerhalb * einer einzigen gemeinsam genutzten Bibliothek] (http://stackoverflow.com/questions/36354247/how-do- i-force-gcc-zu-Anruf-eine-Funktion-direkt-im-Bild-Code). –

Antwort

-1

Sie können die ausführbare Datei statisch verknüpfen. Fügen Sie einfach -static zum endgültigen Link-Befehl hinzu, und alle indirekten Sprünge werden durch direkte Aufrufe ersetzt.

+0

Das ist nur möglich, wenn die gewünschte Bibliothek in einer statischen Version verfügbar ist :( –

+1

'printf' sollte verfügbar sein :-) – hirschhornsalz

+0

Nun, ich gebe dir das ;-) aber das Beispiel ist nur das, ein Beispiel . Sie haben recht, wenn Sie die statische Verknüpfung verwenden, wird dies behoben. Ich kann nicht :(Die eigentliche Funktion, die ich aufrufen möchte, ist nicht in libc, noch als statische lib verfügbar. Deshalb habe ich nach Technik gefragt. –

2

Um den Aufruf zu verknüpfen, benötigen Sie einen Code (.text) relocation, dessen Ergebnis die endgültige Adresse der Funktion in der dynamisch geladenen freigegebenen Bibliothek ist. Eine solche Verlagerung existiert nicht (und moderne statische Linker erlauben sie nicht) auf x86_64 unter Verwendung einer GNU-Toolchain für GNU/Linux, daher können Sie nicht den gesamten Aufruf inline einfügen, wie Sie es wünschen.

Die nächstgelegene Sie ist ein direkter Anruf über das GOT bekommen kann (verhindert PLT):

.section .rodata 
.LC0: 
    .string "Hello, World!\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
    pushq %rbp 
    movq %rsp, %rbp 
    movl $.LC0, %eax 
    movq %rax, %rdi 
    call *[email protected](%rip) 
    nop 
    popq %rbp 
    ret 
    .size main, .-main 

Dieses eine R_X86_64_GLOB_DAT Verlagerung gegen printf in der GOT durch die Sequenz oben verwendet werden soll erzeugen sollte. Sie müssen C-Code vermeiden, da der Compiler im Prolog und Epilog im Allgemeinen eine beliebige Anzahl von Registern verwenden kann, die vom Aufrufer gespeichert werden. Dies zwingt Sie, alle Register um den Funktionsaufruf asm zu speichern und wiederherzustellen oder diese Register für die spätere Verwendung zu korrumpieren in der Wrapper-Funktion. Daher ist es einfacher, den Wrapper in reine Assembly zu schreiben.

Eine weitere Option ist die Kompilierung mit -Wl,-z,now -Wl,-z,relro, die sicherstellt, dass die PLT- und PLT-bezogenen GOT-Einträge beim Start aufgelöst werden, um die Codelokalität und Kompaktheit zu erhöhen. Mit vollem RELRO müssen Sie nur Code in der PLT ausführen und auf Daten in der GOT zugreifen, zwei Dinge, die bereits irgendwo in der Cache-Hierarchie des logischen Kerns sein sollten. Wenn RELRO voll genug ist, um Ihre Anforderungen zu erfüllen, benötigen Sie keine Wrapper und Sie hätten zusätzliche Sicherheitsvorteile.

Die besten Optionen sind wirklich statische Verknüpfung oder LTO, wenn sie für Sie verfügbar sind.

+0

Beachten Sie, dass ein Anruf von Inline-Asm ist knifflig.Die aufgerufene Routine kann eine Reihe von Registern (abhängig von der ABI) Clobber, die Sie gcc wissen müssen –

+0

Absolut einverstanden Mein anfängliches Beispiel in C funktionierte, weil die Situation einfach war und der Compiler Code um den Inline-asm Funktionsaufruf recht klein erzeugte.Sie haben jedoch Recht: Vom Registerer asm-Aufruf geplätscherte Register, die vom Aufrufer gespeichert wurden, können vom Compiler zwischen dem Aufrufpunkt und dem Funktions-Wrapper-Epilog verwendet werden. Deshalb habe ich das Beispiel entfernt und die reine Montage als Empfehlung verwendet. –

+0

Gibt es eine C-Methode, um den Funktionszeiger vom GOT zu erhalten? Sicher in x86-64 Inline Asm zu tun ist klobig und kann nicht so gut sein wie reines C, weil [es gibt keine Möglichkeit, dem Compiler mitzuteilen, dass Sie die rote Zone überlisten wollen, und es geht davon aus, dass Inline-Asm dies nicht tut ] (http://stackoverflow.com/questions/34520013/using-base-pointer-register-in-c-inline-asm/34522750#34522750). Also müsstest du 'asm (" add $ -128,% rsp \ n rufen * foo @ GOTPCREL (% rip) \ n unter $ -128,% rsp ":" a "(retval):" D "(arg1), "S" (arg2), "d" (arg3): "Speicher", "rcx", "r8", ...); ' –

Verwandte Themen