2012-06-01 7 views
6

Nie jestem pewien, co jest dobrym tematem dla tego pytania, ale tutaj ...x86_64: Czy możliwe jest "zastępowanie w linii" referencji PLT/GOT?

W celu wymuszenia lokalizacji kodu/zwartości dla krytycznego fragmentu kodu, szukam sposobu wywoływanie funkcji w zewnętrznej (dynamicznie ładowanej) bibliotece przez "slot skoku" (przeniesienie ELF R_X86_64_JUMP_SLOT) bezpośrednio na stronę wywołania - co linker zwykle umieszcza w PLT/GOT, ale ma te umieszczone bezpośrednio na stronie połączenia .

Gdybym emulować wywołanie jak:

#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; 
}
uzyskać przestrzeń dla słowa 64-bitowego, sama rozmowa działa (proszę, bez komentarzy na temat tej istoty szczęśliwym zbiegiem okoliczności, ponieważ łamie pewne zasady ABI - wszystko to nie są przedmiotem niniejszej pytanie ... i, w moim przypadku, można pracować nad innymi adresami, staram się zachować ten krótki przykład).

Tworzy następujący zestaw:

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
Ale (z powodu korzystania printf jako natychmiastowe, chyba ...?) Adres docelowy jest tu nadal, że haka PLT - ten sam R_X86_64_64 reloc. Łączenie pliku obiektu z libc do rzeczywistego pliku wykonywalnego daje w wyniku:
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
, tj. nadal daje to dwukierunkowe przekierowanie, pierwsze wykonanie transferu do haka PLT, a następnie przeskoczenie do punktu wejścia biblioteki.

Czy istnieje sposób, w jaki mogę polecić kompilatorowi/assemblerowi/łącznikowi do - w tym przykładzie - "inline" docelowego gniazda skoku pod adresem 0x400511? To znaczy. zastąpić "lokalny" (rozwiązany w czasie połączenia programu przez ld) R_X86_64_64 przenieść z "zdalnego" (rozwiązany w czasie ładowania programu przez ld.so) R_X86_64_JUMP_SLOT jeden (i wymusić non-lazy-load dla tej sekcji kodu)? Może pliki map linkerów mogą to umożliwić - jeśli tak, to w jaki sposób?

Edit:
Aby to jasne, pytanie o to, jak to osiągnąć w dynamicznie połączonej wykonywalnego/do zewnętrznej funkcji, która jest dostępna tylko w dynamicznej biblioteki. Tak, to prawda statyczne powiązanie rozwiązuje to w prostszy sposób, ale:

  • istnieją systemy (jak Solaris) gdzie biblioteki statyczne nie są zazwyczaj dostarczane przez sprzedawcę
  • istnieją biblioteki, które nie są dostępne albo sourcecode lub wersje statyczne

Stąd statyczne powiązanie nie jest pomocne tutaj :(

Edit2:
Odkryłam, że w niektórych architekturach (SPARC, zauważalnie, patrz section on SPARC relocations in the GNU as manual), GNU as jest w stanie utworzyć pewne typy odwołań relokacji dla łącznika w miejscu przy użyciu modyfikatorów . Cytowany SPARC użyje %gdop(symbolname), aby zmusić asembler do wysłania instrukcji do linkera z napisem "stwórz tutaj relokację". Asembler Intela na Itanium zna @fptr(symbol)link-relocation operator dla tego samego rodzaju rzeczy (patrz także sekcja 4 w Itanium psABI). Ale czy mechanizm równoważny - coś, co nakazuje asemblerowi emitowanie określonego typu relokacji linkera na określonej pozycji w kodzie - istnieje dla x86_64?

Odkryłem również, że asembler GNU ma dyrektywę .reloc, która podobno ma być używana do tego celu; Nadal, jeśli spróbuję:

#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; 
}

pojawia się błąd z łącznikiem (zauważ, że 7 == R_X86_64_JUMP_SLOT):

error: /tmp/cc6BUEZh.o: unexpected reloc 7 in object file
Assembler tworzy plik obiektu, dla którego readelf mówi:
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 
To jest to, co chcę - ale linker nie bierze tego.
Łącznik ma zaakceptować tylko za pomocą R_X86_64_64 zamiast powyżej; w ten sposób tworzy się ten sam rodzaj binarny jak w pierwszym przypadku ... przekierowanie do [email protected] nie "rozwiązane" ...

+0

co z prelinkowaniem? http://en.wikipedia.org/wiki/Prelink – JohnTortugo

+0

