Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: rizor am 15. July 2010, 12:00
-
Hallo,
ich habe ein Problem mit meinem Linker.
Ich habe mir zwei eigene Sektionen definiert (preboot und boot) und möchte in die boot-Sektion den Multiboot-Header geladen haben.
Leider funktioniert das nicht so ganz und ich weiß nicht warum.
Hier mein Linker-Skript:
/* the linker script to link the kernel */
ENTRY(_start) /* the entry-point of the kernel */
OUTPUT_FORMAT(elf32-i386) /* the output-format */
/* some constants to link the code correctly */
ALIGNMENT = 0x1000; /* the alignment of the sections */
PHYS_ADDRESS = 0x100000; /* the physical address of the kernel */
VIRT_ADDRESS = 0x100000; /* the virtual address of the kernel */
REMAP_OFFSET = VIRT_ADDRESS - PHYS_ADDRESS; /* the offset between physical and virtual addresses */
/* define the different sections of the kernel */
SECTIONS
{
. = VIRT_ADDRESS; /* move the kernel to the correct start-address */
__kernel_virt_entry = .; /* the beginning with virtual addresses */
__kernel_phys_entry = . - REMAP_OFFSET; /* the beginning with physical addresses */
.preboot : AT(ADDR(.preboot) - (REMAP_OFFSET + 0x1000))
{
__preboot_entry = .; /* the entry-symbol to get the space of the preboot-section */
*(.preboot)
__preboot_end = .; /* the end-symbol to get the space of the preboot-section */
}
. = ALIGN(ALIGNMENT);
.boot : AT(ADDR(.boot) - REMAP_OFFSET)
{
__boot_entry = .; /* the entry-symbol of the boot-section */
*(.boot)
__boot_end = .; /* the end-symbol of the boot-section */
}
. = ALIGN(ALIGNMENT);
.init : AT(ADDR(.init) - REMAP_OFFSET)
{
__init_entry = .; /* the entry-symbol of the init-section */
*(.init)
__init_end = .; /* the end-symbol of the init-section */
}
. = ALIGN(ALIGNMENT);
.text : AT(ADDR(.text) - REMAP_OFFSET)
{
__ro_data_entry = .; /* the entry-symbol of the read-only-section */
*(.text)
*(.rodata)
__ro_data_end = .; /* the end-symbol of the read-only-section */
}
. = ALIGN(ALIGNMENT);
.data : AT(ADDR(.data) - REMAP_OFFSET)
{
__rw_data_entry = .; /* the entry-symbol of the read-write-sections */
*(.data)
*(.bss)
__rw_data_end = .; /* the end-symbol of the read-write-sections */
}
. = ALIGN(ALIGNMENT);
__kernel_phys_end = . - REMAP_OFFSET; /* the end of the physical addresses */
__kernel_virt_end = .; /* the end of the virtual addresses */
}
Hier meine Multiboot-Datei:
.section .boot
// the following flags are every time set
#define MB_MAGIC 0x1badb002
/*
* MB_FLAGS[0] == load modules 4k-aligned
* MB_FLAGS[1] == calculate mbs_mem_lower,mbs_mem_upper and mbs_mmap_length, mbs_mmap_addr if possible
* MB_FLAGS[2] == load some informations about the video-mode into the multiboot-structure
* MB_FLAGS[16] == calculates some informations about the kernel-image
*/
#define MB_FLAGS 0x8007
#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS)
// the following flags are defined, if MB_FLAGS[16] is set
#define MB_HEADER_ADDR 0x0
#define MB_LOAD_ADDR 0x0
#define MB_LOAD_END_ADDR 0x0
#define MB_BSS_END_ADDR 0x0
#define MB_ENTRY_ADDR 0x0
// the following flags are defined, if MB_FLAGS[2] is set
#define MB_MODE_TYPE 0x0
#define MB_MODE_WIDTH 0x0
#define MB_MODE_HEIGHT 0x0
#define MB_MODE_DEPTH 0x0
.align 4
.int MB_MAGIC
.int MB_FLAGS
.int MB_CHECKSUM
.int MB_HEADER_ADDR
.int MB_LOAD_ADDR
.int MB_LOAD_END_ADDR
.int MB_BSS_END_ADDR
.int MB_ENTRY_ADDR
.int MB_MODE_TYPE
.int MB_MODE_WIDTH
.int MB_MODE_HEIGHT
.int MB_MODE_DEPTH
Wenn ich mir nun meine boot-Sektion mir readelf oder objdump anschaue, steht dort nur sinnloses Zeug.
Ausgabe von objdump:
Disassembly of section .boot:
00100000 <__boot_entry>:
100000: 02 b0 ad 1b 07 80 add -0x7ff8e453(%eax),%dh
100006: 00 00 add %al,(%eax)
100008: f7 (bad)
100009: cf iret
10000a: 51 push %ecx
10000b: e4 00 in $0x0,%al
mbchk sagt natürlich, dass es keine Multiboot-Header finden konnte.
Woran kann das Problem liegen?
Danke.
Gruß
rizor
-
Schau dir die objdump-Ausgabe nochmal genau an...
02 b0 ad 1b = 0x1badb002 (Magic)
07 80 00 00 = 0x00008007 (Flags)
f7 cf 51 e4 = 0xe451cff7 (Prüfsumme)
Ist doch ein toller Multiboot-Header? Die Frage ist: Ist er in der Datei innerhalb der ersten 8k wie er sein sollte?
-
Okay, habe ich übersehen.
Ich habe mir nun mal mit hexdump die Adresse gesucht.
Dabei kam folgendes Ergebnis:
0005070 0000 0000 0000 0000 b002 1bad 8007 0000
Das sind ja nicht die ersten 8 kB. Woran liegt das?
Muss ich davon noch den elf-header abziehen?
-
Wenn es nicht in den ersten 8k liegt, liegt das meistens daran, dass irgendwelchen anderes Zeug früher in der Datei ist. ;)
Bei dir konkret würde ich vermuten, dass .preboot zu groß ist.Grundsätzlich würde ich den Multiboot-Header ganz an den Anfang im Linkerskript packen, damit er direkt nach dem ELF-Header kommt.
-
Habe das Problem gelöst, allerdings verstehe ich es nicht.
Ich habe eine init-Funktion, die auch in die Sektion muss. Nachdem ich sie dort eingetragen habe, hat es funktioniert.
Wieso wird boot sonst anscheinend anders gelinkt?
Meine preboot-Sektion ist noch leer.
-
rizor warum muss die init Funktion umbedingt in diese section?
-
Ich habe eine Init-Funktion, die das System startet und danach an den speicher manager freigegeben werden soll. In die Sektion soll der ganze Code, der für den Bootvorgang und danach nie wieder benötigt wird. Dadurch soll das System speicherschonender sein.
In die Init-Sektion kommt dann der erste Task (wobei ich nicht weiß, ob die Sektion benutzt wird).
Durch diese Aufteilung kann ich dem Speichermanager ganz einfach sagen, welcheSeiten freigegeben werden sollen.
-
da geht es um ein paar kb ich würde den aufwand sein lassen und lieber an anderer stelle sparen. Das ist meine meinung.
Programm Noob
-
Naja, das System soll so effektiv wie möglich sein.
Dazu gehört für mich auch diese Organisation.
Es heißt ja nicht automatisch, dass ich an anderen Stellen spare ;).
-
Du hast das falsch verstanden du sollst das so im Speicher lassen und statt dessen dann lieber an anderer Stelle Speicherschonend arbeiten. Das bringt mehr.
-
Das Alignment der Sektionen vergrößert dir übrigens das Abbild im Speicher. Bei fünfmaligem alignen auf 4 kiB sind im Durchschnitt 10 kiB Verschwendung zu erwarten.
Eine Nebensache noch:
* MB_FLAGS[16] == calculates some informations about the kernel-image
*/
#define MB_FLAGS 0x8007
Da ist übrigens nicht Bit 16 gesetzt, falls der Kommentar das bedeuten soll. Das gesetzte Bit 15 (0x8000) hat keine zugewiesene Bedeutung. Dass der Bootloader den Kernel trotzdem lädt, ist eigentlich nicht konform mit der Spezifikation.
-
0x8000 ist doch bit-16, oder?
Habe das nochmal mit einem Rechner durchgerechnet und der kommt auf das gleiche ergebnis.
Wenn ich das dein Bit setze, dann wird mein Kernel nicht geladen.
-
0x8000 ist Bit 15, es wird immer ab 0 gezählt. Frag deinen Rechner mal nach 1 << 16. ;)
-
Ich habe mal eine Frage:
Was kann alles vor der ersten Sektion liegen?
Habe mein Linker-Script jetzt dahingehend umgebaut, dass die Multiboot-Sektion ganz am Anang steht, allerdings liegt die Sektion immer noch an Adresse 0x4040 im Elf-File.
Hier meine Readelf-Ausgabe:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .multiboot PROGBITS 00100000 004044 000030 00 0 0 4
[ 2] .rodata PROGBITS 00101000 001000 000472 00 A 0 0 4
[ 3] .text PROGBITS 00101474 001474 00249a 00 AX 0 0 4
[ 4] .data PROGBITS 00104000 004000 000044 00 WA 0 0 32
[ 5] .bss NOBITS 00104060 004044 003120 00 WA 0 0 32
[ 6] .debug_line PROGBITS 00000000 004074 000a29 00 0 0 1
[ 7] .debug_info PROGBITS 00000000 004a9d 001f63 00 0 0 1
[ 8] .debug_abbrev PROGBITS 00000000 006a00 000a71 00 0 0 1
[ 9] .debug_aranges PROGBITS 00000000 007478 0001a0 00 0 0 8
[10] .debug_loc PROGBITS 00000000 007618 000707 00 0 0 1
[11] .debug_pubnames PROGBITS 00000000 007d1f 000646 00 0 0 1
[12] .debug_str PROGBITS 00000000 008365 000ba0 01 MS 0 0 1
[13] .comment PROGBITS 00000000 008f05 000022 01 MS 0 0 1
[14] .debug_frame PROGBITS 00000000 008f28 000584 00 0 0 4
[15] .shstrtab STRTAB 00000000 0094ac 0000b0 00 0 0 1
[16] .symtab SYMTAB 00000000 00982c 000840 10 17 28 4
[17] .strtab STRTAB 00000000 00a06c 0005bb 00 0 0 1
Ich habe Keine Idee, warum es soweit hinten liegt.
-
Wenn ich die Zahlen grad richtig interpretiere, kommen .rodata und .text bei dieser readelf-Ausgabe noch vor .multiboot.
-
Das kann nicht sein, da mir die Map folgendes ausgibt:
0x0000000000001000 ALIGNMENT = 0x1000
0x0000000000100000 PHYS_ADDRESS = 0x100000
0x0000000000100000 VIRT_ADDRESS = 0x100000
0x0000000000000000 REMAP_OFFSET = (VIRT_ADDRESS - PHYS_ADDRESS)
0x0000000000100000 . = VIRT_ADDRESS
0x0000000000100000 __kernel_virt_entry = .
0x0000000000100000 __kernel_phys_entry = (. - REMAP_OFFSET)
0x0000000000100000 __preboot_entry = .
0x0000000000100000 __multiboot_entry = .
.multiboot 0x0000000000100000 0x30 load address 0x00000000000fe000
Allerdings bleibt 0x4044 in der readelf-Ausgabe stehen.
Edit:
Ich baue mir meinen Kernel auf einem 64-bit System verwende allerdings 32-bit-Tools (Gentoo Cross-Compile).
Wieso gibt mir die Map trotzdem 64-bit Adressen?
-
Die Map sagt doch nur was über den Speicher im geladenen Code, nicht in der Datei?
-
Stimmt, aber wie bringe ich den Linker dazu, dass die Sektion auch am Anfang liegt?
Im Linkerscript steht die Multiboot-Sektion explizit am Anfang des Scripts.
-
Nachdem ich mir noch einmal mein Linker-Skript angeschaut habe und der Fehler immer noch nicht auftaucht, habe ich das Skript etwas umgebaut und wollte, dass ihr da nochmal drüber schaut.
/* the entry-point of the kernel */
ENTRY(_start)
/* the output-format */
OUTPUT_FORMAT(elf32-i386)
/* the start address of the kernel */
KERNEL_START = 0x100000;
/* define the different sections of the kernel */
SECTIONS
{
. = 0x9f000;
/* the beginning of the memory-space of the kernel */
__kernel_mem_entry = .;
/* the entry-symbol to get the space of the preboot-section */
__preboot_entry = .;
.preboot : AT(ADDR(.preboot))
{
*(.multiboot)
*(.preboot)
}
/* the end-symbol to get the space of the preboot-section */
__preboot_end = .;
/* move the kernel to the correct start-address */
. = KERNEL_START;
/* the beginning of the kernel without the preboot-area */
__kernel_entry = .;
/* the entry-symbol of the boot-section */
__boot_entry = .;
.boot : AT(ADDR(.boot))
{
*(.boot)
}
/* the end-symbol of the boot-section */
__boot_end = .;
/* the entry-symbol of the read-only-section */
__ro_data_entry = .;
.rodata : AT(ADDR(.rodata))
{
*(.rodata)
}
.text : AT(ADDR(.text))
{
*(.text)
}
/* the end-symbol of the read-only-section */
__ro_data_end = .;
/* the entry-symbol of the read-write-sections */
__rw_data_entry = .;
.data : AT(ADDR(.data))
{
*(.data)
}
.bss : AT(ADDR(.bss))
{
*(.bss)
}
/* the end-symbol of the read-write-sections */
__rw_data_end = .;
/* the end of the kernel */
__kernel_end = .;
/* the end of the memory-space of the kernel */
__kernel_mem_end = .;
}
Mein Linkeraufruf sieht wie folgt aus:
LDFLAGS=-Iinclude -Map kernel.map -nostdlib --warn-section-align -Tarch/i386/config/linker.ld -L/usr/lib/gcc/i686-pc-linux-gnu/4.4.4/libgcc.a -melf_i386 -O0 -o
Die Warn-Ausgabe ergab folgendes:
i686-pc-linux-gnu-ld: warning: changing start of section .text by 2 bytes
i686-pc-linux-gnu-ld: warning: changing start of section .iplt by 2 bytes
i686-pc-linux-gnu-ld: warning: changing start of section .rel.dyn by 2 bytes
i686-pc-linux-gnu-ld: warning: changing start of section .data by 18 bytes
i686-pc-linux-gnu-ld: warning: changing start of section .bss by 28 bytes
i686-pc-linux-gnu-ld: warning: changing start of section .text by 2 bytes
i686-pc-linux-gnu-ld: warning: changing start of section .iplt by 2 bytes
i686-pc-linux-gnu-ld: warning: changing start of section .rel.dyn by 2 bytes
i686-pc-linux-gnu-ld: warning: changing start of section .data by 18 bytes
i686-pc-linux-gnu-ld: warning: changing start of section .bss by 28 bytes
An was kann der Fehler denn alles liegen?
Google ergab leider nicht wirklich viel.
Gruß,
rizor
-
Habe die Multiboot-Sektion mal in die .text-Sektion geschoben und schon lag es an der richtigen Stelle.
Habe dann vermutet, dass es an der Auflistung der Object-Dateien liegt und habe die Multiboot-Datei an den Anfang der Liste gezogen und die Sektion wieder ausgelagert, aber das hat auch nicht funktioniert.
Kann man dem Linker sagen, dass er bestimmte Sektionen in einem bestimmten Bereich ablegen muss?