Předchozí článek, v němž jsme si ve stručnosti popsali konvence používané při volání knihovních funkcí na mikroprocesorech s 64bitovou architekturou x86-64, dnes doplníme, protože si řekneme, jakým způsobem se volají knihovní funkce na 32bitových mikroprocesorech s architekturou ARM. Dnešní díl tedy bude zaměřen více prakticky, neboť assembler se na ARMech s velkou pravděpodobností používá častěji, než je tomu na obecně výkonnějších 64bitových čipech x86-64, které jsou navíc určeny pro odlišný segment trhu.

Obsah

1. Použití assembleru v Linuxu: konvence při volání knihovních funkcí na mikroprocesorech ARM

2. Kostra programu napsaného v assembleru se subrutinou main

3. První demonstrační příklad – návrat ze subrutiny main s předáním návratového kódu

4. Alternativní způsob ukončení subrutiny main instrukcí bx

5. Druhý demonstrační příklad – použití instrukce bx

6. Uložení návratové adresy na zásobník

7. Třetí demonstrační příklad – uložení návratové adresy na zásobník

8. Uložení návratové adresy i registrů, které se používají v subrutině

9. Čtvrtý demonstrační příklad – uložení a obnova obsahu pracovních registrů

10. Volání knihovní funkce puts() z assembleru

11. Pátý demonstrační příklad – zavolání funkce puts()

12. Spuštění příkladu v debuggeru

13. Repositář s demonstračními příklady

14. Odkazy na Internetu

1. Použití assembleru v Linuxu: konvence při volání knihovních funkcí na mikroprocesorech ARM

Na předchozí díl seriálu o použití assembleru v Linuxu, v němž jsme si popsali způsob volání funkcí standardní céčkové knihovny na 64bitových mikroprocesorech s architekturou x86-64, dnes navážeme, protože si řekneme a na několika demonstračních příkladech ukážeme, jakým způsobem je možné provést stejnou činnost, nyní ovšem na 32bitových RISCových mikroprocesorech s architekturou ARM. Jen pro úplnost si připomeňme některé instrukce, které na těchto mikroprocesorech existují a s nimiž se setkáme v navazujících kapitolách. Mnemotechnické zkratky těchto instrukcí i jejich chování se (podle očekávání) odlišují od procesorů Intel:

# Instrukce Význam
1 MOV přenos dat mezi registry, načtení konstanty do registru atd.
2 B nepodmíněný skok popř. skok při splnění určené podmínky
3 BL skok do podprogramu (branch and link)
4 BX skok + přepnutí mikroprocesoru do režimu Thumb či zpět
5 STM uložení vybrané skupiny registrů do operační paměti
6 LDM načtení vybrané skupiny registrů z operační paměti

Základní instrukcí skoku je instrukce pojmenovaná jednoduše B, což je zkratka odvozená od slova branch. 32bitové slovo této instrukce je rozděleno na tři části. V nejvyšších čtyřech bitech se nachází kód podmínky, což v důsledku znamená, že jediná instrukce B může nahradit všechny formy podmíněných skoků (přesněji řečeno čtrnáct typů podmíněných skoků a jeden skok nepodmíněný). Za těmito čtyřmi bity následuje taktéž čtyřbitový operační kód 1010 a ve zbylých 24 bitech instrukčního slova je pak uložena konstanta, z níž se vypočítá offset skoku. Skok je vždy proveden na adresu dělitelnou čtyřmi, tudíž je při výpočtu offsetu 24bitová konstanta posunuta o dva bity doleva.

Další důležitou instrukcí mikroprocesorů ARM je instrukce sloužící pro skok do podprogramu. Zatímco na mnoha jiných architekturách mikroprocesorů se ukládá návratová adresa do zásobníku, na mikroprocesorech ARM je pro uložení návratové hodnoty použit pracovní registr R14 nazývaný z tohoto důvodu taktéž link register. Pro skok do podprogramu se používá instrukce BL, neboli branch and link. Při provádění této instrukce provede mikroprocesor ve skutečnosti dvě operace: vypočítá adresu skoku stejným způsobem, jako tomu bylo u instrukce B (branch) a uloží tuto adresu do registru R15/PC (tato adresa se použije pro načtení následující instrukce). Současně však taktéž vloží adresu aktuální PC-4 do registru R14/LR, protože právě hodnota aktuální PC-4 je adresou instrukce ležící těsně ZA instrukcí skoku (nesmíme zapomenout na to, že se při provedení skoku ve skutečnosti již obsah registru PC stačil dvakrát zvýšit o hodnotu 4).

