Programowanie w asemblerze Linkowanie January 17, 2017
Problem rozmieszczenia (relokacji) Ponieważ w pamięci równocześnie może znajdować się kilka programów, nie można w trakcie kompilacji przewidzieć ich rzeczywistych adresów. Podział pracy: linker: rozmieszczenie pamięci = przygotowanie adresów względnych w programie loader: finalna relokacja
Rozwiazania architektoniczne Relokacja sprzętowa (rejestry relokacji) i pamięć wirtualna uprościły pracę linkera. Każdy program dostaje cała (wirtualna) przestrzeń adresowa. Ale za to pojawiło się dzielenie kodu: podział programu na sekcje kodu i danych.
Zadania linkera Scala moduły binarne Łaczy binarne moduły relokowalne w pojedynczy plik wykonywalny, który można będzie załadować loaderem. Rozwiazuje odwołania zewnętrzne Musza być rozwiazywane w trakcie procesu scalania Odwołanie zewnętrzne: odwołanie do symbolu w innym module. Relokuje symbole Wiaże nazwy symbolicznych opisane względnymi adresami w ramach modułów (plików.o) z konkretnymi adresami absolutnymi w kodzie wykonywalnym. Na przykład: getline w module iosys adres 612 bajtów od poczatku wykonywalnego kodu. Wykonuje code fixups: aktualizuje wszystkie odwołania do tych symboli tak, aby odpowiadały ich nowym adresom.
Relokacja Asembler generuje adresy nierelokowane. Załóżmy np., że dla mov eax,[a] mov [b],eax a ma adres lokalny 0x1234, zaś b jest importowany. Kod po asemblacji A1 34 12 00 00 A3 00 00 00 00 mov eax,[a] mov [b],eax Przy linkowaniu linker ustala, że sekcja zawierajaca a ma być relokowana 0x10000 bajtów, zaś b ma adres 0x9A12 A1 34 12 01 00 A3 12 9A 00 00 mov eax,[a] mov [b],eax
Relokacja Podobne modyfikacja sa wymagane w sekcji danych, jeśli występuja tam wskaźniki, np. tablica adresów procedur. Problemy w procesorach RISC, gdzie adres często budowany przez 2 lub 3 kolejne instrukcje.
Formaty plików binarnych W Unixie pliki binarne (i nie tylko) rozpoczynaja się od 32-bitowej liczby magicznej (magic number), określajacej typ pliku. Tradycyjnym, choć obecnie rzadko używanym, formatem pliku binarnego w Unixie jest a.out. Jego liczba magiczna to 0x407. Zastapiony formatem ELF.
Format a.out Nagłówek a.out Sekcja text Sekcja data Inne sekcje Opcjonalna informacja o relokacji
Format ELF Nagłówek ELF Tablica nagłówków programów (dla loadera) Sekcja.text Sekcja.data Sekcja.bss.symtab.rel.text.rel.data.debug Tablica nagłówków sekcji (informacje o relokacji dla linkera)
Format ELF (Executable and Linking Format) Obecnie podstawowy format w Linuxie o liczbie magicznej 0x177 = ELF Nadaje się zarówno do zapisywania programów wykonywalnych, jak i modułów i bibliotek (a także obrazów pamięci). Ma kilka typów: relokowalny, wykonywalny, shared, core image. Tak więc można w nim umieszczać informacje potrzebne zarówno linkerowi, jak i loaderowi. Na poczatku pliku znajduje się nagłówek, następnie tablica nagłówków programów (opis segmentów dla loadera).
Format ELF Potem następuje właściwa zawartość pliku, czyli sekcje: kod.text dane inicjowane.data dane nieinicjowane.bss.symtab: tablica symboli.rel.text: informacje o relokacjach dla.text.rel.data: informacje o relokacjach dla.data.debug: informacja dla debuggera (gdy użyto gcc -g) Na końcu tablica nagłówków sekcji, opisujaca poszczególne sekcje i przeznaczona dla linkera.
Biblioteki Biblioteki dziela się na statyczne i dzielone. Wszelkie biblioteki statyczne wymagaja relinkowania programu po każdej modyfikacji bibliotek.
Biblioteki dzielone Biblioteki dzielone dziela się na ładowane statycznie i dynamicznie. dla bibliotek ładowanych statycznie adresy (rozmieszczenie) sa ustalane w trakcie ładowania programu biblioteki ładowane dynamicznie moga być doładowywane w miarę potrzeby w trakcie pracy, przy pierwszym wywołaniu ich procedur W bibliotekach dzielonych używa się Position Independent Code (PIC), aby można je było ładować w dowolne miejsce w pamięci (np. w kompilatorze gcc jest do tego opcja).
Linkowanie dynamiczne: metody Implicit linking Z programem zwiazujemy linkage segment, opisujacy wołane procedury zewnętrzne z biblioteki dynamicznej jako pary [nazwa, adres(poczatkowo równy 0, czyli błędny)]. W kodzie wywołania pośrednie przez te adresy, z wyjatkiem pierwszego razu, bo poczatkowo jest to pułapka (trap) do dynamicznego linkera, powodujaca dolinkowanie docelowej biblioteki i wypełnienie pola adresu. Explicit linking Program w swoim prologu podaje wszystkie używane biblioteki dzielone i łaczy się z nimi.
Tworzenie biblioteki dynamicznej Aby zbudować bibliotekę dynamiczna, plik asemblujemy normalnie nasm -f elf pakiet.asm W deklaracjach eksportowanych symboli powinniśmy jednak podawać ich typ: function lub data, np. global random:function,seed:data Inaczej natomiast będziemy go linkować ld -shared -o libpakiet.so pakiet.o
Tworzenie biblioteki dynamicznej Tak zbudowanej biblioteki można teraz używać podobnie jak systemowej ld -L. -dynamic-linker /lib/ld-linux.so.2 \ -o program program.o -l pakiet Dodatkowo przed uruchomieniem program musimy odpowiednio ustawić zmienna środowiska LD_LIBRARY_PATH=. export LD_LIBRARY_PATH Polecenie systemowe ldd podaje, jakich bibliotek dzielonych używa program, natomiast polecenie nm podaje wszystkie symbole zewnętrzne modułu binarnego.