Ten dokument zawiera przetłumaczony i w swobodny sposób pozmieniany tekst standardu ELF. Dokument ten pochodzi z strony Briana Raitera , i został przetłumaczony i użyty za jego zgodą. Tłumaczenie A.D.Danilecki adanilecki _malpa_ cs.put.poznan.pl. Wszelkie błedy są efektem mojej kiepskiej znajomości angielskiego i nie powinny obciążać oryginalnych autorów. Proszę także o uwagi na temat jakości tłumaczenia i propozycje lepszych tłumaczeń kierować na adres adanilecki _malpa_ cs.put.poznan.pl .
lepsze kulawe tłumaczenie niż żadne? Czy też lepsze żadne tłumaczenie niż
kiepskie?
Jeśli znasz dobrze angielski nie zawracaj sobie głowy lekturą tego
dokumentu i znajdź jego angielski oryginał.
W tej chwili dokument posaida wiele niespójności, błędów,
tylko częściowo przeniesiony jest na.php i większość linków nie działa.
Tłumacz (czyli ja :) ) będzie poprawiał dokument w wolnym czasie, jednakże
nie będzie uprzedzał o zamieszczaniu poprawek. Czekaj na wersję 0.7 lub
co jakiś czas (co dwa tygodnie co najmniej) sprawdzaj moją stronę www.
Format pliku wykonywalnego i łączonego (Executable and Linking Format) pierwotnie został wymyślony i opublikowany przez UNIX System Laboratories (USL) jako część interfejsu binarnego aplikacji (Application Binary Interface) czyli tzw. ABI. Komitet standardów interfejsów narzędzi (Tool Interface Standards committee, TIS) wybrał ewoluujący standard ELF jako format przenośnego pliku .o dla najprzeróżniejszych systemów operacyjnych.
Intencją standardu ELF jest zunifikowanie rozwoju oprogramowania poprzez dostarczenie programistom zbioru binarnych interfejsów które będą dostępne dla wielu środowisk programistycznych i wielu systemów operacyjnych. Powinno to zredukować liczbę różnych implementacji interfejsu, w ten sposób zmniejszając potrzebę przekodowywania i rekompilacji kodu.
Ten dokument jest przeznaczony dla programistów którzy tworzą pliki wykonywalne lub obiektowe dla różnych 32bitowych środowisk systemów operacyjnych.
Zauważ: Wszystkie odniesienia do architektury x86 zostały zmienione na odniesienia do architekury Intela.
Część 1 opisuje intelowski ABI dla plików obiektowych, nazywany ELF (Executable and Linking Format). Istnieją trzy główne typy takich plików:
Po materiale wprowadzającym, część pierwsza skupia się na formacie pliku i sposobie odniesienia go do budowy programów. Część druga opisuje również pliki obiektowe, koncentrując się na informacjach niezbędnych do uruchomienia programu.
do spisu treści tej specyfikacji
Pliki obiektowe biorą udział w łączeniu (linkowaniu) programu (budowaniu programu) oraz wykonywaniu programu. Dla wygody i efektywności, format pliku obiektowego zapewnia równoległe widoki zawartości pliku, odwzierciedlające różne potrzeby tych działań. Rysunek 1-1 pokazuje organizację pliku obiektowego.
+ Rysunek 1-1: Format pliku obiektowego.
| Widok ze strony linkera | Widok od strony wykonywania |
| Nagłówek ELF | Nagłówek ELF |
| Tablica nagłowka programu (opcja) | Tablica nagłówka programu |
| Sekcja 1 | Segment 1 |
| ... | Segment 2 |
| Sekcja n | ... |
| Tablica nagłówków sekcji | Tablica nagłówków sekcji (opcja) |
Nagłówek ELF znajduje się na początku i zawiera ,,mapę drogową'' opisującą sposób organizacji pliku. Sekcje zawierają większą część informacji pliku obiektowego dla widoku dla linkera: instrukcje, dane, tablice symboli, informacje o przemieszczeniach (relokacji, ang. relocations), itd. Opis sekcji specjalnych pojawi się później w części pierwszej. Część druga omawia segmenty i widok pliku od strony wykonywania programu.
Tablica nagłówku programu , jeśli obecna, mówi systemowi w jaki sposób stworzyć obraz procesu. Pliku używane do budowy obrazu procesu (wykonania programu) muszą mieć tablicę nagłowka programu; pliki relokacyjne nie. Tablica nagłówków sekcji zawiera informacje opisujące sekcje pliku. Każda sekcja posiada wpis w tablicy; każdy taki wpis podaje informacje takie jak nazwa sekcji, rozmiar itd. Pliki używane podczas łączenia muszą mieć tablicę nagłówków sekcji, inne pliki obiektowe mogą lecz nie muszą jej zawierać.
ZAUWAŻ: Chociaż rysunek pokazuje tablicę nagłówka programu natychmiast po nagłówku ELF, a tablicę nagłówków sekcji dopiero po wszystkich sekcjach, właściwa struktura plików może się różnić od podanej. Co więcej, sekcje i segmenty nie posiadają żadnego ściśle określonego porządku. Tylko nagłówek ELF posiada określoną pozycję w pliku.
do spisu treści tej specyfikacji
Tak jak to tutaj opisujemy, format pliku obiektowego wspiera rozmaite procesory o 8-bitowej i 32-bitowej architekturze. Jednakże jego intencją jest rozszerzalność również na inne więcej- lub mniej- bitowe architektury. Plik obiektowy reprezentuje więc pewne dane kontrolne z formatem niezależnym od sprzętu, czyniąc możliwym zidentyfikowanie plików obiektowych i interpretację ich zawartości w prosty sposób. Pozostała część danych zawiera rozkazy dla procesora docelowego, niezależnie od tego na jakim sprzęcie plik został utworzony.
+ Rysunek 1-2: 32-Bitowe typy danych
| Nazwa | Rozmiar | Wyrównywanie | Cel |
| Elf32_Addr | 4 | 4 | Adresy w programie bez znaku |
| Elf32_Half | 2 | 2 | Liczby całkowite średnie bez znaku |
| Elf32_Off | 4 | 4 | Offset w pliku bez znaku |
| Elf32_Sword | 4 | 4 | Wielkie liczby całkowite ze znakiem |
| Elf32_Word | 4 | 4 | Wielkie liczby całkowite bez znaku |
| unsigned char | 1 | 1 | Małe liczby całkowite bez znaku |
Wszystkie struktury danych które definicuje format pliku podążają za ,,naturalnymi'' wskazówkami co do rozmiaru i wyrównywania odpowiednich klas. Jeśli jest to potrzebne, struktury danych zawierają z góry określone wypełnianie (ang. padding) dla zapewnienia odpowiedniego wyrównania (ang. alignment) dla 4-bajtowych obiektów, tak aby zapewniać wielkość struktur będącą wielokrotnościami czwórki, itd. Dane posiadają również odpowiednie wyrównanie od początku pliku, to znaczy że członek typu Elf32_Addr zostanie wyrównany w 4-bajtowych granicach wewnątrz pliku.
Dla zapewnienia przenośności, ELF nie używa pól bitowych.
do spisu treści tej specyfikacji
Niektóre struktury kontrolne w plikach obiektowych mogą rosnąć, ponieważ nagłówek ELF zawiera ich właściwe rozmiary. Jeżeli format pliku obiektowego się zmienia, program może odkryć że struktury kontrolne są mniejsze lub większe niż tego oczekiwał. Program może więc ignorować dodatkową informację. Traktowanie brakującej informacji zależy od kontekstu i zostanie wyspecyfikowane gdy/jeśli zostaną zdefiniowane dodatkowe rozszerzenia.
+ Rysunek 1-3: Nagłówek ELF
| #define EI_NIDENT 16 | |
| typedef struct { | |
| unsigned char | e_ident[EI_NIDENT]; |
| Elf32_Half | e_type; |
| Elf32_Half | e_machine; |
| Elf32_Word | e_version; |
| Elf32_Addr | e_entry; |
| Elf32_Off | e_phoff; |
| Elf32_Off | e_shoff; |
| Elf32_Word | e_flags; |
| Elf32_Half | e_ehsize; |
| Elf32_Half | e_phentsize; |
| Elf32_Half | e_phnum; |
| Elf32_Half | e_shentsize; |
| Elf32_Half | e_shnum; |
| Elf32_Half | e_shstrndx |
| } Elf32_Ehdr; |
Początkowe bajty znaczą plik jako plik obiektowy i zapewniają dane niezależne od sprzętu na której przyjdzie dekodować i interpretować zawartość pliku. Dokładny opis pojawia się niżej, w ,,Identyfikacji ELF''.
To pole identyfikuje typ pliku obiektowego.
| Nazwa | Wartość | Znaczenie |
| ET_NONE | 0 | Plik bez określonego typu |
| ET_REL | 1 | Plik relokacyjny(przemieszczalny) |
| ET_EXEC | 2 | Plik wykonywalny |
| ET_DYN | 3 | Współdzielony plik obiektowy |
| ET_CORE | 4 | Plik Core |
| ET_LOPROC | 0xff00 | Zależny od procesora |
| ET_HIPROC | 0xffff | Zależny od procesora |
Chociaż zawartość plików ,,core'' jest niezdefiniowana, typ ET_CORE jest zarezerwowany do oznaczania takich plików. Wartości od ET_LOPROC do ET_HIPROC są zarezerwowane dla semantyki specyficznej dla procesora. Inne wartości są zarezerwowane i, gdy zajdzie taka potrzeba, będą przydzielane nowym typom plików.
Wartość tego pola podaje wymaganą architekturę dla danego pliku.
| Nazwa | Wartość | Znaczenie |
| EM_NONE | 0 | Brak specyfikacji architektury |
| EM_M32 | 1 | AT&T WE 32100 |
| EM_SPARC | 2 | SPARC |
| EM_386 | 3 | Intel 80386 |
| EM_68K | 4 | Motorola 68000 |
| EM_88K | 5 | Motorola 88000 |
| EM_860 | 7 | Intel 80860 |
| EM_MIPS | 8 | MIPS RS3000 |
Inne wartości są zarezerwowane i będą przydzielane nowym architekturom gdy zajdzie taka potrzeba. Nazwy ELF specyficzne dla procesora używają nazw architektur w celu rozróżnienia. Na przykład, flagi wspomniane niżej używają przedrostka EF_; flaga nazwana WIDGET dla architektury EM_XYZ będzie się nazywać EM_XYZ_WIDGET.
To pole identyfikuje wersję pliku obiektowego.
| Nazwa | Wartość | Znaczenie |
| EV_NONE | 0 | Błędna wersja |
| EV_CURRENT | 1 | Wersja bieżąca |
Wartość 1 ozancza oryginalny format pliku; gdy pojawią się rozszerzenia, powstaną nowe wersje z wyższymi numerami. Wartość EV_CURRENT, mimo że podana wyżej jako 1, będzie się zmieniać jeśli bedzie to niezbędne to odzwierciedlenia bieżącej wersji.
To pole podaje adres wirtualny pod który system przekaże kontrolę, startując w ten sposób proces. Jeżeli plik nie posiada takiego miejsca, to pole przechowuje zero.
To pole zawiera offset tablicy nagłówka programu w bajtach. Jeśli plik nie posiada tablicy nagłówka programu, to pole zawiera zero.
To pole zawiera offset tablicy nagłówków sekcji w bajtach. Jeżeli plik nie posiada takiej tablicy, to pole zawiera zero.
To pole zawiera flagi specyficzne dla procesora skojarzone z tym plikiem.
Nazwy flag posiadają formę EF_
To pole zawiera rozmiar nagłówka ELF w bajtach.
To pole zawiera rozmiar w bajtach jednej pozycji w
tablicy nagłówka
programu.
Wszystkie pola są tego samego rozmiaru.
To pole zawiera liczbę pól w tablicy nagłówka
programu. Mnożąc
e_phentsize
przez e_phnum otrzymujemy rozmiar tablicy w bajtach. Jeżeli plik nie posaida
tablicy nagłówka programu, e_phnum zawiera zero.
To pole przechowuje rozmiar nagłówka sekcji w bajtach. Nagłówek sekcji
jest jednym polem w tablicy nagłówków sekcji. Wszystkie pola są jednakowego
rozmiaru.
To pole przechowuje liczbę pól w tablicy nagłówków
sekcji. W ten sposób
mnożąc e_shnum przez e_shentsize otrzymujemy rozmiar w bajtach tablicy
nagłówków sekcji. Jeżeli plik nie posiada tablicy nagłówków sekcji, e_shnum
zawiera zero.
To pole zawiera index pól dla tablicy nagłówków sekcji skojarzony z
tablicą
nazw sekcji. Jeśli plik nie posiada takiej tablicy, to pole zawiera
wartość SHN_UNDEF. Zobacz ,,Sekcje'' i
,,Tablica Łańcuchów'' poniżej aby
uzyskać więcej informacji.
Identyfikacja ELF
Jak już wspomniano wyżej, ELF zapewnia plikowi obiektowemu ramę do
współpracy z wieloma procesorami, wieloma sposobami kodowania danych, i
wieloma klasami architektur. Aby wpierać te rodziny plików obiektowych,
początkowe bajty pliku mówią jak interpretować plik, niezależnie
od procesora na którym się o to dowiadujemy i niezależnie od pozostałej
zawartości pliku.
Początkowe bajty nagłówka ELF (i pliku obiektowego) odpowiadają polu
e_ident.
+ Rysunek 1-4: e_ident[] indeksy identyfikacji
Nazwa Wartość Przeznaczenie
==== ===== =======
EI_MAG0 0 Identifikacja pliku
EI_MAG1 1 Identifikacja pliku
EI_MAG2 2 Identifikacja pliku
EI_MAG3 3 Identifikacja pliku
EI_CLASS 4 Klasa pliku
EI_DATA 5 Kodowanie danych
EI_VERSION 6 Wersja pliku
EI_PAD 7 Początek dopełniających bajtów
EI_NIDENT 16 rozmiar e_ident[]
Te indekst dotyczą bajtów które przechowują następujące wartości.
* od EI_MAG0 do EI_MAG3
Pierwsze 4 bajty pliku przechowują ,,magic number'' (liczbę magiczną :) ,
która identyfikuje plik obiektowy ELF.
Nazwa Wartość Pozycja
==== ===== ========
ELFMAG0 0x7f e_ident[EI_MAG0]
ELFMAG1 'E' e_ident[EI_MAG1]
ELFMAG2 'L' e_ident[EI_MAG2]
ELFMAG3 'F' e_ident[EI_MAG3]
* EI_CLASS
Kolejny bajt, e_ident[EI_CLASS], identyfikuje klasę pliku.
Nazwa Wartość Znaczenie
==== ===== =======
ELFCLASSNONE 0 Błędna klasa
ELFCLASS32 1 obiekty 32-bit
ELFCLASS64 2 obiekty 64-bit
Format pliku jest przeznaczony by być przenośnym pomiędzy architekturami
o różnych rozmiarach (np 32-bitowe, 64-bitowe), bez wyznaczania
sprzętu o rozmiarze najmniejszym lub największym. Klasa ELFCLASS32 wspiera
architekturę o plikach i rozmiarze pamięci wirtualnej do 4Gb; używa
podstawowych typów zdefiniowanych wyżej.
Klasa ELFCLASS64 jest zarezerwowana dla architektur 64-bitowych. Jej
pojawienie się tutaj pokazuja jak plik obiektowy może się zmienić, ale w
innym przypadku format 64bitowy jest niewyspecyfikowany. Inne klasy będą
definiowane w miarę potrzeby, z różnymi typami prostymi i rozmiarami dla
danych plików obiektowych.
* EI_DATA
Bajt e_ident[EI_DATA] definiuje sposób kodowania danych specyficznych dla
procesora w pliku obiektowym. Następujące kodowania są zdefiniowane w tej chwili
Nazwa Wartość Znaczenie
==== ===== =======
ELFDATANONE 0 Błędny sposób kodowania
ELFDATA2LSB 1 Patrz niżej
ELFDATA2MSB 2 Patrz niżej
Więcej sposobów na temat kodowania pojawia się poniżej. Inne wartości
są zarezerwowane i będą przydzielane nowy sposobom kodowanie jeśli
okaże się to potrzebne.
* EI_VERSION
Bajt e_ident[EI_VERSION] definiuje numer wersji nagłówka ELF.
Obecnie ta wartość musi sie równać EV_CURRENT, tak jak to objaśniono
wyżej dla e_version.
* EI_PAD
Ta wartość oznacza początek nieużywanych bajtów w e_ident. Te bajty są
zarezerwowane i ustawione na zero; programy czytające pliki obiektowe
powinny je ignorować. Wartość EI_PAD zmieni się w przyszłości gdy obecnie
nieużywane bajty otrzymają jakieś znaczenie.
Kodowanie danych pliku specyfikuje jak interpretować proste obiekty w
pliku. Jak opisano wyżej, klasa ELFCLASS32 używa obiektów które
zajmują 1, 2, i 4 bajty. Pod zdefiniowanym kodowaniem, obiekty są
reprezentowane jak pokazano niżej. Numery bajtów występują w górnych lewych
rogach.
Kodowanie ELFDATA2LSB definiuje 2-komplementarne(dopełniane do 2) wartości,
z najmniej znaczącym bajtem zajmującym najniższy adres .
+ Rysunek 1-5: Kodowanie danych ELFDATA2LSB
0------+
0x0102 | 01 |
+------+
0------1------+
0x010204 | 02 | 01 |
+------+------+
0------1------2------3------+
0x01020304 | 04 | 03 | 02 | 01 |
+------+------+------+------+
ELFDATA2MSB definiuje 2-komplementarne wartości, z najbardziej znaczącym bajtem
zajmującym najniższy adres.
+ Rysunek 1-6: Kodowanie danych ELFDATA2MSB
0------+
0x0102 | 01 |
+------+
0------1------+
0x010204 | 01 | 02 |
+------+------+
0------1------2------3------+
0x01020304 | 01 | 02 | 03 | 04 |
+------+------+------+------+
Informacje o architekturze
Dla identyfikacji pliku w e_ident, 32-bitowa architektura Intela
wymaga następujących wartości.
+ Rysunek 1-7: 32-bitowa identyfikacja dla architektury Intela, e_ident
Pozycja Wartość
======== =====
e_ident[EI_CLASS] ELFCLASS32
e_ident[EI_DATA] ELFDATA2LSB
Identyfikacja procesora pozostaje w polu e_machine nagłówka ELF i musi
posiadać wartość EM_386
Pole e_flags nagłówka ELF przechowuje flagi bitowe związane z plikiem.
32-bitowa architektura Intela nie definiuje zadnych flag, tak więc
to pole zawiera zero.
=========================== Sekcje ===========================
Tablica nagłówków sekcji pliku obiektowego pozwala zlokalizować wszystkie sekcje
pliku. Tablica nagłówków sekcji jest tablicą struktur Elf32_Shdr opisanych
niżej. Indeks tablicy nagłówków sekcji jest wskaźnikiem do tej tablicy.
Pole e_shoff nagłówka ELF podaje offset w bajtach od początku pliku do
tablicy nagłówków sekcji; e_shnum mówi ile pozycji zawiera tablica; e_shentsize
podaje rozmiar w bajtach każdej pozycji.
Niektóre indeksy tablicy nagłówków sekcji są zarezerwowane; plik obiektowy
nie może posiadać sekcji dla tych specjalnych indeksów.
+ Rysunek 1-8 : Specjalne wartości indeksów sekcji
Nazwa Wartość
==== =====
SHN_UNDEF 0
SHN_LORESERVE 0xff00
SHN_LOPROC 0xff00
SHN_HIPROC 0xff1f
SHN_ABS 0xfff1
SHN_COMMON 0xfff2
SHN_HIRESERVE 0xffff
* SHN_UNDEF
Ta wartość znaczy niezdefiniowane, brakujące lub w inny sposób pozbawione
znaczenie odwołanie do sekcji. Na przykład symbol ,,defined'' w odniesieniu
do sekcji numer SHN_UNDEF jest symbolem niezdefiniowanym.
ZAUWAŻ: Chociaż indeks 0 jest zarezerwowany jako wartość niezdefiniowana,
tablica nagłówków sekcji zawiera pozycję dla indeksu 0. To znaczy, jeśli
pole nagłówka ELF e_shnum wynosi 6, czyli że plik ma 6 pozycji w tablicy
nagłówków sekcji, to posiadają one numery od 0 do 5. Zawartość początkowej
pozycji jest zdefiniowana później w tej sekcji.
* SHN_LORESERVE
Ta wartość definiuje dolną granicę zbioru zarezerwowanych indeksów.
* od SHN_LOPROC do SHN_HIPROC
Wartości w tym zbiorze są zarezerwowane dla semantyki specyficznej dla
procesora.
* SHN_ABS
Ta wartość definiuje bezwzlędną wartość dla odpowiadającego odwołania.
Na przykład symbole zdefiniowane jako względne dla sekcji numer SHN_ABS
posiadają bezwzględne, absolutne wartości i nie dotyczy ich relokacja
(ang. relocation).
* SHN_COMMON
Symbole zdefiniowane w tej sekcji są symbolami wspólnymi (common symbols),
tak jak FORTRAN COMMON albo niezalokowane zewnętrzne zmienne C.
* SHN_HIRESERVE
Ta wartość definiuje górną granicę zbioru zarezerwowanych indeksów.
System rezerwuje indeksy pomiędzy SHN_LORESERVE i SHN_HIRESERVE włącznie;
wartości te nie odnoszą się do tablicy nagłówków sekcji, to jest tablica
nagłówków sekcji nie zawiera pozycji dla zarezerwowanych indeksów.
Sekcje zawierają wszystkie informacje w pliku obiektowym, poza nagłówkiem
ELF, tablicą nagłówka programu i tablicą nagłówków sekcji. Co więcej, sekcje
pliku obiektowego spełniają kilka warunków.
* Każda sekcja w pliku obiektowym posiada dokładnie jeden nagłówek sekcji który
ją opisuje. Mogą istnieć nagłówki sekcji nie posiadające sekcji.
* Każda sekcja zajmuje jedną ciągłą (możliwe że pustą) sekwencję bajtów
w pliku.
* Sekcje w pliku nie mogą się nakładać. Żaden bajt nie może być w więcej niż
jednej sekcji.
* Plik obiektowy może posiadać przestrzeń nieaktywną. Różne nagłówki i sekcje
mogą nie pokrywać każdego bajtu w pliku obiektowym. Zawartość takich danych
nieaktywnych jest niezdefiniowana.
Nagłówek sekcji posiada następującą strukturę.
+ Rysunek 1-9: Nagłówek sekcji
typedef struct {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
* sh_name
To pole specyfikuje nazwę sekcji. Jego wartością jest wskaźnik do
tablicy nazw nagłówków sekcji [zobacz ,,Tablica nazw'' poniżej], podając
lokalizację łańcucha zakończonego znakiem pustym.
* sh_type
To pole szufladkuje zawartość i semantykę sekcji. Typy sekcji są opisane
niżej.
* sh_flags
Sekcje wspierają jednobitowe flagi opisujące różne atrybuty . Definicje
flag pojawiają się niżej.
* sh_addr
Jeśli sekcja pojawia się w obrazie pamięci procesu, to pole podaje
pole w którym pierwszy bajt sekcji się pojawia. Wpw, to pole zawiera zero.
* sh_offset
Wartość tego pola podaje offset w bajtach od początku pliku do
pierwszego bajtu sekcji. jeden typ sekcji, SHT_NOBITS opisany niżej, nie
zajmuje miejsca w pliku, i jego sh_offset zajmuje koncepcyjne (ang conceptual??)
umieszczenie w pliku.
* sh_size
To pole podaje rozmiar sekcji w bajtach. Jeżeli typ sekcji nie jest
SHT_NOBITS, sekcja zajmuje sh_size bajtów w pliku. Sekcja typu
SHT_NOBITS może posiadać rozmair niezerowy, ale i tak nie zajmuje miejsca
w pliku.
* sh_link
To pole zawiera łącze do wskaźnika na tablicę nagłówków sekcji
(ang.section header table index link), którego interpretacja zależy od typu
sekcji. Tablica poniżej opisuje wartości.
* sh_info
To pole zawiera dodatkową informację, której interpretacja zależy od typu
sekcji. Tablica poniżej opisuje wartości jakie może przybierać.
* sh_addralign
Niektóre sekcje posiadają ograniczenie na wyrównywanie (alignment)
adresów. na przykład, jeśli sekcja zawiera podwójne słowo, system musi
zapewnić wyrównanie do podwójnego słowa dla całej sekcji. Oznacza to że
wartość pola sh_addr modulo wartość sh_addralign musi wynosić zero
(ang. That is, the value of sh_addr must be congruent to 0, modulo the value of sh_addralign.) Obecnie tylko zero i dodatnie całkowite potęgi dwójki są
dozwolone. Wartość 0 i 1 oznacza że sekcja nie posiada ograniczeń na
wyrównywanie.
* sh_entsize
Niektóre sekcje przechowują tablice o stałych rozmiarach pozycji, takie
jak tablice symboli. Dla takich sekcji to pole podaje rozmiar w bajtach
każdej pozycji. Pole to zawiera zero jeśli sekcja nie posiada tablicy
o stałych rozmiarach pozycji.
Pole sh_type nagłówka sekcji podaje sematykę sekcji.
+ Rysunek 1-10: Typy sekcji, sh_type
Name Value
==== =====
SHT_NULL 0
SHT_PROGBITS 1
SHT_SYMTAB 2
SHT_STRTAB 3
SHT_RELA 4
SHT_HASH 5
SHT_DYNAMIC 6
SHT_NOTE 7
SHT_NOBITS 8
SHT_REL 9
SHT_SHLIB 10
SHT_DYNSYM 11
SHT_LOPROC 0x70000000
SHT_HIPROC 0x7fffffff
SHT_LOUSER 0x80000000
SHT_HIUSER 0xffffffff
* SHT_NULL
Ta wartość oznacza że nagłowek sekcji jest nieaktywny i nie odnosi się
do żadnej istniejącej sekcji. Inne pola nagłówka sekcji posiadają
niezdefiniowane, dowolne wartości.
* SHT_PROGBITS
Ta sekcja zawiera informacje zdefiniowane przez program, których
format i znaczenie jest całkowicie zależne od programu.
* SHT_SYMTAB and SHT_DYNSYM
Ta sekcja zawiera tablicę symboli. Obecnie plik obiektowy może posiadać tylko
jedną sekcję każdego typu, lecz to ograniczenie może zostać złagodzone w
przyszłości. Zazwyczaj SHT_SYMTAB zapewnia symbole do edytowania łączy/linków
(ang. link editing), chociaż może również zostać użyte do dynamicznego
łączenia (ang. dynamic linking). Plik obiektowy może również posiadać sekcję
SHT_DYNSYM, która zawiera minimalny zbiór symboli potrzebnych przy
dynamicznym łączeniu. Zobacz ,,Tablice symboli'' by dowiedzieć się szczegółów.
* SHT_STRTAB
Ta sekcja zawiera tablicę nazw (ang. string table). Plik obiektowy może
posiadać wiele sekcji tego typu. Po więcej szczegółów udaj się do rozdziału
,,Tablica Nazw''.
* SHT_RELA
(?) Sekcja zawiera pozycje relokacji z góry nadanymi dodatkami, takimi jak
typ Elf32_Rela dla 32-bitowej klasy plików obiektowych. (???? ang.
The section holds relocation entries with explicit addends, such as
type Elf32_Rela for the 32-bit class of object files). Plik obiektowy
może posiadać wiele relokacyjnych sekcji. Zobacz ,,Relokacja'' poniżej
po więcej szczegółów.
* SHT_HASH
Sekcja zawiera haszową (mieszającą) tablicę symboli. Wszystkie obiekty
biorące udział w dynamicznym łączeniu muszą zawierać taką tablicę. Obecnie
plik obiektowy może posiadać tylko jedną haszową tablicę, ale to ograniczenie
może zostać złagodzone w przyszłości. Zobacz ,,Tablica Haszowa'' w części
drugiej po więcej szczegółów.
* SHT_DYNAMIC
Sekcja zawiera informacje dla dynamicznego łączenia. Obecnie plik obiektowy
może posiadać tylko jedną dynamiczną sekcję, ale to ograniczenie może ulec
złagodzeniu w przyszłości. Zobacz ,,Sekcja dynamiczna'' w części drugiej
po więcej szczegółów.
* SHT_NOTE
Sekcja zawiera informacje które znaczą w pewien sposób plik. Zobacz ,,Sekcje
z uwagami'' w części drugiej po więcej szczegółów.
* SHT_NOBITS
Sekcja tego typu nie zajmuje przestrzenie w pliku ale poza tym przypomina
sekcję SHT_PROGBITS. Chociaż ta sekcja nie zawiera bajtów, pole sh_offset
zawiera koncepcyjny (ang. conceptual) ofset pliku.
* SHT_REL
Sekcja zawiera pozycje relokacji bez z góry nadanych dodatków takich jak
typ Elf32_Rel dla 32bitowych klas plików obiektowych (?). Plik obiektowy może
posiadać wiele sekcji relokacyjnych. Zobacz ,,Relokacja'' poniżej po więcej
szczegółów.
* SHT_SHLIB
Ta sekcja jest zarezerwowana ale nie posiada wyspecyfikowanej semantylo.
Programy zawierające tą sekcję nie są zgodne z ABI (aplication binary
interface).
* od SHT_LOPROC do SHT_HIPROC
Wartości w tym zbiorze są zarezerwowane dla semantyki zależnej od procesora.
* SHT_LOUSER
Ta wartość podaje dolne ograniczenie zbioru indeksów zarezerwowanych dla
programów użytkowych.
* SHT_HIUSER
Ta wartość podaje górną granicę indeksów zarezerwowanych dla programów
użytkowych. Typy sekcji pomiędzy SHT_LOUSER i SHT_HIUSER mogą być używane
przez aplikacje, bez wchodzenia w konflikt z bieżącymi lub zdefiniowanymi
w przyszłości systemowymi typami sekcji.
Inne wartości określające typy sekcji są zarezerwowane. Tak jak wspomniano
wcześniej, nagłówek sekcji dla indeksu 0 (SHN_UNDEF) istnieje, nawet pomimo
tego że indeks ten znaczy niezdefiniowane odnośniki do sekcji. Ta pozycja
zawiera :
+ Rysunek 1-11: Pozycja w tablicy nagłówków sekcji: Indeks 0
Nazwa Wartość Uwagi
==== ===== ====
sh_name 0 brak nazwy
sh_type SHT_NULL Nieaktywna
sh_flags 0 Brak flag
sh_addr 0 Brak adresu
sh_offset 0 Brak ofestu pliku
sh_size 0 Brak rozmiaru
sh_link SHN_UNDEF Brak informacji o łączeniu
sh_info 0 Brak informacji pomocniczych
sh_addralign 0 Brak dopełniania (alignment)
sh_entsize 0 Brak pozycji
Pole sh_flags nagłówka sekcji zawiera bitową flagę która opisuje atrybuty
sekcji. Zdefiniowane wartości pojawiają się w tablicy niżej, inne wartości
są zarezerwowane.
+ Rysunek 1-12: Flagi atrybutów sekcji, sh_flags
Nazwa Wartość
==== =====
SHF_WRITE 0x1
SHF_ALLOC 0x2
SHF_EXECINSTR 0x4
SHF_MASKPROC 0xf0000000
Jeśli bit flagi jest ustawiony w sh_flagsm atrybut jest ,,włączony'' dla
tej sekcji. W przeciwnym razie atrybut jest ,,wyłączony'' lub nie posiada
zastosowania. Atrybuty niezdefiniowane są ustawione na zero.
* SHF_WRITE
Sekcja zawiera dane które powinny byc zapisywalne podczas wykonywania procesu.
* SHF_ALLOC
Sekcja zajmuje pamięć podczas wykonywania procesu. Pewne sekcje kontrolne
nie znajdują się w obrazie pamięci pliku obiektowego; ten atrybut jest wyłączony
dla tych sekcji.
* SHF_EXECINSTR
Sekcja zawiera wykonywalne kody instrukcji maszynowych.
* SHF_MASKPROC
Wszystkie bity włączone w tą masję są zarezerwowane dla semantyki specyficznej
dla procesora.
Dwa pola w nagłówku sekcji, sh_link i sh_info, zawierają specjalne informacje,
zależące od typu sekcji.
+ Rysunek 1-13: Interpretacja sh_link i sh_info
sh_type sh_link sh_info
======= ======= =======
SHT_DYNAMIC Indeks nagłówka sekcji w tablicy 0
nazw używanej przez pozycje
w tej sekcji
SHT_HASH Indeks nagłówka sekcji w tablicy 0
symboli do której odnosi się
tablica haszowa.
SHT_REL, Indeks nagłówka sekcji w Indeks nagłówka sekcji dla sekcji
SHT_RELA związanej tablicy symboli. do której odnosi się relokacja
SHT_SYMTAB, Indeks nagłówka sekcji w O jeden większe niż indeks tablicy
SHT_DYNSYM związanej tablicy nazw. symboli dla ostatniego lokalnego
symbolu (wiążącego STB_LOCAL).
inne SHN_UNDEF 0
Sekcje specjalne
Różne sekcje przechpwują program i informacje kontrolne. Sekcje wyszczególnione
w liście poniżej są używane przez system i posiadają określone typy i atrybuty.
+ Rysunek 1-14: Sekcje specjalne
Nazwa Typ Atrybuty
==== ==== ==========
.bss SHT_NOBITS SHF_ALLOC+SHF_WRITE
.comment SHT_PROGBITS brak
.data SHT_PROGBITS SHF_ALLOC+SHF_WRITE
.data1 SHT_PROGBITS SHF_ALLOC+SHF_WRITE
.debug SHT_PROGBITS brak
.dynamic SHT_DYNAMIC zobacz niżej
.dynstr SHT_STRTAB SHF_ALLOC
.dynsym SHT_DYNSYM SHF_ALLOC
.fini SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR
.got SHT_PROGBITS zobacz niżej
.hash SHT_HASH SHF_ALLOC
.init SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR
.interp SHT_PROGBITS zobacz niżej
.line SHT_PROGBITS brak
.note SHT_NOTE brak
.plt SHT_PROGBITS zobacz niżej
.rel SHT_REL zobacz niżej
.rela SHT_RELA zobacz niżej
.rodata SHT_PROGBITS SHF_ALLOC
.rodata1 SHT_PROGBITS SHF_ALLOC
.shstrtab SHT_STRTAB brak
.strtab SHT_STRTAB zobacz niżej
.symtab SHT_SYMTAB zobacz niżej
.text SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR
* .bss
Ta sekcja zawiera niezainicjowane dane które odnoszą się do obrazu
pamięci programu. Z definicji system inicjalizuje te dane zerami gdy program
rozpoczyna działanie. Sekcja ta nie zajmuje przestrzeni w pliku, tak jak
to wynika z typu sekcji, SHT_NOBITS
* .comment
Ta sekcja zawiera informacje kontrolne dotyczące wersji.
* .data and .data1
Te sekcje zawierają zainicjalizowane dane które odnoszą się do obrazu pamięci
programu.
* .debug
Ta sekcja zawiera informację dla debuggingu. Zawartość jest niezdefiniowana.
* .dynamic
Ta sekcja zawiera informacje dotyczące dynamicznego łączenia. Atrybuty sekcji
zawierają SHF_ALLOC. To czy bit SHF_WRITE jest ustawiony zależy od procesora.
Patrz część druga po więcej informacji.
* .dynstr
Ta sekcja zawiera łańcuchy potrzebne podczas dynamicznego łączenia, zazwyczaj
łańcuchy reprezentujące nazwy związane z pozycjami tablicy symboli. Patrz
część druga po więcej informacji.
* .dynsym
Ta sekcja zaiwera tablicę symboli dla dynamicznego łączenia, tak jak opisuje
to ,,tablica symboli''. Patrz część druga po więcej informacji.
* .fini
Ta sekcja zawiera wykonywalne instrukcje które odnoszą się do kodu kończącego
proces. To znaczy, gdy program kończy się normalnie, system zapewnia wykonanie
kodu tej sekcji.
* .got
Ta sekcja zawiera tablicę globalnych ofsetów. Zobacz ,,Sekcje specjalne'' w
w części 1 i ,,Tablica globalnych ofsetów'' w części 2 po więcej informacji.
* .hash
Ta tablica zawiera haszową tablicę symboli. Zobacz ,,Tablica Hashowa'' w
części drugiej po więcej informacji.
* .init
Ta sekcja zawiera instrukcje które należą do kodu inicjalizacyjnego procesu.
Oznacza to że kiedy proces startuje, system wykonuje kod w tej sekcji zanim
wywoła główny punkt ,,wejścia'' programu (ang. entry point) (dla programów
w C będzie to na przykład main).
* .interp
Ta sekcja zawiera nazwę scieżki interpretera programu. Jeśli plik
posiada ładowalny segment który zawiera tą sekcję, atrybuty sekcji będą
zawierać bit SHF_ALLOC. W przeciwnym wypadku ten bit będzie wyczyszczony.
Zajrzyj do części drugiej po więcej informacji.
* .line
Ta sekcja zawiera informacje o numerach linii dla debuggingu, które
opisują związek między liniami programu źródłowego i kodem maszynowym. Zawartość
jest niezdefiniowana.
* .note
Ta sekcja zawiera informacje w formacie opisanym przez ,,Sekcje z uwagami''
w części drugiej.
* .plt
Ta sekcja zawiera tablicę procedury łączenia. Zobacz ,,Sekcje specjalne''
w części pierwszej i ,,tablica procedury łączenia'' w części drugiej po
więcej informacji.
* .rel and .rela
Te sekcje zawierają informacje relokacyjne, tak jak poniżej opisuje to
,,Relokacja''. Jeśli plik zawiera ładowalny segment zawierający relokację,
atrybuty sekcji będą zawierać bit SHF_ALLOC, wpw ten bit będzie wyczyszczony.
odnosi się do sekcji do której odnosi się relokacja. W ten sposób
sekcja relokacji dla .text normalnie będzie posiadać nazwę .rel.text lub
.rela.text.
* .rodata and .rodata1
Te sekcje zawierają dane tylko do odczytu które odnoszą się typowo
do niezapisywalnego segmentu w obrazie procesu. Zobacz ,,Nagłówek programu''
w części drugiej po więcej szczegółów.
* .shstrtab
Ta sekcja zawiera nazwy sekcji.
* .strtab
Ta sekcja zawiera łańcuchy, zazwyczaj łańcuchy reprezentujące nazwy
związane z pozycjami tablicy symboli. Jeśli plik posiada ładowalny segment który
zawiera tablicę łańcuchów (nazw) symboli, atrybuty sekcji będą zawierać bit
SHF_ALLOC. W przeciwnym wypadku ten bit zostanie wyczyszczony.
* .symtab
Ta skecja zawiera tablicę symboli tak jak to opisuje ,,Tablica symboli''.
Jeśli plik zawiera ładowalny segment który zawiera tablicę symboli, atrybuty
sekcji będą zawierać bit SHF_ALLOC. Wpw bit ten pozostanie wyczyszczony.
* .text
Ta sekcja zawiera ,,tekst'', kod, wykonywalne instrukcje programu.
Nazwy sekcji zaczynające się od kropki są zarezerwowane dla systemu,
aczkolwiek aplikacje mogą używać tych sekcji jeśli ich istniejące znaczenie
jest wystarczające. Aplikacje mogą używać nazw bez kropki by uniknąć
konfliktów z sekcjami systemowymi. Format pliku obiektowego pozwala definiować
sekcje nie zawierające się w liście wyżej. Plik obiektowy może posiadać
więcej jak jedną sekcję z tą samą nazwą.
Nazwy sekcji zarezerwowane dla architektury procesora są konstruowane
poprzez przyłączenia skrótu określającego architekturę z przodu nazwy sekcji.
Nazwa powinna być wzięta z nazwy architektur użytych dla e_machine.
Na przykład .BLA.psect oznacza sekcję psect zdefiniowaną dla architektury
BLA. Istniejące rozszerzenia są nazywane ich historycznymi nazwami.
Istniejące rozszerzenia
==========================
.sdata .tdesc
.sbss .lit4
.lit8 .reginfo
.gptab .liblist
.conflict
========================= Tablica nazw =========================
Sekcje z tablicami nazw (łańcuchów) zawierają zakończone znakiem
pustym sekwencje znaków, powszechnie nazywane łańcuchami. Plik obiektowy
używa tych łańcuchów do reprezentowania symboli i nazw sekcji. Można
dostać się do łańcucha za pomocą indeksu w sekcji tablicy nazw. Pierwszy bajt,
o indeksie zero, musi z definicji zawierać znak pusty. Tak samo ostatni bajt
tablicy nazw również z definicji musi zawierać znak pusty, zapewniając że
na pewno wszystkie łańcuchy zakończą się znakiem pustym. Łańcuch którego indeks
wynosi zero nie definiuje żadnej nazwy lub inaczej mówiąc nazwę pustą, zależnie
od kontekstu. Dozwolona jest pusta sekcja tablicy nazw; pole sh_size
nagłówka sekcji w takim przypadku zawiera zero. Niezerowe indeksy są
nieprawidłowe dla tablicy pustego łańcucha.
Pole sh_name nagłówka sekcji zawiera indeks do nagłówka sekcji sekcji tablicy
nazw, tak ja to jest wyznaczone przez pole e_shstrndx nagłówka ELF.
Następujące rysunki pokazują tablicę nazw z 25 bajtami i łańcuchami połączonymi
z różnymi indeksami.
Indeks +0 +1 +2 +3 +4 +5 +6 +7 +8 +9
===== == == == == == == == == == ==
0 \0 n a m e . \0 V a r
10 i a b l e \0 a b l e
20 \0 \0 x x \0
+ Rysunek 1-15: Indeksy tablicy nazw
Indeks Łańcuch
===== ======
0 żaden
1 "name."
7 "Variable"
11 "able"
16 "able"
24 Łańcuch pusty
Tak jak pokazuje to przykład, indeks tablicy nazw może odnosić się do
jakiegolwiek bajtu w sekcji. Łańcuch może pojawiać się więcej razy niż razl
mogą istnieć wskaźniki do podłańcuchów i może istnieć wiele wskaźników
do pojedyńczego łańcucha. Dozwolone są również łańcuchy do których nie istnieje
żaden wskaźnik.
========================= Tablica Symboli =========================
Tablica symboli pliku obiektowego przechowuje informacje potrzebne
do zlokalizowania i relokacji symbolicznych definicji i referencji w
programie. Indeks tablicy symboli jest wskaźnikiem do tej tablicy.
Indeks zerowy zarówno wskazuje początkową pozycję w tablicy jak i służy
za niezdefiniowany indeks symbolu. Zawartość początkowej pozycji są
zdefiniowane później w tym punkcie.
Nazwa Wartość
===== =======
STN_UNDEF 0
Pozycja tablicy symboli posiada następujący format.
+ Rysunek 1-16: Pozycja tablicy symboli
typedef struct {
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
* st_name
To pole zawiera indeks do tablicy nazw symboli pliku obiektowego, która
zawiera znakową reprezentację nazw symboli. Jeśli wartość jest niezerowa,
reprezentuje ono indeks tablicy nazw który podaje nazwę symbolu. Wpw pozycja
tablicy symboli nie posiada nazwy.
Zauważ: Zewnętrzne symbole C posiadają takie same nazwy w C i tablicach
symboli plików obiektowych.
* st_value
To pole podaje wartość związanego symbolu. Zależnie od kontekstu może
to być wartość absolutna, adres itd. Szczegóły pojawiają się niżej.
* st_size
Wiele symboli posiada rozmiary z nimi związane. Na przykład rozmiar
obiektu danych jest to numer bajtów zawartych w tym obiekcie. To pole
zawiera 0 jeśli symbol nie posiada rozmiaru lub jego rozmiar jest nieznany.
* st_info
To pole definiuje typ symbolu i związane z nim atrybuty. Lista wartości
i ich znaczeń pojawia się poniżej. Następujący kod pokazuje jak operować
tymi wartościami.
#define ELF32_ST_BIND(i) ((i)>>4)
#define ELF32_ST_TYPE(i) ((i)&0xf)
#define ELF32_ST_INFO(b, t) (((b)<<4)+((t)&0xf))
* st_other
To pole obecnie zawiera 0 i nie posiada zdefiniowanego znaczenia.
* st_shndx
Każda pozycja tablicy symboli jest ,,zdefiniowana'' w relacji do jakiejś
sekcji. To pole zawiera odpowiedni indeks do tablicy nagłówków sekcji. Jak
pokazuje to rysunek 1-8 {*} oraz opisuje związany tekst, niektóre indeksy
sekcji maja specjalne znaczenie.
Wiązanie symbolu determinuje jego widoczność i zachowanie podczas łączenia.
+ Rysunek 1-17: Wiązanie symbolu, ELF32_ST_BIND
Nazwa Wartość
==== =====
STB_LOCAL 0
STB_GLOBAL 1
STB_WEAK 2
STB_LOPROC 13
STB_HIPROC 15
* STB_LOCAL
Lokalne symbole nie są widoczne poza plikiem obiektowym zawierającym ich
definicję. Lokalne symbole o tej samej nazwie mogą istnieć w wielu plikach
współdziałając między sobą.
* STB_GLOBAL
Symbole globalne są widoczne w wszystkich plikach obiektowych które są
łączone. Jedna definicja symbolu globalnego jest wystarczająca do referencji
do tego samego symbolu globalnego w innym pliku.
* STB_WEAK
Słabe symbole przypominają symbole globalne, ale ich definicja posiada
słabszy priorytet.
* od STB_LOPROC do STB_HIPROC
Wartości w tym domkniętym zbiorze są zarezerwowane dla semantyki specyficznej
dla procesora.
Globalne i słabe symbole różnią się na dwa główne sposoby.
* Kiedy edytor łączenia (ang. link editor) łączy kilka relokacyjnych
plików obiektowych nie zezwala na istnienie wielu definicji symboli
STB_GLOBAL o tej samej nazwie. Z drugiej stronym jeśli zdefiniowany symbol
globalny istnieje, istnienie symbolu słabego o identycznej nazwie nie powoduje
wystąpienia błędu. Edytor łączenia uznaje globalną definicję i ignoruje
słabe definicje. Podobnie, jeśli wspólny symbol istnieje (np. symbol dla
którego st_shndx zawiera SHN_COMMON), pojawienie się słabego symbolu o tej
samej nazwie nie powoduje błędu. Edytor łączenia honoruje wspólne (ang. common)
definicje i ignoruje słabe.
* Kiedy edytor łączenia przeszukuje biblioteki, rozpakowuje pola które
zawierają definicje niezdefiniowanych symboli globalnych. Definicja
pola może być symbolem słabym lub globalnym. Edytor łączenia nie
rozpakowuj pól bibliotek (ang. extract archive members) by rozwiązać
niezdefiniowane słabe symbole. Takie symbole posiadają wartość zero.
W każdej tablicy symboli, wszystkie symbole z wiązaniem STB_LOCAL
mają wyższy priorytet niż symbole słabe i globalne. Ja ,,Sekcje'' wyżej
opisują, pole nagłówka sekcji sh_info dla sekcji tablicy symboli zawiera
indeks tablicy symboli dla pierwszego nielokalnego symbolu.
Typ symbolu zapewnia klasyfikację dla związanej jednostki (ang. entity).
+ Rysunek 1-18: Tablica Symboli, ELF32_ST_TYPE
Nazwa Wartość
==== =====
STT_NOTYPE 0
STT_OBJECT 1
STT_FUNC 2
STT_SECTION 3
STT_FILE 4
STT_LOPROC 13
STT_HIPROC 15
* STT_NOTYPE
Typ symbolu nie jest zdefiniowany.
* STT_OBJECT
Symbol jest związany z obiektem danych, takim jak zmienna, tablica itd.
* STT_FUNC
Symbol związany z funkcją lub innym fragmentem wykonywalnego kodu.
* STT_SECTION
Symbol jest związany z sekcją. Pozycja tablicy symboli tego typu
istnieje przede wszystkim dla relokacji i normalnie posiada wiązanie
STB_LOCAL.
* STT_FILE
Zgodnie z przyjętymi konwencjami nazwa tego symbolu podaje nazwę podaje
nazwę pliku źródłowego związanego z plikiem obiektowym. Symbol pliku posiada
wiązanie STB_LOCAL, jego indeks sekcji wynosi SHN_ABS i ma wyższy priorytet
przed innymi symbolami STB_LOCAL w tym pliku, o ile one istnieją.
* od STT_LOPROC do STT_HIPROC
Wartości w tym domkniętym zbiorze są zarezerwowane dla semantyki
specyficznej dla procesora.
Symbole funkcji (te z typem STT_FUNC) w współdzielonym pliku obiektowym
posiadają specjalne znaczenie. Kiedy inny plik obiektowy odnosi się
do funkcji z współdzielonego obiektu, edytor łączenia automatycznie tworzy
pozycję tablicy procedur łączenia dla symbolu do którego się odnosi.
Do symboli dla obiektów współdzielonych z typami innymi niż STT_FUNC nie
będzie można się odnosić automatycznie za pomocą tablicy procedur łączenia
(ang. procedure linkage table).
Jeśli wartość symbolu odnosi się do specyficznej lokalizacji w środku
sekcji, jego pole indeksu sekcji - st_shndx - zawiera indeks do
tablicy nagłówków sekcji. Gdy sekcja przemieszcza się podczas relokacji,
wartość symbolu zmienia się również i odnośniki do symbolu wciąż
wskazują do tej samej lokalizacji w programie. Pewne wartości indeksów
sekcji specjalnych posiadają inną semantykę.
* SHN_ABS
Symbol posiada absolutną wartość która nie będzie się zmieniać z powodów
relokacji.
* SHN_COMMON
Symbol oznacza wspólny blok który jeszcze nie został zaalokowany
(przydzielony). Wartość symbolu podaje ograniczenia wypełniania, podobnie
jak pole sh_addralign sekcji. Oznacza to że edytor łączenie przydzieli
dla symbolu pamięć o adresie który jest wielokrotnością st_value. Rozmiar
symbolu podaje ile bajtów będzie potrzebnych.
* SHN_UNDEF
Ten indeks tablicy sekcji oznacza że symbol jest niezdefiniowany. Kiedy
edytor łączenia łączy ten plik obiektowy z innymi które definiują ten symbol,
odnośniki w tym pliku zostaną połączone z właściwą definicją.
Jak wspomniano wyżej, pozycja tablica symboli dla indeksu 0 (STN_UNDEF) jest
zarezerwowana. Zawiera ona następujące wartości:
+ Rysunek 1-19: Pozycja tablicy symboli: Indeks 0
Nazwa Wartość Uwaga
==== ===== ====
st_name 0 Brak typu
st_value 0 Wartość zerowa
st_size 0 Brak rozmiaru
st_info 0 Brak typu, wiązania lokalne
st_other 0
st_shndx SHN_UNDEF Brak sekcji
Wartości symboli
Pozycja tablicy symboli dla różnych typów plików obiektowych ma nieco
różną interpretację dla pola st_value.
* W plikach relokacyjnych, st_value zawiera ograniczenia dopełniania
(ang. alignment constraints) dla symbolu którego indeks sekcji wynosi
SHN_COMMON.
* W plikach relokacyjnych st_value zawiera ofset dla zdefiniowanego
symbolu (błąd w dokumentacji czy jak? -przyp autora). Oznacza to
że st_value jest ofsetem od początku sekcji identyfikowanej przez st_shndx.
* W plikach wykonywalnych i współdzielonych, st_value zawiera
adres wirtualny. Aby symbole tych plików były bardziej poręczne dla dynamicznego
linkera, ofset sekcji (interpretacja pliku) podaje drogę do wirtualnego adresu
(interpretacja pamięci) do którego nie ma odnosienia numer sekcji.
Chociaż wartości tablicy symboli posiadają podobne znaczenie dla różnych
plików obiektowych, dane pozwalają na efektywny dostęp dla odpowiednich
programów.
========================== Relokacja ==========================
Relokacja jest procesem łączenia odnośników do symboli z definicjami
symboli. Na przykład kiedy prgram wywołuje funkcję, związana z tym instrukcja
call musi przekazać kontrolę pod właściwy adres pod wykonanie. Onnymi słowy
pliki relokacyjne muszą posiadać informacje o tym jak modyfikować zawartość
ich sekcji, umożliwiając w ten sposób współdzielonym i wykonywalnym plikom
obiektowym trzymać właściwe informacje dla obrazu procesu programu. Te dane
to pozycje (ang. entries) , pola relokacji.
+ Rysunek 1-20: Pozycje relokacji
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
Elf32_Sword r_addend;
} Elf32_Rela;
* r_offset
To pole podaje lokalizację do której odnosi się akcja relokacji.
Dla plików relokacyjnych, wartością tego pola jest ofset od początku sekcji
do jednostki pamięci (storage unit) na której się odbije relokacja.
Dla plików wykonywalnych lub obiektów współdzielonych, wartością jest adres
wirtualny jednostki pamięci dotkniętej relokacją.
* r_info
To pole podaje zarówno indeks tablicy symboli z uznaniem relokacji
która musi być dokonana, jak i typ relokacji mającej być zastosowanej.
Na przykład pozycja relokacji instrukcji call zawierałaby indeks tablicy
symboli dla wołanej funkcji. Jeśli indeks ten ma wartość STN_UNDEF, czyli
indeks niezdefiniowanego symbolu, relokacja używa 0 jako ,,wartości symbolu''.
Typy relokacji są specyficzne dla procesora. Kiedy tekst odnosi się do typu
relokacji pozycji relokacji lub indeksu tablicy symboli, oznacza to rezultat
zastosowania odpowiednio ELF32_R_TYPE lub ELF32_R_SYM do pola r_info pozycji.
#define ELF32_R_SYM(i) ((i)>>8)
#define ELF32_R_TYPE(i) ((unsigned char)(i))
#define ELF32_R_INFO(s, t) ((s)<<8+(unsigned char)(t))
* r_addend
To pole definiuje stały dodatek (ang. addend) używany do obliczania
wartości która będzie przechowywana w przemieszczalnym polu.
Jak pokazano wyżej, tylko pozycje Elf32_Rela zawierają z góry określony
dodatek (ang. explocit addend). Pozycje typu Elf32_Rel przechowują
domnieny dodatek w lokalizacji która będzie modyfikowana. Zależnie od
architektury procesora, jedna fomra lub druga będzie niezbędna lub bardziej
wygodna. Również implementacja dla jednej maszyny może używać wyłącznie
jednej formy albo wybierać formę zależnie od kontekstu.
Sekcja relokacji odnosi się do dwóch innych sekcji: tablicy symboli i
sekcji która będzie modyfikowana. Pola sh_info i sh_link nagłówka sekcji,
opisane wyżej w ,,Sekcjach'', opisują te zależności. Pozycje dla różnych
plików obiektowych posiadają nieco inną interpretację pola r_offset.
* W plikach relokacyjnych, r_offset przechowuje ofset sekcji. Oznacza to
że sekcja relokacji sama opisuje jak zmodyfikować inną sekcję w pliku;
ofsety relokacji wyznaczają jednostki pamięci w środku innej sekcji.
* W plikach wykonywalnych i współdzielonych, r_offset zawiera wirtualny adres.
Aby pozycje relokacji w tych plikach bardziej poręcznych dla dynamicznego
linkera, ofset sekcji (interpretacja pliku) podaje drogę do adresu
wirtualnego (interpretacja pamięci).
Chociaż interpretacja r_offset zmienia się dla różnych plików obiektowych aby
zapewnić efektywny dostęp przez odpowiednie programy, znaczenie typów relokacji
pozostaje takie samo.
Typy Relokacji
Pozycje relokacji opisują jak zmieniać następujące instrukcje i pola danych
(numery bitów pojawiają się w dolnych rogach prostokąta).
+ Rysunek 1-21: Pola relokacyjne
+---------------------------+
| word32 |
31---------------------------0
* word32
To definiuje 32bitowe pole zajmujące 4 bajty z dowolnym wypełnianiem.
Te wartości używają tego samego porządku bajtów jak inne wartości słów
w 32bitowej architektury Intela.
3------2------1------0------+
0x01020304 | 01 | 02 | 03 | 04 |
31------+------+------+------0
Obliczenia poniżej przyjmują że akcje przekształcają plik relokacyjny
w albo wykonywalny albo współdzielony plik obiektowy. Koncepcyjnie
edytor łączenia łączy jeden lub więcej plik relokacyjny by stworzyć
wyjście. Najpierw decyduje jak połączyć i zlokalizować pliki wejściowe,
potem uaktualnia wartości symboli i wreszcie przeprowadza relokacje.
Relokacja w odniesieniu do wykonywalnych lub współdzielonych plików obiektowych
jest podobna i powoduje identyczny rezultat. Opisy poniżej używają następującej
symboliki.
* A
Oznacza dodatki użyte do obliczenia wartości przemieszczalnych pól.
* B
Oznacza adres bazowy pod którym współdzielone obiekty zostały załadowane
podczas wykonania do pamięci. Ogólnie współdzielony plik obiektowy jest
zbudowany z 0 jako podstawą adresów wirtualnych, ale adresy podczas wykonywania
będą inne.
* G
Oznacza ofset w globalnej tablicy ofsetów w której adres symbolu
pozycji relokacji pozostaje podczas wykonywania. Zobacz ,,Tablica Globalnych
Ofsetów'' w części drugiej po więcej informacji.
* GOT
Oznacza adres tablicy globalnych ofsetów. Zobacz ,,Tablica Globalnych
Ofsetów'' w części drugiej po więcej informacji.
* L
Oznacza miejsce (ofset sekcji lub adres) pozycji w tablicy procedur
łączenia dla symbolu.(ang. place (section offset or address) of the procedure
linkage table entry for a symbol). Pozycja w tablicy procedur łączenia
przekierowuje wywołanie funkcji do właściwego przeznaczenia. Edytor łączenia
buduje początkową tablicę procedur łączenia a dynamiczny linker modyfikuje
pozycje/wpisy/elementy (entries) podczas wykonywania. Zobacz ,,Tablica
procedur łączenia'' w części drugiej po więcej informacji.
* P
Oznacz miejsce (ofset w sekcji lub adres) jednostki pamięci która jest
przemieszczana (obliczane za pomocą r_offset).
* S
Oznacza wartość symbolu którego indeks pozostaje w pozycji/wpisie relokacji.
Wartość r_offset pozycji relokacji wyznacza ofset lub wirtuany adres pierwszego
bajtu jednostki pamięci dotkniętej przez relokację. Typ relokacji podaje które
bity należy zmieniać i jak obliczać ich wartości. Architektura Systemu V używa
tylko pozycji relokacji Elf32_Rel , pole które ma być przemieszczone zawiera
dodatek (ang. field to be relocated holds the addend). W wszystkich przypadkach
dodatek i obliczony rezultat używają tego samego porządku bajtów.
+ Rysunek 1-22: Typy Relokacji
Nazwa Wartość Pole Obliczenian
==== ===== ===== ===========
R_386_NONE 0 żadne żadne
R_386_32 1 word32 S + A
R_386_PC32 2 word32 S + A - P
R_386_GOT32 3 word32 G + A - P
R_386_PLT32 4 word32 L + A - P
R_386_COPY 5 żadne żadne
R_386_GLOB_DAT 6 word32 S
R_386_JMP_SLOT 7 word32 S
R_386_RELATIVE 8 word32 B + A
R_386_GOTOFF 9 word32 S + A - GOT
R_386_GOTPC 10 word32 GOT + A - P
Niektóre typy relokacji posiadają semantykę wymykającą się prostym obliczeniom.
* R_386_GOT32
Ten typ relokacji oblicza odległość od podstawy tablicy globalnych ofsetów
do pozycji symbolu w tablicy globalnych ofsetów. Dodatkowo instruuje edytor
łączenia by zbudował tablicę globalnych ofsetów.
* R_386_PLT32
Ten typ relokacji oblicza adres pozycji w tablicy procedury łączenia dla
symbolu i dodatkowo poucza edytor łączenia by zbudował tablicę procedur
łączenia.
* R_386_COPY
Edytor łączenia tworzy ten typ relokacji dla dynamicznego łączenia.
Jego pole ofset odnosi się do lokalizacji w zapisywalnym segmencie.
Indeks tablicy symboli definiuje symbol który powinien istnieć zarówno w
bieżącym pliku obiektowym i w współdzielonym obiekcie. Podczas wykonywania
dynamiczny linker kopiuje dane związane z symbolami obiektu współdzielonego
do lokalizacji wyspecyfikowanej przez ofset.
* R_386_GLOB_DAT
Ten typ relokacji jest używany do ustawienia wpisu tablicy globalnych ofsetów
na adres wyspecyfikowanego symbolu. Specjalny typ relokacji pozwala zadecydować
o stosunku między symbolami a pozycjami tablicy globalnych ofsetów.
* R_386_JMP_SLOT {*}
Edytor łączenia tworzy ten typ relokacji dla dynamicznego łączenia.
Jego pole ofset podaje lokalizację wpisu d tablicy procedur łączenia.
Dynamiczny linker modyfikuje wpis/pozycję w tablicy procedur łączenia
by przekazać kontrolę adresowi wyznaczonego obiektu (zobacz też
,,Tablica Procedur Łączenia'' w części drugiej).
* R_386_RELATIVE
Edytor łączenia tworzy ten typ relokacji dla dynamicznego łączenia.
Jego pole ofset podaje lokalizację w środku współdzielonego obiektu
który zawiera wartość reprezentującą względny adres. Dynamiczny linker
oblicza odpowiedni adres wirtualny poprzez dodanie wirtualnego adresu
pod którym współdzielony obiekt był załadowany pod adres względny. Pozycje
relokacji dla tego typu muszą zawierać zero dla indeksu tablicy symboli.
* R_386_GOTOFF
Ten typ relokacji oblicza różnicę między wartością symbolu i adresem
tablicy globalnych ofsetów. Dodatkowo instruuje edytor łączenia by
zbudował tablicę globalnych ofsetów.
* R_386_GOTPC
Ten typ relokacji przypomina R_386_PC32, poza tym że używa adresu
tablicy globalnych ofsetów w obliczeniach. Symbol do którego odnosi się
ta relokacja to zwykle _GLOBAL_OFFSET_TABLE_, co dodatkowo poucza edytor
łączenia by zbudował tablicę globalnych ofsetów.
________________________________________________________________
2. ŁADOWANIE PROGRAMU I DYNAMICZNE ŁĄCZENIE
________________________________________________________________
========================= Wprowadzenie =========================
Część 2 opisuje informacje dotyczące plików obiektowych i akcje systemu
które tworzą działające programy. Niektóre informacje tu podane odnoszą się
do wszystkich systemów, inne są specyficzne dla konkretnego procesora.
Wykonywalne i współdzielone pliki obiektowe statycznie reprezentują program.
Aby wykonać taki program, system używa plików aby utworzyć dynamiczną
reprezentację programu, albo inaczej obraz procesu. Obraz procesu
posiada segmenty zawierające jego tekst (kod), dane, stos itd. Główne
punkty w tej części omawiają następujące tematy:
* Nagłówek programu. Ten punkt dopełnia część pierwszą, opisując struktury
pliku obiektowego które odnoszą się bezpośrednio do wykonania programu.
Główna struktura danych, tablica nagłówka programu, lokalizuje obrazy
segmentu wewnątrz pliku i zawiera inne informacje niezbędne do stworzenia obrazu
pamięci programu.
* Ładowanie programu. System mając plik obiektowy musi załadować go do pamięci
aby program mógł wystartować.
* Dynamiczne łączenie. Gdy system załaduje program, musi skompletować
obraz procesu poprzez rozwiązanie odniesień do symboli pomiędzy plikami
obiektowymi składającymi się na proces.
ZAUWAŻ: Istnieją konwencje nazywania dla stałych ELF które posiadają
zdefiniowane zasięgi procesorów (ang have specified processor ranges). Nazwy
takie jak DT_, PT_ dla rozszerzeń specyficznych dla procesora zawierają nazwę
procesora: np. DT_M32_SPECIAL. Już istniejące rozszerzenia dla konkretnych
procesorów niezgodne z tą konwencją będą jednak wspierane.
Istniejące Rozszerzenia
=======================
DT_JMP_REL
======================== Nagłówek programu =====================
Tablica nagłówka programu pliku obiektowego lub współdzielonego jest
tablicą struktur z których każda opisuje segment lub inną informację której
system potrzebuje aby przygotować program do odpalenia. Segment pliku
obiektowego zawiera jedną lub więcej sekcji, tak jak niżej jest to opisane w
punkcie ,,Zawartość Segmentu''. Nagłówki programu są znaczące tylko dla
współdzielonych i wykonywalnych plików obiektowych. Plik definiuje rozmiar
własnego nagłówka programu za pomocą pól nagłówka ELF : e_phnum i e_phentsize
[Zobacz też ,,Nagłówek ELF'' w części pierwszej].
+ Rysunek 2-1: Nagłówek Programu
typedef struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
* p_type
To pole opisuje jaki rodzaj segmentu opisuje ten element tablicy lub jak
interpretować informache elementu tablicy. Wartości typów i ich znaczenie
opisane są niżej.
* p_offset
To pole podaje ofset od początku pliku na którym znajduje się pierwszy
bajt segmentu.
* p_vaddr
To pole podaje wirtualny adres pod którym pierwszy bajt segmentu znajduje
się w pamięci.
* p_paddr
Na systemach dla których odpowiednie jest fizyczne adresowanie, to pole
jest zarezerwowane dla fizycznego adresu segmentu. Ponieważ System V
ignoruje fizyczne adresowanie dla programów użytkownika/użytkowych, to
pole posiade niezdefiniowaną zawartość dla plików wykonywalnych i obiektów
współdzielonych.
* p_filesz
To pole podaje liczbę bajtów w obrazie segmentu w pliku. Może wynosić zero.
* p_memsz
To pole podaje liczbę bajtów w obrazie segmentu w pamięci. Może wynosić zero.
* p_flags
To pole podaje flagi stosowane dla segmentu. Zdefiniowane wartości flag
pojawiają się niżej.
* p_align
Tak jak to później w tej części opisuje punkt ,,Ładowanie Programu'',
ładowalne segmenty procesu muszą posiadać (ang. congruent) wartości dla
p_vaddr i p_offset, modulo rozmiar strony. To pole podaje wartość dla której
segmenty są wyrównywane w pamięci i pliku. Wartości 0 i 1 oznaczają że nie jest
potrzebne wyrównywanie. W innych przypadkach p_align powinna być dodatnią,
całkowitą potęgą dwójki, a p_vaddr powinno się równać p_offset modulo
p_align.
Niektóre wpisy/pozycje opisują segmenty procesu; inne podają dodatkowe
informacje i nie dotyczą obrazu procesu. Zdefiniowane pozycje mogą pojawić się
w dowolnym porządku, za wyjątkiem przypadków opisanych poniżej. Poniżej opisane
są wartości typów segmentów - inne wartości są zarezerwowane dla użycia w
przyszłości.
+ Rysunek 2-2: Typy segmentów, p_type
Nazwa Wartość
==== =====
PT_NULL 0
PT_LOAD 1
PT_DYNAMIC 2
PT_INTERP 3
PT_NOTE 4
PT_SHLIB 5
PT_PHDR 6
PT_LOPROC 0x70000000
PT_HIPROC 0x7fffffff
* PT_NULL
Element tablicy jest nieużywany; inne wartości pola są niezdefiniowane.
ten typ pozwala tablicy nagłówku programu posiadać ignorowane wpisy.
* PT_LOAD
Element tablicy podaje ładowalny segment, opisany przez p_filesz i
p_memsz. Bajty z początku pliku są mapowane do początku segmentu pamięci.
Jeśli rozmiar pamięci segmentu (p_memsz jest większy niż rozmiar pliku
(p_filesz), dodatkowe bajty z definicji zawierają wartość zero i następują
po zainicjalizowanym obszarze segmentu. Rozmiar pliku nie może być większy
niż rozmiar pamięci. Wpisy łądowalnych segmentów w tablicy nagłówku programu
pojawiają się w porządku rosnącym, uporządkowane wg pola P_vaddr.
* PT_DYNAMIC
Element tablicy specyfikuje informacje tyczące dynamicznego łaczenia. Zobacz
też punkt ,,Sekcje Dynamiczne'' poniżej jeśli chcesz uzyskać więcej informacji.
* PT_INTERP
Element tablicy specyfikuje lokalizację i rozmiar nazwy ścieżki do
programu wywoływanego jako interpreter; mazwa ta zakończona jest zerem.
Ten typ segmentu ma znaczenie tylko i wyłącznie dla plików wykonywalnych
(chociaż może się pojawić również w obiektach współdzielonych). Może też
pojawić się więcej niż raz w pliku. Jeśli jest obecny, musi poprzedzać
jakąkolwiek pozycję/wpis ładowalnego segmentu. Zobacz punkt ,,Interpreter
Programu'' poniżej w celu uzyskania więcej informacji.
* PT_NOTE
Element tablicy specyfikuje lokalizację i rozmiar pomocniczej informacji.
Zobacz też punkt ,,Sekcje Uwag'' poniżej po więcej szczegółów.
* PT_SHLIB
Ten typ segmentu jest zarezerwowany ale nie posiada zdefiniowanej
semantyki. Programy zawierające element tablicy tego typu nie są zgodne
z ABI.
* PT_PHDR
Element tablicy, jeśli obecny, podaje lokalizację i rozmiar samej
tablicy nagłówku programu (ang. location and size program header table
itself), zarówno w pliku jak i w obrazie pamięci programu. ten typ
segmentu nie może pojawić się więcej niż raz w pliku, co więcej, może pojawić
tylko wtedy gdy tablica nagłówka programu jest częścią obrazu pamięci programu.
Jeśli jest obecny, musi poprzedzać jakikolwiek wpis/pozycję (ang entry)
łądowalnego segmentu. Zobacz też punkt ,,Interpreter Programu'' aby uzyskać
więcej informacji.
* PT_LOPROC through PT_HIPROC
Wartości w tym domkniętym zbiorze są zarezerwowane dla semantyki specyficznej
dla procesora.
ZAUWAŻ: O ile nie jest to jawnie wymagane gdzie indziej, wszystkie typy
segmentów nagłówka programu są opcjonalne. To znaczy tablica nagłówka
programu może zawierać tylke te elementy stosowne do jej zawartości.
Adres Bazowy
Obiektowe pliki współdzielone i wykonywalne posiadają adres bazowy, który
jest najniższym wirtualnym adresem związanym z obrazem pamięci pliku
obiektowego programu. Używa się adresu bazowego do relokacji obrazu
pamięci programu podczas dynamicznego łączenia.
Adres bazowy pliku wykonywalnego i współdzielonego jest obliczany podczas
wykonywania z trzech wartości: adresu załadowania pamięci, maksymalnego
rozmiaru strony, i najniższego adresu wirtualnego segmentu ładowalnego programu.
Tak jak to opisuje punkt ,,Ładowanie Programu'' w tym rozdziale, adresy
wirtualne mogą nie reprezentować właściwych adresów wirtualnych obrazu pamięci
programu. Aby obliczyć adres podstawowy, należy odnaleźć adres pamięci
związany z najmniejszą wartością p_vaddr dla segmentu PT_LOAD. Następnie
otrzymuje się adres bazowy obcinajać adres w pamięci do najbliższej
wielokrotności maksymalnego rozmiaru strony pamięci. Zależnie od rodzaju pliku
który jest ładowany do pamięci, adres pamięci może lub nie odpowiadać
wartościom p_vaddr.
Tak jak opisuje to w części 1 punkt ,,Sekcje'', sekcja .bss posiada typ
SHT_NOBITS. Chociaż nie zajmuje przestrzeni w pliku, odnosi się do obrazu
pamięci segmentu. Zwykle te niezainicjalizowane dane pozostają na końcu segmentu
w ten sposób czyniąc p_memsz większy niż p_filesz w związanym elemencie
nagłówka programu.
Sekcja Uwag
Czasami producent lub twórca systemu potrzebuje oznaczyć plik obiektowy
za pomocą specjalnej informacji którą inne programy będą sprawdzać dla
zgodności (ang for conformance and compatibility etc). Sekcje typu SHT_NOTE
i elementy nagłówka programu typu PT_NOTE mogą być używane właśnie do tego celu.
Informacja ta w sekcjach i elementach nagłówka programu zawiera jakąkolwiek
liczbę pozycji, z których każda jest tablicą czterobajtowych słów w formacie
procesora docelowego. Etykiety pojawiające się niżej służą do pomocy przy
objaśnianiu organizacji tych informacji, ale nie są częścią specyfikacji.
+ Rysunek 2-3: Uwagi
namesz
descsz
type
name ...
desc ...
* namesz i name
Pierwsze namesz bajtów w polu ,,name'' (nazwa) zawiera zakończoną zerem
znakową reprezentację właściciela lub oryginalnego twórcy pozycji. Nie ma
żadnego sformalizowanego mechanizmu który pozwalałby unikać konfliktów nazw.
Zgodnie z konwencją producenci używają swoich własnych nazw, takich jak
,,Kompania komputerowa XYZ'' jako identyfikatorów. Jeżeli nie jest obecna żadna
nazwa, namesz zawiera zero. Jeśli jest to konieczne, wykonuje się dopełnianie
(ang. padding) aby zapewnić 4-bajtowe wyrównywanie (ang. alignment).
Takie dopełnianie nie jest zawarte w namesz.
* descsz i desc
Pierwsze descsz bajtów i w polu ,,desc'' (opis) zawiera deskryptor uwagi.
ABI nie zawiera żadnych ograniczeń na zawartość deskryptora. Jeśli nie
istnieje taki deskryptor, descsz zwiera zero. Jeśli jest to konieczne, wykonuje
się dopełnianie (ang. padding) aby zapewnić 4-bajtowe wyrównywanie
(ang. alignment). Takie dopełnianie nie jest zawarte w descsz.
* type
To słowo podaje interpretację deskryptora. Każdy twórca kontroluje swoje
własne typy. Mogą istnieć wielokrotne interpretacje wartości pojedyńczego typu.
Dlatego też program musi rozpoznawać zarówno typ jak i nazwę aby zrozumieć
deskryptor. Obecnie typy muszą być nieujemne. ABI ine definiuje co oznacza
deskryptor.
Aby to zilustrować, następujący segment uwag posiada dwie pozycje:
+ Rysunek 2-4: Przykład segmentu uwag
+0 +1 +2 +3
-------------------
namesz 7
descsz 0 Brak deskryptora
type 1
name X Y Z spc
C o \0 pad
namesz 7
descsz 8
type 3
name X Y Z spc
C o \0 pad
desc word0
word1
ZAUWAŻ: System rezerwuje uwagi bez nazw (namesz=0) i z zerową długością
nazwy (name[0]=='\0') ale obecnie nie definiuje żadnych typów. Wszystkie
inne nazwy muszą zawierać co najmniej jeden jeden znak różny od znaku pustego.
ZAUWAŻ: Uwagi są opcjonalne. Obecność tych informacji nie odbija się
na zgodności programu z ABI, o ile ta informacja nie zmienia zachowania
programu. Jeśli tak się dzieje, program nie jest zgodny z ABI i posiada
niezdefiniowane zachowanie.
======================= Ładowanie Programu =====================
Gdy system tworzy lub powiększa obraz procesu, logicznie kopiuje
segment pliku do segmentu pamięci wirtualnej. Kiedy - i jeśli - system
fizycznie przeczyta plik zależy od zachowania programu, obciążenia systemu
(ang. system load) itd. Proves nie wymaga fizycznej strony dopóki
odnosi się do stron logicznych podczas wykonywania i procesy powszechnie
nie odnoszą się do wielu stron (ang. leave may pages unreferenced)
Dlatego też opóźnienia fizycznego odczytu często ich nie obchodzi (ang obviates
them) opprawiając wydajność systemu. Aby otrzymać efektywność w praktyce, pliki
wykonywalne i współdzielone muszą posiadać obrazy segmentów których
ofsety plików i adresy wirtualne są (ang. congruent) modulo rozmiar strony.
Adresy wirtualne i ofsety plików dla segmentów architektury SYSTEMU V są
(ang. congruent) modulo 4KB (0x1000) lub większej potęgi dwójki. Ponieważ
4 KB jest maksymalnym rozmiatem strony, pliku pozostaną wygodne i nadające się
dla stronicowania niezależnie od rozmiaru fizycznej strony.
+ Rysunek 2-5: Plik wykonywalny
Ofset pliku Plik Adres Wirtualny
=========== ==== ===============
0 Nagłówek ELF
Tablica nagłówka programu
Inne informacje
0x100 Segment Kodu 0x8048100
...
0x2be00 bajtów 0x8073eff
0x2bf00 Segment danych 0x8074f00
...
0x4e00 bajtów 0x8079cff
0x30d00 Inne informacje
...
+ Rysunek 2-6: Segmenty nagłówka programu
Pole Tekst(kod) Dane
====== ==== ====
p_type PT_LOAD PT_LOAD
p_offset 0x100 0x2bf00
p_vaddr 0x8048100 0x8074f00
p_paddr unspecified unspecified
p_filesz 0x2be00 0x4e00
p_memsz 0x2be00 0x5e24
p_flags PF_R+PF_X PF_R+PF_W+PF_X
p_align 0x1000 0x1000
Chociaż ofsety przykładowego pliku i adresy wirtualne są (ang congruent)
modulo 4 KB dla zarówno tekstu (kodu) jak i danych, do czterech stron
plikowych zawiera nieczyste (? ang. impure ?) dane i tekst (zależnie od
rozmiaru strony i rozmiaru bloku systemu).
* Pierwsza strona kodu zawiera nagłówek ELF, nagłówek programu i inne informacje
* Ostatnia strona kodu zawiera kopię początku danych.
* Pierwsza strona danych zawiera kopię początku tekstu.
* Ostatnia strona danych może zawierać informację o pliku nie mające
zastosowania dla działającego procesu.
Logicznie, system wymusza pozwolenia na dostęp do pamięci (ang, memory
permissions) tak jak gdyby każdy segment był kompletny i osobny. Adresy
segmentów są dostosowywane by zapewnić dla każdej logicznej strony w
przestrzeni adresowej istnieje osobny zbiór zezwoleń. W przykładzie
powyżej obszar pliku zawierający koniec tekstu i początek danych byłby
zamapowany dwukrotnie; na jednym wirtualnym adresie dla kodu i osobnym
adresie wirtualnym dla danych.
Koniec segmentu danych wymaga specjalnego traktowania dla niezainicjalizowanych
danych, które system definiuje jako startujące z zerową zawartością. Stąd jeśli
ostania strona danych pliku zawiera informacje nie w logicznej stronie pamięci
dodatkowe dane muszą być ustawione na zeroom a nie na nieznane zawartości
pliku wykonywalnego. ,,Nieczystości'' w pozostałych trzech stronach nie są
logiczną częścią obrazu procesu. Nie jest zdefiniowane czy system je kasuje.
Obraz dla tego programu wyglądałby następująco, przyjmując 4 KB strony.
+ Rysunek 2-7: Segmenty obrazu procesu
Adresy wirtualne Zawartość Segment
=============== ======== =======
0x8048000 Dopełnienie nagłówka Tekst
0x100 bajtów
0x8048100 Segment kodu
...
0x2be00 bajtów
0x8073f00 Dopełnienie danych
0x100 bajtów
0x8074000 Dopełnienie kodu Dane
0xf00 bajtów
0x8074f00 Segment danych
...
0x4e00 bajtów
0x8079d00 Niezainicjalizowane dane
0x1024 zerowych bajtów
0x807ad24 Dopełnienie strony
0x2dc zerowych bajtów
Jeden z aspektów ładowania segmentów różni się pomiędzy plikami wykonywalnymi
i obiektami współdzielonymi. Pliku wykonywalne zazwyczaj zawierają absolutny
kod. Aby pozwolić procesowi na poprawne wykonywanie się, segmenty muszą
przebywać pod wirtualnymi adresami użytymi do zbudowania pliku
wykonywalnego. Stąd system używa wartości p_vaddr niezmienionych jako
adresów wirtualnych
Z drugiej strony, segmenty obiektów współdzielonych zazwyczaj zawierają
kod niezależny od pozycji. Pozwala to wirtualnym adresom segmentów
zmieniać się między jednym procesem a drugim, bez zakłócania zachowania
wykonywania. Chociaż system wybiera adresy wirtualne dla indywidualnych procesów
zarządza względnymi pozycjami segmentów. Ponieważ kod niezależny od pozycji
używa względnego adresowania pomiędzy segmentami, różnica pomiędzy wirtualnymi
adresami w pamięci musi się zgadzać z różnicą pomiędzy wirtualnymi adresami w
pliku. Następująca tablica pokazuje możliwe przydziały wirtualnych adresów dla
obiektów współdzielonych, ilustrując stałe względne pozycje. Tablica ilustruje
również obliczenia bazowych adresów.
+ Rysunek 2-8: Przykład adresów segmentów współdzielonych obiektów.
Źródło Kod Data Adres bazowy
===== ==== ==== ============
Plik 0x200 0x2a400 0x0
Proces 1 0x80000200 0x8002a400 0x80000000
Proces 2 0x80081200 0x800ab400 0x80081000
Proces 3 0x900c0200 0x900ea400 0x900c0000
Proces 4 0x900c6200 0x900f0400 0x900c6000
======================= Dynamiczne łączenie ====================
Interpreter Programu
Plik wykonywalny może mieć jeden element nagłówka programu typu
PT_INTERP. Podczas exec(BA_OS), system otrzymuje ścieżkę z segmentu
PT_INTERP i tworzy obraz procesu inicjującego z segmentu pliku
interpretera: to znaczy, zamiast używać obrazu segmentu oryginalnego
pliku wykonywalnego, system tworzy obraz pamięci z interpretera. Potem
na interpreterze spoczywa odpowiedzialność za przejęcie kontroli od
systemu i zapewnienie środowiska dla programu użytkowego.
Interpreter otrzymuje kontrole w jeden z dwóch sposobów. Po pierwsze,
może otrzymać deskryptor pliku do odczytania pliku wykonywalnego,
zaczynając od początku. Może używać tego deskryptora pliku by odczytywać
i/lub mapować segmenty pliku wykonywalnego do pamięci. Po drugie, zależnie
od formatu pliku wykonywalnego, system może załadować plik wykonywalny
do pamięci zamiast podania interpretorowi deskryptora pliku. Z możliwym
możliwym wyłączeniem deskryptora pliku, stan początkowy procesu
interpretera dopasowuje to co otrzymałby pliku
wykonywalny. Sam interpreter nie może wymagać innego interpretera.
Interpreter może być plikiem wykonywalnym lub obiektem współdzielonym.
* obiekt współdzielony (w normalnym przypadku) jest ładowany jako
niezależny od pozycji, z adresami które mogą się różnić między
jednym procesem a drugim; system tworzy segment w obszarze dynamicznego
segmentu używanym przez mmap(KE_OS) i jemu podobne usługi.
Konsekwetnie (w efekcie? ang consequently) również interpreter
współdzielonego obiektu nie spowoduje konfliktu z oryginalnym
adresem segmentu pliku wykonywalnego.
* Plik wykonywalny jest ładowany pod dopasowane adresy; system tworzy
jego segmenty używając wirtualnych adresów z tablicy nagłówka programu.
W efekcie adresy wirtualne interpretera pliku wykonywalnego mogą
kolidować z pierwszym plikiem wykonywalnyml interpreter jest odpowiedzialny
za rozwiązywanie takich konfliktów.
Dynamiczny Linker
Podczas budowania pliku wykonywalnego który używa dynamicznego łączenia,
edytor łączenia dodaje element nagłówka programu typu PT_INTERP do
pliku wykonywalnego, w ten sposób powiadamiając system by wywoływał
dynamicznego linkera jak interpreter programu.
ZAUWAŻ: lokalizacja dynamicznych linkerów dostarczanych przez system
jest specyficzna dla procesora.
Exec(BA_OS) oraz dynamiczny linker współpracują aby stworzyć obraz procesu
dla programu, co wymaga następujących akcji:
* Dodania segmentu pamięci pliku wykonywalnego do obrazu procesu;
* Dodanie segmentu pamięci wspołdzielonego obiektu do obrazu procesu;
* Wykonania relokacji dla pliku wykonywalnego i jego współdzielonych
obiektów;
* Zamknięcia deskryptora pliku użytego do przeczytania
pliku wykonywalnego, o ile jakiś został dostarczony dynamicznemu linkerowi;
* Przekazania kontroli programowi, czyniąc to tak jak gdyby program
otrzymał kontrolę bezpośrednio od exec(BA_OS).
Edytor łączenia również konstruuje różne dane które są potrzebne
dynamicznemu linkerowi przy wykonywalnych i współdzielonych plikach
obiektowych. Tak jak to wyżej było pokazane w ,,Nagłówku programu'',
ten dane pozostają w segmencie wykonywalnym, czyniąc je dostępnymi
podczas wykonywania. (Jeszcze raz, przypomnienie (ang. recall)
dokładnej zawartości segmentu jest specyficzne dla procesora. Zobacz
dodatek o procesorach dla pełnej informacji).
* Sekcja .dynamic o typie SHT_DYNAMIC zawiera rozmaite dane. Struktura
przebywająca na początku sekcji zawiera adresy innych informacji dla
dynamicznego łączenia.
* Sekcja .hash o typie SHT_HASH zawiera tablicę haszową symboli.
* Sekcje .got i .plt o typie SHT_PROGBITS zawierają dwie oddzielne
tablice: tablice globalnego ofsetu i tablicę procedur łączenia.
Sekcje poniżej wyjaśniają jak dynamiczny linker używa ich i zmienia
tablice aby stworzyć obraz pamięci dla plików obiektowych.
Ponieważ każdy zgodny z ABI program importuje podstawowe usługi
systemowe z biblioteki współdzielonych, dynamiczny linker bierze udział
w każdym wykonaniu programu zgodnego z ABI.
Tak jak to wyjaśnia punkt ,,Ładowanie programu w dodatku o procesorach,
obiekty mogą zajmować wirtualne adresy które różnią się od adresów
zapisanych w tablicy nagłówka programu w pliku. Dynamiczny linker
relokuje obraz pamięci, uaktualniając bezwzględne adresy zanim
aplikacja otrzyma kontrolę. Chociaż wartości bezwzględne adresów
byłyby poprawne gdyby biblioteki były ładowane pod adresem zdefiniowanym
w tablicy nagłówka programu, tak normalnie się nie robi.
Jeżeli środowisko procesu [zobacz exec(BA_OS)] zawiera zmienną nazwaną
LD_BIND_NOW z niepustą wartością, dynamiczny linker podejmuje wszelkie
relokacje zanim przekaże kontrolę programowi. Na przykład wszystkie
ustawienia zmiennej środowiskowej poniżej spowodowałyby to zachowanie.
* LD_BIND_NOW=1
* LD_BIND_NOW=on
* LD_BIND_NOW=off
W przeciwnym razie LD_BIND_NOW albo nie pojawia się w środowisku albo
posiada pustą wartość. Dynamiczny linker może ,,leniwie'' wartościować
pozycje tablicy procedur łączenia, w ten sposób unikając rozwiązywania
symboli i nakładu związanego z relokacją dla funkcji, które nie są
wywoływane. Zobacz też ,,Tablica łączenia procedur'' w tej części po
więcej informacji.
Sekcje dynamiczne
Jeśli plik obiektowy bierze udział w dynamicznym łączeniu, jego
tablica nagłówka programu będzie zawierał element typu PT_DYNAMIC.
Ten ,,segment'' zawiera sekcję .dynamic. Specjalny symbol, _DYNAMIC,
zaznacza sekcję, który zawiera (tlumacz: sekcja czy symbol??) tablicę
następujących struktur:
+ Rysunek 2-9: Struktury Dynamiczne
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Sword d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
extern Elf32_Dyn _DYNAMIC[];
Dla każdego obiektu tego typu, d_tag kontroluje interpretację d_un.
* d_val
Te obiekty Elf32_Word reprezentują wartości całkowite z różnymi
interpretacjami.
* d_ptr
Te obiekty Elf32_Addr reprezentują wirtualne adresy programu. Jak
poprzednio wspomniano, adresy wirtualne pliku mogą być różne od
adresów wirtaulnych w pamięci podczas wykonywania. Podczas interpretowania
adresów zawartych w sekcji dynamicznej, dynamiczny linker oblicza
właściwe adresy, oparte na oryginalnych wartościach w pliku i
adresu bazowego w pamięci. Dla spójności, pliku nie zawierając pozycji
relokacji do ,,poprawiania'' adresów w strukturze dynamicznej.
Następująca tablica wymienia (ang. tag) wymagania dla obiektowych
plików wykonywalnych i współdzielonych. Jeśli tag jest zaznaczony jako
,,obowiązkowy'', wtedy tablica dynamicznego łączenia dla każdego
pliku zgodnego z ABI musi posiadać wpis tego typu. Tak samo ,,opcjonalnie''
oznacza że wpis dla taga może się pojawić ale nie jest wymagany.
+ Rysunek 2-10: Tagi dynamicznej tablicy, d_tag
Nazwa Wartość d_un Wykonywalny Współdzielony
==== ===== ==== ========== =============
DT_NULL 0 ignorownany obowiązkowy obowiązkowy
DT_NEEDED 1 d_val opcjonalny opcjonalny
DT_PLTRELSZ 2 d_val opcjonalny opcjonalny
DT_PLTGOT 3 d_ptr opcjonalny opcjonalny
DT_HASH 4 d_ptr obowiązkowy obowiązkowy
DT_STRTAB 5 d_ptr obowiązkowy obowiązkowy
DT_SYMTAB 6 d_ptr obowiązkowy obowiązkowy
DT_RELA 7 d_ptr obowiązkowy opcjonalny
DT_RELASZ 8 d_val obowiązkowy opcjonalny
DT_RELAENT 9 d_val obowiązkowy opcjonalny
DT_STRSZ 10 d_val obowiązkowy obowiązkowy
DT_SYMENT 11 d_val obowiązkowy obowiązkowy
DT_INIT 12 d_ptr opcjonalny opcjonalny
DT_FINI 13 d_ptr opcjonalny opcjonalny
DT_SONAME 14 d_val ignorowany opcjonalny
DT_RPATH 15 d_val opcjonalny ignorowany
DT_SYMBOLIC 16 ignored ignorowany opcjonalny
DT_REL 17 d_ptr obowiązkowy opcjonalny
DT_RELSZ 18 d_val obowiązkowy opcjonalny
DT_RELENT 19 d_val obowiązkowy opcjonalny
DT_PLTREL 20 d_val opcjonalny opcjonalny
DT_DEBUG 21 d_ptr opcjonalny ignorowany
DT_TEXTREL 22 ignored opcjonalny opcjonalny
DT_JMPREL 23 d_ptr opcjonalny opcjonalny
DT_LOPROC 0x70000000 niezdefiniowany ---------------------
DT_HIPROC 0x7fffffff niezdefiniowany ---------------------
* DT_NULL
Wpis z tagiem DT_NULL oznacza koniec tablicy _DYNAMIC
* DT_NEEDED
Ten element zawiera ofset tablicy łańcuchów zakończonych znakiem
pustym łańcuchów, podających nazwy potrzebnych bibliotek. Ofset
jest indeksem do tablicy zawartej w pozycji DT_STRTAB. Zobacz
,,Zależności obiektów współdzielonych'' dla więcej informacji o tych
nazwach. Tablica dynamiczna może zawierać wielokrotne wpisy z tymi typami.
Porządek tych wpisów jest znaczący, chociaż ich pozycja względem
innych typów nie jest.
* DT_PLTRELSZ
Ten element zawiera totalny rozmiar w bajtach pozycji relokacji
związanych z tablicą procedur łączenia. Jeśli wpis o typir
DT_JMPREL jest obecny, musi mu towarzyszyć DT_PLTRELSZ.
* DT_PLTGOT
Ten element zawiera adres związany z tablicą procedur łączenia i/lun
tablicą globalnych ofsetów. Zobacz punkt dodatku o procesorach po więcej
szczegóły.
* DT_HASH
Ten element zawiera adres haszowej tablicy symboli, opisanej w
,,Tablicy Haszowej''. Ta tablica haszowa odnosi sie do tablicy
symboli wskazywanej/do której się odnosi element DT_SYMTAB.
* DT_STRTAB
Ten element zawiera adresy tablicy nazw, opisanej w części pierwszej.
Nazwy symboli, nazwy bibliotek i inne łańcuchy znajdują się w tej
tablicy.
* DT_SYMTAB
Ten element zawiera adresy tablicy symboli, opisanej w części pierwszej
z wpisami Elf32_Sym dla 32-bitowej klasy plików.
* DT_RELA
Ten element zawiera adres tablicy relokacji, opisanej w części 1.
Wpisy w tej tablicy posiadają z góry znane (ang explicit addends)
dodatki, takie jak Elf32_Rela dla 32-bitowej klasy plików.
Plik obiektowy może posiadać wiele sekcji relokacji. Podczas budowania
tablicy relokacji dla współdzielonego lub wykonywalnego pliku
obiektowego, edytor łączenia (ang. catenates... może concatenates?)
formuje z tych sekcji pojedyncza tablicę. Chociaż sekcje pozostają
niezależne w pliku obiektowym, dynamiczny linker widzi pojedyńczą tablicę.
Kiedy dynamiczny linker tworzy obraz procesu dla pliku wykonywalnego
albo dodaje współdzielony obiekt do obrazu procesu, czyta tablicę relokacji
i podejmuje związane akcje. Jeśli ten element jest obecnym struktura
dynamiczna musi również posiadać elementy DT_RELASZ i DT_RELAENT. Kiedy
relokacja jest ,,obowiązkowa'' dla pliku, albo DT_RELA albo DT_REL mogą
się pojawić (obydwa są dozwolone, lcze nie wymagane).
* DT_RELASZ
Ten element zawiera totalny rozmiar w bajtach tablicy relokacji
DT_RELA.
* DT_RELAENT
Ten element zawiera rozmiar w bajtach wpisu DT_RELA relokacji
* DT_STRSZ
Ten element zawiera rozmiar w bajtach tablicy nazw.
* DT_SYMENT
Ten element zawiera rozmiar w bajtach wpisu tablicy symboli.
* DT_INIT
Ten element zawiera adres funkcji inicjalizacyjnej, omówionej w
punkcie ,,Funkcje Inicjalizacyjne i Kończące'' poniżej.
* DT_FINI
Ten element zawiera adres funkcji kończącej omówionej w punkcie
,,Funkcje Inicjalizujące i konczące''.
* DT_SONAME
Ten element zawiera ofset tablicy nazw/łańcuchów zawierającej
łańcuchy zakończone znakiem pustym, podające nazwę współdzielonego
obiektu. Ofset jest indeksem do tablicy zapisanej w wpisie DT_STRTAB.
Zobacz punkt ,,Zależności w Plikach Obiektowych'' poniżej aby uzyskać
więcej informacji na ten temat.
* DT_RPATH
Ten element zawiera ofset tablicy nazw/łańcuchów zawierającej
łańcuchy zakończone znakiem pustym, podające nazwę ścieżkę przeszukiwań
bibliotek, omówioną w ,,Zależnościach w Plikach Obiektowych''. Ofset jest
indeksem do tablicy zapisanej w wpisie DT_STRTAB.
* DT_SYMBOLIC
Obecność tego elementu w bibliotece współdzielonych obiektów zmienia
algorytm dynamicznego linkera rozwiązywania symbolów dla odniesień
w środku biblioteki. Zamiast zaczynać przeszukiwanie w środku pliku
wykonywalnego, dynamiczny linker zaczyna od samego obiektu
współdzielonego. Jeśli symbol do którego nastąpiło odniesienie nie zawiera
się w obiekcie współdzielonym, dynamiczny linker zaczyna przeszukiwać
pliki wykonywalne i inne obiekty współdzielone w zwykły sposób.
* DT_REL
Ten element jest podobny do DT_RELA, poza tym że jego tablica posiada
dodatki nie wiadome z góry (ang. implicit), takie jak Elf32_Rel dla klasy
plików 32-bitowych. Jeśli ten element jest obecny, struktura dynamiczna
musi posiadać również elementy DT_RELSZ i DT_RELENT.
* DT_RELSZ
Ten element zawiera rozmiar w bajtach tablicy relokacji DT_REL.
* DT_RELENT
Ten element zawiera rozmiar w bajtach wpisu relokacji DT_REL.
* DT_PLTREL
To pole definiuje typ wpisu relokacji do którego odnosi się
tablica procedur łączenia. Wartość d_val zawiera DT_REL lub DT_RELA,
zależnie od kontekstu. Wszystkie relokacje w tablicy procedur łączenia
muszą używać tej samej relokacji (od tłumacza: typu relokacji jak sądzę).
* DT_DEBUG
To pole jest używana do debuggingu. Jego zawartość nie jest zdefiniowana
przez ABI. Programy które dostają się do tego wpisu nie są zgodne z ABI.
* DT_TEXTREL
Nieobecność tego pola oznacza że żaden wpis relokacji nie powinien
powodować modyfikacji niezapisywalnego segmentu, tak jak wyspecyfikowano
w zezwoleniach do segmentu w tablicy nagłówka programu. Jeśli pole to
jest obecne, jeden lub więcej wpis relokacji może zażądać modyfikacje
do niezapisywalnego segmentu, i odpowiednio może przygotowywać
dynamiczny linker.
* DT_JMPREL
Jeśli obecne, to pole pozycji d_prt przechowuje adres pozycji
relokacji związane wyłącznie z tablicą procedur łączenia. Separowanie
tych pozycji relokacji pozwala dynamicznemu linkerowi ignorować je
podczas inicjalizacji procesu, o ile jest włączone ,,leniwe'' wiązanie.
Jeśli to pole jest obecne, odnośne pozycje typu DT_PLTRELSZ i DT_PLTREL
muszą również być obecne.
* od DT_LOPROC do DT_HIPROC
Wartości w tym domkniętym zbiorze są zarezerwowane dla semantyki
specyficznej dla procesora.
Za wyjątkiem elementu DT_NULL na końcu tablicy i względnego porządku
elementów DT_NEEDED, wpisy mogą pojawiać się w dowolnym porządku.
Wartości tagów nie pojawiające się w tablicy są zarezerwowane.
Zależności w obiektach współdzielonych
Kiedy edytor łączenia przetwarza biblitekę (ang. archive library),
ekstrahuje pola biblioteki i kopiuje je do wynikowego pliku
obiektowego. Te statycznie wkompilowane usługi są dostępne podczas
wykonywania bez potrzeby korzystania z usług dynamicznego linkera.
Obiekty współdzielone dostarczają również usług i dynamiczny linker
musi dołączyc odpowiedni współdzielony plik obiektowy to obrazu pliku
do wykonania. Stąd wspołdzielone i wykonywalne pliki obiektowe
opisują ich spsecyficzne zależności.
Kiedy dynamiczny linker tworzy segmenty pamięci dla pliku obiektowego,
zależności (zapisane w wpisie DT_NEEDED struktury dynamicznej) mowią jakie
obiekty współdzielone są potrzebne do wsparcia usług procesu (?).
Poprzez powtarzające łączenie zależności i współdzielonych obiektów do
których się one odnoszą, dynamiczny linker buduje kompletny
obraz procesu. Podczas rozwiązywania symbolicznych odnośników, dynamiczny
linker przegląda tablicę symboli za pomocą przeszukiwania wszerz.
To znaczy najpierw patrzy na tablicę symboli samego programu, potem na
tablice symboli wpisów w DT_NEEDED (zgodnie z ich uporządkowaniem), potem
na wpisy drugiego poziomu DT_NEEDED (od tłumacza tj. wymagane przez te
z DT_NEEDED pierwszego poziomu) itd. Współdzielone pliki obiektowe muszą
być odczytywalne przez proces - inne zezwolenia nie są wymagane.
ZAUWAŻ: Nawet kiedy do wpółdzielonego obiektu istnieje wiele odwołań
w liście zależności, dynamiczny linker dołączy obiekt do procesu tylko raz.
Nazwy w liście zależności są kopiami albo łańcuchów DT_SONAME
albo nazwami ścieżek współdzielonych obiektów użytych di zbudowania pliku
obiektowego. Na przykład, jeśli edytor łączenia zbuduje plik wykonywalny
używając jednego obiektu współdzielonego z wpisem DT_SONAME jako lib1 i
innej biblioteki obiektów współdzielonych w nazwą ścieżki /usr/lib/lib2,
plik wykonywalny zawierać będzie lib1 i /usr/lib/lib2 w swojej liście
zależności.
Jeśli nazwa obiektu współdzielonego zawiera jeden lub więcej znaków
"/" (szleszy jak to mawiał doktor Stefanowski :) ) - gdziekolwiek w nazwie,
tak jak /usr/lib/lib2 jak powyżej albo katalog/plik, dynamiczny linker
używa tej nazwy bezpośrednio jako nazwy ścieżki. Jeśli nazwa nie ma znaków
"/", tak jak wyżej lib1, trzy udogodnienia definiują przeszukiwania
ścieżki obiektów współdzielonych, wg następującej procedury:
* Po pierwsze, tag dynamicznej tablicy DT_RPATH może podać łańcuch
który zawiera listę katalogów, rozdzielanych przez dwukropek (:). Na
przykład, łańcuch /home/dir/lib:/home/dir2/lib: powiadamia dynamicznego
linkera by przeszukiwął najpierw katalog /home/dir/lib a następnie
/home/dir2/lib, i wreszcie katalog domowy aby odnaleźć zależności.
* Po drugie, zmienna w środowisku procesu LD_LIBRARY_PATH [zobacz
exec(BA_OS)] może zawierać listę katalogów jak wyżej, opcjonalnie
zakończonych średnikiem i kolejną listę katalogów. Następujące
wartości byłyby równoważne do poprzedniego przykładu:
LD_LIBRARY_PATH=/home/dir/lib:/home/dir2/lib:
LD_LIBRARY_PATH=/home/dir/lib;/home/dir2/lib:
LD_LIBRARY_PATH=/home/dir/lib:/home/dir2/lib:;
Wszystkie katalogi LD_LIBRARY_PATH są przeszukiwane po tych z DT_RPATH.
Chociaż niektóre programy (takie jak edytor łączenia) traktują
listy przed i po średniku różnie, dynamiczny linker tak nie czyni.
Dynamiczny linker akceptuje notację z średnikiem z semantyką opisaną
powyżej.
*Wreszcie, jeśli pozostałe dwie grupy katalogów nie będą zawierać pożądanej
biblioteki, dynamiczny linker przeszukuje /usr/lib.
ZAUWAŻ: Dla bezpieczeństwa, dynamiczny linker ignoruje
środowiskowe ustawienia przeszukiwania, takie jak LD_LIBRABRY_PATH dla
programów typu set-user i set-group ID. Jednakże przeszukuje katalogi
wyszczególnione w DT_RPATH i /usr/lib.
Tablica globalnych ofsetów
Kod zależny od pozycji w ogólności nie może zawierać absolutnych
wirtualnych adresów. Tablica globalnych ofsetów zawiera absolutne
adresy w prywatnych danych, w ten sposób czyniąc adresy dostępnymi
bez niweczenia niezależności od pozycji i współdzielenia kodu programu.
Program odwołuje się do swojej tablicy globalnych ofsetów używając
adresowania niezależnego od pozycji i wyciągając absolutne wartości, w
ten sposób zmieniając odwołania/odnośniki niezależne od pozycji do
bezwzględnych/absolutnych lokalizacji.
Początkowo, tablica globalnych ofsetów zawiera informacje wymagane
przez swoje wpisy relokacji [zobacz ,,Relokacja'' w części pierwszej].
Gdy system stworzy segmenty pamięci dla ładowalnego pliku obiektowego,
dynamiczny linker przetwarza wpisy relokacji, z których niektóre będą
typu R_386_GLOB_DAT odnoszącego się do tablicy globalnych ofsetów.
Dynamiczny linker znajduje związane wartości symboli, oblicza ich
absolutne wartości i ustawia odpowiednie wpisy w tablicy pamięci na
właściwe wartości. Chociaż absolutne adresy są nieznane kiedy edytor
łączenia buduje plik obiektowy, dynamiczny linker wie adresy wszystkich
segmentów pamięci i stąd umie obliczyć absolutną wartość zawartych tam
symboli.
Jeśli program wymaga bezpośredniego dostępu do absolutnych adresów
symbolu, ten symbol będzie posiadał wpis w tablicy globalnych ofsetów
(Zaraz, zaraz.. a może to jest globalna tablica ofsetów?). Ponieważ
pliki wykonywalne i współdzielone obiekty posiadają oddzielne
tablice globalnych ofsetów adresy symboli mogą pojawić się w kilku
tablicach. Dynamiczny linker przetwarza wszystkie relokacje w tych
tablicach globalnych ofsetów zanim przekazuje kontrolę do jakiegokolwiek
kodu w obrazie procesu, w ten sposób zapewniając dostępność absolutnych
adresów podczas wykonywania.
Wpis w tablicy jest zarezerwowany do trzymania adresów dynamicznej
struktury, do której odwołuje się za pomocą symbolu _DYNAMIC. To pozwala
programowi - takiemu jak dynamiczny linker - odnajdywać swoje własne
struktury dynamiczne jeszcze nie przetwarzając własnch wpisów/pozycji
relokacji. Jest to specjalnie ważne dla dynamicznego linkera, ponieważ
musi on najpierw zainicjować się bez polegania na tym, że inne programy
zrelokują jego obraz pamięci (?). W 32bitowej architekturze intela, wpisy
pierwszy i drugi w tablicy globalnych ofsetów (globalnej tablicy ofsetów?
ang. global offset table) są również zarezerwawane i opisuje je punkt
,,Tablica Procedur Łączenia'' poniżej.
System może wybrać różne adresy segmentów pamięci dla tych samych obiektów
współdzielonych w różnych programach; może nawet wybrać różne adresy
bibliotek dla różnych wykonań tego samego programu. Jednakże segmenty pamięci
nie zmieniają adresów od czasu stworzenia obrazu procesu. Tak długo jak
długo istnieje proces, jego segmenty pamięci pozostają pod raz
dopasowanymi adresami wirtualnymi.
Format i interpretacja tablicy globalnych ofsetów są zależne od procesora.
Dla 32-bitowej architektury Intela, do dostępu do tablicy może zostać
użyty symbol _GLOBAL_OFFSET_TABLE_.
+ Rysunek 2-11: Tablica globalnych ofsetów
extern Elf32_Addr _GLOBAL_OFFSET_TABLE_[];
Symbol _GLOBAL_OFFSET_TABLE_ może przebywać w środku sekcji .got,
pozwalając zarówno na ujemne i dodatnie ,,zapisy'' do tablicy
adresów.
Tablica procedur łączenia
Tak samo jak tablica globalnych ofsetów przekształca adresy
niezależne od pozycji w bezwzględne lokalizacje, tak samo tablica
procedur łączenia przekształca niezależne od pozycji w absolutne
lokalizacje. (zmienić w tablice łączenia procedur!). Edytor łączenia
nie może rozwiązać przekazywania wykonywania (takie jak wołania
funkcji) pomiędzy jednym plikiem wykonywalnym lub współdzielonym a innym.
Na skutek tego edytor łączenia aranżuje sytuację w której program
przekazuje kontrolę do wpisów w tablicy łączenia procedur (ang.
the link editor arranges to have the program transfer control to
procedure linkage table). W architekturze Systemu V, tablice łączenia
procedur przebywają w współdzielonym segmencie kodu, ale używają
adresów z prywatnej globalnej tablicy ofsetów. Dynamiczny linker
determinuje absolutne adresy przeznaczenia i odpowiednio modyfikuje obraz
pamięci globalnej tablicy ofsetów. W ten sposób dynamiczny linker może
przekształcać wpisy bez niweczenia niezależności od pozycji i
współdzielenia kodu/tekstu programu. Pliki wykonywalne i współdzielone
pliki obiektowe posiadają rozdzielne tablice łączenia procedur.
+ Rysunek 2-12: Bezwględna tablica procedur łączenia {*}
.PLT0:pushl got_plus_4
jmp *got_plus_8
nop; nop
nop; nop
.PLT1:jmp *name1_in_GOT
pushl $offset
jmp .PLT0@PC
.PLT2:jmp *name2_in_GOT
pushl $offset
jmp .PLT0@PC
...
+ Rysunek 2-13: Tablica procedur łączenia niezależna od pozycji.
.PLT0:pushl 4(%ebx)
jmp *8(%ebx)
nop; nop
nop; nop
.PLT1:jmp *name1@GOT(%ebx)
pushl $offset
jmp .PLT0@PC
.PLT2:jmp *name2@GOT(%ebx)
pushl $offset
jmp .PLT0@PC
...
ZAUWAŻ: Tak jak to pokazuje rysunek, instrukcje tablicy procedur łączenia
używają innego trybu adresowania operandów dla kodu absolutnego i zależnego
od pozycji. Jest to jednak bez znaczenia, gdyz ich interfejs do linkera
pozostaje taki sam.
Postępując według kroków poniżej, dynamiczny linker i program
,,współpracują'' aby rozwiązać symboliczne odniesienia poprzez tablicę
procedur łączenia i tablicę globalnych ofsetów.
1. Podczas pierwszego tworzenia obrazu pamięci programu, dynamiczny
linker ustawia drugi i trzeci wpis w globalnej tablicy ofsetów na
specjalne wartości. Kroki niżej wyjaśniają więcej na temat tych wartości.
2. Jeśli tablica łączenia procedur jest niezależna od pozycji, adres
globalnej tablicy ofsetów musi przebywać w %ebx. Każdy współdzielony
plik obiektowy w obrazie procesu posiada swoją własną tablicę łączenia
procedur i kontrola przechodzi do wpisu w tablicy łączenia procedur tylko
w środku tego samego pliku obiektowego. Stąd funkcja dokonująca wywołania
(ang. calling function) jest odpowiedzialna za ustawienie bazowego
rejestru globalnej tablicy ofsetów zanim odwoła się do wpisu w tablicy
łączenia procedur.
3. Przyjmijmy dla przykładu że program wywołuje name1, co przekazuje
kontrolę do nazwy/etykiety/symbolu (ang. label) .PLT1.
4. Pierwsza instrukcja powoduje skok do adresu w pozycji w globalnej
tablicy ofsetów dla name1. Początkowo, globalna tanlica ofsetów
przetrzymuje adres następnej instrukcji pushl a nie właściwy adres name1.
5. Program odkłada ofset relokacji (dalej po prostu ofset) na stos.
Ofset relokacji jest 32-bitowym nieujemnym ofsetem w tablicy relokacji.
Wyznaczona pozycja/wpis relokacji będzie posiadać typ R_386_JMP_SLOT,
a jej ofset będzie specyfikować wpis w globalnej tablicy ofsetów użyty
w poprzedniej instrukcji jmp. Wpis relokacji będzie również zawierać
indeks tablicy symboli, w ten sposób zawiadamiając dynamicznego linkera
do jakiego symbolu się odwołujemy, w tym przypadku name1.
6. Po odłożeniu ofsetu relokacji, program wykonuje skok do .PLT0, pierwszego
wpisu w tablicy łączenia procedur. Instrukcja pushl odkłada
wartość drugiego wpisu w globalnej tablicy ofsetów (got_plus_4 albo
4(%ebx) na stosie, w ten sposób podając dynamicznemu linkerowi słowo
identyfikującej informacji. Następnie program wykonuje skok do adresu w
trzecim wpisie globalnej tablicy ofsetów (got_plus_8 albo 8(%ebx), co
przekazuje kontrolę dynamicznemu linkerowi.
7. Kiedy dynamiczny linker otrzymuje kontrolę, czyści (ang. unwinds) stos,
patrzy na wyznaczoną pozycję relokacji, odnajduje wartość symbolu, zachowuje
,,prawdziwy'' adres dla name1 w globalnej tablicy ofsetów i przekazuje
kontrolę do pożądanego przeznaczenia.
8. Następne wykonywanie wpisów w tablicy łączenia procedur będą już
odwoływać się bezpośrednio do name1, bez wołania dynamicznego linkera po
raz drugi. Oznacza to, że instrukcja jmp w .PLT1 przekaże kontrolę do
name1, zamiast ,,przelatwywania przez'' (ang falling through) instrukcję
pushl.
Zmienna środowiskowa LD_BIND_NOW może zmienić zachowanie dynamicznego
linkera. Jeśli jej wartość nie jest pusta, dynamiczny linkera wartościuje
wpisy tablicy procedur łączenia zanim przekaże programowi kontrolę. Oznacza
to że dynamiczny linker przetwarza pozycje relokacji o typie
R_386_JMP_SLOT podczas inicjalizacji procesu. W innym przypadku dynamiczny
linker wartościuje wpisy w tablicy łączenia procedur w ,,leniwy'' sposób,
opóźniając rozwiązywanie symbolów i relokację do pierwszego wykonania
wpisu tablicy.
ZAUWAŻ: ,,Leniwe'' wiązanie zazwyczaj poprawia całkowitą wydajność
aplikacji, ponieważ nieużywane symbole nie powodują niepotrzebnego
nadmiaru (ang overhead) przy dynamicznym łączeniu. Jednakże mogą
pojawić się dwie sytuacje w których ,,leniwe'' wiązanie może
być niepożądane. Po pierwsze, pierwsze odwołanie do funkcji
współdzielonego obiektu może zabrać więcej czasu niż następne wołania,
ponieważ dynamiczny linker przechwytuje wołanie aby rozwiązać symbol.
Niektóre aplikacje nie mogą tolerować takiego nieprzywidywalnego
zachowania. Po drugie, jeśli pojawi się błąd i dynamiczny linker nie
będzie mógł rozwiązać symbolu, dynamiczny linker zakończy program.
Podczas ,,leinwego'' wiązania może to zdarzyć się w dowololnym czasie.
I znowu, niektóre aplikacje nie mogą takiego zachowanie tolerować.
Poprzez wyłączenie ,,leniwego'' wiązania, dynamiczny linker narzuca
pojawianie się wszystkich błędów podczas inicjalizacji procesu, zanim
aplikacja otrzyma kontrolę.
Tablica Haszująca
Tablica haszująca obiektów Elf32_Word wspiera dostęp do tablicy symboli.
Nazwy/oznaczenia (ang.labels) pojawiają się niżej aby wyjaśnić organizację
tablicy haszowej, ale nie są one częścią specyfikacji.
+ Rysunek 2-14: Haszująca tablica symboli
nbucket
nchain
bucket[0]
...
bucket[nbucket - 1]
chain[0]
...
chain[nchain - 1]
Tablica ,,koszyk'' (ang bucket array) zawiera nbucket wpisów, a tablica
łańcuchowa (chain array) zawiera nchain wpisów. Indeksy zaczynają się od
zera. Zarówno ,,koszyk'' jak i ,,łańcuch'' zawierają indeksy tablicy symboli
Tablica ,,łańcuch'' zawiera wpisy równoległe do tablicy symboli. Liczba
wpisów w tablicy symboli powinna byc równa nchain; tak więc indeksy
tablicy symboli akceptują również wpisów w tablicy ,,łańcuch''.
Funkcja haszująca, pokazana niżej, na wejściu otrzymuje nazwę symbolu
i zwraca wartość która może zostać użyta do obliczenia indeksu ,,koszyka''.
Tak więc jeśli funkcja haszująca zwróci wartość x dla nazwy,
bucket[x%nbucket] podaje indeks, y, do zarówno tablicy symboli jak i tablicy
,,łańcuch''. Jeżeli wpis w tablicy symboli nie odpowiada temu który
chcieliśmy znaleźć, chain[y] podaje następny wpis w tablicy symboli z
taką samą wartością haszową. Można w ten sposób podążać za łączami
z tablicy ,,łańcuch'' (chain) dopóki albo wpis w tablicy symboli zawiera
pożądaną nazwę albo wpis w tablicy ,,łańcuch'' zawiera wartość STN_UNDEF.
+ Rysunek 2-15: Funkcja haszująca
unsigned long
elf_hash(const unsigned char *name)
{
unsigned long h = 0, g;
while (*name) {
h = (h << 4) + *name++;
if (g = h & 0xf0000000)
h ^= g >> 24;
h &= ~g;
}
return h;
}
Funkcje Inicjalizujące i Kończące
Gdy dynamiczny linker zbuduje obraz procesu i przeprowadzi relokację,
każdy obiekt współdzielony otrzymuje możliwość wykonania pewnego kodu
inicjującego. Te funkcje inicjalizujące są wywoływane bez żadnego
określonego porządku, ale wszystkie inicjalizacyjne funkcje obiektów
współdzielonych wykonują się zanim plik wykonywalny otrzyma kontrolę.
Podobnie, obiekty współdzielone mogą mieć funkcje kończące, które
są wykonywane za pomocą mechanizmu atexit(BA_OS) gdy proces
podstawowy zacznie sekwencję zakończenia. I znowu porządek w jakim
dynamiczny linker wywoła te funkcje jest niezdefiniowany.
Obiekty współdzielone wyznaczają swoje funkcje inicjalizujące i kończące
za pomocą wpisów w DT_INIT i DT_FINI w strukturze dynamicznej opisanej
w punkcie ,,Sekcje Dynamiczne'' powyżej. Zazwyczaj kod dla tych
funkcji znajduje się w sekcjach .init i .fini, wspomnianych w
,,Sekcjach'' w części pierwszej.
ZAUWAŻ: Chociaż atexit(BA_OS) przetwarzanie na zakończenie zwykle
zostanie wykonane, nie ma żadnej gwarancji że zostanie wykonane po
śmierci procesu. W szczególności proces nie wykona przetwarzania
na zakończenie (kończącego, terminującego) jeśli wywoła _exit
[zobacz exit(BA_OS)] lub jeśli proces umrze ponieważ otrzyma sygnał
którego nie przechwycił lub nie zignorował.
________________________________________________________________
3. Biblioteka C
________________________________________________________________
========================== Biblioteka C ========================
Biblioteka C, libc, zawiera wszystkie symbole zawarte w libsys i na dodatek
zawiera funkcje wypisane w następnych dwóch tablicach. Pierwsz tablica
wypisuje funkcje z standardu ANSI C.
+ Rysunek 3-1: Zawartość libc, Nazwy bez synonimów
abort fputc isprint putc strncmp
abs fputs ispunct putchar strncpy
asctime fread isspace puts strpbrk
atof freopen isupper qsort strrchr
atoi frexp isxdigit raise strspn
atol fscanf labs rand strstr
bsearch fseek ldexp rewind strtod
clearerr fsetpos ldiv scanf strtok
clock ftell localtime setbuf strtol
ctime fwrite longjmp setjmp strtoul
difftime getc mblen setvbuf tmpfile
div getchar mbstowcs sprintf tmpnam
fclose getenv mbtowc srand tolower
feof gets memchr sscanf toupper
ferror gmtime memcmp strcat ungetc
fflush isalnum memcpy strchr vfprintf
fgetc isalpha memmove strcmp vprintf
fgetpos iscntrl memset strcpy vsprintf
fgets isdigit mktime strcspn wcstombs
fopen isgraph perror strlen wctomb
fprintf islower printf strncat
Dodatkowo, libc zawiera następujące usługi:
+ Rysunek 3-2: Zawartość libc, nazwy z synonimami.
__assert getdate lockf ** sleep tell **
cfgetispeed getopt lsearch strdup tempnam
cfgetospeed getpass memccpy swab tfind
cfsetispeed getsubopt mkfifo tcdrain toascii
cfsetospeed getw mktemp tcflow _tolower
ctermid hcreate monitor tcflush tsearch
cuserid hdestroy nftw tcgetattr _toupper
dup2 hsearch nl_langinfo tcgetpgrp twalk
fdopen isascii pclose tcgetsid tzset
__filbuf isatty popen tcsendbreak _xftw
fileno isnan putenv tcsetattr
__flsbuf isnand ** putw tcsetpgrp
fmtmsg ** lfind setlabel tdelete
** = Funkcja jest na poziomie 2 w SVID wydaniu 3 i stąd również na
poziomie 2 w ABI.
Poza symbolami wymienionymi w tablicy ,,Z synonimami'' powyżej,
synonimy w formie _ istnieją dla pozycji które nie są
wymienione z podkreśleniem poprzedzającym ich nazwę. Stąd libc zawiera
na przykład zarówno getopt jak i _getopt.
Z funkcji powyżej wymienonych, następujące nie są gdzie indziej
zdefiniowane:
int __filbuf(FILE *f);
Ta funkcja zwraca następny znak w wejściu dla f, zapełniają jego
(swój? ang. its) bufor jeśli potrzeba. Zwraca EOF jeśli pojawił się błąd.
int __flsbuf(int x, FILE *f);
Ta funkcja opróżnia znaki dla wejścia dla f tak jakby było wywołane
putc(x,f) i następnie dołącza wartość x do wynikowego strumienia
wyjściowego. Zwraca EOF jeśli pojawił się błąd i x wpw.
int _xftw(int, char *, int (*)(char *, struct stat *, int), int);
Odwołania do funkcji ftw(BA_LIB) są podmapowane pod tą funkcję
gdy aplikacje są kompilowane. Ta funkcja jest identyczna jak ftw(BA_LIB)
za wyjątkiem tego że _xftw() bierze wstawiony pierwszy argument, który
musi posiadać wartość 2.
Zobacz inne punkty w tym rozdziale po więcej udogodnień SVID, ANSI C
i POSIXa. Zobacz ,,Interfes Danych systemowych'' później w tym rozdziale
po więcej informacji. (EEE? od tłumacza - ale rozdział właśnie się urywa!)
Globalne symbole danych
Biblioteka libc wymaga aby były zdefiniowane pewne zewnętrzne symbole
danych aby jej funkcje działały poprawnie. Wszystkie symbole danych
wymagane dla biblioteki libsys muszą zostać zapewnione przez libc,
tak samo jak symbole wymienione w tablicy poniżej.
Dla formalnej deklaracji obiektów danych reprezentowanych przez te
symbole zobacz w Definicji Interfejsu Systemu V, Wydanie Trzecie lub
w punkcie ,,Definicje danych'' rodziału szóstego dla odpowiedniego
dodatku o procesorze do ABI Systemu V.
Dla wpisów w następujących tabelach które są w formie -_,
obydwa symbole w każdej parze reprezentują te same dane. Synonimy z
podkreśleniami są dostarczane dla zgodności z standardem ANSI C.
+ Rysunek 3-3: Zawartość libc, Globalne Zewnętrzne symbole danych
getdate_err optarg
_getdate_err opterr
__iob optind
optopt