Programowanie Niskopoziomowe Wykład 1: Wstęp Dr inż. Marek Mika Państwowa Wyższa Szkoła Zawodowa im. Jana Amosa Komeńskiego W Lesznie
Plan Informacje o przedmiocie Języki asemblerowe i ich zastosowania Programy hybrydowe Programy w środowisku systemu operacyjnego Programy wolnostojące Proces tworzenia programu
Informacje o przedmiocie Wymiar: wykład: 15 godzin laboratorium: 15 godzin Liczba punktów ECTS: 4 Forma zaliczenia: wykład: kolokwium zaliczeniowe laboratorium: wykonanie ćwiczeń i sprawozdań bieżące sprawdzanie wiedzy w formie krótkich sprawdzianów
Prowadzący Wykład: dr inż. Marek Mika e-mail: Marek.Mika@cs.put.poznan.pl tel. 61 665 3024 (wtorki 13:30-15:00) Laboratorium: dr inż. Marek Mika
Cel nauczania przedmiotu Zapoznanie studentów z zasadami: niskopoziomowego programowania procesorów rodziny x86 programowania w języku asemblera z użyciem: instrukcji warunkowych, pętli, operacji na liczbach całkowitych, tablic i łańcuchów znaków kompilacji, konsolidacji i debugowania programów asemblerowych programowania hybrydowego programistycznej obsługi sprzętu Interakcji pomiędzy programami asemblerowymi a systemem operacyjnym i innymi aplikacjami myślenia na poziomie sprzętowym
Wymagana wiedza Podstawy programowania w językach wysokiego poziomu Podstawowa wiedza w zakresie architektury systemów komputerowych Podstawowa wiedza w zakresie systemów operacyjnych
Zakres Logika programowania logika Boole a operacje bitowe Koncepcje związane z systemem operacyjnym i sprzętem podstawowe pojęcia związane z reprezentacją danych (liczby binarne) architektura procesora (rejestry, znaczniki) adresowanie pamięci w procesorach x86 Programowanie strukturalne dekompozycja funkcjonalna procedury koncepcja najpierw przemyśl później koduj
Zakres c.d. Java i maszyna wirtualna javy Dostęp do pamięci dyskowej z programistycznego punktu widzenia ze sprzętowego punktu widzenia Tworzenie bibliotek Tworzenie struktur, unii i makr Łączenie asemblera z językami C i C++
Literatura Irvine K. R., Asembler dla procesorów Intel Vademecum profesjonalisty, Helion, Gliwice 2003 Wróbel E., Asembler 8086/88, WNT, Warszawa 1990 Hyde R., Profesjonalne programowanie. Część 1. Zrozumieć komputer, Helion, Gliwice 2005. Małysiak H., Pochopień B., Wróbel E., Mikrokomputery klasy IBM PC, WNT, Warszawa 1992. Mikroprocesory firmy Intel, praca zbiorowa pod redakcją C. Stępnia, PWN, Warszawa 1992.
Materiały http://wazniak.mimuw.edu.pl/index.php?title= Programowanie_niskopoziomowe http://www.cs.put.poznan.pl/mmika/leszno/pn/
Środowisko Sprzęt: PC z procesorem Intel lub AMD (x86) System operacyjny: MS Windows (98, XP, Vista, 7) Linux Oprogramowanie: MASM (Microsoft Micro Assembler) TASM (Turbo Assembler) NASM (Netwide Assembler) GNU Assembler
Język asemblera Najstarszy język programowania, w którym każda podstawowa instrukcja odpowiada jednej instrukcji maszynowej procesora Symboliczny (alfanumeryczny) zapis instrukcji i argumentów w postaci czytelnej dla człowieka język maszynowy używa zapisu binarnego Zwykle liniowa struktura zapisu każdy wiersz tekstu źródłowego to pojedyncza instrukcja procesora lub dyrektywa asemblera Kompilator języka asemblerowego jest nazywany asemblerem Język asemblerowy umożliwia bezpośrednie sterowanie działaniem procesora przez programistę
Języki asemblerowe Instrukcje języka odpowiadają instrukcjom procesora w niektórych asemblerach jedna instrukcja symboliczna może być rozwijana w kilka instrukcji maszynowych (np. MIPS) Każda rodzina procesorów o wspólnym modelu programowym ma inny asembler poszczególne rodziny procesorów różnią się istotnie modelami programowymi rejestry tryby adresowania modele operacji warunkowych instrukcje
Zapis asemblerowy Program składa się z dyrektyw (instrukcji dla kompilatora asemblera) i instrukcji procesora Każda instrukcja lub dyrektywa zajmuje zwykle jeden wiersz Elementy, które mogą wystąpić w wierszu (kolejno): etykieta lub nazwa instrukcja lub dyrektywa argumenty instrukcji lub dyrektywy komentarz Dokładna postać poszczególnych elementów zależy od konkretnego asemblera Istnieją asemblery używające innej postaci zapisu, np. podobnej do języka C
Zapis asemblerowy - przykład Etykieta lub nazwa definiowanego symbolu Instrukcja procesora lub dyrektywa asemblera Argumenty (nazwy, wartości, symbole) Komentarz section.text ; otwarcie sekcji kodu start: org 0x100 mov ah, 9 ; funkcja wypisywania łańcucha mov dx, hello ; adres łańcucha int 0x21 ; wywołanie systemu mov ax, 0x4c00 ; funkcja exit (0x4c) int 0x21 ; wywołanie systemu Hello: db Hello, world!, 13, 10, $
Zastosowanie programowania asemblerowego Programowanie mikrokontrolerów o bardzo ograniczonych zasobach lub architekturze nieprzystosowanej do implementacji języków wysokiego poziomu Systemy wbudowane Fragmenty oprogramowania krytyczne czasowo Dostęp do zasobów procesora niewidocznych dla języka wysokiego poziomu rejestry specjalne instrukcje nie będące odpowiednikami instrukcji języka wysokiego poziomu jednostki wektorowe Obsługa wyjątków Inne procedury jądra systemu operacyjnego Ręczna optymalizacja kodu
Programowanie hybrydowe Łączenie fragmentów programu (procedur) napisanych w różnych językach przykład: C i asembler mogą to być również dwa języki wysokiego poziomu Operacje niekrytyczne czasowo zapisuje się w języku wysokiego poziomu Operacje krytyczne i nierealizowalne w języku wysokiego poziomu zapisywane w asemblerze Konieczne uzgodnienie sposobu przekazywania sterowania z procedur w języku wysokiego poziomu do asemblerowych i odwrotnie
Program w systemie operacyjnym System operacyjny oraz moduł startowy będący częścią aplikacji przygotowują środowisko pracy programu (alokacja pamięci, stos) i zapewniają współpracę programu z komputerem (obsługa wejścia-wyjścia, system plików, komunikacja między procesami) Programista pisze tylko użytkową część programu W taki sposób są tworzone programy działające na komputerach uniwersalnych standard języka C nazywa to hosted environment Program taki może być pisany w dowolnym dostępnym języku, również w asemblerze
Program wolnostojący Program działający na samym sprzęcie komputera, bez wsparcia systemu operacyjnego, ew. z minimalnym wsparciem Program musi najpierw przygotować środowisko pracy alokacja pamięci, w tym stosu Przykłady: jądro systemu operacyjnego program ładujący systemu operacyjnego moduł startowy programu w języku wysokiego poziomu Programy takie muszą być pisane przynajmniej częściowo w asemblerze ew. muszą korzystać z niestandardowych, niskopoziomowych rozszerzeń języka wysokiego poziomu dla konkretnego procesora lub rodziny procesorów
Przykład program ładujący Zawarty w pierwszym bloku nośnika (np. sektorze dysku) Uruchamiany w trybie systemowym przez wbudowane oprogramowanie komputera w komputerach PC podsystem BIOS ładuje program ładujący do pamięci i przekazuje mu sterowanie Operacje wejścia-wyjścia realizowane za pomocą funkcji udostępnianych przez oprogramowanie wbudowane lub poprzez bezpośrednie odwołania do sterowników w PC program ładujący korzysta z funkcji operacji dyskowych, obsługi wyświetlania i ew. klawiatury, zawartych w podsystemie BIOS Działanie: weryfikacja i załadowanie następnego programu ładującego lub jądra systemu przekazanie sterowania do załadowanego programu
Przykład moduł startowy dla języka C Zadanie stworzenie środowiska do wywołania funkcji main() w środowisku systemu operacyjnego Funkcje (niektóre): zainicjowanie alokatora pamięci pobranie z systemu argumentów wywołania programu (linii polecenia) konwersja argumentów wywołania programu na argumenty dla funkcji main() wywołanie funkcji main() obsługa powrotu z funkcji main() implementacja funkcji exit() zakończenie wykonania programu
Proces tworzenia programu Etapy tworzenia programu Postać pośrednia Łączenie (linkowanie) Biblioteki
Etapy tworzenia programu Kompilacja kompilator zamienia zapis w języku wysokiego poziomu na zapis asemblerowy lub od razu na postać pośrednią każdemu plikowi źródłowemu odpowiada oddzielny plik wynikowy Asemblacja plik źródłowy w asemblerze lub plik tymczasowy wygenerowany przez kompilator jest zamieniany przez asembler na postać pośrednią każdemu plikowi źródłowemu odpowiada oddzielny plik w postaci pośredniej Łączenie konsolidator łączy pliki pośrednie i pliki bibliotek tworząc postać wykonywalną
Przykład DOS / Microsoft C Wywołanie: cl hello.c program cl jest powłoką kompilatora Etapy: cl wywołuje kompilator kompilator generuje plik postać pośrednią plik hello.obj cl wywołuje konsolidator link z odpowiednimi argumentami link łączy plik hello.obj z biblioteką języka c biblioteka zawiera moduł startowy crt0 oraz funkcje standardowe, np. printf, scanf itp. moduł crt0 zawiera kod odpowiedzialny za przygotowanie środowiska dla funkcji main i wywołanie funkcji main z oowiednimi argumentami (argc,argv[]) Powstaje plik hello.exe postać wykonywalna programu
Przykład Linux / GCC gcc jest powłoką (tzw. driverern) komppilatora gcc wywołuje kompilator, asembler i konsolidator przekazując im odpowiednie argumenty wywołanie gcc -o hello hello.c -o hello określa nazwę pliku wykonywalnego (bez tej opcji plik nazywałby się a.out) gcc wywołuje kompilator, który tworzy strumień (plik) asemblerowy gcc wywołuje asembler, który tworzy plik pośredni hello.o gcc wywołuje konsolidator ld, który tworzy plik hello używając modułu crt0.o, hello.o i biblioteki standardowej powstaje plik wykonywalny o nazwie hello
Przykład - Windows Przebieg kompilacji jak w przykładzie dla DOS System Windows używa tzw. bibliotek dynamicznych postać wykonywalna programu nie jest kompletna zamiast funkcji standardowych postać wykonywalna programu zawiera informacje o sposobie dołączenia tych funkcji dołączenie funkcji następuje podczas ładowania programu do pamięci dzięki temu wiele programów może korzystać z jednej, wspólnej kopii biblioteki daje to redukcję zajętości pamięci Podobny mechanizm jest dostępny w systemach rodziny Unix (w tym również Linux)
Przykłady - podsumowanie Postać wykonywalna powstaje przez połączenie modułów w postaci pośredniej Moduły te mogą powstać przez kompilację (asemblację) postaci źródłowych pisanych w językach wysokiego poziomu lub asemblerze Wiele kompilatorów (w tym GCC) generuje jawnie postać asemblerową, która jest następnie translowana przez asembler Konsolidator nie wie, w jakim języku był oryginalnie napisany łączony moduł Biblioteki zawierają gotowe do użycia moduły (w zmodyfikowanej postaci pośredniej), wcześniej skompilowane
Kompilacja Kompilator języka wysokiego poziomu tworzy postać asemblerową każdego kompilowanego modułu programu postać ta podlega następnie asemblacji niektóre kompilatory(np. Microsoft C) tworzą od razu postać pośrednią, gotową do konsolidacji
Asemblacja Asemblacja - to inaczej kompilacja postaci asemblerowej programu do postaci pośredniej (lub wykonywalnej) Postać asemblerowa może być stworzona przez programistę lub może być wynikiem działania kompilatora języka wysokiego poziomu
Postać pośrednia Postać pośrednia zawiera binarny kod wykonywalny, w którym część odwołań (np. do zmiennych i procedur) pozostaje niezdefiniowana Typowy program składa się z wielu modułów powiązanych ze sobą moduł może korzystać z funkcji i zmiennych zdefiniowanych w innych modułach Postać pośrednia zawiera opis zewnętrznych odwołań modułu i opis symboli (np. etykiet i nazw zmiennych) udostępnianych przez moduł na zewnątrz wiązanie obiektów następuje poprzez ich nazwy
Konsolidacja Konsolidacja polega na realizacji odwołań do symboli zewnętrznych poprzez związanie tych odwołań z symbolami udostępnianymi przez moduły, w których je zdefiniowano Wiązanie symboli jest możliwe tylko wtedy, gdy występują one w postaci pośredniej jako zewnętrzne i publiczne W wyniku konsolidacji powstaje postać wykonywalna programu, w której wszystkie odwołania są zrealizowane nie dotyczy to odwołań do bibliotek dynamicznych, które podlegają konsolidacji w trakcie ładowania programu do pamięci
Symbole zewnętrzne Symbole zewnętrzne są to symbole zdefiniowane w innych modułach W module korzystającym z symboli zewnętrznych muszą one być zadeklarowane jako zewnętrzne język C słowo kluczowe extern dla danych funkcje niezdefiniowane są domyślnie traktowane jako zewnętrzne asembler wymagana deklaracja słowo kluczowe extern lub extrn
Symbole publiczne Symbole publiczne symbole, z których mogą korzystać inne moduły Symbole publiczne są deklarowane jako publiczne język C: każdy symbol na poziomie zewnętrznym jest traktowany jako publiczny, o ile nie jest zadeklarowany ze słowem kluczowym static asembler: wymagana jawna deklaracja słowo kluczowe public lub gobal
Symbole zewnętrzne i publiczne - przykład ; modul pierwszy.asm -------------------------------------------------------- start: section.text extern say_hello call say_hello ;otwarcie sekcji kodu ;symbol jest zdefiniowany w innym module mov ax, Ox4cOO ; funkcja exit (Ox4c), kod powrotu O int Ox2l ; wywołanie systenu ;---------------------------------------------------------------------------- ; modul drugi.asm ----------------------------------------------------------- section.text ; otwarcie sekcji kodu global say_hello say_hello: ; symbol będzie dostępny na zewoątrz mov ah, 9 ; funkcja wypisania łańcucha mov dx, hello ; adres łańcucha int 0x21 ret section.data ; wywołanie systemu hello: db Hello, world!, 13, 10, $
Program jednomodułowy Program jednomodułowy Bardzo proste programy pisane w języku asemblera mogą składać się z jednego modułu i nie zawierać odwołań do żadnych symboli zewnętrznych Konsolidacja takich programów sprowadza się do wygenerowania postaci wykonywalnej z postaci pośredniej i nie wymaga przeprowadzenia wiązania symboli Niektóre asemblery mogą bezpośrednio generować postać wynikową programów jednomodułowych (np. NASM)
Program jednomodułowy przykład (DOS) ; program w najprostszej postaci binarnej.com ; musi się rozpoczynać od adresu OxlOO ; dane znajdują się w sekcji kodu section.text ; otwarcie sekcji kodu org OxlOO start: mov ab, 9 ; funkcja wypisywania łańcucha mov dx, hello ; adres łańcucha int Ox2l ; wywołanie systemu mov ax, 0x4c00 ; funkcja exit (Ox4c), kod powrotu O int Ox2l ; wywołanie systemu hello: db Hello, world!, 13, 10, $
Biblioteki Biblioteka to kolekcja gotowych modułów w postaci pośredniej, które mogą być łączone z innymi modułami tworzonymi przez programistę Plik biblioteki zawiera postaci pośrednie wielu modułów Biblioteki zawierające funkcje standardowe dla danego języka programowania zwykle dostarczane wraz z kompilatorami są one niezbędne do utworzenia działającego programu Programista może tworzyć własne biblioteki, z których może następnie korzystać w innych programach
Konsolidacja z biblioteką Konsolidator wybiera z biblioteki i łączy tylko te moduły, w których są zdefiniowane symbole zadeklarowane jako zewnętrzne w bazowych modułach pośrednich Wybrane z biblioteki moduły mogą zawierać odwołania do symboli zewnętrznych zdefiniowanych w innych modułach bibliotecznych Wybieranie i łączenie modułów następuje aż do zrealizowania wszystkich odwołań
DZIĘKUJĘ ZA UWAGĘ