Zobacz też: [próbuje uniknąć indeksu PLT dla połączeń * w * jednej wspólnej bibliotece] (http://stackoverflow.com/questions/36354247/how-do- i-force-gcc-to-call-a-function-bezpośrednio-w-pic-code). –

Odpowiedz

-1

Możesz statycznie połączyć plik wykonywalny. Wystarczy dodać -static do końcowego polecenia łącza, a wszystkie pośrednie skoki zostaną zastąpione przez bezpośrednie połączenia.

+0

Możesz to zrobić tylko wtedy, gdy biblioteka, na którą kierujesz reklamy, jest dostępna w wersji statycznej :( –

+1

'printf' powinien być dostępny :-) – hirschhornsalz

+0

No cóż, daję ci to ;-) ale przykład jest właśnie taki, przykład . Masz rację, że jeśli możesz użyć linku statycznego, to rozwiązuje to. Nie mogę :(Rzeczywista funkcja, którą próbuję wywołać, nie znajduje się w bibliotece libc ani nie jest dostępna jako statyczna biblioteka lib. Dlatego właśnie zapytałem o technikę: –

2

Aby wprowadzić połączenie, potrzebny jest kod (.text) relokacja, której wynikiem jest końcowy adres funkcji w dynamicznie ładowanej bibliotece współdzielonej. Nie istnieje taka relokacja (a nowoczesne łącza statyczne nie pozwalają na to) na x86_64 przy użyciu zestawu narzędzi GNU dla systemu GNU/Linux, dlatego nie można wstawiać całego wywołania, jak chcesz.

najbliżej można dostać to bezpośrednie połączenie przez GOT (unika 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 

ten powinien wygenerować R_X86_64_GLOB_DAT relokacji przed printf w GOT być wykorzystywane przez sekwencję powyżej. Musisz unikać kodu C, ponieważ ogólnie kompilator może używać dowolnej liczby rejestrów zapisanych przez wywołującego w prologu i epilogu, a to zmusza Cię do zapisania i przywrócenia wszystkich takich rejestrów wokół wywołania funkcji asm lub ryzyka uszkodzenia rejestrów do późniejszego wykorzystania w funkcji opakowania. Dlatego łatwiej jest napisać opakowanie w czystym zestawie.

Inną opcją jest kompilacja z -Wl,-z,now -Wl,-z,relro, która zapewnia, że ​​pozycje GOT związane z PLT i PLT są rozwiązywane podczas uruchamiania w celu zwiększenia miejsca kodu i zwartości. Przy pełnym RELRO będziesz musiał tylko uruchomić kod w PLT i uzyskać dostęp do danych w GOT, dwie rzeczy, które powinny znajdować się gdzieś w hierarchii pamięci podręcznej logicznego rdzenia. Jeśli pełny RELRO jest wystarczający do zaspokojenia twoich potrzeb, nie będziesz potrzebował opakowań i będziesz mieć dodatkowe zabezpieczenia.

Najlepsze opcje to połączenie statyczne lub LTO, jeśli są dostępne dla Ciebie.

+0

Zauważ, że wykonanie połączenia z inline asm jest trudne. Wywołana procedura może zablokować kilka rejestrów (w zależności od abi), które musisz poinformować gcc o –

+0

Absolutnie uzgodniony Mój początkowy przykład w C działał, ponieważ sytuacja była prosta, a kompilator wygenerował całkiem mały kod wokół wywołania funkcji inline inline.Jednak masz rację, rejestry zapisywane przez wywoływanie, które są clobbered przez inline asm call, mogą być używane przez kompilator między punktem wywołania a epilogiem wrapperów funkcji. Dlatego usunąłem przykład i użyłem czystego zestawu jako zalecenia. –

+0

Czy istnieje sposób C do wyrażenia uzyskanie wskaźnika funkcji z GOT? Robiąc to bezpiecznie w x86-64 wbudowany asm jest nieostry i nie może być tak dobry jak czysty C, ponieważ [nie ma sposobu, aby powiedzieć kompilatorowi, który chcesz odciąć strefę czerwoną, i zakłada, że ​​inline asm nie ] (http://stackoverflow.com/questions/34520013/using-base-pointer-register-in-c-inline-asm/34522750#34522750). Więc musisz "asm" ("dodać -128,% rsp \ n zadzwoń * foo @ GOTPCREL (% rip) \ n sub -128,% rsp": "a" (retval): "D" (arg1), "S" (arg2), "d" (arg3): "pamięć", "rcx", "r8", ...); ' –

Powiązane problemy