PROGRAMOWANIE NISKOPOZIOMOWE PN.04 c Dr inż. Ignacy Pardyka UNIWERSYTET JANA KOCHANOWSKIEGO w Kielcach Rok akad. 2011/2012 1 2 3 Ćwiczenia laboratoryjne c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 1 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 2 / 27 rejestrowe adres operandu może być w rejestrach: EAX, EBX, ECX, EDX, EBP, ESI, EDI pobieranie słowa z pamięci (16-bit) mov ebx, a; ebx = &a mov ax, [ebx]; ax = *ebx pobieranie bajtu z pamięci mov ebx, a; ebx = &a mov al, [ebx]; ax = *ebx adresowanie bezpośrednie jest prostsze, np: mov ax, [a]; SS rejestr zawierający adres segmentu, w którym jest stos ESP rejestr zawierający adres danych na stosie (do zdjęcia przez POP), czyli adres wierzchołka stosu (ang. stack pointer) PUSH odkłada na stos podwójne słowo (4 bajty), uprzednio zmniejszając o 4 adres wierzchołka stosu ESP 4 ESP, dword [ESP] np. push dword 1, push eax, push dword [x] POP pobiera ze stosu podwójne słowo (4 bajty), a następnie zwiększa o 4 adres wierzchołka stosu EAX [ESP], ESP+4 ESP np. pop ecx PUSHA, POPA rejestry: EAX, EBX, ECX, EDX, ESI, EDI, EBP c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 3 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 4 / 27
Instrukcje CALL, RET Konwencje wywołań (ang. calling conventions) Sekwencja czynności procesora dla CALL: 1 adres następnej instrukcji (zawartość EIP) odkłada na stos 2 do rejestru EIP wpisuje adres podprogramu Realizacja instrukcji RET: EIP [ESP], ESP+4 ESP ( = POP EIP) np. mov ebx, inp1; adres danych z segmentu.bss call get_int; pobrane dane trafią pod właściwy adres ;... get_int: call read_int mov [ebx], eax Sekwencja CALL, RET muszą być zdolne do wywoływania samych siebie Parametry (dane) do podprogramu przekazywane za pośrednictwem stosu: przez wartość (ang. by value) jeśli parametr ma nie być zmieniany przez referencję (ang. by reference), tj. przez adres, jeśli parametr ma być zmieniany Przed umieszczeniem na stosie (PUSH), w miarę potrzeby, dane poddać konwersji do dword Uwaga: parametry nie są zdejmowane ze stosu przez podprogram, lecz jedynie czytane! W podprogramie dostęp do parametrów przez adresowanie pośrednie, np. [ESP+4], ale lepiej za pomocą rejestru EBP. c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 5 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 6 / 27 Wywołanie podprogramu w konwencji języka C Program wywołujący w konwencji języka C Prolog podprogramu zawiera: sub_prog: push ebp ; zapamiętać EBP używany przez program wywołujący mov ebp,esp ; teraz ESP można używać w podprogramie Stan stosu na tym etapie: [EBP+4 ] adres powrotny z podprogramu (odłożony na stosie przez instrukcję CALL) [EBP+8 ] pierwszy parametr z listy parametrów podprogramu Parametry ze stosu usuwa program wywołujący, a nie podprogram! Epilog podprogramu: pop ebp ; rekonstrukcja EBP programu wywołującego Przed wywołaniem podprogramu program wywołujący odkłada parametry na stos (najpierw ostatni) Za pomocą instrukcji CALL wywołuje podprogram Po powrocie z podprogramu zdejmuje parametry ze stosu push dword 1; parametr przekazany przez wartość call sub_prog add esp,4 ; był tylko jeden parametr c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 7 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 8 / 27
Zmienne statyczne programu (globalne) przechowywane w segmentach.data lub.bss Zmienne lokalne podprogramu przechowywane na stosie Dzięki temu podprogram może wywołać sam siebie w dowolnym momencie Wywołanie podprogramu skutkuje utworzeniem na stosie tzw. ramki stosu (ang. stack frame), wskazywanej przez EBP. Ramka stosu to obszar zawierający parametry, adres powrotny i zmienne lokalne. Podprogram w notacji języka C void calc_sum(int n, int *sump){ int i, s = 0; for (i = 1; i <= n; i++) s += i; *sump = s;} może mieć taki prolog w asemblerze: calc_sum: push ebp mov ebp, esp sub esp,4 ; liczba bajtów dla zmiennych lokalnych c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 9 / 27 wygodniej użyć instrukcji ENTER 4,0 rezerwującej na stosie 4 bajty dla zmiennej lokalnej s (druga będzie w rejestrze EBX), a 0 oznacza konwencję C. c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 10 / 27 cd. cd. Zmienne lokalne na stosie są standardowo zerowane mov dword [ebp-4],0 ; indeks pętli w rejestrze EBX mov ebx,1 pętlę for przetłumaczymy następująco: for_loop: cmp ebx,[ebp+8] ; i <= n? jnle end_for add [ebp-4], ebx ; s += i inc ebx ; i++ jmp short for_loop end_for: mov ebx, [ebp+12] ; ebx = sump mov eax, [ebp-4] ; eax = s mov [ebx], eax ; *sump = s Epilog podprogramu mov esp,ebp pop ebp można zastąpić instrukcjami leave c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 11 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 12 / 27
Program wielomodułowy Program złożony z wielu plików obiektowych (ang. object code ) rezultaty kompilacji C (np. driver.o) rezultaty asemblacji (np. sub3.o) biblioteki Moduły są konsolidowane przez konsolidator (ang. linker) referencje do etykiet zdefiniowanych w innych modułach muszą być poprawne i możliwe do odnalezienia w czasie konsolidacji dyrektywa extern informuje, że definicja poza modułem bieżącym, np. extern get_int, print_sum dyrektywa global informuje, że dopuszczalne odwołanie z innego modułu, np. global _asm_main Przykład konsolidacji (linker w gcc): > nasm -f coff sub4.asm > nasm -f coff main4.asm > gcc -o sub4.exe sub4.o main4.o driver.c asm_io.o 1 2 3 Ćwiczenia laboratoryjne c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 13 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 14 / 27 Program w asemblerze, czy w C? Nazwy funkcji w asemblerze Cały program w asemblerza b. rzadko. Efektywniej tworzyć program w języku wysokiego poziomu. Małe podprogramy w asemblerze wywoływane z poziomu języka C gdy bezpośredni dostęp do sprzętu trudny lub niemożliwy z poziomu C gdy programista potrafi pisać efektywniej niż kompilator (mimo optymalizacji na żądanie). Kompilator C zakłada, że podprogram nie naruszy stanu następujących rejestrów sprzed wywołania: EBX, ESI, EDI, EBP, CS, DS, SS, ES. EBX, ESI, EDI używane przez C na potrzeby zmiennych typu register Większość kompilatorów (w tym gcc) poprzedza nazwy funkcji i zmiennych globalnych (statycznych) prefiksem _. Dlatego, w takich przypadkach, podprogramy w asemblerze (przeznaczone do łączenia z C) muszą używać nazw rozpoczynających się od prefiksu _, np. _printf W konwencji języka C argumenty dla wywoływanej funkcji kładzie się na stosie w kolejności od ostatniego do pierwszego. Przykładowo: printf("x=%d\n",x); tłumaczony jest tak: segment.data x dd 0 format db "x=%d\n",0 segment.text push dword [x] push dword format ; adres formatu ma być dostępny pod [EBP+8] call _printf add esp,8 ; usuwanie parametrów ze stosu c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 15 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 16 / 27
Zmienne lokalne i wartość funkcji Ustalanie konwencji wywołania funkcji Pobieranie adresu zmiennej lokalnej (np. gdy trzeba wywołać inną funkcję z parametrem przekazywanym przez referencję): lea eax,[ebp-8] Zwracanie wartości funkcji poprzez rejestry: EAX dla typów: char, int, enum,..., pointer wartości rozszerzone do 32-bit. EDX:EAX dla typów 64-bit. ST0 dla typów float, double (coprocessor!) Kompilator gcc pozwala deklarować konwencję za pomocą attribute, np. void f(int) attribute ((cdecl)); cdecl - konwencja C stdcall - konwencja Pascal regparm - trzy początkowe parametry przez rejestry Kompilator Microsoft void cdecl f(int); c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 17 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 18 / 27 Program główny w C (main5.c) wielokrotnego wejścia > gcc -o sub5 main5.c sub5.o asm_io.o Funkcja calc_sum zdefiniowana w pliku sub5.asm #include <stdio.h> #include "cdecl.h" void calc_sum( int, int * ) attribute ((cdecl)); int main( void ){ int n, sum; printf("suma liczb do: "); scanf("%d", &n); calc_sum(n, &sum); printf("suma = %d\n", sum); urn 0; } c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 19 / 27 reentrant wielokrotnego wejścia nie mogą modyfikować własnego kodu nie mogą modyfikować danych globalnych (.data,.bss) wszystkie zmienne na stosie zalety dopuszczalne wywołanie rekurencyjne dopuszczalne współużytkowanie współbieżne używane w natywnych.dll pojedynczy egzemplarz kodu w pamięci efektywna wielowątkowość c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 20 / 27
rekurencyjne Przykład funkcji rekurencyjnej (fact.asm) recursive mogą wywoływać same siebie bezpośrednio pośrednio warunek stopu warunek kończący wywołania warunek stopu jest konieczny gdy spełniony to koniec łańcucha wywołań c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 21 / 27 segment.text global _fact _fact: enter 0,0 mov eax,[ebp+8] ; EAX = n cmp eax,1 jbe jeden ; n<=1 dec eax; n-- push eax call _fact pop ecx mul dword [ebp+8]; edx:eax = eax*[ebp+8] jmp short end_fact jeden: mov eax,1 end_fact: leave c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 22 / 27 w C Zmienne automatic, register i volatile global zmienna globalna definicja poza funkcją umieszczane w segmentach.data lub.bss dostępne z każdej funkcji programu gdy static to dostępne tylko dla funkcji danego modułu static zmienna statyczna lokalne zmienne statyczne umieszczane w segmentach.data lub.bss dostępne tylko w ramach danej funkcji automatic zmienne automatyczne domyślny typ zmiennych w funkcji umieszczane na stosie register zmienna rejestrowa żądanie umieszczenia danych w rejestrze nie będzie spełnione, gdy występuje odwołanie do adresu zmiennej volatile zmienne ulotne wartość zmiennej może być zmieniana przez inny wątek x = 10; y = 20; x może być zmienione w innym wątku z = x + y; z =? nie mogą być przechowywane w rejestrach c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 23 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 24 / 27
Ćwiczenia laboratoryjne Ćwiczenia laboratoryjne 1 2 3 Ćwiczenia laboratoryjne Napisać program wielomodułowy (moduł z funkcją main() w C, poniższe funkcje w modułach asemblera), taki że: funkcje napisane w asemblerze wykonują wskazane operacje na danych typu całkowitego. Wejście/wyjście realizuje moduł w języku C. 1 silnia z modułu a k 2 operacja AND(a k 1, a k ) 3 operacja OR(a k 1, a k ) 4 operacja XOR(a k 1, a k ) 5 operacja NOT (a k ) 6 reszta z dzielenia a k przez 2 k 7 funkcja zwraca liczbę a k z ustawionym bitem k 8 funkcja zwraca liczbę a k po rotacji o k pozycji w lewo Przeanalizować stan ramki stosu (użyć funkcji dump stack). c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 25 / 27 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 26 / 27 Ćwiczenia laboratoryjne A. S. Tanenbaum, Strukturalna organizacja systemów komputerowych, Helion, 2006. J. Biernat, Architektura komputerów, OWPW, 2005. R. Hyde, Profesjonalne programowanie, Helion, 2005. R. Hyde, Asembler. Sztuka programowania, Helion, 2004. G. Mazur, Programowanie niskopoziomowe, http://wazniak.mimuw.edu.pl. P.A. Carter, PC Assembly Language, http://www.drpaulcarter.com/pcasm/. D.W. Lewis, Między asemblerem a językiem C. Podstawy oprogramowania wbudowanego, RM, 2004. c Dr inż. Ignacy Pardyka (Inf.UJK) PN.04 Rok akad. 2011/2012 27 / 27