GNU/Linux >> Znalost Linux >  >> Linux

Jaký je význam každého řádku výstupu sestavy C hello world?

Tady, jak to jde:

        .file   "test.c"

Původní název zdrojového souboru (používaný ladicími programy).

        .section        .rodata
.LC0:
        .string "Hello world!"

V sekci ".rodata" je zahrnut řetězec zakončený nulou ("ro" znamená "pouze pro čtení":aplikace bude moci číst data, ale jakýkoli pokus o zápis do nich vyvolá výjimku).

        .text

Nyní zapisujeme věci do sekce ".text", kam jde kód.

.globl main
        .type   main, @function
main:

Definujeme funkci nazvanou "hlavní" a globálně viditelnou (jiné soubory objektů ji budou moci vyvolat).

        leal    4(%esp), %ecx

Ukládáme do registru %ecx hodnotu 4+%esp (%esp je ukazatel zásobníku).

        andl    $-16, %esp

%esp je mírně upraven tak, aby byl násobkem 16. Pro některé datové typy (formát s plovoucí desetinnou čárkou odpovídající C's double a long double ), výkon je lepší, když jsou přístupy do paměti na adresách, které jsou násobky 16. Zde to není ve skutečnosti potřeba, ale při použití bez příznaku optimalizace (-O2 ...), kompilátor má tendenci produkovat poměrně hodně generického zbytečného kódu (tj. kódu, který by mohl být v některých případech užitečný, ale ne zde).

        pushl   -4(%ecx)

Tohle je trochu divné:v tom okamžiku slovo na adrese -4(%ecx) je slovo, které bylo na vrcholu zásobníku před andl . Kód toto slovo načte (což by mimochodem měla být zpáteční adresa) a znovu jej vloží. Tento druh emuluje to, co by bylo získáno voláním funkce, která měla 16bajtový zarovnaný zásobník. Můj odhad je, že toto push je pozůstatkem sekvence kopírování argumentů. Protože funkce upravila ukazatel zásobníku, musí zkopírovat argumenty funkce, které byly přístupné přes starou hodnotu ukazatele zásobníku. Zde není žádný argument kromě návratové adresy funkce. Upozorňujeme, že toto slovo nebude použito (opět se jedná o kód bez optimalizace).

        pushl   %ebp
        movl    %esp, %ebp

Toto je standardní prolog funkcí:ušetříme %ebp (protože se to chystáme upravit), pak nastavte %ebp ukazovat na rám zásobníku. Poté %ebp se použije pro přístup k argumentům funkce, takže %esp opět zdarma. (Ano, není tam žádný argument, takže to je pro tuto funkci k ničemu.)

        pushl   %ecx

Ušetříme %ecx (budeme jej potřebovat při ukončení funkce, abychom obnovili %esp na hodnotu, kterou měl před andl ).

        subl    $20, %esp

Na zásobníku vyhradíme 32 bajtů (nezapomeňte, že zásobník roste "dolů"). Tento prostor bude použit k uložení argumentů do printf() (to je přehnané, protože existuje jeden argument, který bude používat 4 bajty [to je ukazatel]).

        movl    $.LC0, (%esp)
        call    printf

Argument "posuneme" na printf() (tj. ujišťujeme se, že %esp ukazuje na slovo, které obsahuje argument, zde $.LC0 , což je adresa konstantního řetězce v sekci rodata). Potom zavoláme printf() .

        addl    $20, %esp

Když printf() vrátí, odstraníme prostor přidělený pro argumenty. Toto addl zruší to, co subl výše ano.

        popl    %ecx

Obnovíme %ecx (stlačeno výše); printf() možná jej upravili (konvence volání popisují, který registr může funkce upravit, aniž by je po ukončení obnovovala; %ecx je jedním takovým registrem).

        popl    %ebp

Epilog funkce:obnoví %ebp (odpovídá pushl %ebp výše).

        leal    -4(%ecx), %esp

Obnovujeme %esp na jeho počáteční hodnotu. Tento operační kód se uloží do %esp hodnotu %ecx-4 . %ecx byl nastaven v prvním operačním kódu funkce. Tím se zruší jakákoliv změna %esp , včetně andl .

        ret

Ukončení funkce.

        .size   main, .-main

Tím se nastaví velikost main() funkce:kdykoli během sestavování, ". " je alias pro "adresu, na kterou právě přidáváme věci". Pokud by sem byla přidána další instrukce, šla by na adresu určenou ". ". Tedy ".-main ", zde je přesná velikost kódu funkce main() . .size direktiva instruuje assembler, aby zapsal tyto informace do objektového souboru.

        .ident  "GCC: (GNU) 4.3.0 20080428 (Red Hat 4.3.0-8)"

GCC prostě ráda zanechává stopy své činnosti. Tento řetězec skončí jako druh komentáře v souboru objektu. Linker jej odstraní.

        .section        .note.GNU-stack,"",@progbits

Speciální sekce, kde GCC píše, že kód může pojmout nespustitelný zásobník. Toto je normální případ. Pro některá speciální použití jsou potřeba spustitelné zásobníky (ne standardní C). Na moderních procesorech může jádro vytvořit nespustitelný zásobník (zásobník, který spustí výjimku, pokud se někdo pokusí spustit jako kód nějaká data, která jsou v zásobníku); Někteří lidé to považují za „bezpečnostní prvek“, protože umístění kódu do zásobníku je běžný způsob, jak využít přetečení vyrovnávací paměti. S touto sekcí bude spustitelný soubor označen jako "kompatibilní s nespustitelným zásobníkem", který jádro jako takový rád poskytne.


    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ecx
    subl    $20, %esp

tyto instrukce se ve vašem programu c nesrovnávají, vždy se provádějí na začátku každé funkce (ale záleží na kompilátoru/platformě)

    movl    $.LC0, (%esp)
    call    printf

tento blok odpovídá vašemu volání printf(). první instrukce umístí na zásobník svůj argument (ukazatel na "ahoj světe") a poté zavolá funkci.

    addl    $20, %esp
    popl    %ecx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret

tyto instrukce jsou opačné k prvnímu bloku, jsou to nějaký druh manipulace se zásobníkem. vždy také proveden


Zde je nějaký doplněk k @Thomas Pornin odpověď uživatele.

  • .LC0 lokální konstanta, např. řetězcový literál.
  • .LFB0 začátek místní funkce,
  • .LFE0 ukončení místní funkce,

Přípona těchto štítků je číslo a začíná od 0.

Toto je konvence assembleru gcc.


Linux
  1. Jak vložit text na začátek každého řádku ve Vimu

  2. Iterování přes každý řádek výstupu ls -l

  3. Jaký je význam fork() a grep v Linuxu?

  1. Jaký je druhý stav v ip odkaz zobrazit výstup

  2. Jaký je význam curl -k -i -X ​​v Linuxu?

  3. Co je nástroj příkazového řádku reverzního DNS?

  1. Jaké jsou odpovědnosti jednotlivých komponent pseudoterminálu (pty) (software, hlavní strana, vedlejší strana)?

  2. Jak zjistit, jakou verzi OS X používám z příkazového řádku?

  3. Počítání znaků každého řádku s Wc?