This article is part of a series: Jump to series overview

Bei meinen Betriebssystem-Anfängen kam ich ziemlich schnell an den Punkt, dass etwas mit dem Speicherzugriff des C-Programms nicht richtig funktionierte.

Leicht zu überprüfen war, dass direkt in den Code einkompilierte Zeichen korrekt auf dem Bildschirm ausgegeben wurden. Hingegen Zeichenketten aus dem dynamischen Speicher (Heap) wurden nicht korrekt ausgelesen. Meine erste Vermutung war, dass irgendetwas mit der Heap-Adressierung nicht stimmt, aber wie überprüft man das?

Wenn man sein Programm mit QEMU ablaufen lässt, kann man in den Monitor-Modus wechseln. Dazu drückt man die Tasten für die Mausfixierung (üblicherweise Strg + Alt) und zusätzlich noch 2. Schon ist man im Monitor-Modus und kann Befehle absetzen. Zurückwechseln kann man übrigens wieder mit Strg + Alt + 1.

Adressabbildung bei Paging

Interessant hierbei für mich war zunächst einmal info mem. Allerdings stellte sich heraus, dass Paging noch gar nicht aktiviert war (“PG disabled”). Da ich davon ausging, dass dies schon gemacht wurde, habe ich selbiges mit info registers nachgeprüft. Und tatsächlich zeigte sich, dass das 31. Bit im CR0-Register nicht gesetzt war.

Werden die Daten von der Diskette in den RAM kopiert?

Als nächstes habe ich erstmal überprüft, ob die Daten auf der Diskette in dem Bereich liegen, der eingelesen wird. Eingelesen und in den RAM geschoben habe ich 10 Sektoren (=5120 Bytes) ab dem zweiten Sektor.

Mittels hexedit habe ich die Diskette geöffnet und nach einer kurzen Zeichenkette des Texts im dynamischen Speicher gesucht. Die Daten wurden gefunden und lagen zwischen den Speicherstellen 0x600 und 0x700, also hätten bereits drei Sektoren gereicht (jeder Sektor ist 0x200 Bytes groß, also hätten drei Sektoren ab dem zweiten Sektor von 0x200 bis 0x7ff gereicht).

Da noch kein Paging stattfand, war der nächste sinnvolle Schritt:

memsave [addr] [size] [filename]

Dies speichert einen Speicherbereich in eine Datei auf dem Host-System. [addr] gibt dabei die Startadresse an und [size] die Länge des auszulesenden Blocks. [filename] logischerweise den Dateinamen, unter dem gespeichert werden soll.

Ich habe mir zur Sicherheit das komplette erste MB ausgeben lassen.

memsave 0x0 0x100000 memorydump

Anschließend habe ich wieder mit hexedit nach dem Text gesucht. Er lag wie erhofft tatsächlich im Speicher an der erwarteten Stelle.

Ausgabe der Adressen auf dem Monitor

Da im Kern kein printf verfügbar ist und ich noch keines implementiert hatte, musste ich mir selbst einen schnellen Hack schreiben, um die Adresse ausgeben zu können, auf die mein Zeiger im C-Code nun wirklich zeigt.

Das lief dann in etwa so ab (Oktalausgabe):

// ...
*vidmem++ = (char) ((int) message >> 3 & 0x7) + 48;
vidmem++;
*vidmem++ = (char) ((int) message & 0x7) + 48;

Es zeigte sich, dass standardmäßig eine komplett falsche Adresse im Zeiger stand - nicht die Stelle, an der eigentlich der Text abgelegt wurde.

Damit wurde ich auf eine Warnmeldung aufmerksam, die während des make-Vorgangs ausgegeben wurde (aber nicht zu einem Fehler führte):

/home/eliteinformatiker/opt/cross/lib/gcc/i586-elf/4.8.0/../../../../i586-elf/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000008048060

Und hier steckte der Fehler letztlich. Ich hatte gcc nicht mitgeteilt, dass ich den Linker selbst ausführe (und diesem ein anderes Startsymbol mitteile), sodass gcc die Pointer falsch ausrichtete. Sobald ich -c zu den gcc-Optionen hinzufügte, lief alles richtig und der Pointer zeigte auch auf die richtige Adresse im Speicher.

I do not maintain a comments section. If you have any questions or comments regarding my posts, please do not hesitate to send me an e-mail to blog@stefan-koch.name.