Asembler cz.1 - Wstęp

Opublikowano: 21-03-2017



Oprócz znajomości języków programowania wyższego poziomu (np. C, C++) warto zapoznać się z podstawami Asemblera, by lepiej zrozumieć działanie naszych programów.

Podstawowa znajomość asemblera przyda się podczas analizy zachowania programu, który z niewiadomych przyczyn nie działa poprawnie. Subtelne błędy mogą być spowodowane na przykład: niepoprawnie użytą konstrukcją języka, czy błędem w kompilatorze — choć to drugie rzadko się zdarza.

Ta seria postów o Asemblerze nie będzie kompletnym poradnikiem. Należy je traktować jako przegląd podstawowych zagadnień.

Generowanie kodu Asemblera

Według mnie bardzo dobrą metodą nauki jest analiza kodu wygenerowanego przez kompilator w procesie translacji kodu źródłowego napisanego w języku C do Asemblera. Można w tym celu skorzystać z kompilatora (np. GCC) zainstalowanego na własnym systemie.

int square(int num) {
    return num * num;
}

GCC, do którego będę się odnosił, umożliwia wygenerowanie kodu Asemblera powyższego przykładu za pomocą polecenia:

gcc -O0 -S example_square.c

Po jego wykonaniu otrzymamy plik example_square.s o następującej zawartości:

        .file   "example_square.c"
        .text
        .globl  square
        .type   square, @function
square:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        imull   -4(%rbp), %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   square, .-square
        .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
        .section        .note.GNU-stack,"",@progbits

Oprócz właściwego kodu Asemblera, w powyższym pliku wynikowym znajdują się wskazówki dla kompilatora (wyrażenia zaczynające się od znaku kropki), ale nie czas na to.

Składnia AT&T oraz Intel

GCC wspiera generowanie kodu Asemblera w składni AT&T oraz Intela. Wybór składni, w której kod chcemy otrzymać zależy tylko od naszych preferencji.

Składnia AT&T jest szeroko stosowana w środowisku związanym z systemami Uniksowymi, natomiast składnia Intela rozpowszechniona jest głównie w środowisku związanym z systemami firmy Microsoft.

Nie będę opisywał różnic między nimi, gdyż bardzo dobrze zostało to zrobione na tej stronie w rozdziale "Asembler AT&T kontra składnia Intela". Osobiście preferuję składnię AT&T, dlatego w kolejnych postach będę właśnie ją stosował.

Kod zapisany z użyciem składni Intela również można wygenerować za pomocą GCC. Polecenie, które należy wykonać to:

gcc -O0 -S -masm=intel example_square.c

Poniżej znajduje się przykład kodu źródłowego zapisanego z użyciem składni AT&T (można ją rozpoznać po znakach %) oraz Intela.

square(int):
        pushq   %rbp
        movq    %rsp, %rbp
        movl    %edi, -4(%rbp)
        movl    -4(%rbp), %eax
        imull   -4(%rbp), %eax
        popq    %rbp
        ret
square(int):
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], edi
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, DWORD PTR [rbp-4]
        pop     rbp
        ret

Usługa internetowa

Czasami wygodnie jest skorzystać z usługi dostępnej w Internecie i "wyklikać" odpowiednie opcje, niż wpisywać polecenia w konsoli. Idelnym rozwiązaniem jest Compiler Explorer ‐ C++, który kompiluje kod języka C++ i wyświetla jego odpowiednik w Asemblerze. Powyższe dwa przykłady zostały utworzone właśnie z jego pomocą.

/images/compiler_explorer_cpp.png

Co dalej?

W kolejnym wpisie dokonam analizy przedstawionego przykładu i omówię zasadę działania stosu.

Do zobaczenia!



Pytanie lub komentarz? Zostaw wiadomość!

Powiedz proszę, czy podobał Ci się ten wpis. Chętnie podyskutuję i odpowiem na dodatkowe pytania.

Comments powered by Disqus