Page 1 of 1
Custom bootloader trouble
Posted: Sun Mar 20, 2011 10:14 am
by bytbox
I wrote a custom bootloader - and it works. By which I mean, if my kernel is
Code: Select all
void kmain() {
unsigned char *videoram = (unsigned char *)0xb8000;
videoram[0]='H';
videoram[2]='i';
}
The word "Hi" appears on the screen.
However, if I start using global variables...
Code: Select all
char h = 'H';
void kmain() {
unsigned char *videoram = (unsigned char *)0xb8000;
videoram[0]=h;
videoram[2]='i';
}
Now the 'H' doesn't appear (it's blank), but the 'i' still does. Presumably, something's being put in the wrong location of memory (or being requested /from/ the wrong location of memory). My linker script is almost exactly the same as the one in the "bare bones" tutorial:
Code: Select all
ENTRY (loader)
SECTIONS {
. = 0x00100000;
.text : {
*(.text)
}
.rodata ALIGN (0x1000) : {
*(.rodata)
}
.data ALIGN (0x1000) : {
*(.data)
}
.bss : {
sbss = .;
*(COMMON)
*(.bss)
ebss = .;
}
/DISCARD/ : {
*(.comment)
}
}
I didn't bother putting a multiboot header in my loader.asm.
Code: Select all
global loader ; making entry point visible to linker
extern kmain ; kmain is defined elsewhere
; reserve initial kernel stack space
STACKSIZE equ 0x4000 ; that's 16k.
loader:
mov esp, stack+STACKSIZE ; set up the stack
call kmain ; call kernel proper
cli
hang:
hlt ; halt machine should kernel return
jmp hang
section .bss
align 4
stack:
resb STACKSIZE ; reserve 16k stack on a doubleword boundary
I have verified that the global variable values do appear in the kernel image.
Any idea what sort of thing could be causing this?
(My code is at github.com/bytbox/wyvern if I left something important out.)
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 10:29 am
by Tosi
Do you have a cross compiler, and if so, which versions?
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 10:52 am
by bytbox
Tosi wrote:Do you have a cross compiler, and if so, which versions?
Erm...
$ gcc --version
gcc (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5
$ ld --version
GNU ld (GNU Binutils for Ubuntu) 2.20.51-system.20100908
And I'm using -m32, and linking with -melf_i386
Which I guess doesn't quite count as a cross-compiler, but it's always worked for previous experiments (using grub+multiboot rather than a custom-made bootloader).
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 10:54 am
by Chandra
Here's a little help.
Use -S switch with gcc to output the resultant assembly file of the kernel.
gcc -c kernel.c -S kernel.s
Explore the assembly file kernel.s to see if the global variable h is actually in the data section and contains the byte value 72. If it is there, then make sure you load proper sectors from the disk. Also make sure your linker doesn't shuffle things around.
If it isn't there, things will be interesting..........
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 11:03 am
by bytbox
Chandra wrote:Here's a little help.
Use -S switch with gcc to output the resultant assembly file of the kernel.
gcc -c kernel.c -S kernel.s
I'm assuming you meant
gcc -S kernel.c -o kernel.s
Explore the assembly file kernel.s to see if the global variable h is actually in the data section and contains the byte value 72. If it is there, then make sure you load proper sectors from the disk. Also make sure your linker doesn't shuffle things around.
If it isn't there, things will be interesting..........
h is indeed in there, with byte value 72. I have no reason to think I'm not loading the proper sectors from the disk, but I'll have my bootloader "grep" for byte value 72 and tell me where it is placing it in memory when I have more time.
A question about linker scripts - how does the program know where it's being loaded in memory?
Assembly output was:
Code: Select all
.file "kmain.c"
.globl h
.data
.type h, @object
.size h, 1
h:
.byte 72
.text
.globl kmain
.type kmain, @function
kmain:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
movq $753664, -8(%rbp)
movzbl h(%rip), %eax
movl %eax, %edx
movq -8(%rbp), %rax
movb %dl, (%rax)
movq -8(%rbp), %rax
addq $2, %rax
movb $105, (%rax)
leave
ret
.cfi_endproc
.LFE0:
.size kmain, .-kmain
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
.section .note.GNU-stack,"",@progbits
(Thanks for the help, BTW.)
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 11:15 am
by Chandra
bytbox wrote:I'm assuming you meant gcc -S kernel.c -o kernel.s
No, I meant gcc -c kernel.c -S kernel.s, which gives same result BTW.
bytbox wrote:A question about linker scripts - how does the program know where it's being loaded in memory?
The program doesn't know where it is being loaded in memory. You should know that and you should load the program in memory where it was intended to run (unless you are loading position independent code).
Does this linker script helps?
Code: Select all
OUTPUT_FORMAT("elf32-i386") /* Replace this to match your target output */
ENTRY(loader)
phys = 0x00100000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
}
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 7:20 pm
by bytbox
Does this linker script helps?
Nope.
Using objdump -s, it seems that the issue is that the linker expects the .text section to be loaded starting exactly at 0x10000, but since I'm simply copying the executable into 0x10000 and jmp'ing to it, the elf headers take up space and displace the rest of the executable.
This seems to mean that my bootloader must process the ELF headers and do actual work, rather than simply dump the kernel into memory. Any way around this? (Setting -oformat=binary doesn't seem to have any effect.)
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 7:25 pm
by Casm
bytbox wrote:
Does this linker script helps?
Nope.
Using objdump -s, it seems that the issue is that the linker expects the .text section to be loaded starting exactly at 0x10000, but since I'm simply copying the executable into 0x10000 and jmp'ing to it, the elf headers take up space and displace the rest of the executable.
This seems to mean that my bootloader must process the ELF headers and do actual work, rather than simply dump the kernel into memory. Any way around this? (Setting -oformat=binary doesn't seem to have any effect.)
The assembly code from which you call the kernel function needs to be ORGed to 0x10000, or wherever else it is you are going to load it.
Also, you will need to parse the ELF file when you load it - either that or use a flat binary. The ELF header is there for a reason, and that reason is to tell you where different parts of the file need to be loaded.
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 7:32 pm
by Jezze
Yeah it sounds like you are executing the ELF headers. If you don't want to care about parsing them the offset is probably 4096 so you could jump to 0x10000 + 4096 and it should work.
Another way would be to compile the program as a pure binary without elf headers. Don't remember what flags you would use but you can always look it up.
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 8:25 pm
by bytbox
Oof... got it. Generating a flat binary kernel and loading into the right place (rather than one sector off).
Is there any significant advantage to using ELF (or other actual format) over flat binary?
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 9:14 pm
by Chandra
bytbox wrote:Is there any significant advantage to using ELF (or other actual format) over flat binary?
There are lots of advantages, that's why ELF format is in use. Did you look at the
elf specifications?
Re: Custom bootloader trouble
Posted: Sun Mar 20, 2011 10:26 pm
by Jezze
Actually using ELF for the kernel is not as important as having ELF for modules or user programs where it probably is mandatory if you wan't modern features like relocation of object files and instruction independent code. Linking a module to the kernel could be done by using a symbol table. It might not better, I'm just saying.