Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: tiger717 am 23. October 2012, 21:58
-
Hallo,
Ich habe in den letzten Tagen die Wikieinträge im os-dev.org-Wiki zum Long Mode recht ausführlich studiert ("unsere" Seite dazu ist ja noch recht spärlich) und werde irgendwie nicht schlau, wie ich am besten Kernel und Bootstrap linke.
In diesem (http://wiki.osdev.org/Creating_a_64-bit_kernel) Eintrag werden dazu drei Methoden beschrieben:
Loading...
5.1 With your own boot loader Fällt schonmal weg, lieber Multiboot
5.2 With a separate loader
5.3 With a 32-bit bootstrap in your kernel
-
Hallo,
was willst du denn genau wissen?
Im zweiten Fall ist dein Second-Stage-Bootloader, der als Multiboot-Kernel geladen wird, ein ganz normaler 32-Bit-Kernel und der eigentliche Kernel, der als Multiboot-Modul geladen wird, ein Binary in einem von dir frei wählbaren Format (z.B. flat binary *g*).
Im dritten Fall ist dein Kernel ein 64-Bit-Kernel, an den vorne ein 32-Bit-Bootstrap rangepappt wurde. Das funktioniert ähnlich, wie ein Higher-Half-Kernel im 32-Bit-Modus, bei dem du gewisse Dateien/Sektionen an andere Adressen linkst wie den Rest. Das dort angegebene Linkerscript tut genau das.
Getrennte Dateien machen die Entwicklung einfacher, sind aber später mehr Aufwand in der Handhabung. Was du davon benutzt, ist dir überlassen.
Gruß,
Svenska
-
Entschuldigung, ich sehe gerade, der Editor hat da etwas "vergessen". :-(
Ich werde meine Frage heute Abend nochmal erklären, trotzdem Danke!
-
Okay, ich will eigentlich Methode 3 nehmen, da sie mir am korrektesten erscheint.
1. Ist das so?
2. Ich kompilier ich dann den (64-Bit) Kernel als elf64 und den Bootstrap als elf32 und linke dann das ganze zu elf64, oder? (elf64 kann ja von GRUB2 geladen werden)
3. So wie ich das verstanden habe, besteht der einzige Unterschied zwischen elf32 und elf64 in dem unterschiedlichen Addresslimit (und ein paar unwichtigen zusätzlichen Sektionen). Richtig?
thx
-
Okay, ich will eigentlich Methode 3 nehmen, da sie mir am korrektesten erscheint.
1. Ist das so?
Kommt drauf an, was du mit korrekt meinst. Ich bevorzuge die zweite Methode, weil GRUB Legacy und der Multiboot-Loader in qemu keine ELF64-Dateien laden können. Aber wenn dich das nicht stört, seh ich kein Problem mit deiner Methode.
2. Ich kompilier ich dann den (64-Bit) Kernel als elf64 und den Bootstrap als elf32 und linke dann das ganze zu elf64, oder? (elf64 kann ja von GRUB2 geladen werden)
Ich glaube der Bootstrap muss auch als ELF64 kompiliert werden. Der 32-Bit Code wird durch das .code32 erzeugt, aber der Linker will vermutlich einheitliche Dateitypen haben.
3. So wie ich das verstanden habe, besteht der einzige Unterschied zwischen elf32 und elf64 in dem unterschiedlichen Addresslimit (und ein paar unwichtigen zusätzlichen Sektionen). Richtig?
Es gibt noch ein paar Unterschiede in den Datenstrukturen, aber die betreffen dich nicht solange du keinen eigenen ELF-Loader implementierst. Außerdem kennt der 64-Bit GCC "Red Zones", aber wenn du dich an den osdev.org-Artikel hälst, und die deaktivierst, solltest du kein Problem damit bekommen.
-
Hallo nochmal,
folgendes Problem: nasm kann problemlos 32-Bit Code in elf64 stecken, gcc hat damit aber anscheinend große Probleme. Mit -m32 produziert gcc elf32.
Ich habe deshalb
objcopy -O elf64-little arch/x86/init32.o arch/x86/init.o
probiert. (init32.o ist die Outputdatei von gcc, -m32, init.o ist die Ausgabedatei)
Output von objcopy:
BFD: BFD (GNU Binutils for Ubuntu) 2.22.90.20120924 Erklärung fehlgeschlagen ../../bfd/reloc.c: 6621
make: *** [arch/x86/init.o] Speicherzugriffsfehler (Speicherauszug erstellt)
make: *** Datei »arch/x86/init.o« wird gelöscht
Öhm, ja? Speicherzugriffsfehler? Ich glaube nicht, dass das so gewollt ist...
init.o enthält ein paar Initialisierungsfunktionen, die doch recht ausführlich sind. Ich möchte die nicht unbedingt in Assembler neu schreiben...
Gibt es also eine Möglichkeit, dass gcc 32-bittigen elf64-Code erzeugt?
-
Hallo,
der Segfault klingt nach einem Bug in den Binutils. Bei mir tritt folgende Fehlermeldung auf:
$ objcopy -O elf64-little main64.o test32.o
BFD: BFD (GNU Binutils for Debian) 2.22 assertion fail ../../bfd/reloc.c:6054
BFD: test32.o: unsupported relocation type R_X86_64_32
objcopy:test32.o: Bad value
$
Gruß,
Svenska
-
tyndur macht aber auch irgendwelche Magie mit objcopy, vielleicht kannst du da was abschauen: http://git.tyndur.org/?p=tyndur.git;a=blob;f=src/kernel2/src/arch/amd64/Makefile.all
-
Okay, anscheinend ist der tyndur-Trick doch nicht so genial:
(ld-Output)
arch/x86/init.o: In function `_binary_arch_x86_init32_o_start':
(.data+0x0): multiple definition of `_binary_arch_x86_init32_o_start'
./arch/x86/init.o:(.data+0x0): first defined here
arch/x86/init.o: In function `_binary_arch_x86_init32_o_end':
(.data+0x5cd0): multiple definition of `_binary_arch_x86_init32_o_end'
./arch/x86/init.o:(.data+0x5cd0): first defined here
arch/x86/loader.o: In function `loader':
arch/x86/loader.S:(.text+0x0): multiple definition of `loader'
./arch/x86/loader.o:arch/x86/loader.S:(.text+0x0): first defined here
arch/x86/loader.o: In function `multiboot':
arch/x86/loader.S:(.bss+0x188): multiple definition of `multiboot'
./arch/x86/loader.o:arch/x86/loader.S:(.bss+0x188): first defined here
./arch/x86/loader.o: In function `shortjmp':
arch/x86/loader.S:(.text+0x9b): undefined reference to `init_32'
arch/x86/loader.o: In function `shortjmp':
arch/x86/loader.S:(.text+0x9b): undefined reference to `init_32'
make: *** [image] Fehler 1
Kurzer Blick auf readelf vor der Umwandlung:
$ readelf -s arch/x86/init32.o
Symboltabelle ‚.symtab‛ enthält 42 Einträge:
Num: Wert Size Typ Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS init.c
2: 00000000 0 SECTION LOCAL DEFAULT 5
3: 00000000 0 SECTION LOCAL DEFAULT 7
4: 00000000 0 SECTION LOCAL DEFAULT 8
5: 00000000 0 SECTION LOCAL DEFAULT 9
6: 00000000 0 SECTION LOCAL DEFAULT 11
7: 00000000 0 SECTION LOCAL DEFAULT 12
8: 00000000 0 SECTION LOCAL DEFAULT 13
9: 00000000 0 SECTION LOCAL DEFAULT 15
10: 00000000 0 SECTION LOCAL DEFAULT 25
11: 00000000 0 SECTION LOCAL DEFAULT 27
12: 00000000 0 SECTION LOCAL DEFAULT 29
13: 00000000 0 SECTION LOCAL DEFAULT 17
14: 00000000 0 SECTION LOCAL DEFAULT 19
15: 00000000 0 SECTION LOCAL DEFAULT 21
16: 00000000 0 SECTION LOCAL DEFAULT 23
17: 00000000 0 SECTION LOCAL DEFAULT 30
18: 00000000 0 NOTYPE LOCAL DEFAULT 1 wm4.1.e9f73b8cd07e2ddf8d0
19: 00000000 0 NOTYPE LOCAL DEFAULT 2 wm4.paging.h.6.4b576cfed7
20: 00000000 0 NOTYPE LOCAL DEFAULT 3 wm4.stddef.h.2.6ea241740e
21: 00000000 0 NOTYPE LOCAL DEFAULT 4 wm4.stdbool.h.2.2f4031346
22: 00000000 0 SECTION LOCAL DEFAULT 28
23: 00000000 0 SECTION LOCAL DEFAULT 1
24: 00000000 0 SECTION LOCAL DEFAULT 2
25: 00000000 0 SECTION LOCAL DEFAULT 3
26: 00000000 0 SECTION LOCAL DEFAULT 4
27: 00000000 17 FUNC GLOBAL DEFAULT 5 init_32
28: 000000fb 469 FUNC GLOBAL DEFAULT 5 init_pmm_32
29: 0000009a 97 FUNC GLOBAL DEFAULT 5 init_vmm_32
30: 00000011 137 FUNC GLOBAL DEFAULT 5 memset_32
31: 00000436 567 FUNC GLOBAL DEFAULT 5 pmm_alloc_32
32: 00000000 0 NOTYPE GLOBAL DEFAULT UND pml4
33: 00000a4d 82 FUNC GLOBAL DEFAULT 5 map_page_range_32
34: 00000000 0 NOTYPE GLOBAL DEFAULT UND mmap_count
35: 00000000 0 NOTYPE GLOBAL DEFAULT UND mmap
36: 00000000 0 NOTYPE GLOBAL DEFAULT UND multiboot
37: 000002d0 176 FUNC GLOBAL DEFAULT 5 find_free_page_32
38: 00000380 182 FUNC GLOBAL DEFAULT 5 find_contingous_free_page
39: 00000000 0 NOTYPE GLOBAL DEFAULT UND malloc
40: 0000066d 992 FUNC GLOBAL DEFAULT 5 map_page_32
41: 00000000 0 NOTYPE GLOBAL DEFAULT UND pmm_alloc
Dann
objcopy -B i386:x86-64 -I binary -O elf64-x86-64 arch/x86/init32.o arch/x86/init.o
Daraufhin:
tobias@MD5320:~/os-dev$ readelf arch/x86/init.o -s
Symboltabelle ‚.symtab‛ enthält 5 Einträge:
Num: Wert Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 SECTION LOCAL DEFAULT 1
2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 1 _binary_arch_x86_init32_o
3: 0000000000005cd0 0 NOTYPE GLOBAL DEFAULT 1 _binary_arch_x86_init32_o
4: 0000000000005cd0 0 NOTYPE GLOBAL DEFAULT ABS _binary_arch_x86_init32_o
objcopy hat also mal eben 37 Symbole unter den Tisch fallen lassen. Hmm.
Und die multiple-definition-Fehler kann ich auch nicht verstehen.
P.S. mmap, mmap_count und pml4 sind extern ints.
-
Okay, anscheinend ist der tyndur-Trick doch nicht so genial:
(ld-Output)
Sieht für mich danach aus, als würdest du init.o zweimal an ld übergeben.
objcopy hat also mal eben 37 Symbole unter den Tisch fallen lassen. Hmm.
Das ist auch die Idee in tyndur. Um mal zu erläutern wie tyndur das macht:
Der Loader ist ein 32-Bit "Kernel". In den Loader wird der 64-Bit Kernel eingebettet. Dazu wird der Kernel (der eigentlich im ELF-Format ist) von objdump genommen und als binary interpretiert. objdump erzeugt daraus eine neue ELF-Objektdatei, die nur die start/end-Symbole enthält, und darin eingebettet ist der Kernel. Diese neue Objekt-Datei wird zum Loader dazugelinkt, der nun auch die start/end-Symbole kennt. Der Loader hat einen kleinen ELF-Parser eingebaut, der den Kernel an seine korrekte Adresse kopiert. Anschließend springt der Loader an die Startadresse der Kernels. Die Startadresse steht im ELF-Header.
Wenn du hingegen den 32-Bit-Teil in den 64-Bit-Kernel einbetten willst, kannst du mal ausprobieren, ob du init32.o in eine 64-Bit-ELF umwandeln kannst. objcopy -O elf64-x86-64 init32.o oder so. (Ungetestet.)
-
Okay, anscheinend ist der tyndur-Trick doch nicht so genial:
(ld-Output)
Sieht für mich danach aus, als würdest du init.o zweimal an ld übergeben.
Habe ich auch schon gedacht und 10x gecheckt, ist aber nicht der Fall.
Hier mal der Output von $ nm x86/*.o amd64/*.o -A | grep "_32"x86/init.o:0000000000000380 T find_contingous_free_pages_32
x86/init.o:00000000000002d0 T find_free_page_32
x86/init.o:0000000000000000 T init_32
x86/init.o:00000000000000fb T init_pmm_32
x86/init.o:000000000000009a T init_vmm_32
x86/init.o:000000000000066d T map_page_32
x86/init.o:0000000000000a89 T map_page_range_32
x86/init.o:0000000000000011 T memset_32
x86/init.o:0000000000000436 T pmm_alloc_32
x86/loader.o: U handler_32
x86/loader.o: U init_32
Und zum Beweis auch noch die Makefile: (Ich weiß, ein wenig schmutzig)
CC =gcc
AS =nasm
LD =ld
EMU =qemu-system-i386 -no-kvm -monitor stdio -s -S -cdrom $(ISO)
#WFLAGS =-Wall -Wextra -Wshadow -Wconversion -Wunreachable-code -Werror-implicit-function-declaration -Wuninitialized
WFLAGS =-Wall -Wextra -pedantic -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wno-long-long -Wuninitialized -Wconversion -Wstrict-prototypes
CFLAGS =-nostdinc -nostdlib -nostartfiles -nodefaultlibs -ffreestanding -fno-stack-protector -fno-leading-underscore -fno-strict-aliasing -fno-builtin -fomit-frame-pointer -m64 -g3 -O0 -c -Iinclude -mcmodel=kernel -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow
ASFLAGS =-f elf64
LDFLAGS =-m elf_x86_64 -T linker.ld -nostdlib -nodefaultlibs -Bstatic
STDLIB =lib/std/string.o lib/std/ctype.o lib/std/errno.o lib/std/stdio.o lib/std/stdlib.o lib/std/uchar.o lib/std/wchar.o #lib/std/math_exp.o lib/std/math_misc.o lib/std/math_pow.o lib/std/math_trigo.o
MEMLIB =#lib/ptmalloc3/malloc.o lib/ptmalloc3/ptmalloc3.c
KERNEL =arch/x86/loader.o arch/amd64/kernel.o arch/amd64/handler.o arch/amd64/mm.o arch/amd64/pmm.o arch/amd64/vmm.o arch/amd64/console.o arch/x86/init.o
OBJ =$(STDLIB) $(MEMLIB) $(KERNEL)
KERNELF =kernel.sys
.PHONY: all run
all: image
image: kernel stdlib memlib
$(LD) $(LDFLAGS) $(OBJ)
-objcopy --only-keep-debug $(KERNELF) kernel.sym
-objcopy --strip-debug $(KERNELF)
kernel: $(KERNEL)
memlib: $(MEMLIB)
stdlib: $(STDLIB)
arch/x86/init.o: arch/x86/init.c
$(CC) -nostdinc -nostdlib -nostartfiles -nodefaultlibs -ffreestanding -fno-builtin -fomit-frame-pointer -m32 -g3 -O0 -c -Iinclude -o arch/x86/init32.o $^
objcopy -O elf64-x86-64 arch/x86/init32.o $@
-@rm arch/x86/init32.o
%.o: %.S
$(AS) $(ASFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -o $@ $^
mkimage: all
@cp $(KERNELF) $(BOOTDIR)
grub-mkrescue -o "$(ISO)" $(ISODIR)
-@wait
run: mkimage
$(EMU)
Ich kann es mir nicht erklären...
objcopy hat also mal eben 37 Symbole unter den Tisch fallen lassen. Hmm.
Das ist auch die Idee in tyndur. Um mal zu erläutern wie tyndur das macht:
Der Loader ist ein 32-Bit "Kernel". In den Loader wird der 64-Bit Kernel eingebettet. Dazu wird der Kernel (der eigentlich im ELF-Format ist) von objdump genommen und als binary interpretiert. objdump erzeugt daraus eine neue ELF-Objektdatei, die nur die start/end-Symbole enthält, und darin eingebettet ist der Kernel. Diese neue Objekt-Datei wird zum Loader dazugelinkt, der nun auch die start/end-Symbole kennt. Der Loader hat einen kleinen ELF-Parser eingebaut, der den Kernel an seine korrekte Adresse kopiert. Anschließend springt der Loader an die Startadresse der Kernels. Die Startadresse steht im ELF-Header.
Wenn du hingegen den 32-Bit-Teil in den 64-Bit-Kernel einbetten willst, kannst du mal ausprobieren, ob du init32.o in eine 64-Bit-ELF umwandeln kannst. objcopy -O elf64-x86-64 init32.o oder so. (Ungetestet.)
Ich will eigentlich erstmal meinen Long Mode Code testen. Ich musste ja sehr viel "blind" schreiben.
Andere Frage: Wozu braucht man das end-Symbol? Zum Auslesen braucht man doch eigentlich nur den Header irgendwo am Anfang der Datei?
-
Willst du nicht wissen, wieviel Speicher du für den Kernel mappen/kopieren musst? ;-)
-
Gibt es da nicht eine magische Sache namens... Moment mal... ELF-Header? :-D
Also, im IRC haben Jidder und ich folgendes herausgefunden:
1. Es liegt an meinem verhunzten Linkerscript.
2. GNU ld kann (bei mir) mit EXCLUDE_FILE nicht umgehen. Er ignoriert es einfach und linkt fröhlich init.o nochmal dazu, Effekt siehe oben.
Ich habe jetzt testweise den Linkerscript gefixt (mit anderen Worten: Noch weiter verhunzt), sodass ich erstmal meinen Long Mode-Code reparieren kann. (Erster Testlauf: CPU im SMM, ich überhaupt nicht wie, warum oder wieso. Dann kann das Debuggen ja heiter werden...)
Ich tendiere aber dann doch zu der Version von tyndur.
-
Das Ende bzw. die Länge der Datei ist nützlich, um rauszukriegen, ob die Datei vollständig geladen ist. Theoretisch solltest du für jedes Offset, das du im ELF-Header findest, prüfen, ob es noch in der Datei liegt. Praktisch mach ich das nur für die Zugriffe auf den ELF-Header.
Sicher, dass dein Code im SMM gelandet ist? Qemu wechselt noch vor dem Laden des OS einmal kurz in den SMM und verzeichnet das im Interrupt-Log. Das könnte also gar nichts mit deinem Code zu tun haben.
Eine Idee noch zu EXCLUDE_FILE: Versuch mal nur *loader.o *init.o (ggf. auch im .bootstrap-Teil des Linkerskripts.)