The previous program has 9 program headers, then, the segment mapping indicates in which program header (from 00 to 08) each section is located.
PHDR - Program HeaDeR
Zawiera tabele nagłówków programu i same metadane.
INTERP
Wskazuje ścieżkę do loadera, który ma być użyty do załadowania binarnego do pamięci.
LOAD
Te nagłówki są używane do wskazania jak załadować binarny do pamięci.
Każdy LOAD nagłówek wskazuje obszar pamięci (rozmiar, uprawnienia i wyrównanie) i wskazuje bajty ELF binarnego do skopiowania tam.
Na przykład, drugi ma rozmiar 0x1190, powinien być zlokalizowany na 0x1fc48 z uprawnieniami do odczytu i zapisu i będzie wypełniony 0x528 z offsetu 0xfc48 (nie wypełnia całej zarezerwowanej przestrzeni). Ta pamięć będzie zawierać sekcje .init_array .fini_array .dynamic .got .data .bss.
DYNAMIC
Ten nagłówek pomaga w łączeniu programów z ich zależnościami bibliotecznymi i stosowaniu relokacji. Sprawdź sekcję .dynamic.
NOTE
Przechowuje informacje metadanych dostawcy o binarnym.
GNU_EH_FRAME
Definiuje lokalizację tabel unwind stosu, używanych przez debugery i funkcje obsługi wyjątków C++.
GNU_STACK
Zawiera konfigurację obrony przed wykonywaniem kodu ze stosu. Jeśli jest włączona, binarny nie będzie mógł wykonywać kodu ze stosu.
GNU_RELRO
Wskazuje konfigurację RELRO (Relocation Read-Only) binarnego. Ta ochrona oznaczy jako tylko do odczytu niektóre sekcje pamięci (jak GOT lub tabele init i fini) po załadowaniu programu i przed rozpoczęciem jego działania.
W poprzednim przykładzie kopiuje 0x3b8 bajtów do 0x1fc48 jako tylko do odczytu, wpływając na sekcje .init_array .fini_array .dynamic .got .data .bss.
Zauważ, że RELRO może być częściowy lub pełny, wersja częściowa nie chroni sekcji .plt.got, która jest używana do leniwego wiązania i potrzebuje tej przestrzeni pamięci, aby mieć uprawnienia do zapisu do zapisania adresu bibliotek przy pierwszym wyszukiwaniu ich lokalizacji.
TLS
Definiuje tabelę wpisów TLS, która przechowuje informacje o zmiennych lokalnych wątków.
Section Headers
Nagłówki sekcji dają bardziej szczegółowy widok na binarny ELF.
It also indicates the location, offset, permissions but also the type of data it section has.
Meta Sections
String table: Zawiera wszystkie ciągi potrzebne przez plik ELF (ale nie te, które są faktycznie używane przez program). Na przykład zawiera nazwy sekcji takie jak .text lub .data. A jeśli .text znajduje się na przesunięciu 45 w tabeli ciągów, użyje liczby 45 w polu name.
Aby znaleźć, gdzie znajduje się tabela ciągów, ELF zawiera wskaźnik do tabeli ciągów.
Symbol table: Zawiera informacje o symbolach, takie jak nazwa (przesunięcie w tabeli ciągów), adres, rozmiar i inne metadane dotyczące symbolu.
Main Sections
.text: Instrukcja programu do uruchomienia.
.data: Zmienne globalne z określoną wartością w programie.
.bss: Zmienne globalne pozostawione niezainicjowane (lub zainicjowane na zero). Zmienne tutaj są automatycznie inicjowane na zero, co zapobiega dodawaniu zbędnych zer do binarnego.
.rodata: Stałe zmienne globalne (sekcja tylko do odczytu).
.tdata i .tbss: Podobnie jak .data i .bss, gdy używane są zmienne lokalne dla wątków (__thread_local w C++ lub __thread w C).
.dynamic: Zobacz poniżej.
Symbols
Symbols to nazwane miejsce w programie, które może być funkcją, globalnym obiektem danych, zmiennymi lokalnymi dla wątków...
readelf -s lnstat
Symbol table '.dynsym' contains 49 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000001088 0 SECTION LOCAL DEFAULT 12 .init
2: 0000000000020000 0 SECTION LOCAL DEFAULT 23 .data
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strtok@GLIBC_2.17 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND s[...]@GLIBC_2.17 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.17 (2)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputs@GLIBC_2.17 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.17 (2)
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _[...]@GLIBC_2.34 (3)
9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND perror@GLIBC_2.17 (2)
10: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
11: 0000000000000000 0 FUNC WEAK DEFAULT UND _[...]@GLIBC_2.17 (2)
12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND putc@GLIBC_2.17 (2)
[...]
Każdy wpis symbolu zawiera:
Nazwa
Atrybuty powiązania (słaby, lokalny lub globalny): Lokalny symbol może być dostępny tylko przez sam program, podczas gdy symbole globalne są udostępniane poza programem. Słaby obiekt to na przykład funkcja, która może być nadpisana przez inną.
Typ: NOTYPE (typ nieokreślony), OBJECT (globalna zmienna danych), FUNC (funkcja), SECTION (sekcja), FILE (plik źródłowy dla debuggerów), TLS (zmienna lokalna dla wątku), GNU_IFUNC (funkcja pośrednia do relokacji)
Katalog NEEDED wskazuje, że program musi załadować wspomnianą bibliotekę, aby kontynuować. Katalog NEEDED jest uzupełniany, gdy wspólna biblioteka jest w pełni operacyjna i gotowa do użycia.
Relokacje
Loader musi również relokować zależności po ich załadowaniu. Te relokacje są wskazane w tabeli relokacji w formatach REL lub RELA, a liczba relokacji jest podana w sekcjach dynamicznych RELSZ lub RELASZ.
Jeśli program jest ładowany w innym miejscu niż preferowany adres (zwykle 0x400000), ponieważ adres jest już używany lub z powodu ASLR lub innego powodu, statyczna relokacja poprawia wskaźniki, które miały wartości oczekujące, że binarka zostanie załadowana w preferowanym adresie.
Na przykład każda sekcja typu R_AARCH64_RELATIV powinna mieć zmodyfikowany adres o wartość przesunięcia relokacji plus wartość dodaną.
Dynamiczne Relokacje i GOT
Relokacja może również odnosić się do symbolu zewnętrznego (jak funkcja z zależności). Na przykład funkcja malloc z libC. Wtedy, loader, ładowując libC w adresie, sprawdza, gdzie funkcja malloc jest załadowana, i zapisuje ten adres w tabeli GOT (Global Offset Table) (wskazanej w tabeli relokacji), gdzie powinien być określony adres malloc.
Tabela Łączenia Procedur
Sekcja PLT pozwala na leniwe wiązanie, co oznacza, że rozwiązywanie lokalizacji funkcji będzie wykonywane za pierwszym razem, gdy zostanie ona wywołana.
Więc gdy program wywołuje malloc, tak naprawdę wywołuje odpowiednią lokalizację malloc w PLT (malloc@plt). Przy pierwszym wywołaniu rozwiązuje adres malloc i przechowuje go, więc następnym razem, gdy malloc jest wywoływane, ten adres jest używany zamiast kodu PLT.
Inicjalizacja Programu
Po załadowaniu programu nadszedł czas, aby go uruchomić. Jednak pierwszy kod, który jest uruchamiany, nie zawsze jest funkcją main. Dzieje się tak, ponieważ na przykład w C++, jeśli zmienna globalna jest obiektem klasy, ten obiekt musi być zainicjowanyprzed uruchomieniem main, jak w:
Zauważ, że te zmienne globalne znajdują się w .data lub .bss, ale w listach __CTOR_LIST__ i __DTOR_LIST__ obiekty do zainicjowania i zniszczenia są przechowywane w celu ich śledzenia.
Z kodu C można uzyskać ten sam wynik, używając rozszerzeń GNU:
__attributte__((constructor)) //Add a constructor to execute before__attributte__((destructor)) //Add to the destructor list
Z perspektywy kompilatora, aby wykonać te działania przed i po wykonaniu funkcji main, można stworzyć funkcję init i funkcję fini, które będą odniesione w sekcji dynamicznej jako INIT i FIN. i są umieszczone w sekcjach init i fini ELF.
Inną opcją, jak wspomniano, jest odniesienie do list __CTOR_LIST__ i __DTOR_LIST__ w wpisach INIT_ARRAY i FINI_ARRAY w sekcji dynamicznej, a długość tych list jest wskazywana przez INIT_ARRAYSZ i FINI_ARRAYSZ. Każdy wpis to wskaźnik do funkcji, która będzie wywoływana bez argumentów.
Ponadto, możliwe jest również posiadanie PREINIT_ARRAY z wskaźnikami, które będą wykonywane przed wskaźnikami INIT_ARRAY.
Kolejność inicjalizacji
Program jest ładowany do pamięci, statyczne zmienne globalne są inicjalizowane w .data a niezainicjalizowane są zerowane w .bss.
Wszystkie zależności dla programu lub bibliotek są inicjalizowane i wykonywane jest dynamiczne linkowanie.
Funkcje PREINIT_ARRAY są wykonywane.
Funkcje INIT_ARRAY są wykonywane.
Jeśli istnieje wpis INIT, jest wywoływany.
Jeśli jest to biblioteka, dlopen kończy się tutaj, jeśli program, nadszedł czas na wywołanie rzeczywistego punktu wejścia (funkcja main).
Pamięć lokalna dla wątków (TLS)
Są definiowane za pomocą słowa kluczowego __thread_local w C++ lub rozszerzenia GNU __thread.
Każdy wątek będzie utrzymywał unikalną lokalizację dla tej zmiennej, więc tylko wątek może uzyskać dostęp do swojej zmiennej.
Gdy to jest używane, sekcje .tdata i .tbss są używane w ELF. Które są podobne do .data (zainicjalizowane) i .bss (niezainicjalizowane), ale dla TLS.
Każda zmienna będzie miała wpis w nagłówku TLS określający rozmiar i offset TLS, który jest offsetem, który będzie używany w lokalnym obszarze danych wątku.
Symbol __TLS_MODULE_BASE jest używany do odniesienia się do adresu bazowego pamięci lokalnej wątku i wskazuje na obszar w pamięci, który zawiera wszystkie dane lokalne wątku modułu.