V dalších příkladech budeme často používat instrukci určenou pro výskok (resp. přesněji řečeno návrat) z podprogramu. Zajímavé je, že ve skutečnosti žádná speciální instrukce typu RET nebo RETURN na mikroprocesorech ARM neexistuje. Vše, co musí programátor udělat, je obnovit obsah registru R15/PC z registru R14, a to prostým přesunem dat:

mov PC, LR

U mikroprocesorů ARM podporujících instrukční sadu Thumb, popřípadě i technologii Jazelle (bajtkód JVM) existují i další typy skokových instrukcí. Pro přehlednost jsou všechny skokové instrukce vypsány v následující tabulce:

# Instrukce Význam
1 B prostý skok
2 BL skok + uložení návratové hodnoty do link registru
3 BX skok + přepnutí do režimu Thumb či zpět
4 BLX kombinace instrukcí BL+BX
5 BXJ skok + přepnutí do režimu Jazelle

2. Kostra programu napsaného v assembleru se subrutinou main

Připomeňme si nejdříve, jak vlastně vypadala kostra toho nejjednoduššího programu napsaného v assembleru 32bitových mikroprocesorů ARM. Celý program se skládal pouze ze tří instrukcí, které nejprve naplnily pracovní registry r0 (návratový kód, který lze načíst v shellu) a r7 (číslo syscallu, tedy číslo funkce jádra, která se má zavolat) a následně se instrukcí SVC zavolalo jádro operačního systému. Tato sekvence instrukcí začínala na návěští se jménem _start. Toto jméno je velmi důležité, neboť právě podle něho linker pozná, kam má umístit vstupní bod do programu. Pro jistou si celou kostru aplikace ještě jednou ukažme (povšimněte si, že komentáře je nutné zapisovat znakem @, neboť znak # je vyhrazen pro zápis konstanty; určitou výjimkou jsou komentáře umístěné na samostatném řádku):

# asmsyntax=as

# Sablona pro zdrojovy kod Linuxoveho programu naprogramovaneho
# v assembleru GNU AS.
#
# Autor: Pavel Tisnovsky



# Linux kernel system call table
sys_exit=1



#-----------------------------------------------------------------------------
.section .data



#-----------------------------------------------------------------------------
.section .bss



#-----------------------------------------------------------------------------
.section .text
        <strong>.global _start</strong>      @ tento symbol ma byt dostupny i z linkeru

<strong>_start</strong>:
        mov   r7,$sys_exit  @ cislo sycallu pro funkci "exit"
        mov   r0,#0         @ exit code = 0
        svc   0             @ volani Linuxoveho kernelu

Podobně jako je tomu na architektuře x86-64, i na ARMech platí, že pokud se pro překlad assemblerovského programu použije nástroj gcc, bude kostra aplikace vypadat poněkud odlišně v porovnání s programy, s nimiž jsme se až doposud setkávali. Je tomu tak z toho prostého důvodu, že gcc implicitně vytvoří vstupní bod (entry point) do programu, který je označen návěštím _start a obsahuje nativní kód získaný z objektového souboru crt0.o. Tento kód volá funkci/subrutinu main, z níž se musí řízení programu na konci nějakým způsobem vrátit. Ukončení aplikace vypadá poměrně jednoduše: do registru r0 se uloží návratový kód a následně se řízení programu vrátí ze subrutiny main do volajícího kódu. Instrukce ret sice na ARMech neexistuje, to ovšem nevadí, jak již ostatně víme z předchozí kapitoly, protože ji můžeme jednoduše nahradit instrukcí mov PC, LR:

.section .text
        <strong>.global main</strong>   @ tento symbol ma byt dostupny i linkeru

<strong>main:</strong>

        mov  r0, #0    @ navratova hodnota (exit status)
        mov  pc, lr    @ ukonceni aplikace (rizeni se vrati na adresu ulozenou v LR)

3. První demonstrační příklad – návrat ze subrutiny main s předáním návratového kódu

Pro úplnost si ukažme, jak tedy vypadá celá kostra demonstračního příkladu naprogramovaného pro 32bitové RISCové procesory ARM. Tento příklad je určen pro překlad s využitím nástroje gcc, nikoli kombinací utilit as+ld, proto je v něm deklarováno návěští main namísto _start:

# asmsyntax=as

# Program pro otestovani volani funkci ze standardni knihovny jazyka C
# - varianta urcena pro klasickou 32bitovou architekturu ARM
#
# Autor: Pavel Tisnovsky



#-----------------------------------------------------------------------------
.section .data



#-----------------------------------------------------------------------------
.section .text
        .global main   @ tento symbol ma byt dostupny i linkeru

main:

        mov  r0, #0    @ navratova hodnota (exit status)
        mov  pc, lr    @ ukonceni aplikace (rizeni se vrati na adresu ulozenou v LR)

Porovnejme si, jak se tento program odlišuje od varianty určené pro procesory s architekturou x86-64 (nebo i i386, zde je to jedno):

43_stdc_stub

Pro překlad a slinkování použijte jediný příkaz:

gcc test.s

Výsledkem by měl být spustitelný soubor, jehož velikost dosahuje na mém systému 5268 bajtů (ovšem s jinou verzí gcc či libc může být velikost nepatrně odlišná). Některé symboly je možné ze spustitelného souboru odstranit takto:

strip a.out

Na mém operačním systému (Raspbian) se velikost spustitelného souboru po tomto zásahu zmenší na 2824 bajtů. Jen pro připomenutí – překladem původní aplikace volající přímo jádro operačního systému vznikl spustitelný soubor o velikosti pouhých 312 bajtů.

Pro zajímavost se podívejme, jak vypadá přeložená funkce/subrutina main. Jedná se o pouhé dvě instrukce (to již ostatně víme), přičemž každá instrukce má konstantní šířku 32 bitů:

00008390 <main>:
    8390:       e3a00000        mov     r0, #0
    8394:       e1a0f00e        mov     pc, lr

4. Alternativní způsob ukončení subrutiny main instrukcí bx

Pokud se podíváte na strojový kód, který vznikne překladem céčkovských zdrojových textů do assembleru, zjistíte, že se návrat z funkce/subrutiny main provádí poněkud odlišným způsobem, protože se namísto instrukce mov PC, LR používá instrukce bx LR (branch and exchange), tj. skok na adresu, která je uložena v pracovním registru LR (link register). Teoreticky by sice bylo možné použít i instrukci b (branch), ovšem tato instrukce neumožňuje, aby se namísto offsetu (24bitové konstanty) použil pracovní registr. Překladač programovacího jazyka C většinou vygeneruje tento strojový kód:

main:
        mov  r0, #0    @ navratova hodnota (exit status)
        bx   lr        @ ukonceni aplikace (rizeni se vrati na adresu ulozenou v LR)

Poznámka: v některých případech může instrukce BX přepnout režim procesoru na základě nejnižších bitů adresy.

5. Druhý demonstrační příklad – použití instrukce bx

Ukažme si, jak by mohl vypadat zdrojový kód nejjednoduššího programu napsaného v assembleru, který je překládán pomocí nástroje gcc a tudíž obsahuje pouze funkci/subrutinu main. Program je tvořen pouhými dvěma instrukcemi, přičemž první instrukce nastavuje návratový kód a druhá instrukce ukončí provádění subrutiny main:

# asmsyntax=as

# Program pro otestovani volani funkci ze standardni knihovny jazyka C
# - varianta urcena pro klasickou 32bitovou architekturu ARM
#
# Autor: Pavel Tisnovsky



#-----------------------------------------------------------------------------
.section .data



#-----------------------------------------------------------------------------
.section .text
        .global main   @ tento symbol ma byt dostupny i linkeru

main:
        mov  r0, #0    @ navratova hodnota (exit status)
        bx   lr        @ ukonceni aplikace (rizeni se vrati na adresu ulozenou v LR)

V objektovém kódu nalezneme tyto dvě instrukce. Další části jsou automaticky přidané při linkování a nemusí nás nyní zajímat:

00008390 <main>:
    8390:       e3a00000        mov     r0, #0
    8394:       e12fff1e        bx      lr

Povšimněte si, že díky tomu, že všechny instrukce mají na původní RISCové architektuře ARM shodnou šířku 32bitů, má subrutina main po překladu stejnou velikost, jako u předchozího příkladu:

00008390 <main>:
    8390:       e3a00000        mov     r0, #0
    8394:       e1a0f00e        mov     pc, lr

6. Uložení návratové adresy na zásobník

Pokud se v subrutině main budou volat další subrutiny a knihovní funkce, došlo by ke změně registru LR a tudíž by nebylo možné se jednoduše ze subrutiny main vrátit zpět do volajícího kódu. Z tohoto důvodu je vhodné, aby se obsah registru LR uložil na zásobník a těsně před návratem ze subrutiny main by se obsah tohoto registru opět obnovil. Pro tento účel je možné použít instrukce STM (store multiple) a LDM (load multiple). Jedná se o všestranně použitelné instrukce, u nichž lze specifikovat jak adresu, na kterou se budou ukládat operandy, tak i způsob případného zvýšení či snížení této adresy (právě automatická inkrementace a dekrementace simuluje práci zásobníku). Navíc tyto instrukce nemusí ukládat či načítat jen jeden operand, ale libovolný počet pracovních registrů (ten je zvolen pomocí bitového pole uvnitř instrukčního kódu):

Instrukce Význam
STM R10, {R1} uložení registru R1 na adresu specifikovanou v R10
STM R10, {R1, R2} uložení registrů R1 a R2 na adresu specifikovanou v R10 a na adresu následující
STMFD R10!, {R1} R10 obsahuje adresu vrcholu zásobníku, na nějž je uložen registr R1
STMFD R10!, {R1,R2,R3} R10 obsahuje adresu vrcholu zásobníku, na nějž jsou uloženy registry R1, R2 a R3
STMED R10!, {R1} R10 obsahuje adresu vrcholu zásobníku, na nějž je uložen registr R1
STMED R10!, {R1,R2,R3} R10 obsahuje adresu vrcholu zásobníku, na nějž jsou uloženy registry R1, R2 a R3
STMFA R10!, {R1} jako STMFD, ovšem zásobník roste směrem k vyšším adresám
STMFA R10!, {R1,R2,R3} jako STMFD, ovšem zásobník roste směrem k vyšším adresám

Rozdíl mezi STMFD a STMED spočívá v chování ukazatele na vrchol zásobníku. U varianty F vrchol ukazuje na poslední zapsaný (zaplněný) prvek, zatímco u varianty E ukazuje na první volný prvek.

7. Třetí demonstrační příklad – uložení návratové adresy na zásobník

Ve třetím demonstračním příkladu se ihned po vstupu do subrutiny main uloží link register na zásobník, následně se nastaví návratová hodnota do pracovního registru r0 a v posledním kroku se malým trikem obnoví hodnota uložená na zásobník (byl to obsah link registru), ovšem tentokrát se tato hodnota zapíše přímo do instrukčního čítače (program counter). Tím se vlastně de facto provede již dříve zmíněná instrukce mov PC, LR, resp. přesněji řečeno pseudoinstrukce mov PC, původní_hodnota_LR:

# asmsyntax=as

# Program pro otestovani volani funkci ze standardni knihovny jazyka C
# - varianta urcena pro klasickou 32bitovou architekturu ARM
#
# Autor: Pavel Tisnovsky



#-----------------------------------------------------------------------------
.section .data



#-----------------------------------------------------------------------------
.section .text
        .global main       @ tento symbol ma byt dostupny i linkeru

main:
        stmfd sp!, {lr}    @ ulozeni registru LR na zasobnik
        mov   r0, #42      @ navratova hodnota (exit status)
        ldmfd sp!, {pc}    @ ukonceni aplikace (rizeni se vrati na adresu ulozenou v LR)

I přesto, že jsou instrukce typu LDM a STM velmi univerzální a mohou provádět potenciálně složité operace (například uložení všech pracovních registrů atd.), jsou stále překládány do jediného 32bitového instrukčního slova, což je ostatně vidět z následujícího výpisu získaného disassemblerem:

00008390 <main>:
    8390:       e92d4000        stmfd   sp!, {lr}
    8394:       e3a0002a        mov     r0, #42 ; 0x2a
    8398:       e8bd8000        ldmfd   sp!, {pc}

8. Uložení návratové adresy i registrů, které se používají v subrutině

Vzhledem k tomu, že instrukce typu LDM a STM dokážou načíst či naopak uložit větší množství pracovních registrů, je možné na začátku subrutiny zajistit, aby se na zásobník uložily například registry R4, R5, R6 a LR. Pokud nedodržíte pořadí registrů, vypíše assembler varování, protože kvůli způsobu kódování musí být registry ukládány v pevně daném pořadí:

stmfd sp!, {r4, r5, r6, <strong>lr</strong>}  @ ulozeni zvolenych registru na zasobnik

Návrat ze subrutiny je poté proveden následovně – celý trik spočívá v tom, že poslední registr, který se obnovuje, již není LR ale PC:

ldmfd sp!, {r4, r5, r6, <strong>pc</strong>}  @ obnova registru, ukonceni aplikace (rizeni se vrati na adresu ulozenou v LR)

9. Čtvrtý demonstrační příklad – uložení a obnova obsahu pracovních registrů

Postup, který jsme si popsali v předchozí kapitole nyní použijeme ve čtvrtém demonstračním příkladu, jehož kostra vypadá následovně:

# asmsyntax=as

# Program pro otestovani volani funkci ze standardni knihovny jazyka C
# - varianta urcena pro klasickou 32bitovou architekturu ARM
#
# Autor: Pavel Tisnovsky



#-----------------------------------------------------------------------------
.section .data



#-----------------------------------------------------------------------------
.section .text
        .global main                 @ tento symbol ma byt dostupny i linkeru

main:
        stmfd sp!, {r4, r5, r6, lr}  @ ulozeni zvolenych registru na zasobnik
        mov   r0, #42                @ navratova hodnota (exit status)
        ldmfd sp!, {r4, r5, r6, pc}  @ obnova registru, ukonceni aplikace (rizeni se vrati na adresu ulozenou v LR)

Po překladu se můžeme podívat na způsob zakódování instrukcí, tentokrát pro přehlednost s použitím mnemotechnických zkratek používaných firmou Intel:

00008390 <main>:
    8390:       e92d4070        push    {r4, r5, r6, lr}
    8394:       e3a0002a        mov     r0, #42 ; 0x2a
    8398:       e8bd8070        pop     {r4, r5, r6, pc}

Význam programu se samozřejmě nezměnil, disassembler pouze použil názvy push a pop, které přesně odpovídají tomu, co se v programu děje.

10. Volání knihovní funkce puts() z assembleru

Funkce, které jsou dostupné ve standardní céčkové knihovně, se volají velmi jednoduše instrukcí bl (branch and link), které se předá adresa funkce. Adresa se samozřejmě zjišťuje až při překladu; ve zdrojovém kódu se použije jméno (návěští funkce). Pokud by funkce neměla žádné parametry, vypadalo by volání následovně:

bl    puts                      @ zavolani knihovni funkce puts()

Ve skutečnosti funkce puts očekává jeden parametr, kterým je adresa řetězce, který se má vytisknout. Tento řetězec může být umístěn v datovém segmentu a deklarován pomocí .asciiz (tím se za konec řetězce přidá ukončující nula):

.section .data
hello_world_message:
        .asciz "Hello world!\n"         @ zprava, ktera se ma vytisknout na standardni vystup

Parametry se předávají přes pracovní registry. V tomto konkrétním případě je nutné v registru r0 předat adresu řetězce (resp. adresu jeho prvního znaku). Nejprve tuto adresu načteme, a to pseudoinstrukcí ldr. Tato instrukce se většinou přeloží takovým způsobem, že konstantu (adresu) umístí před či za subrutinu a načtení je provedeno relativně k obsahu registru PC:

ldr   r0, =hello_world_message  @ adresa zpravy, ktera se ma vytisknout

Parametr máme inicializovaný a jiné parametry již funkce puts nepotřebuje, takže ji nyní skutečně můžeme zavolat:

bl    puts                      @ zavolani knihovni funkce puts()

Povšimněte si, že zavoláním se přepíše obsah registru LR, což je jeden z důvodů, proč jsme si jeho obsah uschovali na zásobníku.

11. Pátý demonstrační příklad – zavolání funkce puts()

Celá sekvence volání knihovní funkce puts je použita v dnešním posledním demonstračním příkladu, jehož zdrojový kód je umístěn pod tento odstavec:

# asmsyntax=as

# Program pro otestovani volani funkci ze standardni knihovny jazyka C
# - volani funkce 'puts'
# - varianta urcena pro klasickou 32bitovou architekturu ARM
#
# Autor: Pavel Tisnovsky



#-----------------------------------------------------------------------------
.section .data
hello_world_message:
        .asciz "Hello world!\n"         @ zprava, ktera se ma vytisknout na standardni vystup



#-----------------------------------------------------------------------------
.section .text
        .global main                    @ tento symbol ma byt dostupny i linkeru

main:
        stmfd sp!, {lr}                 @ ulozeni zvolenych registru na zasobnik
        ldr   r0, =hello_world_message  @ adresa zpravy, ktera se ma vytisknout
        bl    puts                      @ zavolani knihovni funkce puts()
        mov   r0, #42                   @ navratova hodnota (exit status)
        ldmfd sp!, {pc}                 @ obnova registru, ukonceni aplikace (rizeni se vrati na adresu ulozenou v LR)

Opět se podívejme na porovnání tohoto programového kódu s jeho ekvivalentem, ovšem naprogramovaným pro mikroprocesory s architekturou x86-64. Manipulace se zásobníkem je na procesorech ARM kratší, a to z toho důvodu, že není nutné zajišťovat zarovnání vrcholu zásobníku na adresu dělitelnou šestnácti:

44_stdc_puts

12. Spuštění příkladu v debuggeru

Na závěr si zkusme odkrokovat chování posledního programu v GNU debuggeru. Překlad proveďte s volbou -g a posléze spusťte debugger:

pi@raspberrypi $ <strong>gdb a.out</strong>
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /mnt/home/pi/temp/44_stdc_puts/a.out...done.

Nastavíme breakpoint na vstup subrutiny main:

(gdb) <strong>break main</strong>
Breakpoint 1 at 0x83cc: file main_arm.s, line 24.

Nyní aplikaci spustíme. Měla by se zastavit na nastaveném breakpointu:

(gdb) <strong>run</strong>
Starting program: /mnt/home/pi/temp/44_stdc_puts/a.out 

Breakpoint 1, main () at main_arm.s:24
24              ldr   r0, =hello_world_message  @ adresa zpravy, ktera se ma vytisknout

Můžeme se podívat na aktuální hodnotu všech pracovních registrů. Zajímat nás bude zejména registru r0 a pc:

(gdb) <strong>info registers</strong>
r0             <strong>0x1</strong>      1
r1             0xbefff724       3204445988
r2             0xbefff72c       3204445996
r3             0x83c8   33736
r4             0x0      0
r5             0x0      0
r6             0x831c   33564
r7             0x0      0
r8             0x0      0
r9             0x0      0
r10            0xb6fff000       3070226432
r11            0x0      0
r12            0xb6fbf000       3069964288
sp             0xbefff5d4       0xbefff5d4
lr             0xb6eaa82c       -1226135508
pc             <strong>0x83cc</strong>   0x83cc <main+4>
cpsr           0x60000010       1610612752

Taktéž si můžeme zobrazit, na jakém místě kódu se právě nacházíme:

(gdb) <strong>disassemble</strong>
Dump of assembler code for function main:
   0x000083c8 <+0>:     push    {lr}
=> 0x000083cc <+4>:     ldr     r0, [pc, #8]    ; 0x83dc <main+20>
   0x000083d0 <+8>:     bl      0x82ec
   0x000083d4 <+12>:    mov     r0, #42 ; 0x2a
   0x000083d8 <+16>:    pop     {pc}
   0x000083dc <+20>:    andeq   r0, r1, r0, lsl #11
End of assembler dump.

Nyní řekneme debuggeru, aby vykonal další instrukci a ihned poté se opět zastavil:

(gdb) <strong>step</strong>
25              bl    puts                      @ zavolani knihovni funkce puts()

Pohledem na disassemblovaný kód se ujistíme, že se řízení skutečně přesunulo pouze na další instrukci:

(gdb) <strong>disassemble </strong>
Dump of assembler code for function main:
   0x000083c8 <+0>:     push    {lr}
   0x000083cc <+4>:     ldr     r0, [pc, #8]    ; 0x83dc <main+20>
=> 0x000083d0 <+8>:     bl      0x82ec
   0x000083d4 <+12>:    mov     r0, #42 ; 0x2a
   0x000083d8 <+16>:    pop     {pc}
   0x000083dc <+20>:    andeq   r0, r1, r0, lsl #11
End of assembler dump.

Současně se změnil obsah pracovního registru r0 (obsahuje adresu řetězce) a samozřejmě i obsah registru pc (obsahuje adresu další instrukce):

(gdb) info registers 
r0             <strong>0x10580</strong>  66944
r1             0xbefff724       3204445988
r2             0xbefff72c       3204445996
r3             0x83c8   33736
r4             0x0      0
r5             0x0      0
r6             0x831c   33564
r7             0x0      0
r8             0x0      0
r9             0x0      0
r10            0xb6fff000       3070226432
r11            0x0      0
r12            0xb6fbf000       3069964288
sp             0xbefff5d4       0xbefff5d4
lr             0xb6eaa82c       -1226135508
pc             <strong>0x83d0</strong>   0x83d0 <main+8>
cpsr           0x60000010       1610612752

Po dalším kroku se na standardní výstup vytiskne zpráva, protože tímto krokem se volala funkce puts:

(gdb) <strong>step</strong>
Hello world!

26              mov   r0, #42                   @ navratova hodnota (exit status)

Program můžeme dokončit příkazem „continue“:

(gdb) <strong>cont</strong>
Continuing.

13. Repositář s demonstračními příklady

Všechny dnes popisované demonstrační příklady byly, podobně jako ve všech předchozích částech tohoto seriálu, společně s podpůrnými skripty určenými pro jejich překlad či naopak pro disassembling, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/presentations/. Všechny příklady jsou určeny pro GNU Assembler. Následují tabulky obsahující odkazy na zdrojové kódy příkladů i na již zmíněné podpůrné skripty:

První demonstrační příklad: šablona pro překlad nástrojem gcc

# Soubor Popis Odkaz do repositáře
1 main.s hlavní program pro procesory x86-64 https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/main.s
2 main_arm_v1.s hlavní program pro procesory ARM (verze 1) https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/main_arm_v1.s
3 main_arm_v2.s hlavní program pro procesory ARM (verze 2) https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/main_arm_v2.s
4 main_arm_v3.s hlavní program pro procesory ARM (verze 3) https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/main_arm_v3.s
5 main_arm_v4.s hlavní program pro procesory ARM (verze 4) https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/main_arm_v4.s
6 assemble skript pro překlad s využitím gcc (verze pro x86-64) https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/assemble
7 assemble_arm_v1 skript pro překlad s využitím gcc (ARM verze 1) https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/assemble_arm_v1
8 assemble_arm_v2 skript pro překlad s využitím gcc (ARM verze 2) https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/assemble_arm_v2
9 assemble_arm_v3 skript pro překlad s využitím gcc (ARM verze 3) https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/assemble_arm_v3
10 assemble_arm_v4 skript pro překlad s využitím gcc (ARM verze 4) https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/assemble_arm_v4
11 disassemble skript pro disassembling https://github.com/tisnik/presentations/blob/master/assembler/43_stdc_stub/disassemble

Poznámka: skript pro disassembling je shodný pro obě architektury mikroprocesorů.

Druhý demonstrační příklad: volání knihovní funkce puts()

# Soubor Popis Odkaz do repositáře
1 main_64bit.s hlavní program pro procesory x86-64 https://github.com/tisnik/presentations/blob/master/assembler/44_stdc_puts/main_64bit.s
2 main_arm.s hlavní program pro procesory ARM https://github.com/tisnik/presentations/blob/master/assembler/44_stdc_puts/main_arm.s
3 assemble_64bit skript pro překlad s využitím gcc (verze pro x86-64) https://github.com/tisnik/presentations/blob/master/assembler/44_stdc_puts/assemble_64bit
4 assemble_arm skript pro překlad s využitím gcc (verze pro ARM) https://github.com/tisnik/presentations/blob/master/assembler/44_stdc_puts/assemble_arm
5 disassemble skript pro disassembling https://github.com/tisnik/presentations/blob/master/assembler/44_stdc_puts/disassemble

Poznámka: opět zde platí, že skript pro disassembling je shodný pro obě architektury mikroprocesorů.

14. Odkazy na Internetu

  1. ARM Documentation: B, BL, BX, BLX, and BXJ
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204j/Cihfddaf.html
  2. Branch and Call Sequences Explained
    https://community.arm.com/groups/processors/blog/2013/09/25/branch-and-call-sequences-explained
  3. Improving ARM Code Density and Performance
    New Thumb Extensions to the ARM Architecture Richard Phelan
  4. The ARM Processor Architecture
    http://www.arm.com/products/processors/technologies/instruction-set-architectures.php
  5. Thumb-2 instruction set
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0344c/Beiiegaf.html
  6. Introduction to ARM thumb
    http://www.eetimes.com/discussion/other/4024632/Introduction-to-ARM-thumb
  7. ARM, Thumb, and ThumbEE instruction sets
    http://www.keil.com/support/man/docs/armasm/armasm_CEGBEIJB.htm
  8. An Introduction to ARM Assembly Language
    http://dev.emcelettronica.com/introduction-to-arm-assembly-language
  9. Processors - ARM
    http://www.arm.com/products/processors/index.php
  10. The ARM Instruction Set
    http://simplemachines.it/doc/arm_inst.pdf
  11. ARM Architecture (Wikipedia)
    http://en.wikipedia.org/wiki/ARM_architecture
  12. C Functions Without Arguments
    https://eklitzke.org/c-functions-without-arguments
  13. GNU Assembler Examples
    http://cs.lmu.edu/~ray/notes/gasexamples/
  14. Simply FPU
    http://www.website.masmforum.com/tutorials/fptute/
  15. Art of Assembly language programming: The 80x87 Floating Point Coprocessors
    https://courses.engr.illinois.edu/ece390/books/artofasm/CH14/CH14-3.html
  16. Art of Assembly language programming: The FPU Instruction Set
    https://courses.engr.illinois.edu/ece390/books/artofasm/CH14/CH14-4.html
  17. INTEL 80387 PROGRAMMER'S REFERENCE MANUAL
    http://www.ragestorm.net/downloads/387intel.txt
  18. x86 Instruction Set Reference: FLD
    http://x86.renejeschke.de/html/file_module_x86_id_100.html
  19. x86 Instruction Set Reference: FLD1/FLDL2T/FLDL2E/FLDPI/FLDLG2/FLDLN2/FLDZ
    http://x86.renejeschke.de/html/file_module_x86_id_101.html
  20. x86 Instruction Set Reference: FLD
    http://x86.renejeschke.de/html/file_module_x86_id_100.html
  21. x86 Instruction Set Reference: FST/FSTP
    http://x86.renejeschke.de/html/file_module_x86_id_117.html
  22. x86 Instruction Set Reference: BTC
    http://x86.renejeschke.de/html/file_module_x86_id_23.html
  23. x86 Instruction Set Reference: BTR
    http://x86.renejeschke.de/html/file_module_x86_id_24.html
  24. x86 Instruction Set Reference: BTS
    http://x86.renejeschke.de/html/file_module_x86_id_25.html
  25. x86 Instruction Set Reference: BSF
    http://x86.renejeschke.de/html/file_module_x86_id_19.html
  26. x86 Instruction Set Reference: BSR
    http://x86.renejeschke.de/html/file_module_x86_id_20.html
  27. x86 Instruction Set Reference: BSWAP
    http://x86.renejeschke.de/html/file_module_x86_id_21.html
  28. x86 Instruction Set Reference: XCHG
    http://x86.renejeschke.de/html/file_module_x86_id_328.html
  29. x86 Instruction Set Reference: SETcc
    http://x86.renejeschke.de/html/file_module_x86_id_288.html
  30. X86 Assembly/Arithmetic
    https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic
  31. Art of Assembly - Arithmetic Instructions
    http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-2.html
  32. The GNU Assembler Tutorial
    http://tigcc.ticalc.org/doc/gnuasm.html
  33. The GNU Assembler - macros
    http://tigcc.ticalc.org/doc/gnuasm.html#SEC109
  34. ARM subroutines & program stack
    http://www.toves.org/books/armsub/
  35. Generating Mixed Source and Assembly List using GCC
    http://www.systutorials.com/240/generate-a-mixed-source-and-assembly-listing-using-gcc/
  36. Calling subroutines
    http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0100a/armasm_cihcfigg.htm
  37. ARM Assembly Language Programming
    http://peter-cockerell.net/aalp/html/frames.html
  38. ASM Flags
    http://www.cavestory.org/guides/csasm/guide/asm_flags.html
  39. Status Register
    https://en.wikipedia.org/wiki/Status_register
  40. Intel x86 JUMP quick reference
    http://unixwiz.net/techtips/x86-jumps.html
  41. Linux assemblers: A comparison of GAS and NASM
    http://www.ibm.com/developerworks/library/l-gas-nasm/index.html
  42. Programovani v assembleru na OS Linux
    http://www.cs.vsb.cz/grygarek/asm/asmlinux.html
  43. Is it worthwhile to learn x86 assembly language today?
    https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1
  44. Why Learn Assembly Language?
    http://www.codeproject.com/Articles/89460/Why-Learn-Assembly-Language
  45. Is Assembly still relevant?
    http://programmers.stackexchange.com/questions/95836/is-assembly-still-relevant
  46. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/onlamp/2004/05/06/writegreatcode.html
  47. Assembly language today
    http://beust.com/weblog/2004/06/23/assembly-language-today/
  48. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubriky/assembler/vyznam-assembleru-dnes-155960cz
  49. Assembler pod Linuxem
    http://phoenix.inf.upol.cz/linux/prog/asm.html
  50. AT&T Syntax versus Intel Syntax
    https://www.sourceware.org/binutils/docs-2.12/as.info/i386-Syntax.html
  51. Linux Assembly website
    http://asm.sourceforge.net/
  52. Using Assembly Language in Linux
    http://asm.sourceforge.net/articles/linasm.html
  53. vasm
    http://sun.hasenbraten.de/vasm/
  54. vasm – dokumentace
    http://sun.hasenbraten.de/vasm/release/vasm.html
  55. The Yasm Modular Assembler Project
    http://yasm.tortall.net/
  56. 680x0:AsmOne
    http://www.amigacoding.com/index.php/680x0:AsmOne
  57. ASM-One Macro Assembler
    http://en.wikipedia.org/wiki/ASM-One_Macro_Assembler
  58. ASM-One pages
    http://www.theflamearrows.info/documents/asmone.html
  59. Základní informace o ASM-One
    http://www.theflamearrows.info/documents/asminfo.html
  60. Linux Syscall Reference
    http://syscalls.kernelgrok.com/
  61. Programming from the Ground Up Book - Summary
    http://savannah.nongnu.org/projects/pgubook/
  62. IBM System 360/370 Compiler and Historical Documentation
    http://www.edelweb.fr/Simula/
  63. IBM 700/7000 series
    http://en.wikipedia.org/wiki/IBM_700/7000_series
  64. IBM System/360
    http://en.wikipedia.org/wiki/IBM_System/360
  65. IBM System/370
    http://en.wikipedia.org/wiki/IBM_System/370
  66. Mainframe family tree and chronology
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_FT1.html
  67. 704 Data Processing System
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP704.html
  68. 705 Data Processing System
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_PP705.html
  69. The IBM 704
    http://www.columbia.edu/acis/history/704.html
  70. IBM Mainframe album
    http://www-03.ibm.com/ibm/history/exhibits/mainframe/mainframe_album.html
  71. Osmibitové muzeum
    http://osmi.tarbik.com/
  72. Tesla PMI-80
    http://osmi.tarbik.com/cssr/pmi80.html
  73. PMI-80
    http://en.wikipedia.org/wiki/PMI-80
  74. PMI-80
    http://www.old-computers.com/museum/computer.asp?st=1&c=1016
  75. The 6502 overflow flag explained mathematically
    http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html
  76. X86 Opcode and Instruction Reference
    http://ref.x86asm.net/coder32.html