Dlaczego pozycja skompilowanego kodu nie jest niezależna?

85

Podczas kompilowania bibliotek współdzielonych w gcc opcja -fPIC kompiluje kod jako niezależny od pozycji. Czy jest jakiś powód (wydajność lub inny), dla którego nie miałbyś kompilować wszystkich pozycji kodu niezależnie?

ojblass
źródło
2
Ale wowest nie jest całkowicie poprawny. Wiele wywołań funkcji i skoków używa skoków względnych, więc po przeniesieniu nie potrzebują nawet tabeli skoków.
Nieznany
patrząc na wygenerowany kod asemblera wygląda na to, że adres funkcji jest załadowany, a kod inny niż fpic wygląda na to, że jest to po prostu skok. Czy źle zrozumiałem twoje stwierdzenie?
ojblass
@ojblass Mam na myśli to, że niektóre skoki są jak „przeskocz o 50 instrukcji do przodu” lub „przeskocz o 5 instrukcji wstecz” zamiast „skoku do 0x400000”. Zatem stwierdzenie, że za każdym razem musisz ładować adres za pomocą -fPIC, nie jest do końca prawdą.
Nieznany
Wikipedii artykuł zawiera opis dobrego. Zasadniczo na niektórych architekturach nie ma bezpośredniego sposobu, aby przejść do adresu względnego. Dlatego PIC jest droższy w użyciu na tych łukach. Zobacz odpowiedź @ EvanTeran, aby uzyskać więcej informacji.
Alexei Sholik

Odpowiedzi:

67

Dodaje pośrednictwo. Z kodem niezależnym od pozycji musisz załadować adres swojej funkcji, a następnie przeskoczyć do niego. Zwykle adres funkcji jest już obecny w strumieniu instrukcji.

najwspanialszy
źródło
33

Ten artykuł wyjaśnia, jak działa PIC i porównuje go z alternatywą - relokacją czasu ładowania . Myślę, że ma to znaczenie dla twojego pytania.

Eli Bendersky
źródło
16
@Nick: Nie zgadzam się. Jeśli to pomaga pytającemu, to jest odpowiedź. Wskazanie na odpowiedni artykuł lub dwa może dostarczyć wielu informacji.
Eli Bendersky,
5
W tym poście nie ma konkluzji, tylko link do artykułu. Nie ma nawet wskazówki, że PIC nie jest używany domyślnie z powodu problemów z wydajnością.
Nick
10
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest zawrzeć tutaj zasadnicze części odpowiedzi i podać link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
Rob
4
@Rob: produktywną rzeczą byłoby zasugerowanie zmiany i nie używanie komentarzy do marudzenia. Ta odpowiedź ma 4 lata. Wtedy SO miał mniej rygorystyczne zasady dotyczące tego, jak powinna wyglądać odpowiedź
Eli Bendersky
6
Ten post pojawił się w sekcji „przegląd” z prośbą o zrobienie tego i tak zrobiłem. Ktoś inny to oznaczył. Ten „jęczący komentarz” jest automatycznie tworzony przez SO, nie przeze mnie.
Rob
27

Tak, istnieją powody dotyczące wydajności. Niektóre dostępy są efektywnie objęte inną warstwą pośrednictwa, aby uzyskać bezwzględną pozycję w pamięci.

Istnieje również GOT (Global offset table), który przechowuje przesunięcia zmiennych globalnych. Dla mnie wygląda to po prostu jak tabela poprawek IAT, która jest klasyfikowana jako zależna od pozycji przez Wikipedię i kilka innych źródeł.

http://en.wikipedia.org/wiki/Position_independent_code

Nieznany
źródło
23

Oprócz zaakceptowanej odpowiedzi. Jedną z rzeczy, która bardzo szkodzi wydajności kodu PIC, jest brak „względnego adresowania IP” na x86. W przypadku „adresowania względnego IP” można poprosić o dane w postaci X bajtów z bieżącego wskaźnika instrukcji. To znacznie uprościłoby kod PIC.

Skoki i wezwania są zwykle związane z EIP, więc tak naprawdę nie stanowią problemu. Jednak dostęp do danych będzie wymagał dodatkowej sztuczki. Czasami rejestr zostanie tymczasowo zarezerwowany jako „wskaźnik bazowy” danych, których wymaga kod. Na przykład powszechną techniką jest nadużywanie sposobu działania wywołań na platformie x86:

call label_1
.dd 0xdeadbeef
.dd 0xfeedf00d
.dd 0x11223344
label_1:
pop ebp            ; now ebp holds the address of the first dataword
                   ; this works because the call pushes the **next**
                   ; instructions address
                   ; real code follows
mov eax, [ebp + 4] ; for example i'm accessing the '0xfeedf00d' in a PIC way

Ta i inne techniki dodają warstwę pośredniego dostępu do danych. Na przykład GOT (Global offset table) używany przez kompilatory gcc.

x86-64 dodał tryb „względny RIP”, który znacznie upraszcza sprawę.

Evan Teran
źródło
1
IIRC MIPS nie ma również adresowania względem PC, z wyjątkiem skoków względnych
phuclv
1
Jest to powszechna technika używana w szelkodzie, aby uzyskać adres, z którego jest wykonywany. Użyłem tego w kilku rozwiązaniach CTF.
sherrellbc
2

Ponieważ implementacja kodu całkowicie niezależnego od pozycji dodaje ograniczenie do generatora kodu, co może uniemożliwić użycie szybszych operacji lub dodać dodatkowe kroki, aby zachować to ograniczenie.

