Page 1 of 1

13k kernel doesn't work

Posted: Wed Jun 21, 2006 12:53 pm
by deficite
I'm coding a basic kernel in C and x86 ASM, and for some reason whenever my kernel grows above a certain size, GRUB says throws an Error 13: Invalid or unsupported executable format. I've been following this tutorial: http://www.osdever.net/bkerndev/index.php?the_id=90

I started getting this whenever I implemented puts() and called it. Whenever I call that function, I get the error 13, but if I don't it works fine. One thing I noticed right away is that when I call puts() the kernel grows to 13k and if I don't it's 8k. I tried to make the kernel bigger by adding huge variables and the like to the program, but the program sticks at 8k. So I tried the next thing. I copied the code from puts() and put it straight into my main() function after initializing the screen. Same problem. So then I thought that it might be the for loop. So I made a random for loop that didn't really do anything and iterated 20 times. The filesize stuck at 8k. It worked. So then I made a character array and made the for loop iterate through the character array. Now it grew to....13 kilobytes again. So, I thought that maybe it was too much data for main() to hold, so I took all of that out of main and just had main call init_screen(); and then enter an endless loop (like it was before). I put puts("Hello, world!"); into init_screen(); so it would not be called from main() if that was the problem. Nope, I still get an error 13.

Does anybody know what the problem might be? I'm using FAT16 for the filesystem and my kernel is just a plain binary that I linked with the LD script provided in the link above.

Posted: Wed Jun 21, 2006 12:56 pm
by carbonBased
Make sure that your multiboot header is at the very beginning of your kernel. It has to be withen a certain number of kb for it to be recognised (I don't recall this limit offhand).

Changes are, the header is near the end, and the more code you add, the farther away it gets.

You should edit your linker script to ensure its near the beginning and/or ensure its the first object on the linker command line.

--Jeff

Posted: Wed Jun 21, 2006 1:10 pm
by deficite
Okay, I put start.o as the first thing on the list for ld and it still didn't fix it. Here's my start.asm code:

Code: Select all

; This is the kernel's entry point. We could either call main here,
; or we can use this to setup the stack or other nice stuff, like
; perhaps setting up the GDT and segments. Please note that interrupts
; are disabled at this point: More on interrupts later!
[BITS 32]
global start
start:
    mov esp, _sys_stack     ; This points the stack to our new stack area
    jmp stublet

; This part MUST be 4byte aligned, so we solve that issue using 'ALIGN 4'
ALIGN 4
mboot:
    ; Multiboot macros to make a few lines later more readable
    MULTIBOOT_PAGE_ALIGN	equ 1<<0
    MULTIBOOT_MEMORY_INFO	equ 1<<1
    MULTIBOOT_AOUT_KLUDGE	equ 1<<16
    MULTIBOOT_HEADER_MAGIC	equ 0x1BADB002
    MULTIBOOT_HEADER_FLAGS	equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
    MULTIBOOT_CHECKSUM	equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
    EXTERN code, bss, end

    ; This is the GRUB Multiboot header. A boot signature
    dd MULTIBOOT_HEADER_MAGIC
    dd MULTIBOOT_HEADER_FLAGS
    dd MULTIBOOT_CHECKSUM
    
    ; AOUT kludge - must be physical addresses. Make a note of these:
    ; The linker script fills in the data for these ones!
    dd mboot
    dd code
    dd bss
    dd end
    dd start

; This is an endless loop here. Make a note of this: Later on, we
; will insert an 'extern _main', followed by 'call _main', right
; before the 'jmp $'.
stublet:
    extern main
    call main
    jmp $

; Shortly we will add code for loading the GDT right here!


; In just a few pages in this tutorial, we will add our Interrupt
; Service Routines (ISRs) right here!



; Here is the definition of our BSS section. Right now, we'll use
; it just to store the stack. Remember that a stack actually grows
; downwards, so we declare the size of the data before declaring
; the identifier '_sys_stack'
SECTION .bss
    resb 8192               ; This reserves 8KBytes of memory here
_sys_stack:
And my linker script:

Code: Select all

OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    . = ALIGN(4096);
  }
  .data : AT(phys + (data - code))
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss : AT(phys + (bss - code))
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}
To me, it looks like it does put it in the beginning, but I am not an expert on linker scripts or assembly language.

Posted: Wed Jun 21, 2006 1:25 pm
by deficite
I just tried upping the 8k on the resb at the bottom of start.asm to 16k and it doesn't change a thing

edit: I ran mbchk and it does say it can't find the multiboot header. I don't know how to fix this, the tutorial says nothing about this at all. You are right that it's at the end though, because I took two of my old builds and mbchk says that the really old one is found at offset 12 and the slightly newer one is offset 28

I opened up the kernel in hexedit and the bultiboot checksum is all the way back at 0x10000C/0x102000. How do I get this at the beginning of the file? The string: "Hello, world!" is all the way at 0x00

Posted: Wed Jun 21, 2006 3:17 pm
by Pelz
You didn't define a section for your asm-Code, so NASM put it into an extra section. (Something strange like "NASMDEFSEG001" or so). LD placed that section after .bss because you didn't mention it in your linker script. I guess a simple "section .text" after [BITS 32] should fix that.

Posted: Thu Jun 22, 2006 12:16 am
by deficite
I added "section .text" in the line right under "[BITS 32]" in start.asm and when I hexedit'd my kernel, the multiboot header is STILL near the end of the binary.

I gzip'd all of the files in my kernel here: http://www.zshare.net/download/kernel-tgz.html

To build from a *nix environment, just type "./build.bash" from the command line. Otherwise, you can look in the script and see what commands I run to build my kernel. Be warned, I haven't gotten around to cleaning up and finishing the build script, so it's a wee bit messy yet. I intend on adding functions to it and such to clean it up.

edit: I switched over to ELF for my kernel, and now it works fine.