Enciklopedija zaštite od požara

Kako otvoriti elf datoteku u windowsu. Što je .ELF ekstenzija datoteke? Čvorovi koji opisuju podatke

Standardni razvojni alati kompiliraju vaš program u datoteku ELF (izvršni i povezivi format) s mogućnošću uključivanja informacija o otklanjanju pogrešaka. Specifikacija formata se može pročitati. Osim toga, svaka arhitektura ima svoje karakteristike, kao što su one kod ARM-a. Pogledajmo na brzinu ovaj format.
Izvršna datoteka ELF formata sastoji se od sljedećih dijelova:
1. Zaglavlje (ELF zaglavlje)
Sadrži opće informacije o datoteci i njezinim glavnim karakteristikama.
2. Tablica zaglavlja programa
Ovo je tablica korespondencije odjeljaka datoteke s memorijskim segmentima, govori učitavaču u koje memorijsko područje da zapiše svaki odjeljak.
3. Odjeljci
Odjeljci sadrže sve informacije u datoteci (program, podaci, informacije o otklanjanju pogrešaka, itd.)
Svaki odjeljak ima vrstu, naziv i druge parametre. Odjeljak ".text" obično pohranjuje kod, ".symtab" - tablicu programskih simbola (nazive datoteka, procedura i varijabli), ".strtab" - tablicu nizova, sekcije s prefiksom ".debug_" - informacije o otklanjanju pogrešaka itd. .d. Osim toga, datoteka mora imati prazan odjeljak s indeksom 0.
4. Tablica zaglavlja odjeljka
Ovo je tablica koja sadrži niz zaglavlja odjeljaka.
O formatu se detaljnije govori u odjeljku Stvaranje ELF-a.

PATULJKI pregled

DWARF je standardizirani format informacija za otklanjanje pogrešaka. Standard se može preuzeti sa službene web stranice. Tu je i sjajan pregled formata: Uvod u DWARF format za otklanjanje pogrešaka (Michael J. Eager).
Zašto su potrebne informacije za otklanjanje pogrešaka? Dopušta:
  • postavite prijelomne točke ne na fizičku adresu, već na broj reda u datoteci izvornog koda ili na naziv funkcije
  • prikaz i promjenu vrijednosti globalnih i lokalnih varijabli, kao i parametara funkcije
  • prikaz stog poziva (trace unatrag)
  • izvršavati program korak po korak ne jednom uputom asemblera, već redovima izvornog koda
Ove informacije su pohranjene u strukturi stabla. Svaki čvor stabla ima roditelja, može imati djecu i naziva se unos podataka za otklanjanje pogrešaka (DIE). Svaki čvor ima svoju oznaku (tip) i popis atributa (svojstava) koji opisuju čvor. Atributi mogu sadržavati bilo što, kao što su podaci ili veze na druge čvorove. Osim toga, postoje informacije pohranjene izvan stabla.
Čvorovi su podijeljeni u dvije glavne vrste: čvorovi koji opisuju podatke i čvorovi koji opisuju kod.
Čvorovi koji opisuju podatke:
  1. Vrste podataka:
    • Osnovni tipovi podataka (čvor s tipom DW_TAG_base_type), kao što je tip int u C.
    • Kompozitni tipovi podataka (pokazivači, itd.)
    • Nizovi
    • Strukture, klase, sindikati, sučelja
  2. Objekti podataka:
    • konstante
    • parametri funkcije
    • varijable
    • itd.
Svaki podatkovni objekt ima atribut DW_AT_location koji specificira kako se izračunava adresa na kojoj se podaci nalaze. Na primjer, varijabla može imati fiksnu adresu, biti u registru ili na stogu, biti član klase ili objekta. Ova adresa se može izračunati na prilično kompliciran način, pa standard predviđa takozvane izraze lokacije, koji mogu sadržavati slijed izjava iz posebnog internog stroja za stog.
Čvorovi koji opisuju kod:
  1. Procedure (funkcije) - čvorovi s oznakom DW_TAG_subprogram. Čvorovi potomci mogu sadržavati opise varijabli - parametara funkcija i lokalnih varijabli funkcije.
  2. Jedinica za kompilaciju. Sadrži informacije o programu i roditelj je svih ostalih čvorova.
Gore opisane informacije nalaze se u odjeljcima ".debug_info" i ".debug_abbrev".
Druge podatke:
  • Informacije o brojevima redaka (odjeljak "debug_line")
  • Podaci o makronaredbi (odjeljak "debug_macinfo")
  • Informacije o okviru poziva (odjeljak ".debug_frame")

Stvaranje ELF-a

Izradit ćemo EFL datoteke koristeći libelf biblioteku iz paketa elfutils. Na webu postoji dobar članak o korištenju libelfa - LibELF by Example (nažalost, u njemu je vrlo kratko opisano stvaranje datoteka) kao i dokumentacija.
Izrada datoteke sastoji se od nekoliko koraka:
  1. kleveta inicijalizacija
  2. Izrada zaglavlja datoteke (ELF zaglavlja)
  3. Izrada tablice zaglavlja programa
  4. Napravite odjeljke
  5. Napišite datoteku
Razmotrite korake detaljnije
kleveta inicijalizacija
Prvo ćete morati pozvati funkciju elf_version(EV_CURRENT) i provjeriti rezultat. Ako je jednako EV_NONE, došlo je do pogreške i ne mogu se poduzeti daljnje radnje. Zatim moramo kreirati datoteku koja nam je potrebna na disku, dobiti njen deskriptor i proslijediti ga funkciji elf_begin:
Elf * elf_begin(int fd, Elf_Cmd cmd, Elf *elf)
  • fd - deskriptor datoteke novootvorene datoteke
  • cmd - način rada (ELF_C_READ za čitanje informacija, ELF_C_WRITE za pisanje ili ELF_C_RDWR za čitanje/pisanje), mora odgovarati načinu otvorene datoteke (ELF_C_WRITE u našem slučaju)
  • elf - potreban samo za rad s arhivskim datotekama (.a), u našem slučaju morate proći 0
Funkcija vraća pokazivač na generirani handle koji će se koristiti u svim libelf funkcijama, 0 se vraća u slučaju pogreške.
Napravite zaglavlje
Novo zaglavlje datoteke stvara funkcija elf32_newehdr:
Elf32_Ehdr * elf32_newehdr(Elf *elf);
  • elf - ručka koju vraća funkcija elf_begin
Vraća 0 u slučaju pogreške ili pokazivač na strukturu - zaglavlje ELF datoteke:
#define EI_NIDENT 16 typedef struct ( unsigned char e_ident; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; ) Elf32_Ehdr;

Neka od njegovih polja se popunjavaju na standardni način, neka trebamo ispuniti:

  • e_ident - niz identifikacijskih bajtova, ima sljedeće indekse:
    • EI_MAG0, EI_MAG1, EI_MAG2, EI_MAG3 - ova 4 bajta trebaju sadržavati znakove 0x7f, "ELF", što je funkcija elf32_newehdr već učinila za nas
    • EI_DATA - označava vrstu kodiranja podataka u datoteci: ELFDATA2LSB ili ELFDATA2MSB. Morate postaviti ELFDATA2LSB ovako: e_ident = ELFDATA2LSB
    • EI_VERSION - verzija zaglavlja datoteke, već postavljena za nas
    • EI_PAD - ne dirati
  • e_type - vrsta datoteke, može biti ET_NONE - bez tipa, ET_REL - datoteka koja se može premjestiti, ET_EXEC - izvršna datoteka, ET_DYN - dijeljena objektna datoteka, itd. Moramo postaviti vrstu datoteke na ET_EXEC
  • e_machine - arhitektura potrebna za ovu datoteku, na primjer EM_386 - za Intel arhitekturu, za ARM moramo ovdje napisati EM_ARM (40) - pogledajte ELF za ARM arhitekturu
  • e_version - verzija datoteke, mora biti postavljena na EV_CURRENT
  • e_entry - adresa ulazne točke, nama nije potrebna
  • e_phoff - pomak u datoteci zaglavlja programa, e_shoff - pomak zaglavlja odjeljka, ne popunjavati
  • e_flags - zastavice specifične za procesor, za našu arhitekturu (Cortex-M3) treba postaviti na 0x05000000 (ABI verzija 5)
  • e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum - ne dirajte
  • e_shstrndx - sadrži broj odjeljka u kojem se nalazi tablica nizova sa zaglavljima odjeljka. Budući da još nemamo nijedan odjeljak, ovaj broj ćemo naknadno postaviti.
Izrada zaglavlja programa
Kao što je već spomenuto, tablica zaglavlja programa je tablica korespondencije između odjeljaka datoteke i memorijskih segmenata, koja učitavaču govori gdje da upiše svaki odjeljak. Zaglavlje je kreirano pomoću funkcije elf32_newphdr:
Elf32_Phdr * elf32_newphdr(Elf *elf, veličina_t broj);
  • vilenjak - naša ručka
  • count - broj elemenata tablice za kreiranje. Budući da ćemo imati samo jedan odjeljak (s programskim kodom), tada će count biti jednak 1.
