Wie bekommt der Kernel eine ausführbare Binärdatei, die unter Linux läuft?
Lesezeit: 6 Minuten
Daniel
Wie bekommt der Kernel eine ausführbare Binärdatei, die unter Linux läuft?
Es scheint eine einfache Frage zu sein, aber jemand kann mir helfen, tief zu graben? Wie wird die Datei in den Speicher geladen und wie wird der Ausführungscode gestartet?
Kann mir jemand helfen und Schritt für Schritt sagen, was passiert?
Ciro Santilli OurBigBook.com
Die besten Momente der exec Systemaufruf unter Linux 4.0
Der beste Weg, all das herauszufinden, ist das GDB-Step-Debuggen des Kernels mit QEMU: Wie debuggt man den Linux-Kernel mit GDB und QEMU?
fs/exec.c definiert den Systemaufruf at SYSCALL_DEFINE3(execve
Einfach weiterleiten an do_execve.
do_execve
Weiterleiten an do_execveat_common.
do_execveat_common
Um die nächste Hauptfunktion zu finden, verfolgen Sie den Rückgabewert retval ist zuletzt geändert.
Beginnt mit dem Bau von a struct linux_binprm *bprm um das Programm zu beschreiben, und übergibt es an exec_binprm ausführen.
exec_binprm
Folgen Sie erneut dem Rückgabewert, um den nächsten größeren Aufruf zu finden.
search_binary_handler
Handler werden durch die ersten magischen Bytes der ausführbaren Datei bestimmt.
Die beiden häufigsten Handler sind die für interpretierte Dateien (#! Magie) und für ELF (\x7fELF magic), aber es gibt noch andere in den Kernel eingebaute, zB a.out. Und Benutzer können sich aber auch selbst registrieren /proc/sys/fs/binfmt_misc
Der ELF-Handler ist unter definiert fs/binfmt_elf.c.
Siehe auch: Warum schreiben die Leute den #!/usr/bin/env python shebang in die erste Zeile eines Python-Skripts?
Das formats Liste enthält alle Handler.
Jede Handler-Datei enthält etwas wie:
static int __init init_elf_binfmt(void)
{
register_binfmt(&elf_format);
return 0;
}
und elf_format ist ein struct linux_binfmt in dieser Datei definiert.
__init ist magisch und fügt diesen Code in einen magischen Abschnitt ein, der aufgerufen wird, wenn der Kernel startet: Was bedeutet __init im Linux-Kernel-Code?
Abhängigkeitsinjektion auf Linker-Ebene!
Es gibt auch einen Rekursionszähler, falls sich ein Interpreter unendlich selbst ausführt.
Versuche dies:
echo '#!/tmp/a' > /tmp/a
chmod +x /tmp/a
/tmp/a
Wieder einmal folgen wir dem Rückgabewert, um zu sehen, was als nächstes kommt, und sehen, dass es kommt von:
retval = fmt->load_binary(bprm);
wo load_binary ist für jeden Handler in der Struktur definiert: Polymorphismus im C-Stil.
fs/binfmt_elf.c:load_binary
Funktioniert eigentlich:
parsen der ELF-Datei gemäß der ELF-Spezifikation, hier ist eine Übersicht über das ELF-Dateiformat: How to make an executable ELF file in Linux using a hex editor?
Richten Sie den anfänglichen Programmstatus des Prozesses basierend auf der geparsten ELF-Datei ein, insbesondere:
anfängliche Registereinrichtung in a struct pt_regs
Bei der anfänglichen Einrichtung des virtuellen Speichers wird der Speicher in den ELF-Segmenten angegeben: Was ist der Unterschied zwischen Abschnitt und Segment im ELF-Dateiformat
Anruf start_threadwodurch der Prozess als verfügbar markiert wird, um vom Planer geplant zu werden
Schließlich entscheidet der Scheduler, den Prozess auszuführen, und muss dann zu der in gespeicherten PC-Adresse springen struct pt_regs und gleichzeitig in einen weniger privilegierten CPU-Zustand wie Ring 3 / EL0 wechseln: Was sind Ring 0 und Ring 3 im Kontext von Betriebssystemen?
Der Scheduler wird regelmäßig durch eine Uhrenhardware geweckt, die periodisch Interrupts generiert, wie sie beispielsweise zuvor vom Kernel konfiguriert wurden das alte x86 PIT oder der ARM-Timer. Der Kernel registriert auch Handler, die den Scheduler-Code ausführen, wenn die Timer-Interrupts ausgelöst werden.
TODO: Quellenanalyse weiter fortsetzen. Was ich als nächstes erwarte:
Der Kernel analysiert den INTERP-Header des ELF, um den dynamischen Loader zu finden (normalerweise auf /lib64/ld-linux-x86-64.so.2).
falls vorhanden:
Der Kernel bildet den dynamischen Lader und die auszuführende ELF in den Speicher ab
Der dynamische Lader wird gestartet und nimmt einen Zeiger auf die ELF im Speicher.
Jetzt im Userland analysiert der Loader irgendwie elf Header und tut es dlopen auf sie
dlopen verwendet einen konfigurierbaren Suchpfad, um diese Bibliotheken zu finden (ldd und Freunde), speichere sie und informiere die ELF irgendwie, wo sie ihre fehlenden Symbole finden kann
Loader ruft die _start der ELF
andernfalls lädt der Kernel die ausführbare Datei direkt ohne den dynamischen Lader in den Arbeitsspeicher.
Es muss daher insbesondere prüfen, ob die ausführbare Datei PIE ist oder nicht, und ob sie an einer zufälligen Stelle im Speicher abgelegt wird: Was ist die Option -fPIE für positionsunabhängige ausführbare Dateien in gcc und ld?
“Die Formatliste enthält alle Handler”, welche Datei enthält diese Formatliste??
Zwei Systemaufrufe von dem Linux Kernel sind relevant. Das Gabel Systemaufruf (oder evtl vfork oder clone) wird verwendet, um einen neuen Prozess zu erstellen, der dem aufrufenden ähnlich ist (jeder Linux-Benutzerlandprozess außer init wird erstellt von fork oder Freunde). Das ausführen Systemaufruf den Adressraum des Prozesses durch einen neuen ersetzen (im Wesentlichen durch sort-of mmmap-ing von Segmenten aus der ausführbaren ELF-Datei und von anonymen Segmenten, dann Initialisierung der Register, einschließlich des Stapelzeigers). Das x86-64 ABI-Ergänzung und die Anleitung zum Linux-Assembler Details angeben.
Die dynamische Verknüpfung erfolgt danach execve und beinhaltet die /lib/x86_64-linux-gnu/ld-2.13.so Datei, die für ELF als “Interpreter” angesehen wird.
Es scheint etwas mit dem Elf-Format und der Unterroutine Fork zu tun zu haben. Und es könnte auch an /lib/ld-linux.so.2 beteiligt sein, um diese dynamischen Bibliotheken zu laden.
– Daniel
5. Dezember 2011 um 2:28 Uhr
fork ist keine Subroutine, sondern ein Systemaufruf, und wie alle Systemaufrufe ist es aus Sicht der Anwendung eine atomare Operation. Das ELF-Format ist mit dem verwandt execve Systemaufruf (nicht an die fork eines). Und die ld-linuc.so.2 Der dynamische Loader wird innerhalb der ausführbaren ELF-Datei referenziert, ist also verwandt mit execve und ELF.