Ładowanie plików w formacie ELF - procedura do_load_elf_binary
Plik w formacie ELF podzielony
jest na sekcje - każda sekcja ma swoją wielkość w pliku, docelową wielkość
w pamięci, adres początkowy itp. Informacje te przechowywane są w nagłówku
sekcji.Plik ELF zawiera więc krótki nagłówek nadrzędny, zawierający m.
in. oznaczenie maszyny docelowej oraz liczbę sekcji, a po nim następują
nagłówki poszczególnych kawałków. Każdy z nich zawiera położenie odp. sekcji
w pliku. Nagłówek jednej z sekcji może wskazywać na konieczność załadowania
interpretatora ELF.
Pierwszą akcją procedury jest
sprawdzienie, czy badany plik jest rzeczywiście typu ELF, oraz czy jest
wykonywalny. Następnie sprawdzamy, czy kod programu jest przeznaczony dla
naszej maszyny - elf_check_arch(elf_ex.e_machine)). Następnie
sprawdzamy, czy system plików umożliwia stronicowanie zbiorów - do umieszczania
programu w pamięci używać będziemy do_mmap. Później wczytujemy
nagłówki wszystkich sekcji do pam. jądra. Następnie przeglądamy załadowane
informacje ustalając nazwę interpretatora, jeśli jest konieczny (tylko
jedna sekcja może podawać konieczność wczytania interpretatora - w przeciwnym
przypadku zwracamy błąd). Następnie sprawdzamy, czy interpretator jest
biblioteką typu ELF, czy a.out i jeśli w tym drugim przypadku dodajemy
deskryptor otwartego pliku ELF jako parametr wywołania interpretatora (?!).
Dalej już następuje "punkt bez wyjścia" - usuwamy kontekst starego programu
(flush_old_exec). Teraz
ponownie przebiegamy wszystkie nagłówki, aby ustalić rozmiar pamięci potrzebny
na dane nieinicjowane wszystkich sekcji pliku mających atrybut PT_LOAD,
oraz odwozorowujemy odpowiednie fragmenty pliku do pamięci wirualnej (do_mmap).
Teraz ładujemy bibliotekę interpretującą, jeśli jest to konieczne. W tym
celu wywołujemy load_elf_interpreter lub load_aout_interpreter.
Procedury te zawierają zdublowany kod funkcji "do_load_elf_library"
i "do_load_aout_library". Do zabicia procesu
w razie niepowodzenia już po wywołaniu flush_old_exec używamy sygnału SIGSEGV
(!?). Jeśli używany interpretator nie jest /usr/lib/libc.so.1
ani /usr/lib/ld.so.1, to personality
procesu ustawiamy na PER_SVR4. Uaktualniamy licznik instancji domeny wykonywania
i formatu odp. starego i nowego procesu. Podobnie, jak przy ładowaniu plików
a.out przerabiamy teraz parametry wywołonia i środowisko na tablice (char*)
w pamięci procesu (create_elf_tables). Teraz już przydzielamy
dodatkową pamięć na zmienne nieinicjowane wartościami z pliku i zerujemy
ją (padzero) Jeśli proces jest typu PER_SVR4, to ustawiamy
zerową stronę jako tylko do odczytu. Następnie uruchamiamy już nowy proces
(start_thread) i blokujemy go, jeśli ma podlegać śledzeniu
(pracy krokowej).
Wczytywanie biblioteki dzielonej typu ELF - procedura do_load_elf_library
Kod tej procedury pokrywa się w dużej mierze z do_load_elf_binary. Biblioteka może jednak zawierać co najwyżej dwie sekcje, z których tylko jedna może być typu PT_LOAD. Wyszukujemy tę sekcję i odwzorowujemy ją do pamięci wirtualnej. Następnie zerujemy "końcówkę" ostatniej strony przydzielonej na kod i dane inicjowane wartościami z pliku (padzero). Teraz już tylko pozostaje przydzielić pamięć na dane nieinicjowane.
Pozostałą część pliku binfmt_elf.c stanwi kod związany z wykonywaniem "core dump" - zrzutu pamięci procesu na dysk. Ze względu na brak związku z algorytmem exec, nie opisujemy tej części.