| Zeile 1: | Zeile 1: | ||
| − | In diesem Tutorial werde ich beschreiben wie man einen in [[C]] programmierten [[Kernel]] mithilfe von [[GRUB]] bootet. | + | In diesem Tutorial werde ich beschreiben, wie man einen in [[C]] programmierten [[Kernel]] mithilfe von [[GRUB]] bootet. |
== Hinweis für Windowsianer == | == Hinweis für Windowsianer == | ||
| − | Ich werde hier beschreiben, wie ich es unter [[Linux]] mache. Unter [[Windows]] geht es auch, aber du musst sicherstellen, dass du ELF als Binärformat benutzt | + | Ich werde hier beschreiben, wie ich es unter [[Linux]] mache. Unter [[Windows]] geht es auch, aber du musst sicherstellen, dass du ELF als Binärformat benutzt – das heißt, du solltest dir erst entweder den Artikel [[Crosscompiler für Windows]] (Verwenden von [[Benutzer:Jidder | Jidder]]s Crosscompiler) oder [[Cross-Compiler]] (Erstellen eines eigenen Crosscompilers) durchlesen und eine entsprechende Entwicklungsumgebung aufsetzen. |
== Kernel == | == Kernel == | ||
| Zeile 64: | Zeile 64: | ||
* Die Flags können geändert werden, am besten schaut man sich die [[Multiboot]]-Spezifikation an. | * Die Flags können geändert werden, am besten schaut man sich die [[Multiboot]]-Spezifikation an. | ||
* Den Stack kann man natürlich an eine beliebige Stelle setzen, aber an der 2MB-Grenze wird er uns nicht stören. | * Den Stack kann man natürlich an eine beliebige Stelle setzen, aber an der 2MB-Grenze wird er uns nicht stören. | ||
| − | * Es wird die Adresse der Multiboot-Structure nicht umsonst an den C-Kernel übergeben. Am besten liest man so ziemlich am Anfang des Kernels die Informationen aus ihr. In der Multiboot-Structure steht z.B wie viel Speicher der Rechner hat | + | * Es wird die Adresse der Multiboot-Structure nicht umsonst an den C-Kernel übergeben. Am besten liest man so ziemlich am Anfang des Kernels die Informationen aus ihr. In der Multiboot-Structure steht z. B. wie viel Speicher der Rechner hat oder welche Module von GRUB mitgeladen wurden. |
== Zusammenbauen == | == Zusammenbauen == | ||
| − | Nun müssen wir den Kernel | + | Nun müssen wir den Kernel „zusammenbauen“. Dafür müssen wir den Assembler-Code assemblieren, den C-Code kompilieren und am Ende beides linken. |
=== Assemblieren === | === Assemblieren === | ||
| Zeile 79: | Zeile 79: | ||
Damit erstellen wir eine Objektdatei, mit der wir nachher noch linken können. Es ist wichtig, dass keine Includes und damit auch keine Funktionen aus der Standardbibliothek des Hostsystems benutzt werden. Diese müssen erst selbst implementiert werden. | Damit erstellen wir eine Objektdatei, mit der wir nachher noch linken können. Es ist wichtig, dass keine Includes und damit auch keine Funktionen aus der Standardbibliothek des Hostsystems benutzt werden. Diese müssen erst selbst implementiert werden. | ||
Eine genau Beschreibung der einzelnen Kommandozeilenparameter findet sich im [http://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html#Invoking-GCC GCC Handbuch (englisch)]. An dieser Stelle nur so viel: | Eine genau Beschreibung der einzelnen Kommandozeilenparameter findet sich im [http://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html#Invoking-GCC GCC Handbuch (englisch)]. An dieser Stelle nur so viel: | ||
| − | * -m32 | + | * -m32 weist gcc an, den Code für den 32-Bit-Protected-Mode (im Gegensatz zu -m64) zu erstellen |
* -ffreestanding weißt gcc an nicht davon auszugehen, dass eine Standardbibliothek zur Verfügung steht | * -ffreestanding weißt gcc an nicht davon auszugehen, dass eine Standardbibliothek zur Verfügung steht | ||
* -nostdinc weißt gcc an keine Header aus der Standardbibliothek des Hostsystems bereitzustellen | * -nostdinc weißt gcc an keine Header aus der Standardbibliothek des Hostsystems bereitzustellen | ||
=== Linken === | === Linken === | ||
| − | Meiner Meinung nach ist dies der schwierigste Teil des | + | Meiner Meinung nach ist dies der schwierigste Teil des „Zusammenbauens“. Beim Linken wird unser Assembler-Kernel und unser C-Kernel miteinander verbunden und es wird z. B. dem Assembler-Kernel auch gesagt wo die main-Funktion des C-Kernels liegt. Wir brauchen erstmal eine Konfigurationsdatei/[[Linkerscript]] für ''ld'': |
<pre> | <pre> | ||
| Zeile 124: | Zeile 124: | ||
Wenn man später mehr Dateien hat, können diese natürlich auch dazugelinkt werden. Man sollte den fertigen Kernel jetzt in einer Datei namens ''kernel.bin'' haben. | Wenn man später mehr Dateien hat, können diese natürlich auch dazugelinkt werden. Man sollte den fertigen Kernel jetzt in einer Datei namens ''kernel.bin'' haben. | ||
| − | Mehr Informationen was genau dieses Linkerscript genau macht und warum es notwendig bzw. sinnvoll ist findet ihr im Artikel [[Linkerscript]]. An dieser Stelle nur so viel: | + | Mehr Informationen darüber, was genau dieses Linkerscript genau macht und warum es notwendig bzw. sinnvoll ist, findet ihr im Artikel [[Linkerscript]]. An dieser Stelle nur so viel: |
| − | Mit | + | Mit „ENTRY(loader)“ wird ld mitgeteilt, dass der Kernel bei der Funktion loader (siehe [[C-Kernel mit GRUB#Kernel - Assembler | Assemblerteil des Kernels]]) beginnt. Dies wird in einem Datenfeld in der ELF-Header der „kernel.bin“ festgehalten und von GRUB benötigt um zur richtigen Adresse zu springen. |
| − | Der Rest des Linkerscripts gibt an wo die Sektionen, das sind die Bereiche welche man in Assembler mittels des | + | Der Rest des Linkerscripts gibt an wo die Sektionen, das sind die Bereiche, welche man in Assembler mittels des „.section .text“ beispielsweise direkt angeben kann, aus den ELF-Objektdateien (kernel_asm.o und kernel_c.o) in der ELF-Binärdatei „kernel.bin“ bzw. nach dem Laden im Speicher landen. Das „. = 0x00100000;“ gibt an, dass die „kernel.bin“ an die Adresse 1 MB gelinkt wird, d. h. sich nach dem Laden durch GRUB an die Adresse 1 MB und folgende im (physischen) Speicher befindet. 1 MB ist als Adresse gewählt, da GRUB keine Dateien unter diese Adresse lädt, der Grund dafür ist, dass der Speicherbereich zwischen 0 und 1 MB durch das BIOS, den Bootloader selbst und verschiedene Hardware belegt ist. Siehe dazu auch den Artikel über [[Speicherbereiche]]. |
== GRUB-Image erstellen == | == GRUB-Image erstellen == | ||
| Zeile 134: | Zeile 134: | ||
=== Floppy-Image erstellen === | === Floppy-Image erstellen === | ||
| − | Wir erstellen uns erstmal ein Floppy-Image und formatieren es mit [[ext2]]. Es können auch andere [[Dateisystem]] benutzt werden, z.B [[FAT]], man muss nur drauf achten, dass GRUB dieses Dateisystem unterstützt. | + | Wir erstellen uns erstmal ein Floppy-Image und formatieren es mit [[ext2]]. Es können auch andere [[Dateisystem]] benutzt werden, z. B .[[FAT]], man muss nur drauf achten, dass GRUB dieses Dateisystem unterstützt. |
Mit ''dd'' erzeugen wir ein 1,44MB großes Floppy-Image: | Mit ''dd'' erzeugen wir ein 1,44MB großes Floppy-Image: | ||
| Zeile 174: | Zeile 174: | ||
Fertig ist unser GRUB-Image! Man kann es jetzt noch mit | Fertig ist unser GRUB-Image! Man kann es jetzt noch mit | ||
$ dd if=floppy.img of=/dev/fd0 | $ dd if=floppy.img of=/dev/fd0 | ||
| − | auf eine Floppydisk kopieren und damit booten. Wenn alles klappt seht ihr | + | auf eine Floppydisk kopieren und damit booten. Wenn alles klappt, seht ihr „Hello World“ auf dem Bildschirm. |
== Vorschläge == | == Vorschläge == | ||
| − | * Auf Dauer wird es anstregend die ganzen | + | * Auf Dauer wird es anstregend, die ganzen Befehle einzutippen. Am einfachsten ist es eine [[Makefile]] zu benutzen. |
* Um nicht immer neustarten zu müssen, kann man einen Emulator wie [[QEMU]] oder [[Bochs]] nehmen. Dann muss man das Image auch erst gar nicht auf Diskette kopieren. | * Um nicht immer neustarten zu müssen, kann man einen Emulator wie [[QEMU]] oder [[Bochs]] nehmen. Dann muss man das Image auch erst gar nicht auf Diskette kopieren. | ||
* Mit ''color light-blue/black light-cyan/blue'' in menu.lst kann man dem Bootloader Farbe verpassen. | * Mit ''color light-blue/black light-cyan/blue'' in menu.lst kann man dem Bootloader Farbe verpassen. | ||