PROGRAMOWANIE NISKOPOZIOMOWE PN.06 c Dr inż. Ignacy Pardyka UNIWERSYTET JANA KOCHANOWSKIEGO w Kielcach Rok akad. 2011/2012 c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 1 / 22
1 Asembler i C++ Struktury w C Konsekwencje przeciążania w C++ Referencje w C++ Funkcje inline w C++ Klasy w C++ Dziedziczenie Polimorfizm 2 Ćwiczenia laboratoryjne Zadania Literatura c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 2 / 22
Struktury w C Asembler i C++ Struktury w C służą do grupowania danych w ramach jednej zmiennej złożonej zwiększanie przejrzystości kodu łatwiejsze przekazywanie danych do funkcji poprawa lokalności kodu c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 3 / 22
Struktury w C Struktury w C służą do grupowania danych w ramach jednej zmiennej złożonej zwiększanie przejrzystości kodu łatwiejsze przekazywanie danych do funkcji poprawa lokalności kodu implementacja w asemblerze jako tablice o zmiennych rozmiarach elementy o różnych rozmiarach identyfikacja elementu przez nazwę (lub znacznik) adres startowy struktury offset elementu (wyznacza kompilator) c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 3 / 22
Struktury w C Struktury w C służą do grupowania danych w ramach jednej zmiennej złożonej zwiększanie przejrzystości kodu łatwiejsze przekazywanie danych do funkcji poprawa lokalności kodu implementacja w asemblerze jako tablice o zmiennych rozmiarach elementy o różnych rozmiarach identyfikacja elementu przez nazwę (lub znacznik) adres startowy struktury offset elementu (wyznacza kompilator) do funkcji przekazywać można przez wartość (kopia na stos!) przez referencję (wskaźnik do struktury) c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 3 / 22
Przykład struktury Asembler i C++ Struktury w C przykładowa struktura w języku C struct S { short int x; // 2 bajty int y; // 4 bajty double z; // 8 bajtów }; c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 4 / 22
Przykład struktury Asembler i C++ Struktury w C przykładowa struktura w języku C struct S { short int x; // 2 bajty int y; // 4 bajty double z; // 8 bajtów }; funkcja z parametrem wskazującym na strukturę S ; void zero_y(s * sp); %define y_offset 4 _zero_y: enter 0,0 mov eax,[ebp+8]; EAX = sp mov dword [eax+y_offset],0 ; sp->y = 0 leave ret c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 4 / 22
Zmienne w strukturze Asembler i C++ Struktury w C makro (w stddef.h) zwracające offset elementu, np. offsetof(s,y) c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 5 / 22
Zmienne w strukturze Asembler i C++ Struktury w C makro (w stddef.h) zwracające offset elementu, np. offsetof(s,y) kompilator gcc układa zmienne w strukturze domyślnie na granicy podwójnego słowa można zdefiniować typ zmiennych umieszczanych na granicy bajtu (1), słowa (2) albo podwójnego słowa (4) jako parametr dla alligned typedef short int unaligned_int attribute ((alligned(1))); c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 5 / 22
Struktury w C Zmienne w strukturze makro (w stddef.h) zwracające offset elementu, np. offsetof(s,y) kompilator gcc układa zmienne w strukturze domyślnie na granicy podwójnego słowa można zdefiniować typ zmiennych umieszczanych na granicy bajtu (1), słowa (2) albo podwójnego słowa (4) jako parametr dla alligned typedef short int unaligned_int attribute ((alligned(1))); strukturę można pakować na minimum bajtów: struct S {... } attribute ((packed)); c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 5 / 22
Struktury w C Pola bitowe struktury w strukturze C można definiować pola bitowe, np. struct S { unsigned f1: 3; // 3 LSB unsigned f2:10; unsigned f3:11; unsigned f4: 8; // 8 MSB }; c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 6 / 22
Struktury w C Pola bitowe struktury w strukturze C można definiować pola bitowe, np. struct S { unsigned f1: 3; // 3 LSB unsigned f2:10; unsigned f3:11; unsigned f4: 8; // 8 MSB }; zmienna typu S (32-bitowa) będzie udostępniać pola bitowe, na których można wykonywać operacje c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 6 / 22
Struktury w C Pola bitowe struktury w strukturze C można definiować pola bitowe, np. struct S { unsigned f1: 3; // 3 LSB unsigned f2:10; unsigned f3:11; unsigned f4: 8; // 8 MSB }; zmienna typu S (32-bitowa) będzie udostępniać pola bitowe, na których można wykonywać operacje uwaga na układ bajtów zmiennej wielobajtowej w pamięci: big endian, little endian c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 6 / 22
Struktury w C Pola bitowe struktury w strukturze C można definiować pola bitowe, np. struct S { unsigned f1: 3; // 3 LSB unsigned f2:10; unsigned f3:11; unsigned f4: 8; // 8 MSB }; zmienna typu S (32-bitowa) będzie udostępniać pola bitowe, na których można wykonywać operacje uwaga na układ bajtów zmiennej wielobajtowej w pamięci: big endian, little endian częste zastosowanie: dane dla kontrolerów urządzeń c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 6 / 22
Konsekwencje przeciążania w C++ Przeciążanie funkcji w C++ funkcje w C++ mogą być przeciążone (overloaded): ta sama nazwa do identyfikacji różnych funkcji #include <stdio.h> void f(int x){ printf("%d\n",x);} void f(double x){ printf("%g\n",x);} c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 7 / 22
Konsekwencje przeciążania w C++ Przeciążanie funkcji w C++ funkcje w C++ mogą być przeciążone (overloaded): ta sama nazwa do identyfikacji różnych funkcji #include <stdio.h> void f(int x){ printf("%d\n",x);} void f(double x){ printf("%g\n",x);} DJGPP modyfikuje nazwy dodając przyrostek F z kodem typu argumentu: dla integer: _f Fi dla double : _f Fd c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 7 / 22
Konsekwencje przeciążania w C++ Jak kompilator C++ modyfikuje nazwy modyfikacja nazw (ang. mangling) gdy więcej argumentów funkcji, np. void f(int i, int y, double z) typy argumentów kodowane kolejno _f Fiid c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 8 / 22
Konsekwencje przeciążania w C++ Jak kompilator C++ modyfikuje nazwy modyfikacja nazw (ang. mangling) gdy więcej argumentów funkcji, np. void f(int i, int y, double z) typy argumentów kodowane kolejno _f Fiid identycznie modyfikowane są nazwy zmiennych globalnych c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 8 / 22
Konsekwencje przeciążania w C++ Jak kompilator C++ modyfikuje nazwy modyfikacja nazw (ang. mangling) gdy więcej argumentów funkcji, np. void f(int i, int y, double z) typy argumentów kodowane kolejno _f Fiid identycznie modyfikowane są nazwy zmiennych globalnych a także funkcje standardowe, np. prototyp int printf(const char *,...); c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 8 / 22
Konsekwencje przeciążania w C++ Jak kompilator C++ modyfikuje nazwy modyfikacja nazw (ang. mangling) gdy więcej argumentów funkcji, np. void f(int i, int y, double z) typy argumentów kodowane kolejno _f Fiid identycznie modyfikowane są nazwy zmiennych globalnych a także funkcje standardowe, np. prototyp int printf(const char *,...); po kompilacji nazwa funkcji: _printf FPCce c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 8 / 22
Konsekwencje przeciążania w C++ Jak kompilator C++ modyfikuje nazwy modyfikacja nazw (ang. mangling) gdy więcej argumentów funkcji, np. void f(int i, int y, double z) typy argumentów kodowane kolejno _f Fiid identycznie modyfikowane są nazwy zmiennych globalnych a także funkcje standardowe, np. prototyp int printf(const char *,...); po kompilacji nazwa funkcji: _printf FPCce gdzie kolejno: F funkcja, P pointer, C const, c character, e ellipsis c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 8 / 22
Konsekwencje przeciążania w C++ Jak kompilator C++ modyfikuje nazwy modyfikacja nazw (ang. mangling) gdy więcej argumentów funkcji, np. void f(int i, int y, double z) typy argumentów kodowane kolejno _f Fiid identycznie modyfikowane są nazwy zmiennych globalnych a także funkcje standardowe, np. prototyp int printf(const char *,...); po kompilacji nazwa funkcji: _printf FPCce gdzie kolejno: F funkcja, P pointer, C const, c character, e ellipsis gdy trzeba wołać funkcje z biblioteki C, to wskazać kompilatorowi: extern "C" int printf(const char *,...); c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 8 / 22
Referencje w C++ Asembler i C++ Referencje w C++ referencje służą do przekazywania parametrów do funkcji bez konieczności używania wskaźników explicite, np. void f(int &x){ x++;} int main(){ int y = 5; f(y); // kompilator użyje adresu y printf("%d\n", y); return 0; } c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 9 / 22
Referencje w C++ Asembler i C++ Referencje w C++ referencje służą do przekazywania parametrów do funkcji bez konieczności używania wskaźników explicite, np. void f(int &x){ x++;} int main(){ int y = 5; f(y); // kompilator użyje adresu y printf("%d\n", y); return 0; } odpowiednią funkcję f piszemy w asemblerze tak, jakby prototyp był: void f(int * xp); c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 9 / 22
Funkcje inline w C++ Asembler i C++ Funkcje inline w C++ inline eliminuje konieczność wywoływania funkcji realizującej prosty kod, np. inline int g(int x){ return x*x;} int main(){ int y, x = 5; y = g(x); printf("%d\n", y); return 0; } c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 10 / 22
Funkcje inline w C++ Asembler i C++ Funkcje inline w C++ inline eliminuje konieczność wywoływania funkcji realizującej prosty kod, np. inline int g(int x){ return x*x;} int main(){ int y, x = 5; y = g(x); printf("%d\n", y); return 0; } y = g(x) zostanie skompilowane bez korzystania z instrukcji call mov eax,[ebp-8] ; imul eax,eax mov [ebp-4],eax ; x y c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 10 / 22
Klasy w C++ Asembler i C++ Klasy w C++ opisują typ obiektu c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 11 / 22
Klasy w C++ Asembler i C++ Klasy w C++ opisują typ obiektu klasa jest specjalną strukturą zawierającą składniki dane metody (funkcje składowe) przechowywane w pamięci poza strukturą class Simple{ public: Simple(); // konstruktor obiektów ~Simple(); // destruktor int get_data() const; void set_data(int); private: int data; } c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 11 / 22
Klasy w C++ Asembler i C++ Klasy w C++ opisują typ obiektu klasa jest specjalną strukturą zawierającą składniki dane metody (funkcje składowe) przechowywane w pamięci poza strukturą class Simple{ public: Simple(); // konstruktor obiektów ~Simple(); // destruktor int get_data() const; void set_data(int); private: int data; } kompilator generuje kod w asemblerze służący do przekazania funkcji składowej niejawnego parametru wskaźnika this do obiektu danej klasy (tj. tego, na którym metoda ma operować) c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 11 / 22
Funkcje składowe klasy Simple Klasy w C++ Simple::Simple(){ data = 0;} Simple::~Simple(){} int Simple::get_data() const { return data; } void Simple::set_data(int x) { data = x; } c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 12 / 22
Klasy w C++ Kod asemblera dla funkcji składowej funkcja składowa set_data(int x) w konwencji NASM _set_data 6Simplei: push ebp mov ebp, esp mov eax, [ebp+8]; EAX = this mov edx, [ebp+12]; EDX = x mov [eax], edx ; offset dla data jest zero leave ret offset = 0, bo tylko data w strukturze klasy Simple jako ostatni parametr wywołania tej funkcji na stos odkładany musi być adres obiektu, tj. this tak jakby prototyp w C był: void set_data(simple * this, int x); c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 13 / 22
Dziedziczenie B od A Asembler i C++ Dziedziczenie #include <cstddef> #include <iostream> using namespace std; class A { public: void cdecl m() { // Microsoft this przekazałby przez ECX cout << "A::m()" << endl;} int ad; }; class B : public A { public: void cdecl m() { cout << "B::m()" << endl;} int bd; }; c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 14 / 22
Dziedziczenie Funkcja otrzymująca wskaźnik do obiektu funkcja f void f(a *p) { p->ad = 5; p->m();} c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 15 / 22
Dziedziczenie Funkcja otrzymująca wskaźnik do obiektu funkcja f void f(a *p) { p->ad = 5; p->m();} w asemblerze zawiera sztywne wywołanie metody A::m() _f FP1A: push ebp mov ebp, esp mov eax, [ebp+8]; adres obiektu mov dword [eax], 5; mov eax, [ebp+8]; push eax ; adres obiektu na stos call _m 1A ; "sztywne" wywołanie A::m() add esp, 4 leave ret c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 15 / 22
Polimorfizm Asembler i C++ Polimorfizm aby funkcja f wywołała metodę B::m() gdy parametrem f jest wskaźnik do obiektu klasy B, trzeba wskazać kompilatorowi C++ by tworzył kod asemblera dla metod wirtualnych, np. #include <cstddef> #include <iostream> using namespace std; class A { public: virtual void cdecl m() { cout << "A::m()" << endl;} int ad; }; class B : public A { public: virtual void cdecl m() { cout << "B::m()" << endl;} int bd; }; c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 16 / 22
Polimorfizm Asembler i C++ Polimorfizm kompilator dla każdej klasy tworzy tablicę vtable, w której umieszcza adresy metod wirtualnych c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 17 / 22
Polimorfizm Polimorfizm kompilator dla każdej klasy tworzy tablicę vtable, w której umieszcza adresy metod wirtualnych jeśli metoda virtualna jest dostępna w B na zasadzie dziedziczenia z A, to w tablicy wirtualnej dla obiektów klasy B umieszczany jest adres metody zdefiniowanej w A c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 17 / 22
Polimorfizm Polimorfizm kompilator dla każdej klasy tworzy tablicę vtable, w której umieszcza adresy metod wirtualnych jeśli metoda virtualna jest dostępna w B na zasadzie dziedziczenia z A, to w tablicy wirtualnej dla obiektów klasy B umieszczany jest adres metody zdefiniowanej w A kompilator Windows, np. g++ umieszcza adres tablicy vtable w dodatkowym polu na początku struktury obiektu danej klasy (offset = 0) c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 17 / 22
Polimorfizm Polimorfizm kompilator dla każdej klasy tworzy tablicę vtable, w której umieszcza adresy metod wirtualnych jeśli metoda virtualna jest dostępna w B na zasadzie dziedziczenia z A, to w tablicy wirtualnej dla obiektów klasy B umieszczany jest adres metody zdefiniowanej w A kompilator Windows, np. g++ umieszcza adres tablicy vtable w dodatkowym polu na początku struktury obiektu danej klasy (offset = 0) właściwa metoda wywoływana za pomocą instrukcji call z adresem metody wziętym z tablicy vtable c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 17 / 22
Polimorfizm Polimorfizm kompilator dla każdej klasy tworzy tablicę vtable, w której umieszcza adresy metod wirtualnych jeśli metoda virtualna jest dostępna w B na zasadzie dziedziczenia z A, to w tablicy wirtualnej dla obiektów klasy B umieszczany jest adres metody zdefiniowanej w A kompilator Windows, np. g++ umieszcza adres tablicy vtable w dodatkowym polu na początku struktury obiektu danej klasy (offset = 0) właściwa metoda wywoływana za pomocą instrukcji call z adresem metody wziętym z tablicy vtable jest to przykład późnego wiązania (ang. late binding) c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 17 / 22
Polimorfizm Kod asemblera dla funkcji f wywołującej metody wirtualne?f@@yaxpava@@@z: push ebp mov ebp, esp mov eax, [ebp+8] mov dword [eax+4], 5 ; p->ad = 5; mov ecx, [ebp+8] ; ecx = p mov edx, [ecx] ; adres vtable pod offset = 0 obiektu push eax ; push "this" pointer call dword [edx] ; jest tylko jedna metoda wirtualna add esp, 4 pop ebp ret c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 18 / 22
Polimorfizm Budowanie programu z modułów C++ i asemblera > nasm -f coff big_math.asm > g++ -c big_int.cpp > g++ -c test_big_int.cpp > g++ -o test_big_int test_big_int.o big_int.o big_math.o c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 19 / 22
Ćwiczenia laboratoryjne 1 Asembler i C++ Struktury w C Konsekwencje przeciążania w C++ Referencje w C++ Funkcje inline w C++ Klasy w C++ Dziedziczenie Polimorfizm 2 Ćwiczenia laboratoryjne Zadania Literatura c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 20 / 22
Zadania Ćwiczenia laboratoryjne Zadania 1 Napisać obiektowy program wielomodułowy (moduł z funkcją main() w C++, poniższe funkcje składowe klas w modułach asemblera), taki że: funkcje napisane w asemblerze wykonują wskazane operacje bitowe na obiektach zawierających pole danych w postaci dwuwymiarowej tablicy danych typu całkowitego. Deklaracje klas i wejście/wyjście realizuje moduł w języku C++. operacja AND(A, B) operacja OR(A, B) operacja XOR(A, B) operacja NOT (A) operacja NAND(A, B) 2 Napisać program (funkcje w asemblerze), który będzie kodował wprowadzany ze standardowego wejścia łańcuch znaków ASCII w tablicy 64 64 bajtów (np. pikseli obrazu) wykorzystując do tego celu 2 najmłodsze bity każdego elementu tablicy (zastosować strukturę z polami bitowymi). c Dr inż. Ignacy Pardyka (Inf.UJK) PN.06 Rok akad. 2011/2012 21 / 22
Literatura Ćwiczenia laboratoryjne Literatura 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.06 Rok akad. 2011/2012 22 / 22