Z pogranicza

Asembler ATamp;T i wstawki w GCC

  • 5 komentarzy
  • 962 odsłony
  • Oceń ten tekst jako pierwszy
Na wstępie zaznaczam, iż nie jest to kurs asemblera a jedynie
porównanie dwóch jego "wersji". Większość z was zapewne - o ile
miała okazję poznać asemblera - jest przyzwyczajona do notacji
Intela używanej przez takie kompilatory jak NASM, TASM czy
MASM (czyli większość działajacych pod windowsem). Natomiast
kompilator GNU używa składni AT&T, także wstawki w kodzie
pod GCC pisze sie w tejże składni.

Główne różnice


Zaczne od rzeczy, która zapewne będzie się wam ciągle mylić,
podam przykład:

    mov eax , ebx


Zawartość rejestru ebx zostanie przeniesiona do rejestru eax.
W asemblerze AT&T jest odwrotnie, najpierw podajemy źródło a
potem cel. Teraz "prefixy" - w skladni Intela nie ma czegoś
takiego jak prefix, natomiast w AT&T dodajemy przed nazwą
rejestru %, przed nazwą zmiennej lub stałej wartości $. Następną
ważną rzeczą są tzw. sufiksy. Jest to specyfikacja wielkości
danych którą przetważamy. Żeby to wyjaśnić podam przykład:

Intel:
    mov eax , ebx
    mov ax  , bx
    mov al  , bl

AT&T:
    movl %ebx , %eax
    movw %bx  , %ax
    movb %bl  , %al


Jak widać dla operacji na liczbach 32 bitowych używamy sufiksu
l, dla liczb 16 bitowych - w, dla liczb 8 bitowych - b. Teraz
adresowanie pamięci :-). Intelowy "prototyp" adresowania wygląda
tak:

    [wskaźnik bazowy + wskaźnik do indeksu*skala indeksowania + adres]


w AT&T wygląda to troche inaczej:

    adres(wskaźnik bazowy , wskaźnik do indeksu , skala indeksowania)


Do deklaracji zmiennych nie używamy tak jak w składni intela db,
dw, dd, tutaj korzystamy odpowiednio ze słów .byte , .word , .long
dla danych 8, 16 i 32 - bitowych.

Przykład


Oczywiście bez przykładu sie nie obejdzie, pokaże tu prosty program, który
można skompilować na DOSowym pakiecie DJGPP (port narzędzi GNU):

# example.s
 
.section .text
.p2align 1
.globl _main
_main:
    xorl %ecx , %ecx  
    movl $tablica , %eax   
 
  loop:
    movb 0(%eax,%ecx,1)  , %bl  
    cmpb %cl , %bl
    incl %ecx
    je   loop
 
    leave
    ret
 
.section .data
 
tablica: 
    .byte 0,1,2,3,4,5,6,7,8,9,0
 
# gcc example.s -o example.exe


Program będzie kopiował kolejne bajty "tablicy" do rejestru BL i porównywał
wartość z wartością rejetru CL, więc dopóki elementy "tablicy" będą rosły pętla
będzie powtarzana, ostatnie 0 przerwie pętle.

Ogólnie o wstawkach


Czasami potrzebujemy wstawić do naszego programu w C/C++ kod asemblera, w kompilatorze
GNU nie skorzystawmy z tradycyjnej składi, musimy wykorzystać (poznaną wcześniej :)) AT&T. Aby wstawić pojedynczą
instrukcje piszemy np.:

    asm("xorl %eax , %eax");


Aby wstawić kilka instrukcji robimy troche inaczej:

    asm("xorl %eax , %eax\n\t"
        "xorl %ebx , %ebx\n\t"
        "xorl %ecx , %ecx\n\t");


Do czego nam te \n i \t? Dlatego, że "string" w funkcji asm będzie wstawiony
bezpośrednio do kodu programu, możemy to sprawdzić generując kod asemblera
zamiast kompilować programu do postaci wykonywalnej, weźmy na przykład:

// example2.c
 
int main()
{
    asm("\txorl %eax , %eax\n\t\t"
        "xorl %ebx , %ebx\n" );
    return 0;
}
 
// gcc -S example2.c


Powinien powstać plik example2.s, możecie zauważyć, że dwie linijki

    xorl %eax , %eax
    xorl %ebx , %ebx


będą bardziej wysunięte od innych, dzieki temu, że dodaliśmy dwa razy znak
\t , oczywiście nie musimy przesadzać, ten przykład był aby nam pokazac
jak kod jest wstawiany :), \n jest raczej wymagane ;) natomiast \t możemy pominąć.

Przykład


Teraz przykład użycia asemblera w kodzie C:

// example3.c
 
#include <stdio.h>
 
char tab[] = "ABCD";
 
int main()
{
    printf("%s -> ", tab );
    asm(" movl $_tab , %eax \n\t" 
        " xorl %ecx , %ecx\n\t"
        "loop1:\n\t "
        " cmpb $0 , 0(%eax,%ecx,1)\n\t"
        " je   end1\n\t"
        " incb 0(%eax,%ecx,1)\n\t"
        " incl %ecx\n\t"
        " jmp  loop1\n\t"
        "end1:\n\t" );
 
    printf("%s \n" , tab );
    return 0;
} 
 
// gcc example3.c


Program najpierw wyświetla tablice tab potem (wstawka asemblera) zwiększa
każdy jej element o 1 i wyświetla tablice jeszcze raz. Należy zauważyć, że
w kodzie asemblera użyłem _tab a nie tab, zrobiłem to dlatego, iż kompilator C sam
dodaje _ przed nazwami zmiennych, należy o tym pamiętać! Warto też zrobić
gcc -S example3.c :).

Zakończenie


Za jakiś czas może dodam coś jeszcze... Narazie mam ogromny brak czasu nawet na
myślenie :D.  Ale mam nadzieję, że artykuł się spodobał, jeżeli masz jakieś pytania
lub znalazłeś błąd w artykule to koniecznie daj mi znać ( [email protected] )!

5 komentarzy

ic3 2005-01-05 13:54

tez sie nad tym zastanawialem, dzieki za pomysl, postaram sie go urzeczywistnic. :) tabelki rulez :D

AklimX 2005-01-04 14:00

świetne! możnaby jeszcze zrobić tabelkę z porównaniem obu składni - jak to psory mówią: "tabelka najlepszym przyjacielem ucznia" :D

ic3 2005-01-03 23:08

Dzieki Wolverine :). Jest to moj pierwszy art wiec wszystkie uwagi mile widziane ! W koncu czlowiek uczy sie na bledach... :)

Wolverine 2005-01-03 13:53

ode mnie 6 :) fajny art

Qyon 2005-01-02 22:25

Popraw błędy ortograficzne!!!