Oni opisuju loader-u kako da učita ELF u memoriju:
readelf-lWlnstatElffiletypeisDYN (Position-Independent Executablefile)Entrypoint0x1c00Thereare9programheaders,startingatoffset64ProgramHeaders:TypeOffsetVirtAddrPhysAddrFileSizMemSizFlgAlignPHDR0x0000400x00000000000000400x00000000000000400x0001f80x0001f8R0x8INTERP0x0002380x00000000000002380x00000000000002380x00001b0x00001bR0x1[Requesting program interpreter: /lib/ld-linux-aarch64.so.1]LOAD0x0000000x00000000000000000x00000000000000000x003f7c0x003f7cRE0x10000LOAD0x00fc480x000000000001fc480x000000000001fc480x0005280x001190RW0x10000DYNAMIC0x00fc580x000000000001fc580x000000000001fc580x0002000x000200RW0x8NOTE0x0002540x00000000000002540x00000000000002540x0000e00x0000e0R0x4GNU_EH_FRAME0x0036100x00000000000036100x00000000000036100x0001b40x0001b4R0x4GNU_STACK0x0000000x00000000000000000x00000000000000000x0000000x000000RW0x10GNU_RELRO0x00fc480x000000000001fc480x000000000001fc480x0003b80x0003b8R0x1SectiontoSegmentmapping:SegmentSections...0001.interp02.interp.note.gnu.build-id.note.ABI-tag.note.package.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.plt.text.fini.rodata.eh_frame_hdr.eh_frame03.init_array.fini_array.dynamic.got.data.bss04.dynamic05.note.gnu.build-id.note.ABI-tag.note.package06.eh_frame_hdr0708.init_array.fini_array.dynamic.got
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
Contains the program header tables and metadata itself.
INTERP
Indicates the path of the loader to use to load the binary into memory.
LOAD
These headers are used to indicate how to load a binary into memory.
Each LOAD header indicates a region of memory (size, permissions and alignment) and indicates the bytes of the ELF binary to copy in there.
For example, the second one has a size of 0x1190, should be located at 0x1fc48 with permissions read and write and will be filled with 0x528 from the offset 0xfc48 (it doesn't fill all the reserved space). This memory will contain the sections .init_array .fini_array .dynamic .got .data .bss.
DYNAMIC
This header helps to link programs to their library dependencies and apply relocations. Check the .dynamic section.
NOTE
This stores vendor metadata information about the binary.
GNU_EH_FRAME
Defines the location of the stack unwind tables, used by debuggers and C++ exception handling-runtime functions.
GNU_STACK
Contains the configuration of the stack execution prevention defense. If enabled, the binary won't be able to execute code from the stack.
GNU_RELRO
Indicates the RELRO (Relocation Read-Only) configuration of the binary. This protection will mark as read-only certain sections of the memory (like the GOT or the init and fini tables) after the program has loaded and before it begins running.
In the previous example it's copying 0x3b8 bytes to 0x1fc48 as read-only affecting the sections .init_array .fini_array .dynamic .got .data .bss.
Note that RELRO can be partial or full, the partial version do not protect the section .plt.got, which is used for lazy binding and needs this memory space to have write permissions to write the address of the libraries the first time their location is searched.
TLS
Defines a table of TLS entries, which stores info about thread-local variables.
Section Headers
Section headers gives a more detailed view of the ELF binary
It also indicates the location, offset, permissions but also the type of data it section has.
Meta Sections
String table: Sadrži sve stringove potrebne ELF datoteci (ali ne i one koje program zapravo koristi). Na primer, sadrži imena sekcija kao što su .text ili .data. I ako je .text na offsetu 45 u tabeli stringova, koristiće broj 45 u polju name.
Da bi se pronašlo gde se nalazi tabela stringova, ELF sadrži pokazivač na tabelu stringova.
Symbol table: Sadrži informacije o simbolima kao što su ime (offset u tabeli stringova), adresa, veličina i više metapodataka o simbolu.
Main Sections
.text: Instrukcija programa koja se izvršava.
.data: Globalne promenljive sa definisanom vrednošću u programu.
.bss: Globalne promenljive koje nisu inicijalizovane (ili su inicijalizovane na nulu). Promenljive ovde se automatski inicijalizuju na nulu, čime se sprečava dodavanje bespotrebnih nula u binarni fajl.
.rodata: Konstantne globalne promenljive (sekcija samo za čitanje).
.tdata i .tbss: Kao .data i .bss kada se koriste promenljive lokalne za nit (__thread_local u C++ ili __thread u C).
.dynamic: Vidi ispod.
Symbols
Symbols je imenovana lokacija u programu koja može biti funkcija, globalni objekat podataka, promenljive lokalne za nit...
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)
[...]
Svaki unos simbola sadrži:
Ime
Atributi vezivanja (slab, lokalni ili globalni): Lokalni simbol može biti pristupljen samo od strane samog programa, dok su globalni simboli deljeni van programa. Slabi objekat je, na primer, funkcija koja može biti zamenjena drugom.
Tip: NOTYPE (tip nije specificiran), OBJECT (globalna podatkovna varijabla), FUNC (funkcija), SECTION (sekcija), FILE (izvorni kod za debagere), TLS (varijabla lokalna za nit), GNU_IFUNC (indirektna funkcija za relokaciju)
Direktorijum NEEDED ukazuje da program treba da učita pomenutu biblioteku kako bi nastavio. Direktorijum NEEDED se završava kada je deljena biblioteka potpuno operativna i spremna za korišćenje.
Relokacije
Loader takođe mora da relokira zavisnosti nakon što ih učita. Ove relokacije su označene u tabeli relokacija u formatima REL ili RELA, a broj relokacija je dat u dinamičkim sekcijama RELSZ ili RELASZ.
Ako je program učitan na mestu koje se razlikuje od preferirane adrese (obično 0x400000) zato što je adresa već zauzeta ili zbog ASLR ili bilo kog drugog razloga, statička relokacija ispravlja pokazivače koji su imali vrednosti očekujući da će binarni fajl biti učitan na preferiranoj adresi.
Na primer, svaka sekcija tipa R_AARCH64_RELATIV treba da modifikuje adresu na osnovu relokacionog pomaka plus vrednost dodatka.
Dinamičke Relokacije i GOT
Relokacija može takođe referencirati spoljašnji simbol (kao što je funkcija iz zavisnosti). Kao što je funkcija malloc iz libC. Tada, učitavač prilikom učitavanja libC na adresu proverava gde je učitana malloc funkcija, i upisuje ovu adresu u GOT (Global Offset Table) tabelu (naznačenu u relokacionoj tabeli) gde bi trebala biti specificirana adresa malloc.
Tabela Povezivanja Procedura
PLT sekcija omogućava obavljanje lenje vezivanja, što znači da će se rešavanje lokacije funkcije izvršiti prvi put kada se pristupi.
Dakle, kada program poziva malloc, zapravo poziva odgovarajuću lokaciju malloc u PLT (malloc@plt). Prvi put kada se pozove, rešava adresu malloc i čuva je, tako da sledeći put kada se pozove malloc, ta adresa se koristi umesto PLT koda.
Inicijalizacija Programa
Nakon što je program učitan, vreme je da se pokrene. Međutim, prvi kod koji se izvršava nije uvek main funkcija. To je zato što, na primer, u C++ ako je globalna promenljiva objekat klase, ovaj objekat mora biti inicijalizovanpre nego što se main izvrši, kao u:
Napomena da se ove globalne promenljive nalaze u .data ili .bss, ali u listama __CTOR_LIST__ i __DTOR_LIST__ objekti za inicijalizaciju i destrukciju se čuvaju kako bi se pratili.
Iz C koda je moguće dobiti isti rezultat koristeći GNU ekstenzije:
__attributte__((constructor)) //Add a constructor to execute before__attributte__((destructor)) //Add to the destructor list
Sa perspektive kompajlera, da bi se izvršile ove radnje pre i posle izvršavanja main funkcije, moguće je kreirati init funkciju i fini funkciju koje bi bile referencirane u dinamičkom odeljku kao INIT i FIN. i smeštene su u init i fini odeljke ELF-a.
Druga opcija, kao što je pomenuto, je da se referenciraju liste __CTOR_LIST__ i __DTOR_LIST__ u INIT_ARRAY i FINI_ARRAY stavkama u dinamičkom odeljku, a dužina ovih stavki je označena sa INIT_ARRAYSZ i FINI_ARRAYSZ. Svaka stavka je pokazivač na funkciju koja će biti pozvana bez argumenata.
Štaviše, takođe je moguće imati PREINIT_ARRAY sa pokazivačima koji će biti izvršeni preINIT_ARRAY pokazivača.
Redosled inicijalizacije
Program se učitava u memoriju, statičke globalne promenljive se inicijalizuju u .data i neinicijalizovane se postavljaju na nulu u .bss.
Sve zavisnosti za program ili biblioteke su inicijalizovane i dinamičko povezivanje se izvršava.
PREINIT_ARRAY funkcije se izvršavaju.
INIT_ARRAY funkcije se izvršavaju.
Ako postoji INIT stavka, ona se poziva.
Ako je u pitanju biblioteka, dlopen ovde završava, ako je program, vreme je da se pozove pravi ulazni tačka (main funkcija).
Thread-Local Storage (TLS)
Definisani su korišćenjem ključne reči __thread_local u C++ ili GNU ekstenzije __thread.
Svaki nit će održavati jedinstvenu lokaciju za ovu promenljivu tako da samo nit može pristupiti svojoj promenljivoj.
Kada se ovo koristi, odeljci .tdata i .tbss se koriste u ELF-u. Koji su slični .data (inicijalizovano) i .bss (neinicijalizovano) ali za TLS.
Svaka promenljiva će imati stavku u TLS zaglavlju koja specificira veličinu i TLS offset, što je offset koji će koristiti u lokalnom području podataka niti.
__TLS_MODULE_BASE je simbol koji se koristi za referenciranje osnovne adrese skladišta lokalnih niti i ukazuje na područje u memoriji koje sadrži sve podatke lokalnih niti modula.