Obsah
1. Nástroj objdump: švýcarský nožík pro vývojáře
2. První seznámení s objdumpem
3. Aplikace typu „Hello World“ naprogramovaná v assembleru
4. Zjištění podrobnějších informací o objektovém souboru hello_world.o
5. Zobrazení obsahu všech sekcí v objektovém kódu
6. Zpětný překlad (disassembling) z objektového souboru
7. Zjištění podrobnějších informací o nativním spustitelném souboru a.out
8. Zpětný překlad (disassembling) ze souboru a.out
9. Zobrazení obsahu sekcí ve formě nestrukturovaných dat
10. Zdrojový kód příkladu naprogramovaného v jazyku C, jenž závisí na dynamické knihovně
11. Zjištění základních informací o výsledném spustitelném souboru
12. Zjištění, které dynamické knihovny program využívá
14. Disassembling vybrané funkce
15. Vylepšení vyhledání funkce pro disassembling, vizualizace lokálních podmíněných skoků a smyček
16. Deklarace většího množství prázdných sekcí s různými atributy
17. Výpis všech sekcí z objektového souboru i z výsledného binárního souboru
18. Deklarace většího množství sekcí obsahujících statická data
19. Opětovný výpis všech sekcí z objektového souboru i z výsledného binárního souboru
1. Nástroj objdump: švýcarský nožík pro vývojáře
Jedním z velmi užitečných nástrojů, které jsou součástí balíčku GNU Binutils, je i nástroj nazvaný objdump. Tento nástroj umožňuje získávat důležité informace jak z objektových souborů (tedy ze souborů s koncovkou .o, které tvoří vstup pro linker), tak i z dynamicky linkovaných knihoven (.so) i ze spustitelných (nativních) souborů. Nástroj objdump lze použít pro čtení a zobrazení informací ze souborů ve formátu ELF určených jak pro Linux, tak i pro další operační systémy (*BSD, Solaris, …), a to včetně Androidu. Dnes si některé možnosti objdumpu ukážeme na praktických příkladech; prozatím však vynecháme práci s dynamicky linkovanými knihovnami.
Při popisu možností nástroje objdump se nepřímo seznámíme i s dalšími utilitami. Především se jedná o linker a taktéž o utility readelf, strings, nm a ldd. Taktéž se, opět nepřímo, setkáme s problematikou sekcí (segmentů) definovaných v hlavičkách souborů ELF. Většina překladačů používá standardní sekce pro programový kód, data určená pouze pro čtení (konstanty), oblast inicializovaných dat a konečně oblast dat neinicializovaných (zde je uložena pouze délka sekce). Tyto sekce mají standardizovaná jména: .text, .rodata, .data a .bss. Ovšem nic nám nebrání ve vytvoření sekcí dalších.
2. První seznámení s objdumpem
Utilita objdump je použitelná k mnoha účelům popsaným v navazujících kapitolách. Její univerzálnost je ostatně patrná i při pohledu na množství parametrů a přepínačů, které lze zadávat z příkazového řádku:
$ objdump Usage: objdump <option(s)> <file(s)> Display information from object <file(s)>. At least one of the following switches must be given: -a, --archive-headers Display archive header information -f, --file-headers Display the contents of the overall file header -p, --private-headers Display object format specific file header contents -P, --private=OPT,OPT... Display object format specific contents -h, --[section-]headers Display the contents of the section headers -x, --all-headers Display the contents of all headers -d, --disassemble Display assembler contents of executable sections -D, --disassemble-all Display assembler contents of all sections --disassemble=</gcsym>/gc Display assembler contents from -S, --source Intermix source code with disassembly --source-comment[=</gctxt>/gc] Prefix lines of source code with -s, --full-contents Display the full contents of all sections requested -g, --debugging Display debug information in object file -e, --debugging-tags Display debug information using ctags style -G, --stabs Display (in raw form) any STABS info in the file -W[lLiaprmfFsoRtUuTgAckK] or --dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames, =frames-interp,=str,=loc,=Ranges,=pubtypes, =gdb_index,=trace_info,=trace_abbrev,=trace_aranges, =addr,=cu_index,=links,=follow-links] Display DWARF info in the file --ctf=SECTION Display CTF info from SECTION -t, --syms Display the contents of the symbol table(s) -T, --dynamic-syms Display the contents of the dynamic symbol table -r, --reloc Display the relocation entries in the file -R, --dynamic-reloc Display the dynamic relocation entries in the file @</gcfile>/gc Read options from -v, --version Display this program's version number -i, --info List object formats and architectures supported -H, --help Display this information
Nástroj objdump je možné použít i pro disassembling strojového kódu, i když pro tento účel existují i sofistikovanější a interaktivní nástroje. To, které procesorové architektury a formáty jsou pro nainstalovanou verzi objdumpu dostupné, lze zjistit tímto příkazem:
$ objdump -i
Výstup zobrazený pro variantu objdumpu nainstalovanou na autorově počítači s architekturou x86–64:
BFD header file version (GNU Binutils for Ubuntu) 2.34 elf64-x86-64 (header little endian, data little endian) i386 elf32-i386 (header little endian, data little endian) i386 elf32-iamcu (header little endian, data little endian) iamcu elf32-x86-64 (header little endian, data little endian) i386 pei-i386 (header little endian, data little endian) i386 pei-x86-64 (header little endian, data little endian) i386 elf64-l1om (header little endian, data little endian) l1om elf64-k1om (header little endian, data little endian) k1om elf64-little (header little endian, data little endian) i386 l1om k1om iamcu elf64-big (header big endian, data big endian) i386 l1om k1om iamcu elf32-little (header little endian, data little endian) i386 l1om k1om iamcu elf32-big (header big endian, data big endian) i386 l1om k1om iamcu pe-x86-64 (header little endian, data little endian) i386 pe-bigobj-x86-64 (header little endian, data little endian) i386 pe-i386 (header little endian, data little endian) i386 srec (header endianness unknown, data endianness unknown) i386 l1om k1om iamcu symbolsrec (header endianness unknown, data endianness unknown) i386 l1om k1om iamcu verilog (header endianness unknown, data endianness unknown) i386 l1om k1om iamcu tekhex (header endianness unknown, data endianness unknown) i386 l1om k1om iamcu binary (header endianness unknown, data endianness unknown) i386 l1om k1om iamcu ihex (header endianness unknown, data endianness unknown) i386 l1om k1om iamcu plugin (header little endian, data little endian) elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 pei-i386 pei-x86-64 i386 elf64-x86-64 elf32-i386 ----------- elf32-x86-64 pei-i386 pei-x86-64 l1om ------------ ---------- ----------- ------------ -------- ---------- k1om ------------ ---------- ----------- ------------ -------- ---------- iamcu ------------ ---------- elf32-iamcu ------------ -------- ---------- elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big i386 ---------- ---------- elf64-little elf64-big elf32-little elf32-big l1om elf64-l1om ---------- elf64-little elf64-big elf32-little elf32-big k1om ---------- elf64-k1om elf64-little elf64-big elf32-little elf32-big iamcu ---------- ---------- elf64-little elf64-big elf32-little elf32-big pe-x86-64 pe-bigobj-x86-64 pe-i386 srec symbolsrec verilog tekhex i386 pe-x86-64 pe-bigobj-x86-64 pe-i386 srec symbolsrec verilog tekhex l1om --------- ---------------- ------- srec symbolsrec verilog tekhex k1om --------- ---------------- ------- srec symbolsrec verilog tekhex iamcu --------- ---------------- ------- srec symbolsrec verilog tekhex binary ihex plugin i386 binary ihex ------ l1om binary ihex ------ k1om binary ihex ------ iamcu binary ihex ------
Relativně novou vlastností objdumpu je možnost vizualizace cílů lokálních skoků. Například pro tento zdrojový špagetový kód s několika lokálními skoky ..
_start: nop jmp .L1 .L4: nop int 0x80 # volani Linuxoveho kernelu .L2: mov ebx, 1 # exit code = 1 nop jmp .L3 .L1: mov eax, sys_exit # cislo sycallu pro funkci "exit" nop jmp .L2 .L3: dec ebx # zmena exit code nop jmp .L4
…je možné zobrazit tuto strukturu se šipkami mířícími na cíle skoků:
jumps.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 </gc_start>/gc: 0: 90 nop 1: /----- eb 0b jmp e </gcsys_exit+0xd>/gc 3: /-----|----> 90 nop 4: | | cd 80 int 0x80 6: | /--|----> bb 01 00 00 00 mov ebx,0x1 b: | | | 90 nop c: | | | /-- eb 08 jmp 16 </gcsys_exit+0x15>/gc e: | | \--|-> b8 01 00 00 00 mov eax,0x1 13: | | | 90 nop 14: | \-----|-- eb f0 jmp 6 </gcsys_exit+0x5>/gc 16: | \-> ff cb dec ebx 18: | 90 nop 19: \----------- eb e8 jmp 3 </gcsys_exit+0x2>/gc
3. Aplikace typu „Hello World“ naprogramovaná v assembleru
V navazujících kapitolách budeme zkoumat objektový soubor a taktéž výsledný nativní soubor vygenerovaný assemblerem resp. linkerem ze zdrojového kódu klasického demonstračního příkladu typu „Hello World“. Námi použitá varianta je naprogramována v assembleru mikroprocesorů x86 a v jejím zápisu je použita syntaxe používaná firmou Intel a nikoli AT&T syntaxe. Zdrojový kód tohoto příkladu naleznete na adrese https://github.com/tisnik/presentations/blob/master/assembler/06_gas_intel_hello_world/hello_world.s:
# asmsyntax=as # Jednoducha aplikace typu "Hello world!" naprogramovana # v assembleru GNU as - pouzita je "Intel" syntaxe. # # Autor: Pavel Tisnovsky .intel_syntax noprefix # Linux kernel system call table sys_exit=1 sys_write=4 #----------------------------------------------------------------------------- .section .data hello_lbl: .string "Hello World!\n" # string, ktery JE ukoncen nulou #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start # tento symbol ma byt dostupny i linkeru _start: mov eax, sys_write # cislo syscallu pro funkci "write" mov ebx, 1 # standardni vystup mov ecx, offset hello_lbl # adresa retezce, ktery se ma vytisknout mov edx, 13 # pocet znaku, ktere se maji vytisknout int 0x80 # volani Linuxoveho kernelu mov eax, sys_exit # cislo sycallu pro funkci "exit" mov ebx, 0 # exit code = 0 int 0x80 # volani Linuxoveho kernelu
Tento demonstrační příklad nejprve přeložíme assemblerem do objektového kódu hello_world.o a následně linkerem vytvoříme spustitelný soubor a.out (lze samozřejmě zvolit jiné jméno, ale proč nezůstat u klasiky):
$ as hello_world.s -o hello_world.o $ ld -s hello_world.o
4. Zjištění podrobnějších informací o objektovém souboru hello_world.o
V této kapitole si ukážeme, jakým způsobem je možné získat podrobnější informace o objektovém souboru hello_world.o, jenž vznikl překladem výše uvedeného zdrojového souboru hello_world.s assemblerem, konkrétně GNU Assemblerem. Pro získání informací o objektovém souboru pochopitelně použijeme nástroj objdump.
Nejprve si necháme zobrazit hlavičku objektového souboru. Pro tento účel se používá přepínač -f. Povšimněte si, že se především zobrazí použitý formát (zde konkrétně ELF neboli Executable and Linkable Format, což je dnes na Linuxu standard), cílová architektura (x86–64) a příznaky určující, že soubor má relokační tabulku (bitová maska 0×01) a tabulku symbolů (bitová maska 0×10), příznakové slovo má tedy hodnotu 0×01+0×10=0×11. A konečně se vypíše počáteční adresa, k jejímuž významu se ještě vrátíme:
$ objdump -f hello_world.o hello_world.o: file format elf64-x86-64 architecture: i386:x86-64, flags 0x00000011: HAS_RELOC, HAS_SYMS start address 0x0000000000000000
Z předchozího výpisu je zřejmé, že objektový soubor obsahuje i takzvanou tabulku symbolů. Její obsah si můžeme zobrazit, a to použitím přepínače -t:
$ objdump -t hello_world.o hello_world.o: file format elf64-x86-64 SYMBOL TABLE: 0000000000000000 l d .text 0000000000000000 .text 0000000000000000 l d .data 0000000000000000 .data 0000000000000000 l d .bss 0000000000000000 .bss 0000000000000001 l *ABS* 0000000000000000 sys_exit 0000000000000004 l *ABS* 0000000000000000 sys_write 0000000000000000 l .data 0000000000000000 hello_lbl 0000000000000000 g .text 0000000000000000 _start
Každý symbol z tabulky symbolů je vypsán na samostatném řádku. Jednotlivé sloupce mají tento význam:
Sloupec | Příklad | Význam |
---|---|---|
1 | 0000000000000001 | hodnota symbolu |
2 | l d | sedm skupin s popisem symbolu, každá skupina reprezentovaná jedním znakem |
3 | .bss | sekce (segment), v němž je symbol definován (*ABS* značí bez sekce) |
4 | 0000000000000000 | velikost symbolu nebo zarovnání |
5 | sys_exit | jméno symbolu |
Druhý sloupec je nejzajímavější. Je v něm zakódováno mnoho informací o daném symbolu, a to sedmi znaky (z nichž některé mohou být nahrazeny mezerou):
Skupina | Znak | Stručný popis |
---|---|---|
1 | l,g,mezera,! | lokální či globální symbol, popř. oboje |
2 | w,mezera | označuje tzv. weak symbol |
3 | C,mezera | konstruktor nebo běžný symbol |
4 | W,mezera | warning nebo běžný symbol |
5 | I,mezera | odkaz na jiný symbol (či běžný symbol) |
6 | d,D,mezera | symbol pro ladění, dynamický symbol či běžný symbol |
7 | F,f,O | jméno funkce, jméno souboru, běžný symbol |
A konečně si ukažme, jak je možné přepínačem -x zobrazit všechny hlavičky v objektovém souboru. Jedná se vlastně o kombinaci přepínačů -f, -t, -a, -h a -p:
$ objdump -x hello_world.o hello_world.o: file format elf64-x86-64 hello_world.o architecture: i386:x86-64, flags 0x00000011: HAS_RELOC, HAS_SYMS start address 0x0000000000000000 Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000022 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 1 .data 0000000e 0000000000000000 0000000000000000 00000062 2**0 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 0000000000000000 0000000000000000 00000070 2**0 ALLOC SYMBOL TABLE: 0000000000000000 l d .text 0000000000000000 .text 0000000000000000 l d .data 0000000000000000 .data 0000000000000000 l d .bss 0000000000000000 .bss 0000000000000001 l *ABS* 0000000000000000 sys_exit 0000000000000004 l *ABS* 0000000000000000 sys_write 0000000000000000 l .data 0000000000000000 hello_lbl 0000000000000000 g .text 0000000000000000 _start RELOCATION RECORDS FOR [.text]: OFFSET TYPE VALUE 000000000000000b R_X86_64_32 .data
5. Zobrazení obsahu všech sekcí v objektovém kódu
V případě, že použijeme přepínač -s, můžeme si nechat zobrazit obsah všech sekcí (segmentů) nalezených v objektovém kódu – a to jak v hexadecimální podobě, tak i ve formě řetězce (ovšem jen pro ASCII znaky). Výsledek bude v našem případě vypadat následovně:
$ objdump -s hello_world.o hello_world.o: file format elf64-x86-64 Contents of section .text: 0000 b8040000 00bb0100 0000b900 000000ba ................ 0010 0d000000 cd80b801 000000bb 00000000 ................ 0020 cd80 .. Contents of section .data: 0000 48656c6c 6f20576f 726c6421 0a00 Hello World!..
offset, obsah šestnácti bajtů vypsaný formou hexa číslic sdružených do slov, ASCII znaky zobrazené formou řetězce (popř. tečky pro ty znaky, které nepatří do ASCII).
6. Zpětný překlad (disassembling) z objektového souboru
Nástroj objdump dokáže zjistit i mnohé další užitečné informace z dat uložených v objektovém souboru. Mezi nejzajímavější vlastnost patří zpětný překlad (neboli disassembling), což se typicky týká obsahu sekce (segmentu) nazvané .text, v níž je uložen přeložený objektový kód. Podívejme se nyní na to, jak dobře nástroj objdump porozumí sekvenci instrukcí uložených v této sekci:
$ objdump -d hello_world.o hello_world.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <_start>: 0: b8 04 00 00 00 mov $0x4,%eax 5: bb 01 00 00 00 mov $0x1,%ebx a: b9 00 00 00 00 mov $0x0,%ecx f: ba 0d 00 00 00 mov $0xd,%edx 14: cd 80 int $0x80 16: b8 01 00 00 00 mov $0x1,%eax 1b: bb 00 00 00 00 mov $0x0,%ebx 20: cd 80 int $0x80
V porovnání se zdrojovým kódem ze čtvrté kapitoly je patrné, že ve výchozím nastavení použil nástroj objdump formát instrukcí (tedy mnemotechnická jména instrukcí) i operandů (pořadí, formát zápisu), který odpovídá syntaxi zavedenou společností AT&T. Pokud preferujete, podobně jako autor tohoto článku, spíše syntax používanou společností Intel, je nutné použít přepínač -M intel_mnemonic nebo jen -M intel (což je jiný přepínač, který však v našem konkrétním případě vytiskne stejný výsledek). Výsledek se již bude do značné míry podobat původnímu zdrojovému kódu:
$ objdump -d -M intel_mnemonic hello_world.o hello_world.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <_start>: 0: b8 04 00 00 00 mov eax,0x4 5: bb 01 00 00 00 mov ebx,0x1 a: b9 00 00 00 00 mov ecx,0x0 f: ba 0d 00 00 00 mov edx,0xd 14: cd 80 int 0x80 16: b8 01 00 00 00 mov eax,0x1 1b: bb 00 00 00 00 mov ebx,0x0 20: cd 80 int 0x80
Ve skutečnosti je výchozí formát výstupu jakýmsi kompromisem mezi syntaxí Intelu a AT&T. V případě AT&T by totiž instrukce měly obsahovat suffix, konkrétně jeden znak, z něhož lze vyčíst délku operandů. I tento trik objdump podporuje:
hello_world.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <_start>: 0: b8 04 00 00 00 movl $0x4,%eax 5: bb 01 00 00 00 movl $0x1,%ebx a: b9 00 00 00 00 movl $0x0,%ecx f: ba 0d 00 00 00 movl $0xd,%edx 14: cd 80 int $0x80 16: b8 01 00 00 00 movl $0x1,%eax 1b: bb 00 00 00 00 movl $0x0,%ebx 20: cd 80 int $0x80
7. Zjištění podrobnějších informací o nativním spustitelném souboru a.out
Prozatím jsme nástroj objdump používali pro zjištění informací z objektových souborů – ostatně právě kvůli této vlastnosti získal tento nástroj své jméno. Ovšem podobné informace je možné v případě potřeby získat i z výsledných spustitelných (nativních) souborů a.out (které samozřejmě mohou mít odlišné jméno a na Linuxu typicky nemají žádnou koncovku).
Vraťme se však k souboru a.out, který vznikl překladem a slinkováním zdrojového kódu ze čtvrté kapitoly. Nejdříve si necháme zobrazit hlavičku tohoto souboru:
$ objdump -f a.out a.out: file format elf64-x86-64 architecture: i386:x86-64, flags 0x00000102: EXEC_P, D_PAGED start address 0x0000000000401000
Zobrazit si můžeme i souhrnné základní informace o zkoumaném souboru. Opět použijeme přepínač, který již známe – -x:
$ objdump -x a.out a.out: file format elf64-x86-64 a.out architecture: i386:x86-64, flags 0x00000102: EXEC_P, D_PAGED start address 0x0000000000401000 Program Header: LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**12 filesz 0x00000000000000e8 memsz 0x00000000000000e8 flags r-- LOAD off 0x0000000000001000 vaddr 0x0000000000401000 paddr 0x0000000000401000 align 2**12 filesz 0x0000000000000022 memsz 0x0000000000000022 flags r-x LOAD off 0x0000000000002000 vaddr 0x0000000000402000 paddr 0x0000000000402000 align 2**12 filesz 0x000000000000000e memsz 0x000000000000000e flags rw- Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000022 0000000000401000 0000000000401000 00001000 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 0000000e 0000000000402000 0000000000402000 00002000 2**0 CONTENTS, ALLOC, LOAD, DATA SYMBOL TABLE: no symbols
8. Zpětný překlad (disassembling) ze souboru a.out
Vzhledem k tomu, že sekce .text obsahuje i ve výsledných spustitelných souborech sekvenci strojových instrukcí, můžeme si velmi snadno zobrazit disassemblovaný obsah této sekce. K tomuto účelu použijeme již známý přepínač -d. Ve výchozím nastavení se přitom používá syntaxe podle firmy AT&T, což je ostatně vidět na výpisu níže (operandy instrukcí jsou v pořadí zdroj, cíl, u jmen registrů se používají procenta atd.):
$ objdump -d a.out a.out: file format elf64-x86-64 Disassembly of section .text: 0000000000401000 <.text>: 401000: b8 04 00 00 00 mov $0x4,%eax 401005: bb 01 00 00 00 mov $0x1,%ebx 40100a: b9 00 20 40 00 mov $0x402000,%ecx 40100f: ba 0d 00 00 00 mov $0xd,%edx 401014: cd 80 int $0x80 401016: b8 01 00 00 00 mov $0x1,%eax 40101b: bb 00 00 00 00 mov $0x0,%ebx 401020: cd 80 int $0x80
Opět pochopitelně můžeme přepínačem -M zvolit odlišnou syntaxi, zde konkrétně syntaxi používanou společností Intel:
$ objdump -M intel -d a.out a.out: file format elf64-x86-64 Disassembly of section .text: 0000000000401000 <.text>: 401000: b8 04 00 00 00 mov eax,0x4 401005: bb 01 00 00 00 mov ebx,0x1 40100a: b9 00 20 40 00 mov ecx,0x402000 40100f: ba 0d 00 00 00 mov edx,0xd 401014: cd 80 int 0x80 401016: b8 01 00 00 00 mov eax,0x1 40101b: bb 00 00 00 00 mov ebx,0x0 401020: cd 80 int 0x80
9. Zobrazení obsahu sekcí ve formě nestrukturovaných dat
Přepínačem -s si můžeme nechat vypsat obsah sekcí nebo vybrané sekce ve formě nestrukturovaných dat. Nástroj objdump v tomto případě vypisuje data podobně, jako hexa prohlížeče: na každém řádku je počáteční offset, následuje obsah šestnácti bajtů ve formě hexadecimálního výpisu a tytéž data, ovšem interpretovaná jako ASCII text (ostatní znaky se zobrazí formou tečky):
$ objdump -s a.out a.out: file format elf64-x86-64 Contents of section .text: 401000 b8040000 00bb0100 0000b900 204000ba ............ @.. 401010 0d000000 cd80b801 000000bb 00000000 ................ 401020 cd80 .. Contents of section .data: 402000 48656c6c 6f20576f 726c6421 0a00 Hello World!..
Většinou mám smysl tímto způsobem zobrazit obsah sekcí .data nebo .rodata, tedy takto:
$ objdump -s -j.data a.out a.out: file format elf64-x86-64 Contents of section .data: 402000 48656c6c 6f20576f 726c6421 0a00 Hello World!..
10. Zdrojový kód příkladu naprogramovaného v jazyku C, jenž závisí na dynamické knihovně
V dalších kapitolách budeme zkoumat objektový kód i výsledný spustitelný soubor, který vznikne překladem a slinkováním jednoduchého programu napsaného v jazyce C. Povšimněte si, že v tomto programu používáme funkce definované v hlavičkovém souboru math.h a implementované v knihovně libm.so, kterou je nutné slinkovat explicitně:
#include <stdio.h> #include <math.h> double compute_sin(double x) { return sin(x); } int main(void) { double x; for (x = 0; x < M_PI/2.0; x+=M_PI/40.0) { double y = compute_sin(x); printf("%f\t%f\n", x, y); } }
Překlad do objektového souboru zajistí příkaz:
$ gcc -c test.c
Překlad a slinkování do výsledného binárního souboru můžeme provést jediným příkazem:
$ gcc test.c -lm
11. Zjištění základních informací o výsledném spustitelném souboru
Spustitelný soubor vzniklý překladem z céčka již obsahuje o mnoho víc informací, než tomu bylo při překladu z assembleru. Pro jeho analýzu můžeme kromě objdump použít i další nástroje. Užitečné základní informace o souboru získáme například nástrojem readelf:
$ readelf -h a.out ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x1080 Start of program headers: 64 (bytes into file) Start of section headers: 14792 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 13 Size of section headers: 64 (bytes) Number of section headers: 31 Section header string table index: 30
Předchozí výpis obsahuje i informace o tom, kde lze nalézt začátky hlaviček i sekcí atd. Vypišme si tedy nejdříve všechny hlavičky, a to včetně privátních hlaviček. Použijeme přitom přepínač -p:
$ objdump -p a.out a.out: file format elf64-x86-64 Program Header: PHDR off 0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3 filesz 0x00000000000002d8 memsz 0x00000000000002d8 flags r-- INTERP off 0x0000000000000318 vaddr 0x0000000000000318 paddr 0x0000000000000318 align 2**0 filesz 0x000000000000001c memsz 0x000000000000001c flags r-- LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**12 filesz 0x0000000000000660 memsz 0x0000000000000660 flags r-- LOAD off 0x0000000000001000 vaddr 0x0000000000001000 paddr 0x0000000000001000 align 2**12 filesz 0x0000000000000295 memsz 0x0000000000000295 flags r-x LOAD off 0x0000000000002000 vaddr 0x0000000000002000 paddr 0x0000000000002000 align 2**12 filesz 0x0000000000000198 memsz 0x0000000000000198 flags r-- LOAD off 0x0000000000002da0 vaddr 0x0000000000003da0 paddr 0x0000000000003da0 align 2**12 filesz 0x0000000000000270 memsz 0x0000000000000278 flags rw- DYNAMIC off 0x0000000000002db0 vaddr 0x0000000000003db0 paddr 0x0000000000003db0 align 2**3 filesz 0x0000000000000200 memsz 0x0000000000000200 flags rw- NOTE off 0x0000000000000338 vaddr 0x0000000000000338 paddr 0x0000000000000338 align 2**3 filesz 0x0000000000000020 memsz 0x0000000000000020 flags r-- NOTE off 0x0000000000000358 vaddr 0x0000000000000358 paddr 0x0000000000000358 align 2**2 filesz 0x0000000000000044 memsz 0x0000000000000044 flags r-- 0x6474e553 off 0x0000000000000338 vaddr 0x0000000000000338 paddr 0x0000000000000338 align 2**3 filesz 0x0000000000000020 memsz 0x0000000000000020 flags r-- EH_FRAME off 0x0000000000002020 vaddr 0x0000000000002020 paddr 0x0000000000002020 align 2**2 filesz 0x000000000000004c memsz 0x000000000000004c flags r-- STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4 filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw- RELRO off 0x0000000000002da0 vaddr 0x0000000000003da0 paddr 0x0000000000003da0 align 2**0 filesz 0x0000000000000260 memsz 0x0000000000000260 flags r-- Dynamic Section: NEEDED libm.so.6 NEEDED libc.so.6 INIT 0x0000000000001000 FINI 0x0000000000001288 INIT_ARRAY 0x0000000000003da0 INIT_ARRAYSZ 0x0000000000000008 FINI_ARRAY 0x0000000000003da8 FINI_ARRAYSZ 0x0000000000000008 GNU_HASH 0x00000000000003a0 STRTAB 0x0000000000000488 SYMTAB 0x00000000000003c8 STRSZ 0x0000000000000092 SYMENT 0x0000000000000018 DEBUG 0x0000000000000000 PLTGOT 0x0000000000003fb0 PLTRELSZ 0x0000000000000030 PLTREL 0x0000000000000007 JMPREL 0x0000000000000630 RELA 0x0000000000000570 RELASZ 0x00000000000000c0 RELAENT 0x0000000000000018 FLAGS 0x0000000000000008 FLAGS_1 0x0000000008000001 VERNEED 0x0000000000000530 VERNEEDNUM 0x0000000000000002 VERSYM 0x000000000000051a RELACOUNT 0x0000000000000003 Version References: required from libm.so.6: 0x09691a75 0x00 03 GLIBC_2.2.5 required from libc.so.6: 0x09691a75 0x00 02 GLIBC_2.2.5
12. Zjištění, které dynamické knihovny program využívá
V případě, že je zapotřebí zjistit, které dynamické knihovny program využívá (tedy načítá po svém spuštění), se pro tento účel typicky používá nástroj ldd:
$ ldd a.out
Ten v našem konkrétním případě vypisuje čtyři knihovny:
linux-vdso.so.1 (0x00007fff88572000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f58652b1000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f58650bf000) /lib64/ld-linux-x86-64.so.2 (0x00007f586541d000)
Pro podobný účel můžeme použít i nástroj objdump, jenž ovšem vypisuje informace o sdílených knihovnách uložených přímo ve strukturách souborů ELF:
$ objdump -p a.out | grep NEEDED NEEDED libm.so.6 NEEDED libc.so.6
Proč se však oba výpisy od sebe odlišují? V případě objdumpu se skutečně vypisují informace uložené v souboru ELF (tedy objektovém souboru nebo spustitelném souboru), ovšem ldd navíc zjišťuje i tranzitivní závislosti obou dynamických knihoven, tj. nejdříve zjistí stejné informace jako objdump a posléze si interně spustí další dotazy:
$ ldd /lib/x86_64-linux-gnu/libm.so.6 linux-vdso.so.1 (0x00007ffdcd98f000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fed71c7a000) /lib64/ld-linux-x86-64.so.2 (0x00007fed71fd3000) $ ldd /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2 (0x00007fc61926e000) linux-vdso.so.1 (0x00007ffdc73f5000)
13. Přečtení verze překladače
Překladač GCC C, ale i mnohé další překladače, jsou ve výsledném binárním spustitelném souboru „podepsány“. Konkrétně verzi překladače GCC C je možné na příkazové řádce zjistit tímto příkazem:
$ gcc --version gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Naprosto tytéž údaje, které jsou v předchozím výstupu zobrazeny na prvním řádku, nalezneme v sekci nazvané .comment získané a vypsané nástrojem objdump:
$ objdump -s --section .comment a.out a.out: file format elf64-x86-64 Contents of section .comment: 0000 4743433a 20285562 756e7475 20392e34 GCC: (Ubuntu 9.4 0010 2e302d31 7562756e 7475317e 32302e30 .0-1ubuntu1~20.0 0020 342e3129 20392e34 2e3000 4.1) 9.4.0.
Stejné údaje lze (i když mnohdy s poněkud nepřesným výsledkem) zjistit nástrojem strings
$ strings a.out | grep ^GCC GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Podobně můžeme namísto objdump či strings použít příkaz readelf:
$ readelf -p .comment a.out String dump of section '.comment': [ 0] GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
A konečně mnohem přesnější informace zjistíme po zadání přepínače -n nástroji readelf:
$ readelf -n a.out Displaying notes found in: .note.gnu.property Owner Data size Description GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0 Properties: x86 feature: IBT, SHSTK Displaying notes found in: .note.gnu.build-id Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: 43d1b44a3766e664647e1079689a207655f84806 Displaying notes found in: .note.ABI-tag Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 3.2.0
14. Disassembling vybrané funkce
Poměrně často se setkáme s požadavkem na zobrazení instrukcí, které vznikly překladem určité konkrétní funkce, například funkce main atd. Jedno z často doporučovaných řešení spočívá ve filtraci výstupu objdumpu, například nástrojem awk. Toto řešení je sice skutečně často citované, ale není nejlepší:
$ objdump -d -M intel a.out | awk -v RS= '/^[[:xdigit:]]+ <main>/' 000000000000118a : 118a: f3 0f 1e fa endbr64 118e: 55 push rbp 118f: 48 89 e5 mov rbp,rsp 1192: 48 83 ec 10 sub rsp,0x10 1196: 66 0f ef c0 pxor xmm0,xmm0 119a: f2 0f 11 45 f0 movsd QWORD PTR [rbp-0x10],xmm0 119f: eb 50 jmp 11f1 </gcmain+0x67>/gc 11a1: 48 8b 45 f0 mov rax,QWORD PTR [rbp-0x10] 11a5: 66 48 0f 6e c0 movq xmm0,rax 11aa: e8 ba ff ff ff call 1169 </gccompute_sin>/gc 11af: 66 48 0f 7e c0 movq rax,xmm0 11b4: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 11b8: f2 0f 10 45 f8 movsd xmm0,QWORD PTR [rbp-0x8] 11bd: 48 8b 45 f0 mov rax,QWORD PTR [rbp-0x10] 11c1: 66 0f 28 c8 movapd xmm1,xmm0 11c5: 66 48 0f 6e c0 movq xmm0,rax 11ca: 48 8d 3d 37 0e 00 00 lea rdi,[rip+0xe37] # 2008 </gc_IO_stdin_used+0x8>/gc 11d1: b8 02 00 00 00 mov eax,0x2 11d6: e8 85 fe ff ff call 1060 </gcprintf@plt>/gc 11db: f2 0f 10 4d f0 movsd xmm1,QWORD PTR [rbp-0x10] 11e0: f2 0f 10 05 28 0e 00 movsd xmm0,QWORD PTR [rip+0xe28] # 2010 </gc_IO_stdin_used+0x10>/gc 11e7: 00 11e8: f2 0f 58 c1 addsd xmm0,xmm1 11ec: f2 0f 11 45 f0 movsd QWORD PTR [rbp-0x10],xmm0 11f1: f2 0f 10 05 1f 0e 00 movsd xmm0,QWORD PTR [rip+0xe1f] # 2018 </gc_IO_stdin_used+0x18>/gc 11f8: 00 11f9: 66 0f 2f 45 f0 comisd xmm0,QWORD PTR [rbp-0x10] 11fe: 77 a1 ja 11a1 </gcmain+0x17>/gc 1200: b8 00 00 00 00 mov eax,0x0 1205: c9 leave 1206: c3 ret 1207: 66 0f 1f 84 00 00 00 nop WORD PTR [rax+rax*1+0x0] 120e: 00 00
Podobně si můžeme zobrazit instrukce tvořící tělo funkce compute_sin:
$ objdump -d -M intel a.out | awk -v RS= '/^[[:xdigit:]]+ <compute_sin>/' 0000000000001169 </gccompute_sin>/gc: 1169: f3 0f 1e fa endbr64 116d: 55 push rbp 116e: 48 89 e5 mov rbp,rsp 1171: 48 83 ec 10 sub rsp,0x10 1175: f2 0f 11 45 f8 movsd QWORD PTR [rbp-0x8],xmm0 117a: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] 117e: 66 48 0f 6e c0 movq xmm0,rax 1183: e8 e8 fe ff ff call 1070 </gcsin@plt>/gc 1188: c9 leave 1189: c3 ret
15. Vylepšení vyhledání funkce pro disassembling, vizualizace lokálních podmíněných skoků a smyček
Ve skutečnosti za nás může vyhledání konkrétní funkce (resp. obecně symbolu) provést přímo nástroj objdump, a to následovně:
$ objdump -d -M intel --disassemble=compute_sin a.out a.out: file format elf64-x86-64 Disassembly of section .init: Disassembly of section .plt: Disassembly of section .plt.got: Disassembly of section .plt.sec: Disassembly of section .text: 0000000000001169 <compute_sin>: 1169: f3 0f 1e fa endbr64 116d: 55 push rbp 116e: 48 89 e5 mov rbp,rsp 1171: 48 83 ec 10 sub rsp,0x10 1175: f2 0f 11 45 f8 movsd QWORD PTR [rbp-0x8],xmm0 117a: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] 117e: 66 48 0f 6e c0 movq xmm0,rax 1183: e8 e8 fe ff ff call 1070 <sin@plt> 1188: c9 leave 1189: c3 ret Disassembly of section .fini:
Pokud z nějakého důvodu překáží výpis hlaviček ostatních sekcí, můžeme zkombinovat vyhledání s filtrací podle sekce (zde konkrétně podle standardní sekce .text):
$ objdump -d -M intel -j.text --disassemble=compute_sin a.out a.out: file format elf64-x86-64 Disassembly of section .text: 0000000000001169 <compute_sin>: 1169: f3 0f 1e fa endbr64 116d: 55 push rbp 116e: 48 89 e5 mov rbp,rsp 1171: 48 83 ec 10 sub rsp,0x10 1175: f2 0f 11 45 f8 movsd QWORD PTR [rbp-0x8],xmm0 117a: 48 8b 45 f8 mov rax,QWORD PTR [rbp-0x8] 117e: 66 48 0f 6e c0 movq xmm0,rax 1183: e8 e8 fe ff ff call 1070 <sin@plt> 1188: c9 leave 1189: c3 ret
A konečně přepínačem –visualize-jumps či –visualize-jumps=color je možné si nechat zobrazit cíle lokálních skoků v rámci jedné funkce:
31: f2 0f 11 45 f0 movsd QWORD PTR [rbp-0x10],xmm0 36: /----- eb 61 jmp 99 </gcmain+0x78>/gc 38: /--|----> 48 8b 45 f0 mov rax,QWORD PTR [rbp-0x10] 3c: | | 66 48 0f 6e c0 movq xmm0,rax 41: | | /-- e8 00 00 00 00 call 46 </gcmain+0x25>/gc 42: R_X86_64_PLT32 compute_sin-0x4 46: | | \-> 66 48 0f 7e c0 movq rax,xmm0 4b: | | 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 4f: | | 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # 56 </gcmain+0x35>/gc 52: R_X86_64_PC32 .rodata-0x4 56: | | b8 00 00 00 00 mov eax,0x0 5b: | | /-- e8 00 00 00 00 call 60 </gcmain+0x3f>/gc 5c: R_X86_64_PLT32 printf-0x4 60: | | \-> f2 0f 10 45 f8 movsd xmm0,QWORD PTR [rbp-0x8] 65: | | 48 8b 45 f0 mov rax,QWORD PTR [rbp-0x10] 69: | | 66 0f 28 c8 movapd xmm1,xmm0 6d: | | 66 48 0f 6e c0 movq xmm0,rax 72: | | 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # 79 </gcmain+0x58>/gc 75: R_X86_64_PC32 .rodata+0x15 79: | | b8 02 00 00 00 mov eax,0x2 7e: | | /-- e8 00 00 00 00 call 83 </gcmain+0x62>/gc 7f: R_X86_64_PLT32 printf-0x4 83: | | \-> f2 0f 10 4d f0 movsd xmm1,QWORD PTR [rbp-0x10] 88: | | f2 0f 10 05 00 00 00 00 movsd xmm0,QWORD PTR [rip+0x0] # 90 </gcmain+0x6f>/gc 8c: R_X86_64_PC32 .rodata+0x1c 90: | | f2 0f 58 c1 addsd xmm0,xmm1 94: | | f2 0f 11 45 f0 movsd QWORD PTR [rbp-0x10],xmm0 99: | \----> f2 0f 10 05 00 00 00 00 movsd xmm0,QWORD PTR [rip+0x0] # a1 </gcmain+0x80> 9d: R_X86_64_PC32 .rodata+0x24 a1: | 66 0f 2f 45 f0 comisd xmm0,QWORD PTR [rbp-0x10] a6: \-------- 77 90 ja 38 </gcmain+0x17>/gc a8: b8 00 00 00 00 mov eax,0x0
16. Deklarace většího množství prázdných sekcí s různými atributy
V dalším textu se pokusíme analyzovat objektový soubor (a taktéž výsledný binární soubor vytvořený linkerem), v němž je definováno větší množství sekcí (segmentů). U jednotlivých sekcí jsou uvedeny i jejich atributy, tj. zda daná sekce obsahuje kód nebo data, zda je možné do sekce zapisovat či z ní jenom číst atd. Povšimněte si způsobu zápisu jmen sekcí s tečkou na začátku i způsobu specifikace atributů realizovaného formou řetězce. Tento zdrojový kód naleznete na adrese https://github.com/tisnik/presentations/blob/master/assembler/multiple_sections1/sections1.s:
# asmsyntax=as .intel_syntax noprefix # Linux kernel system call table sys_exit=1 sys_write=4 #----------------------------------------------------------------------------- # ruzne sekce (segmenty) se specifickymi atributy .section .section_a .section .section_b,"x" .section .section_c,"a" .section .section_d,"l" .section .section_e,"w" #----------------------------------------------------------------------------- .section .data hello_lbl: .string "Hello World!\n" # string, ktery JE ukoncen nulou #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start # tento symbol ma byt dostupny i linkeru _start: mov eax, sys_write # cislo syscallu pro funkci "write" mov ebx, 1 # standardni vystup mov ecx, offset hello_lbl # adresa retezce, ktery se ma vytisknout mov edx, 13 # pocet znaku, ktere se maji vytisknout int 0x80 # volani Linuxoveho kernelu mov eax, sys_exit # cislo sycallu pro funkci "exit" mov ebx, 0 # exit code = 0 int 0x80 # volani Linuxoveho kernelu
17. Výpis všech sekcí z objektového souboru i z výsledného binárního souboru
V prvním kroku provedeme překlad výše uvedeného zdrojového textu do objektového souboru a ve druhém kroku necháme linker vytvořit spustitelný nativní soubor a.out:
$ as sections1.s -o sections1.o $ ld -s sections1.o
V objektovém souboru by se měly nacházet deklarace všech sekcí, a to včetně našich pěti sekcí. To ostatně jednoduše zjistíme nástrojem objdump. Povšimněte si, že tyto sekce mají nulovou velikost a různě nastavené atributy (READONLY, CODE, ALLOC, CONTENTS, DATA atd.):
$ objdump -h sections1.o sections1.o: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000022 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 1 .data 0000000e 0000000000000000 0000000000000000 00000062 2**0 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 0000000000000000 0000000000000000 00000070 2**0 ALLOC 3 .section_a 00000000 0000000000000000 0000000000000000 00000070 2**0 CONTENTS, READONLY 4 .section_b 00000000 0000000000000000 0000000000000000 00000070 2**0 CONTENTS, READONLY, CODE 5 .section_c 00000000 0000000000000000 0000000000000000 00000070 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .section_d 00000000 0000000000000000 0000000000000000 00000070 2**0 CONTENTS, READONLY 7 .section_e 00000000 0000000000000000 0000000000000000 00000070 2**0 CONTENTS
Naproti tomu se ve spustitelném souboru a.out nachází pouze sekce s nenulovou délkou. Opět si to velmi snadno ověříme nástrojem objdump:
$ objdump -h a.out a.out: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000022 0000000000401000 0000000000401000 00001000 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 0000000e 0000000000402000 0000000000402000 00002000 2**0 CONTENTS, ALLOC, LOAD, DATA
18. Deklarace většího množství sekcí obsahujících statická data
Zdrojový kód ze šestnácté kapitoly nyní nepatrně upravíme, a to konkrétně takovým způsobem, že do každé nově definované sekce bude přidán řetězec, tedy statická data. Výsledný zdrojový kód je dostupný na adrese https://github.com/tisnik/presentations/blob/master/assembler/multiple_sections2/sections2.s:
# asmsyntax=as .intel_syntax noprefix # Linux kernel system call table sys_exit=1 sys_write=4 #----------------------------------------------------------------------------- # ruzne sekce (segmenty) se specifickymi atributy .section .section_a .string "SECTION A" .section .section_b,"x" .string "SECTION B" .section .section_c,"a" .string "SECTION C" .section .section_d,"l" .string "SECTION D" .section .section_e,"w" .string "SECTION E" #----------------------------------------------------------------------------- .section .data hello_lbl: .string "Hello World!\n" # string, ktery JE ukoncen nulou #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start # tento symbol ma byt dostupny i linkeru _start: mov eax, sys_write # cislo syscallu pro funkci "write" mov ebx, 1 # standardni vystup mov ecx, offset hello_lbl # adresa retezce, ktery se ma vytisknout mov edx, 13 # pocet znaku, ktere se maji vytisknout int 0x80 # volani Linuxoveho kernelu mov eax, sys_exit # cislo sycallu pro funkci "exit" mov ebx, 0 # exit code = 0 int 0x80 # volani Linuxoveho kernelu
19. Opětovný výpis všech sekcí z objektového souboru i z výsledného binárního souboru
Opět si po překladu a slinkování zdrojového kódu příkazy:
$ as sections2.s -o sections2.o $ ld -s sections2.o
vypišme všechny sekce, které nástroj objdump nalezne v objektovém kódu. U každé sekce se vypisuje její velikost, virtuální i fyzická adresa, zarovnání a atributy sekce (segmentu):
$ objdump -h sections2.o sections2.o: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000022 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE 1 .data 0000000e 0000000000000000 0000000000000000 00000062 2**0 CONTENTS, ALLOC, LOAD, DATA 2 .bss 00000000 0000000000000000 0000000000000000 00000070 2**0 ALLOC 3 .section_a 0000000a 0000000000000000 0000000000000000 00000070 2**0 CONTENTS, READONLY 4 .section_b 0000000a 0000000000000000 0000000000000000 0000007a 2**0 CONTENTS, READONLY, CODE 5 .section_c 0000000a 0000000000000000 0000000000000000 00000084 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .section_d 0000000a 0000000000000000 0000000000000000 0000008e 2**0 CONTENTS, READONLY 7 .section_e 0000000a 0000000000000000 0000000000000000 00000098 2**0 CONTENTS
Vidíme, že jsou vypsány všechny sekce a námi vytvořené sekce již nejsou prázdné (mají délku 0×a=10 bajtů).
Neprázdné sekce jsou zachovány i ve výsledném nativním spustitelném souboru, o čemž se můžeme snadno přesvědčit:
$ objdump -h a.out a.out: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000022 0000000000401000 0000000000401000 00001000 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .section_c 0000000a 0000000000402000 0000000000402000 00002000 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .data 0000000e 000000000040300a 000000000040300a 0000200a 2**0 CONTENTS, ALLOC, LOAD, DATA 3 .section_a 0000000a 0000000000000000 0000000000000000 00002018 2**0 CONTENTS, READONLY 4 .section_b 0000000a 0000000000000000 0000000000000000 00002022 2**0 CONTENTS, READONLY, CODE 5 .section_d 0000000a 0000000000000000 0000000000000000 0000202c 2**0 CONTENTS, READONLY 6 .section_e 0000000a 0000000000000000 0000000000000000 00002036 2**0 CONTENTS
Názvy sekcí nalezneme i v tabulce symbolů:
$ objdump -t sections2.o sections2.o: file format elf64-x86-64 SYMBOL TABLE: 0000000000000000 l d .text 0000000000000000 .text 0000000000000000 l d .data 0000000000000000 .data 0000000000000000 l d .bss 0000000000000000 .bss 0000000000000001 l *ABS* 0000000000000000 sys_exit 0000000000000004 l *ABS* 0000000000000000 sys_write 0000000000000000 l d .section_a 0000000000000000 .section_a 0000000000000000 l d .section_b 0000000000000000 .section_b 0000000000000000 l d .section_c 0000000000000000 .section_c 0000000000000000 l d .section_d 0000000000000000 .section_d 0000000000000000 l d .section_e 0000000000000000 .section_e 0000000000000000 l .data 0000000000000000 hello_lbl 0000000000000000 g .text 0000000000000000 _start
A konečně si ukažme výpis obsahu jednotlivých sekcí. I zde nám pomůže nástroj objdump:
$ objdump -s sections2.o sections2.o: file format elf64-x86-64 Contents of section .text: 0000 b8040000 00bb0100 0000b900 000000ba ................ 0010 0d000000 cd80b801 000000bb 00000000 ................ 0020 cd80 .. Contents of section .data: 0000 48656c6c 6f20576f 726c6421 0a00 Hello World!.. Contents of section .section_a: 0000 53454354 494f4e20 4100 SECTION A. Contents of section .section_b: 0000 53454354 494f4e20 4200 SECTION B. Contents of section .section_c: 0000 53454354 494f4e20 4300 SECTION C. Contents of section .section_d: 0000 53454354 494f4e20 4400 SECTION D. Contents of section .section_e: 0000 53454354 494f4e20 4500 SECTION E.
20. Odkazy na Internetu
- Objdump Command in Linux with Examples
https://www.geeksforgeeks.org/objdump-command-in-linux-with-examples/ - Linux objdump Command Explained for Beginners (7 Examples)
https://www.howtoforge.com/linux-objdump-command/ - objdump – Unix, Linux Command
https://www.tutorialspoint.com/unix_commands/objdump.htm - objdump (Wikipedia)
https://en.wikipedia.org/wiki/Objdump - Linux Objdump Command Examples (Disassemble a Binary File)
https://www.thegeekstuff.com/2012/09/objdump-examples/ - How to use the ObjDump tool with x86
https://resources.infosecinstitute.com/topic/how-to-use-the-objdump-tool-with-x86/ - 10+ objdump Command Examples in Linux
https://www.sanfoundry.com/objdump-command-usage-examples-in-linux/ - objdump(1) – Linux manual page
https://www.man7.org/linux/man-pages/man1/objdump.1.html - Object file (Wikipedia)
https://en.wikipedia.org/wiki/Object_file - Executable and Linkable Format
https://en.wikipedia.org/wiki/Executable_and_Linkable_Format - Basic Linker Script Concepts
https://www.zeuthen.desy.de/dv/documentation/unixguide/infohtml/binutils/docs/ld/Basic-Script-Concepts.html#Basic-Script-Concepts - virtual and physical addresses of sections in elf files
https://stackoverflow.com/questions/6218384/virtual-and-physical-addresses-of-sections-in-elf-files - Executable and Linkable Format
https://en.wikipedia.org/wiki/Executable_and_Linkable_Format - ELF 101: a Linux executable walkthrough
https://upload.wikimedia.org/wikipedia/commons/e/e4/ELF_Executable_and_Linkable_Format_diagram_by_Ange_Albertini.png - ELF (Executable and Linkable Format)
https://wiki.osdev.org/ELF - elf(5) — Linux manual page
https://www.man7.org/linux/man-pages/man5/elf.5.html - What does each column of objdump's Symbol table mean?
https://stackoverflow.com/questions/6666805/what-does-each-column-of-objdumps-symbol-table-mean - Category:book:X86 Assembly
https://en.wikibooks.org/wiki/Category:Book:X86_Assembly - x86 Assembly/GNU assembly syntax
https://en.wikibooks.org/wiki/X86_Assembly/GNU_assembly_syntax - Symbol Table
https://wiki.osdev.org/Symbol_Table