Vraća 0 u slučaju pogreške ili pokazivač na zaglavlje programa.
Svaki element u tablici zaglavlja opisan je sljedećom strukturom:
typedef struct ( Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags;2 Elf32_W Elf32_W Elf_dr_W Elf_dr_W Elf32_Word p_filesz;
  • p_type - tip segmenta (odjeljka), ovdje moramo navesti PT_LOAD - segment koji se može učitati
  • p_offset - pomaci u datoteci odakle počinju podaci odjeljka koji će se učitati u memoriju. Imamo .text odjeljak, koji će se nalaziti odmah nakon zaglavlja datoteke i zaglavlja programa, možemo izračunati pomak kao zbroj duljina ovih zaglavlja. Duljina bilo koje vrste može se dobiti pomoću funkcije elf32_fsize:
    size_t elf32_fsize(vrsta_elfa, broj veličine_t, nepotpisana int verzija); tip - ovdje je konstanta ELF_T_xxx, trebat će nam veličine ELF_T_EHDR i ELF_T_PHDR; count - broj elemenata željene vrste, verzija - mora biti postavljen na EV_CURRENT
  • p_vaddr, p_paddr - virtualna i fizička adresa na koju će se učitavati sadržaj sekcije. Budući da nemamo virtualne adrese, postavljamo je jednaku fizičkoj, u najjednostavnijem slučaju - 0, jer će se tu učitavati naš program.
  • p_filesz, p_memsz - veličina odjeljka u datoteci i memoriji. Imamo ih iste, ali budući da još ne postoji dio s programskim kodom, instalirat ćemo ih naknadno
  • p_flags - dopuštenja za učitani memorijski segment. Može biti PF_R - čitanje, PF_W - pisanje, PF_X - izvršavanje ili kombinacija oboje. Postavite p_flags na PF_R + PF_X
  • p_align - poravnanje segmenta, imamo 4
Napravite odjeljke
Nakon izrade naslova, možete početi stvarati odjeljke. Prazan odjeljak kreira se pomoću funkcije elf_newscn:
Elf_Scn * elf_newscn(Elf *elf);
  • elf - ručka koju je ranije vratila funkcija elf_begin
Funkcija vraća pokazivač odjeljka ili 0 u slučaju pogreške.
Nakon izrade odjeljka, trebate ispuniti zaglavlje odjeljka i izraditi deskriptor podataka odjeljka.
Pokazivač na zaglavlje odjeljka možemo dobiti pomoću funkcije elf32_getshdr:
Elf32_Shdr * elf32_getshdr(Elf_Scn *scn);
  • scn je pokazivač odjeljka koji smo dobili od funkcije elf_newscn.
Zaglavlje odjeljka izgleda ovako:
typedef struct (elf32_word sh_name; elf32_word sh_type; elf32_word sh_flags; elf32_addr sh_addr; elf32_off sh_offset; elf32_word sh_size; elf32; ° °; OlfddDral;
  • sh_name - naziv odjeljka - pomak u tablici nizova zaglavlja odjeljka (section.shstrtab) - pogledajte "Tablice nizova" ispod
  • sh_type - vrsta sadržaja odjeljka, postavi SHT_PROGBITS za odjeljak s programskim kodom, SHT_STRTAB za odjeljke s tablicom nizova, SHT_SYMTAB za tablicu simbola
  • sh_flags - zastavice sekcija koje se mogu kombinirati, a od kojih su nam potrebne samo tri:
    • SHF_ALLOC - znači da će se odjeljak učitati u memoriju
    • SHF_EXECINSTR - odjeljak sadrži izvršni kod
    • SHF_STRINGS - odjeljak sadrži tablicu nizova
    Sukladno tome, za odjeljak .text s programom morate postaviti zastavice SHF_ALLOC + SHF_EXECINSTR
  • sh_addr - adresa na kojoj će se odjeljak učitati u memoriju
  • sh_offset - pomak odjeljka u datoteci - ne dirajte, biblioteka će se instalirati umjesto nas
  • sh_size - veličina odjeljka - ne dirati
  • sh_link - sadrži broj povezanog odjeljka, potrebnog za povezivanje odjeljka s odgovarajućom tablicom nizova (vidi dolje)
  • sh_info - dodatne informacije ovisno o vrsti odjeljka, postavljeno na 0
  • sh_addralign - poravnanje adrese, ne dirati
  • sh_entsize - ako se odjeljak sastoji od nekoliko elemenata iste duljine, označava duljinu takvog elementa, ne dirajte
Nakon što ispunite zaglavlje, trebate stvoriti deskriptor podataka odjeljka pomoću funkcije elf_newdata:
Elf_Data * elf_newdata(Elf_Scn *scn);
  • scn je novostečeni pokazivač na novi odjeljak.
Funkcija vraća 0 u slučaju pogreške ili pokazivač na strukturu Elf_Data koju treba ispuniti:
typedef struct ( void* d_buf; Elf_Type d_type; size_t d_size; off_t d_off; size_t d_align; unsigned d_version; ) Elf_Data;
  • d_buf - pokazivač na podatke koji se upisuju u odjeljak
  • d_type - vrsta podataka, ELF_T_BYTE je pogodan za nas svugdje
  • d_size - veličina podataka
  • d_off - pomak odjeljka, postavljen na 0
  • d_align - poravnanje, može se postaviti na 1 - nema poravnanja
  • d_version - verzija, mora biti postavljena na EV_CURRENT
Posebni odjeljci
Za naše potrebe, morat ćemo stvoriti minimalno potreban skup odjeljaka:
  • .text - odjeljak s programskim kodom
  • .symtab - tablica simbola datoteke
  • .strtab - tablica nizova koja sadrži nazive simbola iz odjeljka .symtab, budući da potonji ne pohranjuje sama imena, već njihove indekse
  • .shstrtab - tablica nizova koja sadrži nazive odjeljaka
Svi odjeljci su izrađeni kako je opisano u prethodnom odjeljku, ali svaki poseban odjeljak ima svoje karakteristike.
Odjeljak.tekst
Ovaj odjeljak sadrži izvršni kod, tako da morate postaviti sh_type na SHT_PROGBITS, sh_flags na SHF_EXECINSTR + SHF_ALLOC, sh_addr na adresu na koju će se ovaj kod učitati
Odjeljak.symtab
Odjeljak sadrži opis svih simbola (funkcija) programa i datoteka u kojima su opisani. Sastoji se od sljedećih elemenata, svaki dug 16 bajtova:
typedef struct ( Elf32_Word st_name; Elf32_Addr st_value; Elf32_Word st_size; unsigned char st_info; unsigned char st_other; Elf32_Half st_shndx; ) Elf32_Sym;
  • st_name - naziv simbola (indeks u tablici nizova.strtab)
  • st_value - vrijednost (ulazna adresa za funkciju ili 0 za datoteku). Budući da Cortex-M3 ima skup instrukcija Thumb-2, ova adresa mora biti neparna (prava adresa + 1)
  • st_size - duljina koda funkcije (0 za datoteku)
  • st_info - tip simbola i njegov opseg. Postoji makro za definiranje vrijednosti ovog polja
    #define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf))
    gdje je b opseg, a t tip karaktera
    Opseg može biti STB_LOCAL (simbol nije vidljiv iz drugih objektnih datoteka) ili STB_GLOBAL (vidljiv). Da pojednostavimo, koristimo STB_GLOBAL.
    Vrsta simbola - STT_FUNC za funkciju, STT_FILE za datoteku
  • st_other - postavljeno na 0
  • st_shndx - indeks odjeljka za koji je definiran simbol (indeks odjeljka.tekst), odnosno SHN_ABS za datoteku.
    Indeks odjeljka pomoću scn ručke može se odrediti pomoću elf_ndxscn:
    size_t elf_ndxscn(Elf_Scn *scn);

Ovaj odjeljak je kreiran na uobičajen način, samo sh_type treba postaviti na SHT_SYMTAB, a indeks section.strtab treba biti upisan u polje sh_link, tako da se ovi odjeljci povezuju.
Odjeljak.strtab
Ovaj odjeljak sadrži nazive svih simbola iz odjeljka .symtab. Napravljen kao običan odjeljak, ali sh_type treba biti postavljen na SHT_STRTAB, sh_flags na SHF_STRINGS, tako da ovaj odjeljak postaje tablica nizova.
Podaci za odjeljak mogu se prikupiti prilikom prolaska kroz izvorni tekst u niz, pokazivač na koji se zatim upisuje u deskriptor podataka odjeljka (d_buf).
Odjeljak.shstrtab
Odjeljak - tablica nizova, sadrži zaglavlja svih dijelova datoteke, uključujući i vlastito zaglavlje. Izrađuje se na isti način kao i odjeljak .strtab. Nakon kreiranja, njegov indeks mora biti upisan u polje e_shstrndx zaglavlja datoteke.
String tablice
Tablice nizova sadrže uzastopne nizove koji završavaju nultim bajtom, prvi bajt u toj tablici također mora biti 0. Indeks retka u tablici je jednostavno pomak u bajtovima od početka tablice, tako da prvi niz "name" ima indeks 1, sljedeći niz "var" ima indeks 6.
Indeks 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \0 n a m e \0 v a r \0
Napišite datoteku
Dakle, zaglavlja i odjeljci su već formirani, sada ih treba upisati u datoteku i završiti s libelfom. Upisivanje se vrši funkcijom elf_update:
off_t elf_update(Elf *elf, Elf_Cmd cmd);
  • vilenjak - ručka
  • cmd - naredba, mora biti jednaka ELF_C_WRITE za pisanje.
Funkcija vraća -1 u slučaju pogreške. Tekst pogreške može se dobiti pozivom funkcije elf_errmsg(-1), koja će vratiti pokazivač na redak s greškom.
Završavamo rad s bibliotekom s funkcijom elf_end, kojoj prosljeđujemo naš deskriptor. Ostaje samo zatvoriti prethodno otvorenu datoteku.
Međutim, naša generirana datoteka ne sadrži informacije o otklanjanju pogrešaka, koje ćemo dodati u sljedećem odjeljku.

Stvaranje PATULJA

Napravit ćemo informacije za otklanjanje pogrešaka koristeći knjižnicu koja dolazi s pdf datotekom s dokumentacijom (libdwarf2p.1.pdf - sučelje knjižnice proizvođača za DWARF).
Stvaranje informacija za otklanjanje pogrešaka sastoji se od sljedećih koraka:
  1. Izrada čvorova (DIE - Unos informacija za ispravljanje pogrešaka)
  2. Stvaranje atributa čvora
  3. Izrada tipova podataka
  4. Izrada procedura (funkcija)
Razmotrite korake detaljnije
Inicijalizacija proizvođača libdwarf-a
Mi ćemo generirati informacije za otklanjanje pogrešaka u vrijeme kompajliranja u isto vrijeme kada stvaramo simbole u odjeljku .symtab, tako da bi se inicijalizacija knjižnice trebala obaviti nakon inicijalizacije libelfa, kreiranja ELF zaglavlja i zaglavlja programa, prije kreiranja odjeljaka.
Za inicijalizaciju ćemo koristiti funkciju dwarf_producer_init_c. Knjižnica ima još nekoliko inicijalizacijskih funkcija (dwarf_producer_init, dwarf_producer_init_b), koje se razlikuju u nekim nijansama opisanim u dokumentaciji. U principu, bilo koji od njih se može koristiti.

Dwarf_P_Debug dwarf_producer_init_c(Dwarf_Unsigned flags, Dwarf_Callback_Func_c func, Dwarf_Handler errhand, Dwarf_Ptr errarg, void * user_data, Dwarf_Error *error)

  • zastavice - kombinacija "ili" nekoliko konstanti koje definiraju neke parametre, na primjer, bitnu dubinu informacija, redoslijed bajtova (little-endian, big-endian), format premještanja, od kojih nam svakako trebaju DW_DLC_WRITE i DW_DLC_SYMBOLIC_RELOCATIONS
  • func - funkcija povratnog poziva koja će biti pozvana prilikom kreiranja ELF sekcija s informacijama za otklanjanje pogrešaka. Za više pojedinosti pogledajte odjeljak "Izrada odjeljaka s informacijama o otklanjanju pogrešaka" u nastavku.
  • errhand je pokazivač na funkciju koja se poziva kada dođe do pogreške. Može se proslijediti 0
  • errarg - podaci koji će biti proslijeđeni funkciji errhand, mogu se postaviti na 0
  • user_data - podaci koji će biti proslijeđeni funkciji func, mogu se postaviti na 0
  • error - vraćen kod pogreške
Funkcija vraća Dwarf_P_Debug - deskriptor koji se koristi u svim sljedećim funkcijama, ili -1 u slučaju pogreške, dok će pogreška sadržavati kod pogreške (tekst poruke o pogrešci možete dobiti po njenom kodu pomoću funkcije dwarf_errmsg, prosljeđujući ovaj kod na to)
Kreiranje čvora (DIE - Unos informacija za otklanjanje pogrešaka)
Kao što je gore opisano, informacije za otklanjanje pogrešaka čine strukturu stabla. Da biste stvorili čvor ovog stabla, trebate:
  • stvorite ga s funkcijom dwarf_new_die
  • dodajte mu atribute (svaka vrsta atributa dodaje se svojom funkcijom, koja će biti opisana kasnije)
Čvor je kreiran pomoću funkcije dwarf_new_die:
Patuljak_P_Die dwarf_new_die(Dwarf_P_Debug dbg, Dwarf_Tag new_tag, Patuljak_P_Die roditelj, Patuljak_P_Die dijete, Patuljak_P_Die left_sibling, Dwarf_P_Die desni_sibling, Patuljak_Error) *err
  • new_tag - oznaka čvora (vrsta) - konstanta DW_TAG_xxxx, koja se može naći u datoteci libdwarf.h
  • roditelj, dijete, lijevi_sibling, desni_sibling - odnosno roditelj, dijete, lijevi i desni susjedi čvora. Nije potrebno specificirati sve ove parametre, dovoljno je navesti jedan, umjesto ostalih staviti 0. Ako su svi parametri 0, čvor će biti korijenski ili izoliran
  • pogreška - sadržavat će kod pogreške kada se pojavi
Funkcija vraća DW_DLV_BADADDR u slučaju neuspjeha ili Dwarf_P_Die ručku čvora u slučaju uspjeha
Stvaranje atributa čvora
Postoji cijela obitelj dwarf_add_AT_xxxx funkcija za stvaranje atributa čvora. Ponekad je problematično odrediti koja funkcija treba stvoriti potreban atribut, pa sam čak nekoliko puta kopao po izvornom kodu knjižnice. Neke od funkcija bit će opisane ovdje, neke u nastavku - u relevantnim odjeljcima. Svi oni uzimaju parametar ownerdie, oznaku čvora kojem će biti dodan atribut i vraćaju kod pogreške u parametru pogreške.
Funkcija dwarf_add_AT_name dodaje atribut "name" (DW_AT_name) čvoru. Većina čvorova mora imati naziv (na primjer, procedure, varijable, konstante), neki možda nemaju naziv (na primjer, jedinica za kompilaciju)
Dwarf_P_Attribute dwarf_add_AT_name(Dwarf_P_Die ownerdie, char *name, Dwarf_Error *error)
  • naziv - stvarna vrijednost atributa (naziv čvora)

Funkcije dwarf_add_AT_signed_const, dwarf_add_AT_unsigned_const dodaju navedeni atribut i njegovu potpisanu (nepotpisanu) vrijednost u čvor. Potpisani i nepotpisani atributi se koriste za postavljanje konstantnih vrijednosti, veličina, brojeva redaka i tako dalje. Format funkcije:
Dwarf_P_Attribute dwarf_add_AT_(un)signed_const(Dwarf_P_Debug dbg, Dwarf_P_Die ownerdie, Patuljak_Half attr, Patuljak_Signed value, Dwarf_Error *error)
  • dbg - Dwarf_P_Debug deskriptor primljen tijekom inicijalizacije biblioteke
  • attr - atribut čija je vrijednost postavljena - konstanta DW_AT_xxxx, koja se može pronaći u datoteci libdwarf.h
  • vrijednost - vrijednost atributa
Vratite DW_DLV_BADADDR u slučaju pogreške ili oznaku atributa u slučaju uspjeha.
Stvaranje kompilacijske jedinice
Svako stablo mora imati korijen – u našem slučaju to je jedinica za prevođenje koja sadrži informacije o programu (na primjer, naziv glavne datoteke, korišteni programski jezik, naziv prevoditelja, osjetljivost znakova (varijable, funkcije) prema slučaju, glavnoj funkciji programa, početnoj adresi itd.). itd.). U principu, nisu potrebni nikakvi atributi. Na primjer, napravimo informacije o glavnoj datoteci i kompajleru.
Informacije o glavnoj datoteci
Atribut "name" (DW_AT_name) koristi se za pohranu informacija o glavnoj datoteci, koristite funkciju dwarf_add_AT_name kao što je prikazano u odjeljku "Stvaranje atributa čvora".
Informacije o kompajleru
Koristimo funkciju dwarf_add_AT_producer:
Dwarf_P_Attribute dwarf_add_AT_name(Dwarf_P_Die ownerdie, char *proizvođački niz, Dwarf_Error *error)
  • proizvođač_niz - niz s informacijskim tekstom
Vraća DW_DLV_BADADDR u slučaju pogreške ili oznaku atributa u slučaju uspjeha.
Stvaranje zajedničkog unosa informacija
Obično, kada se pozove funkcija (potprogram), njeni parametri i povratna adresa se stavljaju na stog (iako svaki prevodilac to može učiniti na svoj način), sve se to naziva okvirom poziva. Debuggeru su potrebne informacije o formatu okvira kako bi ispravno odredio povratnu adresu iz funkcije i izgradio backtrace - lanac poziva funkcije koji nas je doveo do trenutne funkcije i parametara tih funkcija. Također obično specificira registre procesora koji su pohranjeni na stogu. Kod koji rezervira prostor na stogu i sprema registre procesora naziva se prolog funkcije, kod koji vraća registre i stog naziva se epilog.
Ove informacije uvelike ovise o prevodiocu. Na primjer, prolog i epilog ne moraju biti na samom početku i na kraju funkcije; nekad se koristi okvir, nekad ne; registri procesora mogu se pohraniti u druge registre i tako dalje.
Dakle, debugger mora znati kako registri procesora mijenjaju svoju vrijednost i gdje će biti pohranjeni prilikom ulaska u proceduru. Ove informacije se nazivaju Call Frame Information - informacije o formatu okvira. Za svaku adresu u programu (koja sadrži kod) naznačena je adresa okvira u memoriji (Canonical Frame Address - CFA) i informacije o registrima procesora, na primjer, možete odrediti da:
  • predmet nije sačuvan u proceduri
  • registar ne mijenja svoju vrijednost u postupku
  • registar je pohranjen na stogu na adresi CFA+n
  • registar je pohranjen u drugom registru
  • registar je pohranjen u memoriji na nekoj adresi, koja se može izračunati na prilično neočigledan način
  • itd.
Budući da se podaci moraju navesti za svaku adresu u kodu, oni su vrlo obimni i pohranjeni su u komprimiranom obliku u odjeljku .debug_frame. Budući da se malo mijenja od adrese do adrese, samo su njegove promjene kodirane u obliku DW_CFA_hhhh instrukcija. Svaka instrukcija označava jednu promjenu, na primjer:
  • DW_CFA_set_loc - pokazuje na trenutnu adresu u programu
  • DW_CFA_advance_loc - Pomiče pokazivač za određeni broj bajtova
  • DW_CFA_def_cfa - specificira adresu okvira steka (numerička konstanta)
  • DW_CFA_def_cfa_register - specificira adresu okvira steka (preuzeto iz registra procesora)
  • DW_CFA_def_cfa_expression - specificira kako treba izračunati adresu okvira stoga
  • DW_CFA_same_value - označava da se velika i mala slova ne mijenjaju
  • DW_CFA_register - označava da je registar pohranjen u drugom registru
  • itd.
Elementi odjeljka .debug_frame su unosi koji mogu biti dva tipa: Common Information Entry (CIE) i Frame Description Entry (FDE). CIE sadrži informacije koje su zajedničke mnogim FDE unosima, grubo govoreći, opisuje određenu vrstu postupka. FDE opisuje svaki poseban postupak. Prilikom ulaska u proceduru, program za ispravljanje pogrešaka prvo izvršava upute iz CIE, a zatim iz FDE.
Moj prevodilac generira procedure gdje je CFA u registru sp (r13). Kreirajmo CIE za sve postupke. Za ovo postoji funkcija dwarf_add_frame_cie:
Dwarf_Unsigned dwarf_add_frame_cie(Dwarf_P_Debug dbg, char *augmenter, Patuljak_Small code_align, Dwarf_Small data_align, Dwarf_Small ret_addr_reg, Dwarf_Ptr init_Unbytes, Dwarf_Unbytes, Dwarf_Small, Dwarf_Small;
  • augmenter - UTF-8 kodirani niz, čija prisutnost ukazuje da postoje dodatne informacije specifične za platformu za CIE ili FDE. Stavite prazan red
  • code_align - poravnanje koda u bajtovima (imamo 2)
  • data_align - poravnanje podataka u okviru (postavi -4, što znači da svi parametri zauzimaju 4 bajta na stogu i raste u memoriji)
  • ret_addr_reg - registar koji sadrži povratnu adresu iz procedure (imamo 14)
  • init_bytes - niz koji sadrži DW_CFA_xxxx upute. Nažalost, ne postoji prikladan način za generiranje ovog niza. Možete ga formirati ručno ili zaviriti u elf datoteku koju je generirao C prevodilac, što sam i učinio. Za moj slučaj, sadrži 3 bajta: 0x0C, 0x0D, 0, što znači DW_CFA_def_cfa: r13 od 0 (CFA je u registru r13, pomak je 0)
  • init_bytes_len - duljina niza init_bytes
Funkcija vraća DW_DLV_NOCOUNT u slučaju pogreške ili CIE ručku koja se treba koristiti prilikom kreiranja FDE-a za svaku proceduru, o čemu ćemo raspravljati kasnije u odjeljku "Stvaranje FDE procedure".
Izrada tipova podataka
Prije kreiranja procedura i varijabli, prvo morate stvoriti čvorove koji odgovaraju tipovima podataka. Postoji mnogo tipova podataka, ali svi se temelje na osnovnim tipovima (elementarni tipovi poput int, double, itd.), drugi tipovi su izgrađeni od osnovnih.
Osnovni tip je čvor s oznakom DW_TAG_base_type. Mora imati sljedeće atribute:
  • "ime" (DW_AT_name)
  • "kodiranje" (DW_AT_encoding) - znači točno koje podatke ovaj osnovni tip opisuje (na primjer, DW_ATE_boolean - boolean, DW_ATE_float - pomični zarez, DW_ATE_signed - cijeli broj s predznakom, DW_ATE_unsigned - cijeli broj bez predznaka, itd.)
  • "veličina" (DW_AT_byte_size - veličina u bajtovima ili DW_AT_bit_size - veličina u bitovima)
Čvor također može sadržavati druge neobavezne atribute.
Na primjer, za stvaranje 32-bitnog cjelobrojnog potpisanog osnovnog tipa "int", morat ćemo stvoriti čvor s oznakom DW_TAG_base_type i postaviti njegove atribute DW_AT_name - "int", DW_AT_encoding - DW_ATE_signed, DW_AT_byte_size - 4.
Nakon kreiranja osnovnih tipova, možete stvoriti izvedenice od njih. Takvi čvorovi moraju sadržavati atribut DW_AT_type - referencu na njihov osnovni tip. Na primjer, pokazivač na int - čvor s oznakom DW_TAG_pointer_type mora sadržavati referencu na prethodno kreirani tip "int" u atributu DW_AT_type.
Funkcija dwarf_add_AT_reference stvara atribut s referencom na drugi čvor:
Dwarf_P_Attribute dwarf_add_AT_reference(Dwarf_P_Debug dbg, Dwarf_P_Die ownerdie, Patuljak_Half attr, Patuljak_P_Die otherdie, Patuljak_Error *error)
  • attr - atribut, u ovom slučaju DW_AT_type
  • otherdie - ručka za čvor tipa na koji se upućuje
Izrada procedura
Za kreiranje procedura, moram objasniti još jednu vrstu informacija za otklanjanje pogrešaka - Informacije o broju redaka. Služi za mapiranje svake strojne instrukcije na određeni redak izvornog koda i također za omogućavanje debugiranja programa redak po redak. Ove informacije su pohranjene u odjeljku .debug_line. Kad bismo imali dovoljno prostora, onda bi to bilo pohranjeno kao matrica, po jedan red za svaku instrukciju sa ovakvim stupcima:
  • naziv izvorne datoteke
  • broj reda u ovoj datoteci
  • broj stupca u datoteci
  • je li instrukcija početak naredbe ili blok naredbi
  • itd.
Takva bi matrica bila vrlo velika, pa se mora komprimirati. Prvo, duple linije se uklanjaju, a drugo, ne spremaju se same linije, već se samo mijenjaju. Ove promjene izgledaju kao naredbe za konačni stroj, a sama informacija se već smatra programom koji će ovaj stroj "izvršiti". Naredbe ovog programa izgledaju ovako: DW_LNS_advance_pc - unaprijed brojilo programa na neku adresu, DW_LNS_set_file - postavi datoteku u kojoj je definirana procedura, DW_LNS_const_add_pc - unaprijedi brojač programa za nekoliko bajtova itd.
Teško je stvoriti ove informacije na tako niskoj razini, pa biblioteka libdwarf nudi nekoliko funkcija koje olakšavaju ovaj zadatak.
Skupo je pohranjivanje naziva datoteke za svaku instrukciju, pa se umjesto naziva njezin indeks pohranjuje u posebnu tablicu. Za izradu indeksa datoteke upotrijebite funkciju dwarf_add_file_decl:
Dwarf_Unsigned dwarf_add_file_decl(Dwarf_P_Debug dbg, char *name, Dwarf_Unsigned dir_idx, Dwarf_Unsigned time_mod, Patuljak_Unsigned duljina, Patuljak_Error *error)
  • naziv - naziv datoteke
  • dir_idx - indeks mape u kojoj se datoteka nalazi. Indeks se može dobiti pomoću funkcije dwarf_add_directory_decl. Ako se koriste puni putevi, možete postaviti 0 kao indeks mape i uopće ne koristiti dwarf_add_directory_decl
  • time_mod - vrijeme izmjene datoteke, može se izostaviti (0)
  • duljina - veličina datoteke, također neobavezno (0)
Funkcija će vratiti indeks datoteke ili DW_DLV_NOCOUNT u slučaju pogreške.
Za kreiranje informacija o broju reda postoje tri funkcije dwarf_add_line_entry_b, dwarf_lne_set_address, dwarf_lne_end_sequence, koje ćemo razmotriti u nastavku.
Stvaranje informacija za otklanjanje pogrešaka za proceduru prolazi kroz nekoliko koraka:
  • stvaranje simbola procedure u odjeljku .symtab
  • stvaranje čvora procedure s atributima
  • stvaranje FDE procedure
  • stvaranje parametara postupka
  • generiranje informacija o broju redaka
Izrada simbola procedure
Simbol procedure kreira se kako je gore opisano u odjeljku "Section.symtab". U njemu su simboli procedura isprepleteni simbolima datoteka u kojima se nalazi izvorni kod tih procedura. Prvo kreiramo simbol datoteke, zatim procedure. To čini datoteku aktualnom, a ako je sljedeća procedura u trenutnoj datoteci, simbol datoteke nije potrebno ponovno kreirati.
Stvaranje čvora procedure s atributima
Prvo kreiramo čvor pomoću funkcije dwarf_new_die (pogledajte odjeljak "Stvaranje čvorova"), navodeći oznaku DW_TAG_subprograma kao oznaku, a jedinicu kompilacije (ako je ovo globalna procedura) ili odgovarajući DIE (ako je lokalno) kao roditelj. Zatim stvaramo atribute:
  • naziv procedure (funkcija dwarf_add_AT_name, pogledajte "Stvaranje atributa čvora")
  • broj reda u datoteci gdje počinje kod procedure (atribut DW_AT_decl_line), funkcija dwarf_add_AT_unsigned_const (pogledajte "Stvaranje atributa čvora")
  • početna adresa procedure (atribut DW_AT_low_pc), funkcija dwarf_add_AT_targ_address, vidi dolje
  • krajnja adresa procedure (atribut DW_AT_high_pc), funkcija dwarf_add_AT_targ_address, vidi dolje
  • tip rezultata koji vraća procedura (atribut DW_AT_type je veza na prethodno kreirani tip, pogledajte "Stvaranje tipova podataka"). Ako procedura ne vrati ništa, ovaj atribut nije potrebno kreirati.
Atributi DW_AT_low_pc i DW_AT_high_pc moraju se kreirati pomoću funkcije dwarf_add_AT_targ_address_b posebno dizajnirane za ovo:
Dwarf_P_Attribute dwarf_add_AT_targ_address_b(Dwarf_P_Debug dbg, Patuljak_P_Die ownerdie, Patuljak_Half attr, Patuljak_Nesigned pc_value, Dwarf_Unsigned sym_index, Dwarf_Error)
  • attr - atribut (DW_AT_low_pc ili DW_AT_high_pc)
  • pc_value - vrijednost adrese
  • sym_index - indeks simbola procedure u tablici .symtab. Neobavezno, možete proslijediti 0
Funkcija će vratiti DW_DLV_BADADDR u slučaju pogreške.
Izrada FDE procedure
Kao što je gore spomenuto u odjeljku "Stvaranje zajedničkog unosa informacija", za svaki postupak morate stvoriti deskriptor okvira, koji se događa u nekoliko faza:
  • stvaranje novog FDE (pogledajte Stvaranje zajedničkog unosa informacija)
  • prilaganje kreiranog FDE-a općem popisu
  • dodavanje instrukcija u generirani FDE
Možete stvoriti novi FDE s funkcijom dwarf_new_fde:
Dwarf_P_Fde dwarf_new_fde(Dwarf_P_Debug dbg, Patuljak_Error *error)
Funkcija će vratiti ručicu novom FDE ili DW_DLV_BADADDR u slučaju pogreške.
Možete dodati novi FDE na popis pomoću dwarf_add_frame_fde :
Dwarf_Unsigned dwarf_add_frame_fde(Dwarf_P_Debug dbg, Dwarf_P_Fde fde, Patuljak_P_Die die, Dwarf_Unsigned cie, Dwarf_Addr virt_addr, Dwarf_Unsigned code_len, Patuljak_rr_Un)
  • fde - upravo primljena ručka
  • die - Procedura DIE (pogledajte Stvaranje čvora procedure s atributima)
  • cie - CIE deskriptor (pogledajte Stvaranje zajedničkog unosa informacija)
  • virt_addr - početna adresa naše procedure
  • code_len - duljina procedure u bajtovima
Funkcija će vratiti DW_DLV_NOCOUNT u slučaju pogreške.
Nakon svega ovoga, možemo dodati DW_CFA_hhhh upute u naš FDE. To se radi s funkcijama dwarf_add_fde_inst i dwarf_fde_cfa_offset. Prvi dodaje danu instrukciju na popis:
Patuljak_P_Fde dwarf_add_fde_inst(Dwarf_P_Fde fde, Patuljak_Small op, Patuljak_Unsigned val1, Dwarf_Unsigned val2, Patuljak_Error *error)
  • op - kod instrukcije (DW_CFA_hhhh)
  • val1, val2 - parametri instrukcije (različiti za svaku instrukciju, pogledajte Standard, odjeljak 6.4.2 Upute za okvir poziva)
Funkcija dwarf_fde_cfa_offset dodaje instrukciju DW_CFA_offset:
Patuljak_P_Fde dwarf_fde_cfa_offset(Dwarf_P_Fde fde, Patuljak_Nepotpisana reg., Patuljak_Signed offset, Patuljak_Error *pogreška)
  • fde - ručka za kreirani FDE
  • reg - registar koji je upisan u okvir
  • pomak - njegov pomak u okviru (ne u bajtovima, već u elementima okvira, pogledajte Stvaranje zajedničkog unosa informacija, data_align)
Na primjer, prevodilac kreira proceduru čiji prolog sprema registar lr (r14) u okvir stoga. Prije svega, trebate dodati instrukciju DW_CFA_advance_loc s prvim parametrom jednakim 1, što znači napredovanje registra računala za 2 bajta (pogledajte Stvaranje zajedničkog unosa informacija, code_align), zatim dodati DW_CFA_def_cfa_offset s parametrom 4 (postavka pomak podataka u okviru za 4 bajta) i pozvati funkciju dwarf_fde_cfa_offset s parametrom reg=14 offset=1, što znači upisivanje r14 registra u okvir s pomakom od -4 bajta od CFA.
Izrada parametara postupka
Stvaranje parametara procedure slično je stvaranju običnih varijabli, pogledajte "Stvaranje varijabli i konstanti"
Generiranje informacija o broju linije
Ova informacija se kreira ovako:
  • na početku postupka započinjemo blok instrukcija s funkcijom dwarf_lne_set_address
  • za svaki redak koda (ili strojnu instrukciju) stvaramo informacije o izvornom kodu (dwarf_add_line_entry)
  • na kraju postupka dovršavamo blok uputa s funkcijom dwarf_lne_end_sequence
Funkcija dwarf_lne_set_address postavlja adresu na kojoj počinje blok instrukcija:
Dwarf_Unsigned dwarf_lne_set_address(Dwarf_P_Debug dbg, Dwarf_Addr offs, Dwarf_Unsigned symidx, Dwarf_Error *error)
  • offs - adresa procedure (adresa prve strojne instrukcije)
  • sym_idx - indeks simbola (opcionalno, možete odrediti 0)

Funkcija dwarf_add_line_entry_b dodaje informacije o redovima izvornog koda u odjeljku .debug_line. Ovu funkciju zovem za svaku strojnu instrukciju:
Dwarf_Unsigned dwarf_add_line_entry_b(Dwarf_P_Debug dbg, Dwarf_Unsigned file_index, Dwarf_Addr code_offset, Dwarf_Unsigned lineno, Dwarf_Signed column_number, Dwarf_Bool is_source_stmt_begin, Dwarf_Bool is_basic_block_begin, Dwarf_Bool is_epilogue_begin, Dwarf_Bool is_prologue_end, Dwarf_Unsigned isa, Dwarf_Unsigned discriminator, Dwarf_Error *error)
  • file_index - indeks datoteke izvornog koda dobivenog ranije pomoću funkcije dwarf_add_file_decl (pogledajte "Izrada procedura")
  • code_offset - adresa trenutne strojne instrukcije
  • lineno - broj retka u datoteci izvornog koda
  • column_number - broj stupca u datoteci izvornog koda
  • is_source_stmt_begin - 1 ako je trenutna instrukcija prva u kodu u lineno retku (ja uvijek koristim 1)
  • is_basic_block_begin - 1 ako je trenutna instrukcija prva u bloku izraza (uvijek koristim 0)
  • is_epilogue_begin - 1 ako je trenutna instrukcija prva u epilogu procedure (nije potrebno, uvijek imam 0)
  • is_prologue_end - 1 ako je trenutna instrukcija posljednja u prologu postupka (obavezno!)
  • isa - arhitektura skupa instrukcija (arhitektura skupa instrukcija). Svakako navedite DW_ISA_ARM_thumb za ARM Cortex M3!
  • diskriminator. Jedna pozicija (datoteka, redak, stupac) izvornog koda može odgovarati različitim strojnim uputama. U tom slučaju moraju se postaviti različiti diskriminatori za skupove takvih uputa. Ako takvih slučajeva nema, trebao bi biti 0
Funkcija vraća 0 (uspjeh) ili DW_DLV_NOCOUNT (pogreška).
Konačno, funkcija dwarf_lne_end_sequence završava proceduru:
Dwarf_Unsigned dwarf_lne_end_sequence(Dwarf_P_Debug dbg, Dwarf_Addr adresa; Dwarf_Error *error)
  • adresa - adresa trenutne strojne upute
Vraća 0 (uspjeh) ili DW_DLV_NOCOUNT (pogreška).
Time se dovršava stvaranje postupka.
Stvaranje varijabli i konstanti
Općenito, varijable su prilično jednostavne. Imaju naziv, memorijsku lokaciju (ili registar procesora) gdje se nalaze njihovi podaci i vrstu tih podataka. Ako je varijabla globalna - njezin roditelj bi trebao biti kompilacijski jedinica, ako je lokalni - odgovarajući čvor (ovo se posebno odnosi na parametre procedure, oni moraju imati samu proceduru kao roditelj). Također možete odrediti u kojoj se datoteci, retku i stupcu nalazi deklaracija varijable.
U najjednostavnijem slučaju, vrijednost varijable nalazi se na nekoj fiksnoj adresi, ali mnoge varijable se dinamički stvaraju prilikom ulaska u proceduru na stog ili registar, ponekad izračunavanje adrese vrijednosti može biti prilično netrivijalno. Standard pruža mehanizam za opisivanje gdje se nalazi vrijednost varijable – lokacijski izrazi. Adresni izraz je skup instrukcija (DW_OP_xxxx konstante) za stroj za stog nalik na četvrtinu, zapravo je to zasebni jezik s granama, procedurama i aritmetičkim operacijama. Ovaj jezik nećemo pregledavati u cijelosti, zapravo će nas zanimati samo nekoliko uputa:
  • DW_OP_addr - specificira adresu varijable
  • DW_OP_fbreg - Označava pomak varijable od osnovnog registra (obično pokazivač steka)
  • DW_OP_reg0 ... DW_OP_reg31 - označava da je varijabla pohranjena u odgovarajućem registru
Da biste stvorili odredišni izraz, prvo morate kreirati prazan izraz (dwarf_new_expr), dodati mu upute (dwarf_add_expr_addr, dwarf_add_expr_gen, itd.) i dodati ga čvoru kao vrijednost atributa DW_AT_location (dwarf_add_AT_location_expression).
Funkcija za kreiranje praznog adresnog izraza vraća oznaku ili 0 u slučaju pogreške:
Dwarf_Expr dwarf_new_expr(Dwarf_P_Debug dbg, Dwarf_Error *error)
Da biste izrazu dodali upute, koristite funkciju dwarf_add_expr_gen:
Dwarf_Unsigned dwarf_add_expr_gen(Dwarf_P_Expr expr, Patuljak_Small opcode, Patuljak_Unsigned val1, Dwarf_Unsigned val2, Patuljak_Error *error)
  • opcode - šifra operacije, konstanta DW_OP_hhhh
  • val1, val2 - parametri instrukcije (vidi standard)

Da biste eksplicitno postavili adresu varijable, treba koristiti funkciju dwarf_add_expr_addr umjesto prethodne:
Dwarf_Unsigned dwarf_add_expr_addr(Dwarf_P_Expr expr, Patuljak_Unsigned adresa, Patuljasti_Signed sym_index, Patuljak_Error *error)
  • expr - ručka adresnog izraza kojemu je dodana instrukcija
  • adresa - varijabla adresa
  • sym_index - indeks simbola u tablici .symtab. Neobavezno, možete proslijediti 0
Funkcija također vraća DW_DLV_NOCOUNT u slučaju pogreške.
I konačno, možete dodati stvoreni adresni izraz čvoru pomoću funkcije dwarf_add_AT_location_expr:
Dwarf_P_Attribute dwarf_add_AT_location_expr(Dwarf_P_Debug dbg, Dwarf_P_Die ownerdie, Patuljak_Half attr, Dwarf_P_Expr loc_expr, Dwarf_Error *error)
  • ownerdie - čvor kojemu je dodan izraz
  • attr - atribut (u našem slučaju DW_AT_location)
  • loc_expr - ručka za prethodno kreirani adresni izraz
Funkcija vraća ručku atributa ili DW_DLV_NOCOUNT u slučaju pogreške.
Varijable (kao i parametri procedure) i konstante su obični čvorovi s oznakom DW_TAG_varijable, DW_TAG_formal_parameter i DW_TAG_const_type respektivno. Potrebni su im sljedeći atributi:
  • naziv varijable/konstante (funkcija dwarf_add_AT_name, pogledajte "Stvaranje atributa čvora")
  • broj retka u datoteci u kojoj je varijabla deklarirana (atribut DW_AT_decl_line), funkcija dwarf_add_AT_unsigned_const (pogledajte "Stvaranje atributa čvora")
  • indeks naziva datoteke (atribut DW_AT_decl_file), funkcija dwarf_add_AT_unsigned_const (pogledajte "Stvaranje atributa čvora")
  • varijabilni/konstantni tip podataka (atribut DW_AT_type je veza na prethodno kreiranu vrstu, pogledajte "Stvaranje vrsta podataka")
  • izraz adrese (vidi gore) - potreban za varijablu ili parametar procedure
  • ili vrijednost - za konstantu (atribut DW_AT_const_value, pogledajte "Stvaranje atributa čvora")
Izrada odjeljaka s informacijama o otklanjanju pogrešaka
Nakon što stvorite sve čvorove informacijskog stabla za otklanjanje pogrešaka, s njim možete početi formirati elf sekcije. To se događa u dvije faze:
  • prvo morate pozvati funkciju dwarf_transform_to_disk_form, koja će pozvati funkciju koju smo napisali za stvaranje željenih elf sekcija jednom za svaki odjeljak
  • za svaki odjeljak, funkcija dwarf_get_section_bytes vratit će nam podatke koji će se morati zapisati u odgovarajući odjeljak
Funkcija
dwarf_transform_to_disk_form (dwarf_P_Debug dbg, Dwarf_Error* pogreška)
pretvara informacije za otklanjanje pogrešaka koje smo stvorili u binarni format, ali ništa ne zapisuje na disk. Vratit će broj kreiranih elf sekcija ili DW_DLV_NOCOUNT u slučaju pogreške. U ovom slučaju, za svaki odjeljak bit će pozvana funkcija povratnog poziva, koju smo proslijedili prilikom inicijalizacije biblioteke u funkciju dwarf_producer_init_c. Ovu funkciju moramo sami napisati. Njegova specifikacija je:
typedef int (*Dwarf_Callback_Func_c)(char* naziv, veličina int, patuljasti_nepotpisani tip, patuljaste_nepotpisane zastavice, patuljasta_nepotpisana veza, patuljasti_nepotpisani podaci, patuljasti_nepotpisani* indeks_naziva_sekta, void * korisnički_podaci)
  • ime - naziv elf odjeljka koji će se stvoriti
  • size - veličina presjeka
  • type - vrsta sekcije
  • zastave - zastavice sekcija
  • link - polje veze odjeljka
  • info - informacijsko polje odjeljka
  • sect_name_index - trebate vratiti indeks odjeljka s premještanjima (opcionalno)
  • user_data - prosljeđuje nam se na isti način kako smo ga postavili u funkciji inicijalizacije knjižnice
  • pogreška - ovdje možete proslijediti kod pogreške
U ovoj funkciji moramo:
  • kreirajte novi odjeljak (funkcija elf_newscn, pogledajte Stvaranje odjeljaka)
  • kreirajte zaglavlje odjeljka (funkcija elf32_getshdr, ibid.)
  • ispravno ispunite (vidi ibid.). To je jednostavno jer polja zaglavlja odjeljka odgovaraju parametrima naše funkcije. Polja koja nedostaju sh_addr, sh_offset, sh_entsize bit će postavljena na 0, a sh_addralign na 1
  • vrati indeks kreiranog odjeljka (funkcija elf_ndxscn, vidi "Section.symtab") ili -1 u slučaju pogreške (postavljanjem koda pogreške na pogrešku)
  • također moramo preskočiti odjeljak ".rel" (u našem slučaju) vraćanjem 0 kada se vraćamo iz funkcije
Po završetku, funkcija dwarf_transform_to_disk_form će vratiti broj stvorenih particija. Morat ćemo napraviti petlju od 0 kroz svaki odjeljak, slijedeći ove korake:
  • kreirajte podatke za pisanje u odjeljak pomoću funkcije dwarf_get_section_bytes:
    Dwarf_Ptr dwarf_get_section_bytes(Dwarf_P_Debug dbg, Dwarf_Signed patuljasti_section, Dwarf_Signed *elf_section_index, Dwarf_Unsigned *length, Dwarf_Error* pogreška)
    • patuljasti_sekcija - broj odjeljka. Trebao bi biti u rasponu 0..n, gdje je n broj koji nam vraća funkcija dwarf_transform_to_disk_form
    • elf_section_index - vraća indeks odjeljka u koji se upisuju podaci
    • duljina - duljina ovih podataka
    • greška - ne koristi se
    Funkcija vraća pokazivač na primljene podatke ili 0 (ako
    kada više nema odjeljaka za stvaranje)
  • kreirajte deskriptor podataka za trenutni odjeljak (funkcija elf_newdata, pogledajte Stvaranje odjeljaka) i ispunite ga (vidi ibid.) postavljanjem:
    • d_buf - pokazivač na podatke koje smo primili iz prethodne funkcije
    • d_size - veličina ovih podataka (ibid.)
Završetak rada s knjižnicom
Nakon formiranja sekcija, možete završiti rad s libdwarfom s funkcijom dwarf_producer_finish:
Dwarf_Unsigned dwarf_producer_finish(Dwarf_P_Debug dbg, Dwarf_Error* pogreška)
Funkcija vraća DW_DLV_NOCOUNT u slučaju pogreške.
Napominjem da se pisanje na disk u ovoj fazi ne izvodi. Snimanje se mora obaviti korištenjem funkcija iz odjeljka "Izrada ELF-a - Pisanje datoteke".

Zaključak

To je sve.
Ponavljam, kreiranje informacija za otklanjanje pogrešaka vrlo je opsežna tema i nisam se dotakao mnogih tema, samo sam otvorio veo. Oni koji žele mogu otići duboko u beskonačnost.
Ako imate pitanja, pokušat ću odgovoriti na njih.

ELF format

ELF format ima nekoliko vrsta datoteka koje smo do sada različito nazivali, kao što su izvršna datoteka ili objektna datoteka. Međutim, ELF standard razlikuje sljedeće vrste:

1. Datoteka se premješta(datoteka koja se može premjestiti) koja pohranjuje upute i podatke koji se mogu povezati s drugim objektnim datotekama. Rezultat takvog povezivanja može biti izvršna datoteka ili zajednička objektna datoteka.

2. Dijeljena objektna datoteka(shared object file) također sadrži upute i podatke, ali se može koristiti na dva načina. U prvom slučaju, može se povezati s drugim datotekama koje se mogu premjestiti i zajedničkim objektnim datotekama, što rezultira stvaranjem nove objektne datoteke. U drugom slučaju, kada se program pokrene za izvršenje, operativni sustav ga može dinamički povezati s izvršnom datotekom programa, uslijed čega će se stvoriti izvršna slika programa. U potonjem slučaju govorimo o zajedničkim knjižnicama.

3. Izvršna pohranjuje potpuni opis koji sustavu omogućuje stvaranje slike procesa. Sadrži upute, podatke, opis potrebnih zajedničkih objektnih datoteka i potrebne simboličke informacije i informacije za otklanjanje pogrešaka.

Na sl. 2.4 prikazuje strukturu izvršne datoteke s kojom operativni sustav može kreirati sliku programa i pokrenuti program za izvršenje.

Riža. 2.4. Struktura izvršne datoteke u ELF formatu

Zaglavlje ima fiksno mjesto u datoteci. Ostale komponente postavljene su prema informacijama pohranjenim u zaglavlju. Dakle, zaglavlje sadrži opći opis strukture datoteke, mjesto pojedinih komponenti i njihove veličine.

Budući da zaglavlje ELF datoteke definira njezinu strukturu, razmotrimo je detaljnije (Tablica 2.4).

Tablica 2.3. ELF polja zaglavlja

Polje Opis
e_ident Niz bajtova, od kojih svaki definira neke opće karakteristike datoteke: format datoteke (ELF), broj verzije, arhitekturu sustava (32-bitna ili 64-bitna) itd.
e_vrsta Vrsta datoteke kao ELF format podržava više vrsta
e_stroj Arhitektura hardverske platforme za koju je ova datoteka stvorena. U tablici. 2.4 prikazuje moguće vrijednosti ovog polja
e_verzija Broj verzije ELF formata. Obično se definira kao EV_CURRENC (trenutno), što znači najnovija verzija
e_entry Virtualna adresa na koju će sustav prenijeti kontrolu nakon učitavanja programa (ulazna točka)
e_phoff Mjesto (pomak od početka datoteke) tablice zaglavlja programa
e_shoff Mjesto tablice zaglavlja odjeljka
e_ehsize Veličina zaglavlja
e_phentsize Veličina svakog zaglavlja programa
e_phnum Broj naslova programa
e_shentsize Veličina zaglavlja svakog segmenta (odjeljka).
e_shnum Broj naslova segmenata (odjeljaka).
e_shstrndx Mjesto segmenta koji sadrži tablicu nizova

Tablica 2.4. Vrijednosti polja e_machine zaglavlja ELF datoteke

Značenje Hardverska platforma
EM_M32 AT&T WE 32100
EM_SPARC Sunce SPARC
EM_386 Intel 80386
EM_68K Motorola 68000
EM_88K Motorola 88000
EM_486 Intel 80486
EM_860 Intel i860
EM_MIPS MIPS RS3000 Big Endian
EM_MIPS_RS3_LE MIPS RS3000 Little Endian
EM_RS6000 RS6000
EM_PA_RISC PA-RISC
EM_nCUBE nCUBE
EM_VPP500 Fujitsu VPP500
EM_SPARC32PLUS Sunce SPARC 32+

Informacije sadržane u tablici zaglavlja programa govore kernelu kako stvoriti sliku procesa iz segmenata. Većina segmenata se kopira (mapira) u memoriju i predstavlja odgovarajuće segmente procesa kada se izvršava, kao što su segmenti koda ili podataka.

Zaglavlje svakog segmenta programa opisuje jedan segment i sadrži sljedeće informacije:

Tip segmenta i radnje operativnog sustava s ovim segmentom

Položaj segmenta u datoteci

Početna adresa segmenta u virtualnoj memoriji procesa

Veličina segmenta datoteke

Veličina memorijskog segmenta

Oznake pristupa segmentu (pisanje, čitanje, izvršavanje)

Neki segmenti imaju tip LOAD, koji nalaže kernelu da kreira strukture podataka koje odgovaraju tim segmentima, tzv. područja, koji definiraju susjedne dijelove virtualne memorije procesa i njihove pridružene atribute. Segment, čija je lokacija u ELF datoteci naznačena u odgovarajućem zaglavlju programa, bit će preslikan na stvoreno područje, čija je virtualna početna adresa također naznačena u zaglavlju programa. Segmenti ovog tipa uključuju, na primjer, segmente koji sadrže programske upute (kod) i njegove podatke. Ako je veličina segmenta manja od veličine područja, neiskorišteni prostor može biti popunjen nulama. Takav mehanizam se posebno koristi kod stvaranja neinicijaliziranih procesnih podataka (BSS). Više ćemo o područjima govoriti u 3. poglavlju.

Segment tipa INTERP pohranjuje programski tumač. Ova vrsta segmenta koristi se za programe koji zahtijevaju dinamičko povezivanje. Bit dinamičkog povezivanja je da se pojedine komponente izvršne datoteke (zajedničke objektne datoteke) povezuju ne u fazi kompilacije, već u fazi pokretanja programa za izvršenje. Naziv datoteke koja je uređivač dinamičkih veza, pohranjen je u ovom segmentu. Tijekom izvođenja programa, kernel kreira sliku procesa koristeći navedeni linker. Dakle, nije izvorni program taj koji se u početku učitava u memoriju, već dinamički linker. U sljedećem koraku, dinamički linker radi s UNIX kernelom kako bi stvorio potpunu izvršnu sliku. Dinamički uređivač učitava potrebne zajedničke objektne datoteke, čiji su nazivi pohranjeni u zasebnim segmentima izvorne izvršne datoteke, te obavlja potrebno postavljanje i povezivanje. Konačno, kontrola se prenosi na izvorni program.

Konačno, tablica zaglavlja dovršava datoteku. sekcije ili sekcije(odjeljak). Odjeljci (odjeljci) definiraju odjeljke datoteke koji se koriste za povezivanje s drugim modulima tijekom kompilacije ili dinamičkog povezivanja. Sukladno tome, naslovi sadrže sve potrebne informacije za opisivanje ovih odjeljaka. Odjeljci u pravilu sadrže detaljnije informacije o segmentima. Tako se, na primjer, segment koda može sastojati od nekoliko odjeljaka, kao što je hash tablica za pohranjivanje indeksa simbola koji se koriste u programu, odjeljak za inicijalizacijski kod programa, tablica povezivanja koju koristi dinamički uređivač i odjeljak koji sadrži stvarne upute za program.

Vratit ćemo se na ELF format u 3. poglavlju kada budemo raspravljali o organizaciji procesne virtualne memorije, ali za sada prijeđimo na sljedeći uobičajeni format, COFF.

Iz knjige Umjetnost Unix programiranja Autor Raymond Eric Steven

Iz knjige Računalni priručnik Autor Kolisničenko Denis Nikolajevič

Iz knjige Sažetak, seminarski rad, diploma na računalu Autor Balovsyak Nadezhda Vasilievna

5.2.6. Windows INI format Mnogi programi u sustavu Microsoft Windows koriste tekstualni format podataka, kao što je primjer u primjeru 5-6. U ovom primjeru, izborni resursi pod nazivom account, directory, numeric_id i developer povezani su s imenovanim projektima python, sng, f etchmail i py-howto. U snimci

Iz knjige Najnoviji računalni tutorial Autor Beluntsov Valery

14.5.3. Format ćelije Format određuje kako će se prikazati vrijednost ćelije. Format je usko povezan s tipom podataka ćelije. Vrsta je na vama. Ako ste unijeli broj, onda je to numerički tip podataka. Sam Excel pokušava odrediti format prema vrsti podataka. Na primjer, ako ste unijeli tekst, onda

Iz knjige Umjetnost Unix programiranja Autor Raymond Eric Steven

PDF format PDF je skraćenica od Portable Document Format (Portable Document Format). Ovaj format je stvoren posebno za uklanjanje problema s prikazom informacija u datotekama. Njegova prednost je što će, prvo, dokument spremljen u PDF formatu biti isti

Iz knjige TCP/IP arhitektura, protokoli, implementacija (uključujući IP verziju 6 i IP sigurnost) autorica Faith Sidney M

Format datoteke Kada korisnik počne raditi s datotekom, sustav mora znati u kojem je formatu napisana i kojim programom je treba otvoriti. Na primjer, ako datoteka sadrži običan tekst, tada se može čitati u bilo kojem tekstualnom programu

Iz knjige Yandex za sve autor Abramzon M. G.

5.2.2. RFC 822 format Metaformat RFC 822 izveden je iz tekstualnog formata internetskih poruka e-pošte. RFC 822 je glavni internetski RFC standard koji opisuje ovaj format (naknadno zamijenjen RFC 2822). MIME (višenamjensko proširenje internetskih medija) format

Od Macromedia Flash Professional 8. Grafika i animacija autor Dronov V. A.

5.2.3. Format Cookie-jar Fortune(1) koristi format kolačića za svoju vlastitu bazu nasumičnih citata. Pogodan je za unose koji su jednostavno blokovi nestrukturiranog teksta. Razdjelnik zapisa u ovom formatu je znak

Iz knjige Računalna obrada zvuka Autor Zagumennov Aleksandar Petrovič

5.2.4. Format spremnika za zapise Ograničenja zapisa kolačića u posudi dobro se uklapaju u metaformat RFC 822 za zapise koji čine format koji se u ovoj knjizi spominje kao "record-jar". Ponekad je potreban tekstualni format koji podržava više unosa s različitim skupom eksplicitnih naziva

Iz knjige Operativni sustav UNIX Autor Robačevski Andrej M.

5.2.6. Windows INI format Mnogi programi u sustavu Microsoft Windows koriste tekstualni format podataka, kao što je primjer u primjeru 5-6. U ovom primjeru, izborni resursi pod nazivom account, directory, numeric_id i developer povezani su s imenovanim projektima python, sng, fetchmail i py-howto. U snimci

Iz knjige Uredsko računalo za žene Autor Pasternak Evgenija

19.5 Generički format URL-a Rezimirajući gore navedeno, napominjemo da:? URL počinje s korištenim pristupnim protokolom.? Za sve aplikacije osim online vijesti i e-pošte, nakon toga slijedi graničnik://.? Zatim je navedeno ime hosta poslužitelja.? Konačno

Iz knjige autora

3.3.1. RSS format Vijesti s web-mjesta možete čitati na različite načine. Najlakši način je s vremena na vrijeme posjetiti stranicu i pogledati nove poruke. Možete postaviti program koji se povezuje na kanal vijesti i sam prima naslove ili bilješke vijesti, prema

Iz knjige autora

MP3 format MP3 format je stvoren za distribuciju glazbenih datoteka komprimiranih kodekom MPEG 1 razine 3. Trenutno je najpopularniji format za distribuciju glazbe putem Interneta i šire. Podržavaju ga apsolutno svi programi za snimanje i obradu zvuka, za

Iz knjige autora

MP3 Format Metoda kompresije zvuka, kao i format komprimiranih audio datoteka, koji je predložila međunarodna organizacija MPEG (Moving Pictures Experts Group - Video Recording Experts Group), temelji se na perceptivnom audio kodiranju. Raditi na stvaranju učinkovitih algoritama kodiranja

Iz knjige autora

Format ELF Format ELF ima nekoliko vrsta datoteka koje smo do sada različito nazivali, kao što su izvršna datoteka ili objektna datoteka. Međutim, ELF standard razlikuje sljedeće tipove:1. Premjenjiva datoteka koja sadrži upute i podatke koji se mogu

Iz knjige autora

Format broja Konačno smo došli do formata broja. Već sam to više puta spomenuo, sada ću sve staviti na police (iako ste već mogli razumjeti opće značenje) Brojevi u Excelu mogu se prikazati u raznim formatima. U ovom ćemo odjeljku govoriti o tome koji formati brojeva postoje i kako

U ovoj recenziji ćemo govoriti samo o 32-bitnoj verziji ovog formata, jer nam 64-bitna još ne treba.

Svaka ELF datoteka (uključujući objektne module ovog formata) sastoji se od sljedećih dijelova:

  • zaglavlje ELF datoteke;
  • Tablica odjeljaka programa (može biti odsutna u objektnim modulima);
  • Odjeljci ELF datoteke;
  • Tablica odjeljaka (možda nije prisutna u izvršnom modulu);
  • Iz razloga izvedbe, ELF format ne koristi bitna polja. I sve strukture su obično 4-bajtne usklađene.

Pogledajmo sada vrste koje se koriste u zaglavljima ELF datoteka:

Sada razmotrite zaglavlje datoteke:

#define EI_NIDENT 16 struct elf32_hdr ( unsigned char e_ident; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; /* Entry point */ Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; );

Niz e_ident sadrži informacije o sustavu i sastoji se od nekoliko potpolja.

Struktura ( unsigned char ei_magic; unsigned char ei_class; unsigned char ei_data; unsigned char ei_version; unsigned char ei_pad;)

  • ei_magic - konstantna vrijednost za sve ELF datoteke, jednaka (0x7f, "E", "L", "F")
  • ei_class - klasa datoteke ELF (1 - 32 bita, 2 - 64 bita što ne uzimamo u obzir)
  • ei_data - određuje redoslijed bajtova za ovu datoteku (ovaj redoslijed ovisi o platformi i može biti izravan (LSB ili 1) ili obrnut (MSB ili 2)) Za Intelove procesore dopuštena je samo vrijednost 1.
  • ei_version je prilično beskorisno polje, a ako nije jednako 1 (EV_CURRENT), onda se datoteka smatra nevažećom.

Polje ei_pad mjesto je gdje operativni sustavi pohranjuju svoje identifikacijske podatke. Ovo polje može biti prazno. Ni nama je svejedno.

Polje zaglavlja e_type može sadržavati više vrijednosti, za izvršne datoteke mora biti ET_EXEC jednako 2

e_machine - određuje procesor na kojem se ova izvršna datoteka može pokrenuti (za nas je vrijednost EM_386 3)

Polje e_version odgovara polju ei_version iz zaglavlja.

Polje e_entry definira početnu adresu programa, koja se stavlja u eip prije pokretanja programa.

Polje e_phoff specificira pomak od početka datoteke gdje se nalazi tablica odjeljka programa, koji se koristi za učitavanje programa u memoriju.

Neću navoditi svrhu svih polja, nisu sva potrebna za učitavanje. Opisat ću još samo dva.

Polje e_phentsize definira veličinu unosa u tablici odjeljka programa.

A polje e_phnum specificira broj unosa u tablici odjeljka programa.

Tablica odjeljaka (neprogramska) koristi se za povezivanje programa. nećemo to razmatrati. Također, nećemo razmatrati dinamički povezane module. Ova tema je prilično komplicirana, nije prikladna za prvo poznanstvo. :)

Sada o odjeljcima programa. Format unosa tablice odjeljka programa je sljedeći:

Strukturirajte elf32_phdr( Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word_Word; W p_p_);

Više o poljima.

  • p_type - definira tip odjeljka programa. Može imati nekoliko vrijednosti, ali nas zanima samo jedna. PT_LOAD(1). Ako je odjeljak ovog tipa, tada se namjerava učitati u memoriju.
  • p_offset - određuje pomak u datoteci od koje počinje ovaj odjeljak.
  • p_vaddr Određuje virtualnu adresu na koju se ovaj odjeljak treba učitati u memoriju.
  • p_paddr - definira fizičku adresu na koju treba učitati ovaj odjeljak. Ovo polje se ne mora koristiti i ima smisla samo za neke platforme.
  • p_filesz - Određuje veličinu odjeljka u datoteci.
  • p_memsz - određuje veličinu odjeljka u memoriji. Ova vrijednost može biti veća od prethodne. Polje p_flag definira vrstu pristupa odjeljcima u memoriji. Neke dionice je dopušteno izvoditi, neke snimati. Svatko je dostupan za čitanje u postojećim sustavima.

Učitavanje ELF formata.

S naslovom smo malo shvatili. Sada ću dati algoritam za učitavanje binarne datoteke ELF formata. Algoritam je shematski, ne biste ga trebali smatrati radnim programom.

Int LoadELF (unsigned char *bin) (struct elf32_hdr *EH = (struct elf32_hdr *)bin; struct elf32_phdr *EPH; if (EH->e_ident != 0x7f || // Control MAGIC EH->e_ident != "E" || EH->e_ident != "L" || EH->e_ident != "F" || EH->e_ident != ELFCLASS32 || // Kontrolna klasa EH->e_ident != ELFDATA2LSB || // redoslijed bajtova EH->e_ident != EV_CURRENT || // verzija EH->e_type != ET_EXEC || // tip EH->e_machine != EM_386 || // platforma EH->e_version != EV_CURRENT) // i opet verzija, za svaki slučaj vrati ELF_WRONG; EPH = (struct elf32_phdr *)(bin + EH->e_phoff); dok (EH->e_phnum--) ( if (EPH->p_type == PT_LOAD) memcpy (EPH->p_vaddr, bin + EPH->p_offset, EPH->p_filesz); EPH = (struct elf32_phdr *)((unsigned char *)EPH + EH->e_phentsize)); ) return ELF_OK; )

Ozbiljno, vrijedno je analizirati polja EPH->p_flags i postaviti prava pristupa odgovarajućim stranicama, a jednostavno kopiranje ovdje neće raditi, ali to se više ne odnosi na format, već na dodjelu memorije. Stoga, nećemo sada o tome.

PE format.

Na mnogo načina, sličan je ELF formatu, i nije iznenađujuće da bi također trebali postojati odjeljci dostupni za preuzimanje.

Kao i sve u Microsoftu :) PE format je baziran na EXE formatu. Struktura datoteke je:

  • 00h - EXE zaglavlje (neću ga razmatrati, staro je kao Dos. :)
  • 20h - OEM zaglavlje (u njemu ništa značajno);
  • 3ch - stvarni pomak PE zaglavlja u datoteci (dword).
  • stol za kretanje stuba;
  • stub;
  • PE zaglavlje;
  • stol za objekte;
  • objekti datoteke;

stub je program koji radi u stvarnom načinu rada i radi neke preliminarne radove. Možda nije dostupan, ali ponekad može biti potreban.

Zanima nas nešto drugo, PE header.

Njegova struktura je ovakva:

Struktura pe_hdr (nepotpisani dugi pe_sign; nepotpisani kratki pe_cpuptipe; nepotpisani kratki pe_objnum; nepotpisani dugi pe_time; nepotpisani dugi pe_cofftbl_off; nepotpisani dugi pe_cofftbl_size; nepotpisani pe_nt; unsagd; neznatirani pE; ; unsigned long pe_entry; unsigned long pe_code_base; unsigned long pe_data_base; unsigned long pe_image_base; unsigned long pe_obj_align; unsigned long pe_file_align; // ... pa, i puno drugih stvari, nevažno. );

Puno stvari je tu. Dovoljno je reći da je veličina ovog zaglavlja 248 bajtova.

A glavna stvar je da se većina tih polja ne koristi. (Tko tako gradi?) Ne, naravno, imaju dobro poznatu svrhu, ali moj testni program, na primjer, sadrži nule u poljima pe_code_base, pe_code_size itd., ali radi dobro. Sam zaključak sugerira da se datoteka učitava na temelju tablice objekata. O tome ćemo razgovarati.

Tablica objekata slijedi odmah nakon PE zaglavlja. Unosi u ovoj tablici imaju sljedeći format:

Struktura pe_ohdr ( unsigned char o_name; unsigned long o_vsize; unsigned long o_vaddr; unsigned long o_psize; unsigned long o_poff; unsigned char o_reserved; unsigned long o_flags; );

  • o_name - naziv odjeljka, apsolutno je indiferentan za učitavanje;
  • o_vsize - veličina odjeljka u memoriji;
  • o_vaddr - memorijska adresa u odnosu na ImageBase;
  • o_psize - veličina odjeljka u datoteci;
  • o_poff - pomak odjeljka u datoteci;
  • o_flags - zastavice sekcija;

Ovdje se vrijedi detaljnije zadržati na zastavama.

  • 00000004h - koristi se za kod sa 16-bitnim pomacima
  • 00000020h - odjeljak koda
  • 00000040h - inicijalizirani odjeljak podataka
  • 00000080h - neinicijalizirani odjeljak podataka
  • 00000200h - komentari ili bilo koja druga vrsta informacija
  • 00000400h - preslojni dio
  • 00000800h - neće biti dio slike programa
  • 00001000h - opći podaci
  • 00500000h - zadano poravnanje osim ako nije drugačije navedeno
  • 02000000h - može se isprazniti iz memorije
  • 04000000h - nije u predmemoriji
  • 08000000h - nije moguće stranice
  • 10000000h - podijeljeno
  • 20000000h - izvedivo
  • 40000000h - može se čitati
  • 80000000h - možete napisati

Opet, neću biti s shared i overlay sekcijama, zanimaju nas kod, podaci i prava pristupa.

Općenito, ove su informacije već dovoljne za preuzimanje binarne datoteke.

Učitavanje PE formata.

int LoadPE (unsigned char *bin) (struct elf32_hdr *PH = (struct pe_hdr *) (bin + *((unsigned long *)&bin)); // Naravno, kombinacija nije jasna... samo uzmite dword na offset 0x3c // I izračunaj adresu zaglavlja PE u slici datoteke struct elf32_phdr *POH; if (PH == NULL || // Kontroliraj pokazivač PH->pe_sign != 0x4550 || // PE potpis ("P", "E", 0, 0) PH->pe_cputype != 0x14c || // i386 (PH->pe_flags & 2) == 0) // datoteka se ne može pokrenuti! vrati PE_WRONG; POH = (struct pe_ohdr *)( (unsigned char *)PH + 0xf8); while (PH->pe_obj_num--) ( if ((POH->p_flags & 0x60) != 0) // ili kod ili inicijalizirani podaci memcpy (PE->pe_image_base + POH- >o_vaddr, bin + POH- >o_poff, POH->o_psize); POH = (struct pe_ohdr *)((unsigned char *)POH + sizeof (struct pe_ohdr)); ) return PE_OK; )

Ovo opet nije gotov program, već algoritam učitavanja.

I opet, mnoge točke nisu obuhvaćene, jer nadilaze temu.

Ali sada je vrijedno razgovarati malo o postojećim značajkama sustava.

Značajke sustava.

Unatoč fleksibilnosti zaštita koje su dostupne u procesorima (zaštita na razini tablica deskriptora, zaštita na razini segmenta, zaštita na razini stranice), u postojećim sustavima (i u Windowsima i u Unixu) u potpunosti se koristi samo zaštita stranica, što, iako može spriječiti pisanje koda, ali ne može spriječiti izvršavanje podataka. (Možda je to razlog obilja ranjivosti sustava?)

Svi segmenti su adresirani od linearne adrese nula i protežu se do kraja linearne memorije. Razgraničenje procesa događa se samo na razini tablice stranica.

U tom smislu, svi moduli nisu povezani s početnih adresa, već s dovoljno velikim pomakom u segmentu. U sustavu Windows, osnovna adresa u segmentu je 0x400000, na Unixu (Linux ili FreeBSD) je 0x8048000.

Neke su značajke također povezane sa stranicama memorije.

ELF datoteke su povezane na način da granice i veličine odjeljaka padaju na blokove datoteke od 4 kilobajta.

A u PE formatu, unatoč činjenici da sam format omogućuje poravnavanje odjeljaka od 512 bajtova, koristi se 4k poravnanje odjeljka, manje poravnanje u sustavu Windows ne smatra se točnim.

Ako vaše računalo ima antivirusni program limenka skenirajte sve datoteke na računalu, kao i svaku datoteku pojedinačno. Možete skenirati bilo koju datoteku tako da desnom tipkom miša kliknete datoteku i odaberete odgovarajuću opciju za skeniranje datoteke na viruse.

Na primjer, na ovoj slici, datoteka moja-datoteka.elf, zatim trebate kliknuti desnom tipkom miša na ovu datoteku i u izborniku datoteka odabrati opciju "scan with AVG". Odabirom ove opcije otvorit će se AVG Antivirus i skenirati datoteku na viruse.


Ponekad može doći do pogreške pogrešna instalacija softvera, što može biti posljedica problema koji se dogodio tijekom procesa instalacije. To može ometati vaš operativni sustav povežite svoju ELF datoteku s ispravnom softverskom aplikacijom, utječući na tzv "asocijacije proširenja datoteke".

Ponekad jednostavno ponovno instaliranje Dolphina (emulator) može riješiti vaš problem pravilnim povezivanjem ELF-a s Dolphinom (emulatorom). U drugim slučajevima može doći do problema s povezivanjem datoteka loše programiranje softvera razvojnog programera, a možda ćete morati kontaktirati razvojnog programera za daljnju pomoć.


Savjet: Pokušajte ažurirati Dolphin (emulator) na najnoviju verziju kako biste bili sigurni da imate najnovije zakrpe i ažuriranja.


Ovo se može činiti previše očitim, ali često sama ELF datoteka može uzrokovati problem. Ako ste primili datoteku putem privitka e-pošte ili ste je preuzeli s web-mjesta, a proces preuzimanja je prekinut (na primjer, zbog nestanka struje ili nekog drugog razloga), datoteka je možda oštećena. Ako je moguće, pokušajte nabaviti novu kopiju ELF datoteke i pokušajte je ponovno otvoriti.


Pažljivo: Oštećena datoteka može uzrokovati kolateralnu štetu prethodnom ili postojećem zlonamjernom softveru na vašem računalu, stoga je važno održavati svoje računalo ažurnim s ažuriranim antivirusnim programom.


Ako je vaša ELF datoteka povezan s hardverom na vašem računalu da otvorite datoteku koja vam je možda potrebna ažurirati upravljačke programe uređaja povezane s ovom opremom.

Ovaj problem obično povezana s vrstama medijskih datoteka, koji ovise o uspješnom otvaranju hardvera unutar računala, npr. zvučna kartica ili video kartica. Na primjer, ako pokušavate otvoriti audio datoteku, ali je ne možete otvoriti, možda ćete morati ažuriranje upravljačkih programa zvučne kartice.


Savjet: Ako kada pokušate otvoriti ELF datoteku dobijete Poruka o pogrešci u vezi s .SYS datotekom, problem bi vjerojatno mogao biti povezan s oštećenim ili zastarjelim upravljačkim programima uređaja koje je potrebno ažurirati. Ovaj se proces može olakšati korištenjem softvera za ažuriranje upravljačkih programa kao što je DriverDoc.


Ako koraci nisu riješili problem i još uvijek imate problema s otvaranjem ELF datoteka, to može biti zbog nedostatak raspoloživih resursa sustava. Neke verzije ELF datoteka mogu zahtijevati značajnu količinu resursa (npr. memorija/RAM, procesorska snaga) za ispravno otvaranje na vašem računalu. Ovaj problem je prilično čest ako istovremeno koristite prilično stari računalni hardver i mnogo noviji operativni sustav.

Ovaj se problem može pojaviti kada računalo teško izvršava zadatak jer operativni sustav (i druge usluge koje rade u pozadini) mogu troši previše resursa za otvaranje ELF datoteke. Pokušajte zatvoriti sve aplikacije na računalu prije otvaranja datoteke igre Nintendo Wii. Oslobađanjem svih dostupnih resursa na vašem računalu osigurat ćete najbolje uvjete za pokušaj otvaranja ELF datoteke.


Ako ti dovršio sve gore navedene korake a vaša ELF datoteka se i dalje neće otvoriti, možda ćete morati pokrenuti nadogradnja hardvera. U većini slučajeva, čak i sa starijim verzijama hardvera, procesorska snaga i dalje može biti više nego dovoljna za većinu korisničkih aplikacija (osim ako ne radite puno CPU-intenzivnog posla kao što je 3D renderiranje, financijsko/znanstveno modeliranje ili medijski intenzivan rad ) . Tako, vjerojatno je da vaše računalo nema dovoljno memorije(češće se naziva "RAM" ili RAM) za obavljanje zadatka otvaranja datoteke.

Verzija ovog odgovora s dobrim TOC-om i više sadržaja: http://www.cirosantilli.com/elf-hello-world (kliknite ovdje ograničenje od 30k znakova)

Standardi

ELF daje LSB:

  • generički jezgro: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/elf-generic.html
  • jezgra AMD64: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-AMD64/LSB-Core-AMD64/book1.html

LSB se uglavnom odnosi na druge standarde s manjim proširenjima, posebice:

    generički (oba SCO):

    • System V ABI 4.1 (1997) http://www.sco.com/developers/devspecs/gabi41.pdf, ne 64 bita, iako je za njega rezerviran magični broj. Isto za glavne datoteke.
    • System V ABI ažuriranje DRAFT 17 (2003) http://www.sco.com/developers/gabi/2003-12-17/contents.html dodaje 64 bita. Ažurira samo poglavlja 4. i 5. prethodnog dokumenta: ostali ostaju važeći i i dalje se pozivaju.
  • specifična arhitektura:

    • IA-32: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-IA32/LSB-Core-IA32/elf-ia32.html upućuje uglavnom na http://www.sco.com/developers/ devspecs/abi386-4.pdf
    • AMD64: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-AMD64/LSB-Core-AMD64/elf-amd64.html , u osnovi upućuje na http://www.x86-64.org/ dokumentaciju /abi.pdf

Zgodan životopis možete pronaći na:

Njegova se struktura može ispitati korištenjem jednostavnih načina kao što su readelf i objdump.

Napravite primjer

Razložimo minimalni primjer izvedbe Linux x86-64:

Odjeljak .data hello_world db "Hello world!", 10 hello_world_len equ $ - hello_world section .text globalni _start _start: mov rax, 1 mov rdi, 1 mov rsi, hello_world mov rdx, s hello_world rdx, s hello_world 0 rdx, s hello_world 0

Sastavljeno sa

Nasm -w+all -f elf64 -o "hello_world.o" "hello_world.asm" ld -o "hello_world.out" "hello_world.o"

  • NASM 2.10.09
  • Binutils verzija 2.24 (sadrži ld)
  • Ubuntu 14.04

Ne koristimo C program jer bi to zakompliciralo analizu koja bi bila razina 2 :-)

heksadecimalni prikazi binarnog

hd hello_world.o hd hello_world.out

Globalna struktura datoteke

ELF datoteka sadrži sljedeće dijelove:

  • ELF zaglavlje. Označava položaj tablice zaglavlja odjeljka i tablice zaglavlja programa.

    Tablica zaglavlja odjeljka (neobavezno u izvršnoj). Svaki od njih ima zaglavlja odjeljka e_shnum , od kojih svaki označava položaj odjeljka.

    N particije s N<= e_shnum (необязательно в исполняемом файле)

    Tablica zaglavlja programa (samo za izvršne datoteke). Svaki od njih ima zaglavlja programa e_phnum, od kojih svako označava položaj segmenta.

    N segmenata, s N<= e_phnum (необязательно в исполняемом файле)

Redoslijed ovih dijelova nije fiksiran: jedina fiksna stvar je ELF zaglavlje, koje mora biti prvo u datoteci: Uobičajeni dokumenti kažu:

ELF zaglavlje

Najlakši način za gledanje zaglavlja je:

readelf -h hello_world.o readelf -h hello_world.out

Bajt u objektnoj datoteci:

00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF...........| 00000010 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 |..>.............| 00000020 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 | [e-mail zaštićen]| 00000030 00 00 00 00 40 00 00 00 00 00 40 00 07 00 03 00 |[e-mail zaštićen]@.....|

00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF...........| 00000010 02 00 3e 00 01 00 00 00 b0 00 40 00 00 00 00 00 |..> [e-mail zaštićen]| 00000020 40 00 00 00 00 00 00 00 10 01 00 00 00 00 00 00 |@...............| 00000030 00 00 00 00 40 00 38 00 02 00 40 00 06 00 03 00 |[e-mail zaštićen]@.....|

Predstavljena struktura:

Typedef struct ( unsigned char e_ident; Elf64_Half e_type; Elf64_Half e_machine; Elf64_Word e_version; Elf64_Addr e_entry; Elf64_Off e_phoff; Elf64_Off e_shoff; Elf64_Word e_flags; Elf64_Half e_ehsize; Elf64_Half e_phentsize; Elf64_Half e_phnum; Elf64_Half e_shentsize; Elf64_Half e_shnum; Elf64_Half e_shstrndx; ) Elf64_Ehdr;

Propadanje ručno:

    0 0: EI_MAG = 7f 45 4c 46 = 0x7f "E", "L", "F" : ELF magični broj

    0 4: EI_CLASS=02=ELFCLASS64: 64-bitni vilenjak

    0 5: EI_DATA = 01 = ELFDATA2LSB: veliki krajnji podaci

    0 6: EI_VERSION = 01: verzija formata

    0 7: EI_OSABI (samo 2003.) = 00 = ELFOSABI_NONE: Nema proširenja.

    0 8: EI_PAD = 8x 00: rezervirani bajtovi. Mora biti postavljeno na 0.

    1 0: e_type = 01 00 = 1 (veliki endian) = ET_REl: format koji se može premjestiti

    U izvršnoj 02 00 za ET_EXEC.

    1 2: e_machine = 3e 00 = 62 = EM_X86_64: AMD64 arhitektura

    1 4: e_verzija = 01 00 00 00: treba biti 1

    1 8: e_entry = 8x 00: ulazna točka izvršne adrese, ili 0 ako nije primjenjivo, kao za objektnu datoteku, budući da nema ulazne točke.

    U izvršnoj je to b0 00 40 00 00 00 00 00 . TODO: što još možemo instalirati? Čini se da kernel stavlja IP izravno u ovu vrijednost, nije tvrdo kodiran.

    2 0: e_phoff = 8x 00: pomak tablice zaglavlja programa, 0 ako nije.

    40 00 00 00 u izvršnoj datoteki, što znači da počinje odmah nakon ELF zaglavlja.

    2 8: e_shoff = 40 7x 00 = 0x40: pomak datoteke tablice zaglavlja odjeljka, 0 ako nema.

    3 0: e_zastavice = 00 00 00 00 TODO. Posebno za Arch.

    3 4: e_ehsize = 40 00: veličina ovog elf zaglavlja. Zašto je ovo polje? Kako se to može promijeniti?

    3 6: e_phentsize = 00 00: veličina svakog zaglavlja programa, 0 ako nema.

    38 00 u izvršnoj datoteci: duljina datoteke je 56 bajtova

    3 8: e_phnum = 00 00: broj unosa zaglavlja programa, 0 ako nema.

    02 00 u izvršnom fajlu: postoje 2 unosa.

    3 A: e_shentsize i e_shnum = 40 00 07 00: veličina zaglavlja odjeljka i broj unosa

Tablica zaglavlja odjeljka

Niz struktura Elf64_Shdr.

Svaki unos sadrži metapodatke o tom odjeljku.

e_shoff ELF zaglavlja ovdje daje početnu poziciju, 0x40.

e_shentsize i e_shnum iz ELF zaglavlja kažu da imamo 7 unosa, svaki dug 0x40.

Dakle, tablica uzima bajtove od 0x40 do 0x40 + 7 + 0x40 - 1 = 0x1FF.

Neki naslovi odjeljaka rezervirani su za određene vrste odjeljaka: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections na primjer. .text zahtijeva tip SHT_PROGBITS i SHF_ALLOC + SHF_EXECINSTR

readelf -S hello_world.o:

Postoji 7 zaglavlja odjeljenja, počevši od Offset 0x40: Zaglavlja odjeljka: Naziv Vrsta Adresena veličina veličine EntsSize Flags Link Info Poravnavanje [0] NULL 0000000000000000 00000000 00000000000000 00000000000000 0 0 0 [1] .DATA PROGBITS ĆE 2] .text PROGBITS 0000000000000000 00000210 0000000000000027 0000000000000000 AX 0 0 16 [ 3] .shstrtab STRTAB 0000000000000000 00000240 0000000000000032 0000000000000000 0 0 1 [ 4] .symtab SYMTAB 0000000000000000 00000280 00000000000000a8 0000000000000018 5 6 4 [ 5] .strtab STRTAB 0000000000000000 00000330 0000000000000034 0000000000000000 0 0 1 [ 6] .rela.text RELA 0000000000000000 00000370 0000000000000018 0000000000000018 4 2 4 Ključ za zastavice: W (pisati), A (execul) (execul S), M (execul) I (informacije), L (redoslijed veze), G (grupa), T (TLS), E (isključi), x (nepoznato) O (potrebna je dodatna obrada OS) o (specifična za OS), p (specifična za procesor)

struct , predstavljen svakim unosom:

Typedef struktura ( Elf64_Word sh_name; Elf64_Word sh_type; Elf64_Xword sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; Elf64_Xword sh_size; Elf64_Word sh_size; Elf64_Word sh_type; Elf64_Xword sh_flags; Elf64_Addr sh_addr; Elf64_Off sh_offset; Elf64_Xword sh_size; Elf64_Word sh_size; Elf64_Word sh_size; Elf64_Word sh_size; Elf64_Word sh_link; Elf64_Word sh4_link_X;

Odjeljci

Indeksni odjeljak 0

Sadržano u bajtovima od 0x40 do 0x7F.

Prvi odjeljak je uvijek čaroban: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html kaže:

Ako je broj particija veći ili jednak SHN_LORESERVE (0xff00), e_shnum se postavlja na SHN_UNDEF (0) i stvarni broj unosa tablice zaglavlja particije nalazi se u polju sh_size zaglavlja particije na indeksu 0 (inače, sh_size član početnog unosa sadrži 0).

Postoje i drugi magični odjeljci na slici 4-7: Indeksi posebnih odjeljaka.

Kod indeksa 0 potreban je SHT_NULL. Postoje li druge namjene za ovo: Koja je upotreba odjeljka SHT_NULL u ELF-u? ?

.odjeljak podataka

Podaci su odjeljak 1:

00000080 01 00 00 00 01 00 00 00 03 00 00 00 00 00 00 00 |................| 00000090 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 |................| 000000a0 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000b0 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

    Ovdje 1 kaže da naziv ovog odjeljka počinje prvim znakom ovog odjeljka i završava prvim NUL znakom, čineći string.data .

    Podaci su jedan od naziva odjeljaka koji ima unaprijed definirano značenje http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html

    Ovi odjeljci pohranjuju inicijalizirane podatke koji doprinose memorijskoj slici programa.

  • 80 4: sh_type = 01 00 00 00: SHT_PROGBITS: ELF ne specificira sadržaj odjeljka, već samo način na koji ga program interpretira. U redu je, budući da .data .

    80 8: sh_flags = 03 7x 00: SHF_ALLOC i SHF_EXECINSTR: http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#sh_flags , prema zahtjevu iz odjeljka .data

    90 0: sh_addr = 8x 00: na koju će virtualnu adresu odjeljak biti smješten u vrijeme izvođenja, 0 ako nije postavljen

    90 8: sh_offset = 00 02 00 00 00 00 00 00 = 0x200: broj bajtova od početka programa do prvog bajta u ovom odjeljku

    a0 0: sh_size = 0d 00 00 00 00 00 00 00

    Ako uzmemo 0xD bajtova, počevši od sh_offset 200, vidimo:

    00000200 48 65 6c 6c 6f 20 77 6f 72 6c 64 21 0a 00 |Zdravo svijete!.. |

    AHA! Dakle, naš niz "Hello world!" je u odjeljku podataka, kao što smo rekli, nalazi se na NASM-u.

    Nakon što završimo hd, gledat ćemo na to ovako:

    Readelf -x .podaci hello_world.o

    koji izlazi:

    Hex dump odjeljka ".data": 0x00000000 48656c6c 6f20776f 726c6421 0a Zdravo svijete!.

    NASM postavlja pristojna svojstva za ovaj odjeljak jer se magično odnosi na .data: http://www.nasm.us/doc/nasmdoc7.html#section-7.9.2

    Također imajte na umu da je ovo bio pogrešan odabir odjeljka: dobar C prevodilac bi umjesto toga stavio redak u .rodata, jer je samo za čitanje, a to bi omogućilo nastavak optimizacije OS-a.

    a0 8: sh_link i sh_info = 8x 0: ne primjenjuju se na ovu vrstu odjeljka. http://www.sco.com/developers/gabi/2003-12-17/ch4.sheader.html#special_sections

    b0 0: sh_addralign = 04 = TODO: zašto je ovo poravnanje potrebno? Je li to samo za sh_addr i također za znakove unutar sh_addr?

    b0 8: sh_entsize = 00 = odjeljak ne sadrži tablicu. Ako je != 0, to znači da odjeljak sadrži tablicu zapisa fiksne veličine. U ovoj datoteci možemo vidjeti iz readelf izlaza da je to slučaj za odjeljke .symtab i .rela.text.

.tekstualni dio

Sada kada smo ručno napravili jedan odjeljak, diplomirajmo i koristimo readelf -S ostalih odjeljaka.

Naziv Vrsta Adresa Veličina pomaka EntSize Flags Link Info Align [ 2] .text

Tekst je izvršan, ali se ne može pisati: ako mu pokušamo napisati Linux segfaults. Pogledajmo imamo li stvarno kod:

Objdump -d hello_world.o

Hello_world.o: format datoteke elf64-x86-64 Rastavljanje odjeljka .text: 0000000000000000<_start>: 0: b8 01 00 00 00 mov $0x1,%eax 5: bf 01 00 00 00 mov $0x1,%edi a: 48 be 00 00 00 00 00 movabs $0x0,%rsi 01: 0 ba 0d 00 00 00 mov $0xd,%edx 19: 0f 05 syscall 1b: b8 3c 00 00 00 mov $0x3c,%eax 20: bf 00 00 00 00 mov $0x0,%edi 0,%edi 0

Ako imamo grep b8 01 00 00 na hd-u, vidimo da se to događa samo na 00000210, što kaže ovaj odjeljak. I veličina je 27, što također odgovara. Stoga moramo govoriti o ispravnom odjeljku.

Ovo izgleda kao ispravan kod: upis nakon kojeg slijedi izlaz .

Najzanimljiviji dio je linija a koja radi:

Movabs $0x0,%rsi

proslijediti adresu niza u sistemski poziv. Trenutno je 0x0 samo rezervirano mjesto. Nakon vezanja, promijenit će se:

4000ba: 48 be d8 00 60 00 00 movabs $0x6000d8,%rsi

Ova izmjena je moguća zbog podataka u odjeljku .rela.text.

SHT_STRTAB

Odjeljci s sh_type == SHT_STRTAB nazivaju se tablice nizova.

Takve sekcije koriste drugi odjeljci kada se trebaju koristiti nazivi nizova. Odjeljak Upotreba kaže:

  • koju liniju koriste
  • koji je indeks u tablici ciljnih redaka gdje red počinje

Tako, na primjer, možemo imati tablicu stringova koja sadrži: TODO: trebamo li početi s \0?

Podaci: \0 a b c \0 d e f \0 Indeks: 0 1 2 3 4 5 6 7 8

A ako drugi odjeljak želi koristiti niz d e f , oni moraju pokazivati ​​na indeks 5 tog odjeljka (slovo d).

Poznate tablice nizova:

  • .shstrtab
  • .strtab

.shstrtab

Vrsta odjeljka: sh_type == SHT_STRTAB .

Uobičajeni naziv: redak zaglavlja naslova odjeljka.

Naziv odjeljka.shstrtab je rezerviran. Standard kaže:

Ovaj odjeljak sadrži nazive odjeljaka.

Ovaj odjeljak specificira polje e_shstrnd samog ELF zaglavlja.

Indeksi redaka ovog odjeljka označeni su poljem sh_name zaglavlja odjeljka koja označavaju retke.

Ovaj odjeljak ne navodi SHF_ALLOC, tako da se neće pojaviti u izvršnoj datoteki.

Readelf -x .shstrtab hello_world.o

Hex dump of section ".shstrtab": 0x00000000 002e6461 7461002e 74657874 002e7368 ..data..text..sh 0x00000010 73747274 6162002e 73796d74 6162002e strtab..symtab.. 0x00000020 73747274 6162002e 72656c61 2e746578 strtab..rela.tex 0x00000030 7400 t.

Podaci u ovom odjeljku su u fiksnom formatu: http://www.sco.com/developers/gabi/2003-12-17/ch4.strtab.html

Ako pogledamo druge nazive odjeljaka, možemo vidjeti da svi sadrže brojeve, npr. odjeljak .text je označen brojem 7.

Tada svaki red završava kada se pronađe prvi NUL znak, npr. znak 12 \0 odmah iza .text\0 .

.symtab

Vrsta odjeljka: sh_type == SHT_SYMTAB .

Uobičajeni naziv: tablica simbola.

Prvo napomenimo da:

  • sh_link = 5
  • sh_info=6

U odjeljku SHT_SYMTAB ovi brojevi znače da:

  • Žice
  • koji daju nazive simbola nalaze se u odjeljku 5, .strtab
  • podaci o preseljenju nalaze se u odjeljku 6, .rela.text

Dobar alat visoke razine za rastavljanje ovog odjeljka:

Nm hello_world.o

koji daje:

0000000000000000 T _start 0000000000000000 d hello_world 000000000000000d a hello_world_len

Ovo je, međutim, prikaz visoke razine koji izostavlja određene vrste znakova i označava znakove. Detaljniji pregled može se dobiti pomoću:

Readelf -s hello_world.o

koji daje:

Tablica simbola ".Symtab" sadrži 7 unosa: NUM: VELIČINA VELIKA TIP VIS NDX IME 0: 0000000000000000 0 ITPIPE LOKALNO DEFAULT UND 1: 00000000000000 0 FILE LOKALNI DEFAULT ABS Hello_World.asm 2: 0000000000 0 Odjeljak Local 1 3: 00000000 _

Binarni format tablice dokumentiran je na http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html

Readelf -x .symtab hello_world.o

Što daje:

Hex ispis odjeljka ".symtab": 0x00000000 00000000 00000000 00000000 00000000 ................ 0x00000010 00000000 00000000 0100 ... 0x00000020 00000000 000000 00000000 00000000 ................ 0x00000030 00000000 000000 00000000 000000 ...... ........ 0x00000050 00000000 00000000 00000000 00000000 ...... 0x00000070 00000000 00000000 00000000 000........... 0x00000090 2d000000 10000200 00000000 00000000 -............. 0x000000a0 00000000 00000000 ........

Unosi su tipa:

Typedef struktura ( Elf64_Word st_name; unsigned char st_info; unsigned char st_other; Elf64_Half st_shndx; Elf64_Addr st_value; Elf64_Xword st_size; ) Elf64_Sym;

Kao i tablica particija, prvi unos je magičan i postavljen je na fiksne besmislene vrijednosti.

Unos 1 ima ELF64_R_TYPE == STT_FILE. ELF64_R_TYPE se nastavlja unutar st_info.

Analiza bajtova:

    10 8: st_name = 01000000 = znak 1 u .strtab, koji do sljedećeg \0 radi hello_world.asm

    Ovaj fragment informacijske datoteke može koristiti povezivač da odredi koji segmenti segmenta dolaze.

    10 12: st_info = 04

    Bitovi 0-3 = ELF64_R_TYPE = Tip = 4 = STT_FILE: Glavna svrha ovog unosa je korištenje st_name za određivanje naziva datoteke generirane ovom objektnom datotekom.

    Bitovi 4-7 = ELF64_ST_BIND = Veza = 0 = STB_LOCAL. Potrebna vrijednost za STT_FILE.

    10 13: st_shndx = Tablica simbola Tablica zaglavlja Indeks = f1ff = SHN_ABS . Obavezno za STT_FILE.

    20 0: st_value = 8x 00: potrebno za vrijednost za STT_FILE

    20 8: st_size = 8x 00: nema dodijeljene veličine

Sada iz čitanja brzo tumačimo ostalo.

STT_SECTION

Postoje dva takva elementa, jedan pokazuje na .data, a drugi na .text (indeksi odjeljka 1 i 2).

Broj: Vrijednost Veličina Vrsta Vezi Vis Ndx Naziv 2: 0000000000000000 0 LOKALNI ZADANO ODJELJAK 1 3: 0000000000000000 0 LOKALNI ZADANO ODJELJAK 2

TODO, koja im je svrha?

STT_NOTYPE

Zatim unesite najvažnije znakove:

Broj: Vrijednost Veličina Vrsta Vezi Vis Ndx Naziv 4: 0000000000000000 0 NOTYPE LOCAL DEFAULT 1 hello_world 5: 000000000000000d 0 NOTYPE LOCAL DEFAULT ABS hello_world0000000 start0000000000

hello_world nalazi se u odjeljku .data (indeks 1). Ova vrijednost je 0: ukazuje na prvi bajt ovog odjeljka.

Početak je označen GLOBALNOM vidljivošću, kao što smo napisali:

globalni_početak

u NASM-u. To je neophodno jer se treba smatrati ulaznom točkom. Za razliku od C, NASM oznake su lokalne prema zadanim postavkama.

hello_world_len ukazuje na poseban st_shndx == SHN_ABS == 0xF1FF.

0xF1FF je odabran kako ne bi bio u sukobu s drugim odjeljcima.

st_value == 0xD == 13, što je vrijednost koju smo tamo pohranili na asembleru: duljina niza Hello World! .

To znači da pomak neće utjecati na ovu vrijednost: ona je konstanta.

Ovo je mala optimizacija koju naš asembler radi za nas i ima ELF podršku.

Kada bismo koristili adresu hello_world_len bilo gdje, asembler je ne bi uspio označiti kao SHN_ABS i povezivač bi kasnije imao dodatno premještanje.

SHT_SYMTAB u izvršnoj datoteki

Prema zadanim postavkama, NASM postavlja .symtab u izvršnu datoteku.

Ovo se koristi samo za otklanjanje pogrešaka. Bez simbola, potpuno smo slijepi i moramo sve redizajnirati.

Možete ga ukloniti pomoću objcopy i izvršni će i dalje raditi. Takve izvršne datoteke nazivaju se podijeljene izvršne datoteke.

.strtab

Sadrži nizove za tablicu znakova.

U ovom odjeljku, sh_type == SHT_STRTAB .

Pokazuje na sh_link == 5 odjeljka .symtab.

Readelf -x .strtab hello_world.o

Hex dump of section ".strtab": 0x00000000 0068656c 6c6f5f77 6f726c64 2e61736d .hello_world.asm 0x00000010 0068656c 6c6f5f77 6f726c64 0068656c .hello_world.hel 0x00000020 6c6f5f77 6f726c64 5f6c656e 005f7374 lo_world_len._st 0x00000030 61727400 art.

To znači da je ograničenje ELF razine da globalne varijable ne mogu sadržavati NUL znakove.

.rela.tekst

Vrsta odjeljka: sh_type == SHT_RELA .

Uobičajeni naziv: premjestiti odjeljak.

Rela.text sadrži podatke o premještanju koji određuju kako se adresa treba promijeniti kada se posljednja izvršna datoteka poveže. Ovo označava bajtove tekstualnog područja koje treba promijeniti kada dođe do povezivanja s ispravnim memorijskim mjestima.

U osnovi, pretvara tekst objekta koji sadrži adresu rezerviranog mjesta 0x0:

A:48 be 00 00 00 00 00 movabs $0x0,%rsi 11:00 00 00

na stvarni izvršni kod koji sadrži konačni 0x6000d8:

4000ba: 48 be d8 00 60 00 00 movabs $0x6000d8,%rsi 4000c1: 00 00 00

Naveden je sh_info = 6 odjeljka .symtab.

readelf -r hello_world.o daje:

Odjeljak za premještanje ".rela.text" na pomaku 0x3b0 sadrži 1 unos: Offset Info Type Sym. Vrijednost Sim. Naziv + dodatak 00000000000c 000200000001 R_X86_64_64 0000000000000000 .podaci + 0

Odjeljak ne postoji u izvršnom fajlu.

Stvarni bajtovi:

00000370 0c 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 |................| 00000380 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

Predstavljena struktura:

Typedef struktura (Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; ) Elf64_Rela;

    370 0: r_offset = 0xC: adresa na adresu.tekst čija će adresa biti promijenjena

    370 8: r_info = 0x200000001. Sadrži 2 polja:

    • ELF64_R_TYPE = 0x1: vrijednost ovisi o točnoj arhitekturi.
    • ELF64_R_SYM = 0x2: Indeks particije na koju upućuje adresa, dakle .data, koji je na indeksu 2.

    AMD64 ABI kaže da se tip 1 zove R_X86_64_64 i da predstavlja operaciju S + A gdje:

    • S: vrijednost simbola u objektnoj datoteci, ovdje 0 jer pokazujemo na 00 00 00 00 00 00 00 00 iz movabs $0x0,%rsi
    • a: dodatak prisutan u polju r_added

    Ova adresa se dodaje particiji na kojoj se pokreće premještanje.

    Ova operacija premještanja radi na 8 bajtova.

    380 0: r_zbroj = 0

Dakle, u našem primjeru zaključujemo da će nova adresa biti: S + A = .data + 0 , a time i prva u odjeljku podataka.

Tablica naslova programa

Prikazuje se samo u izvršnoj datoteci.

Sadrži informacije o tome kako se izvršna datoteka treba smjestiti u virtualnu memoriju procesa.

Izvršnu datoteku kreira objektna datoteka povezivača. Glavni zadaci koje povezivač obavlja:

    odrediti koji dijelovi objektnih datoteka ulaze u koje segmente izvršne datoteke.

    U Binutils-u se svodi na raščlanjivanje skripte za izgradnju i rad s puno zadanih postavki.

    Možete dobiti skriptu povezivača koja se koristi s ld --verbose i instalirati prilagođenu s ld -T .

    kretati se kroz tekstualne odjeljke. Ovisi o tome koliko više particija stane u memoriju.

readelf -l hello_world.out daje:

Elf file type is EXEC (Executable file) Entry point 0x4000b0 There are 2 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000000d7 0x00000000000000d7 R E 200000 LOAD 0x00000000000000d8 0x00000000006000d8 0x00000000006000d8 0x000000000000000d 0x000000000000000d RW 200000 Section do Segment mapping: Segment Sections... 00 .tekst 01 .podaci

U ELF zaglavlju e_phoff , e_phnum i e_phentsize rekli su nam da postoje 2 programska zaglavlja koja počinju na 0x40 i imaju svako 0x38 bajta, tako da su:

00000040 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 |................| 00000050 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 |[e-mail zaštićen]@.....| 00000060 d7 00 00 00 00 00 00 00 d7 00 00 00 00 00 00 00 |................| 00000070 00 00 20 00 00 00 00 00 |.. ..... |

00000070 01 00 00 00 06 00 00 00 | ........| 00000080 d8 00 00 00 00 00 00 00 d8 00 60 00 00 00 00 00 |.........`.....| 00000090 d8 00 60 00 00 00 00 00 0d 00 00 00 00 00 00 00 |...`.............| 000000a0 0d 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00 |......... .....| typedef struktura ( Elf64_Word p_type; Elf64_Word p_flags; Elf64_Off p_offset; Elf64_Addr p_vaddr; Elf64_Addr p_paddr; Elf64_Xword p_filesz; Elf64_Xword p_memsz; Elf_6_ign_4; Elf64_;

Raspad prvog:

  • 40 0: p_type = 01 00 00 00 = PT_LOAD: TODO. Mislim da to znači da će se učitati u memoriju. Druge vrste ne moraju nužno biti.
  • 40 4: p_flags = 05 00 00 00 = dopuštenja za izvršavanje i čitanje, nemojte pisati TODO
  • 40 8: p_offset = 8x 00 TODO: što je ovo? Izgleda kao pomaci od početka segmenata. No, bi li to značilo da su neki segmenti isprepleteni? Možete se malo poigrati s tim: gcc -Wl,-Ttext-segment=0x400030 hello_world.c
  • 50 0: p_vaddr = 00 00 40 00 00 00 00 00: početna adresa virtualne memorije za učitavanje ovog segmenta
  • 50 8: p_paddr = 00 00 40 00 00 00 00 00: početna fizička adresa za učitavanje u memoriju. Samo pitanja za sustave u kojima program može postaviti fizičku adresu. Inače, kao i sa sustavima System V, sve se može dogoditi. Čini se da će NASM samo kopirati p_vaddrr
  • 60 0: p_filesz = d7 00 00 00 00 00 00 00: TODO vs p_memsz
  • 60 8: p_memsz = d7 00 00 00 00 00 00 00: TODO
  • 70 0: p_align = 00 00 20 00 00 00 00 00: 0 ili 1 znači da nije potrebno poravnanje TODO, što to znači? inače suvišan s drugim poljima

Drugi je sličan.

Mapiranje odsječka do segmenta:

odjeljak za čitanje nam govori da:

  • 0 - segment.tekst . Da, zato je izvršan, a ne upisiv.
  • 1 - segment.podaci .

Slični postovi