Może to być akceptowalny kompromis, aby uzyskać wieloprocesorowość bez systemu pamięci wirtualnej, w której ufasz procesom, że nie będą atakować wzajemnie pamięci i może być konieczne załadowanie określonej aplikacji pod dowolnym adresem podstawowym.

W wielu nowoczesnych systemach kompromisy w zakresie wydajności są różne, a program ładujący przemieszczający się jest często tańszy (kosztuje każde pierwsze załadowanie kodu) niż najlepsze, co optymalizator może zrobić, jeśli ma wolne panowanie. Ponadto dostępność wirtualnych przestrzeni adresowych ukrywa przede wszystkim większość motywacji do niezależności pozycji.

RBerteig
źródło
1

Ponadto sprzęt pamięci wirtualnej w większości nowoczesnych procesorów (używany przez większość współczesnych systemów operacyjnych) oznacza, że ​​wiele kodu (wszystkie aplikacje przestrzeni użytkownika, z wyjątkiem dziwacznego użycia mmap itp.) Nie musi być niezależne od pozycji. Każdy program otrzymuje własną przestrzeń adresową, która według niego zaczyna się od zera.

smcameron
źródło
4
Ale nawet w przypadku kodu PIC VM-MMU jest potrzebny, aby mieć pewność, że ta sama biblioteka .so jest ładowana do pamięci tylko raz, gdy jest używana przez różne pliki wykonywalne.
mmmmmmmm
1

position-independent code ma narzut wydajności w większości architektur, ponieważ wymaga dodatkowego rejestru.

Więc to jest w celu wydajności.

Eric Wang
źródło
0

Obecnie system operacyjny i kompilator domyślnie tworzą cały kod jako kod niezależny od pozycji. Spróbuj skompilować bez flagi -fPIC, kod skompiluje się dobrze, ale otrzymasz tylko ostrzeżenie. Windows podobnie jak Windows używa techniki zwanej mapowaniem pamięci, aby to osiągnąć.

Govardhan Murali
źródło
-5

Pytanie pochodzi z 2009 roku. Minęło dziesięć lat, a teraz cały kod jest właściwie niezależny od pozycji. Jest to teraz wymuszane przez systemy operacyjne i kompilatory. Nie ma możliwości rezygnacji. Cały kod jest kompilowany na siłę z PIE, a flaga -no-pic / -no-pie jest ignorowana, jako część tej wymówki ASLR. Powodem tego jest spowolnienie dawniej szybkich aplikacji i sprzedaż nowszego sprzętu pod pozorem zwiększonego bezpieczeństwa. Jest to całkowicie irracjonalne, ponieważ teraz duże rozmiary pamięci pozwalają nam w ogóle pozbyć się piekła dynamicznego linkowania, kompilując wszystkie aplikacje statycznie.

To samo działo się wcześniej, kiedy ludzie w milczeniu akceptowali realny tryb i odbieranie innym wolności. I pamiętam, że MMU ulega znacznemu spowolnieniu z powodu przełączania kontekstu i opóźnienia tłumaczenia adresu. Nie znajdziesz MMU w systemach krytycznych dla wydajności, takich jak te używane przez naukowców do próbkowania eksperymentów fizycznych.

Nie narzekasz, bo nawet nie wiesz, że wszystkie te koła szkoleniowe utrudniają Twój kod. Co mogę powiedzieć? Ciesz się 2 razy wolniejszym oprogramowaniem dzięki PIC! Co więcej, wraz z pojawieniem się LLVM, wkrótce zostanie wymuszony JIT (kod zarządzany), bez dostępu do wbudowanego asemblera x86, co dodatkowo spowolni każdy kod C / C ++. „Ci, którzy poświęcają wolność dla bezpieczeństwa, na nic nie zasługują”.

SmugLispWeenie
źródło
To tylko zestawienie faktów: 10 lat temu PIC był opcjonalny, ale dziś jest domyślny i obowiązkowy. Wątpię, aby kod inny niż PIE był obsługiwany w kolejnych wersjach systemu operacyjnego. Tak jak obsługa trybu rzeczywistego została usunięta po Windows 9x. Tak więc pytanie, czy używać PIC, czy nie, staje się bardziej teoretycznym tematem informatycznym, chyba że w jakiś sposób odblokujesz system operacyjny i ponownie włączysz jego obsługę. Najważniejszą rzeczą, którą ludzie powinni wiedzieć o PIC, jest to, że jest na tyle wolny, że kompilatory do tej pory obsługiwały kompilację statyczną i istniały statyczne wersje większości bibliotek DLL.
SmugLispWeenie
1
Twoje pierwsze zdania to tylko zestawienie faktów. Reszta to opinia granicząca z konspiracją.
Mitch Lindgren
Po prostu porozmawiaj z ludźmi, zapytaj ich o opinię. Osobiście odkryłem, że PIC vs non-PIC również stało się kwestią ideologii. PIC to programowy odpowiednik komunizmu, w którym kod jest produkowany masowo i każdy otrzymuje tę samą kopię. Non-PIC to programistyczny odpowiednik Kapitalizmu, w którym istnieje wiele konkurujących ze sobą wersji tego samego kodu. Dlatego ludzie o bardziej lewicowym nastawieniu podświadomie wspierają PIC, aby udowodnić, że ich ulubiona ideologia może sprawdzić się przynajmniej w informatyce. Ci sami ludzie odradzaliby używanie osobiście zmodyfikowanego libpng.
SmugLispWeenie
2
Czy nie możemy umieszczać politycznych tyrad na stronie programistycznej, dziękuję
Ryan McCampbell