Multiboot checker says it's "ok" but grub complains

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
dc740
Member
Member
Posts: 40
Joined: Sat Jul 04, 2009 12:43 am
Location: Argentina

Multiboot checker says it's "ok" but grub complains

Post by dc740 »

As you read in the title. I have this link script:

Code: Select all

OUTPUT_FORMAT("elf32-i386")
OUTPUT_ARCH("i386")
ENTRY(start)			/* Kernel starts at "start" symbol. */
PHYS_ADDR = 0X00100000;
VIRT_ADDR = 0XC0100000;

SECTIONS {

   . = PHYS_ADDR;

   .__mbHeader : AT ( ADDR( .__mbHeader ) ) {
    	_start = .;
    	_mboot = .;
    *(.__mbHeader)
    . = ALIGN(4);
   }

  .text VIRT_ADDR : AT(PHYS_ADDR) {
  	*(.text)
  	*(.rodata) *(.rodata.*)
  	. = ALIGN(4096);
  } = 0x90

  .data : {
  		_data = .;
  		*(.data)
  		. = ALIGN(4096);
  }

  /* BSS (zero-initialized data) is after everything else. */

  .bss : {
  _start_bss = .;
  *(.bss)
  . = ALIGN(4096);
  _end_bss = .;
  }


  _end = .;
}
Multiboot checker says everything is ok:

Code: Select all

../build/toy: The Multiboot header is found at the offset 4101.
../build/toy: Page alignment is turned on.
../build/toy: Memory information is turned on.
../build/toy: Address fields is turned on.
header_addr = 0x100005
load_addr = 0x100000
load_end_addr = 0xC0103000
bss_end_addr = 0xC0104000
entry_addr = 0xC01003EC
../build/toy: All checks passed.
I use object dump to verify the information from entry_addr... everything is ok.
Then I load the kernel using grub and it gives me the horrible 13 ERROR
Yes, this one:
http://wiki.osdev.org/Grub_Error_13

I use GNU Gas Assembler... so I used the code from the especification to create my multiboot header... that's "ok" according to the multiboot checker.

This is the header:

Code: Select all

.equ MBOOT_PAGE_ALIGN, 1<<0    // Load kernel and modules on a page boundary
.equ MBOOT_MEM_INFO, 1<<1    // Provide your kernel with memory info
.equ MBOOT_ADDRESS_FIELD, 1<<16
.equ MBOOT_HEADER_MAGIC, 0x1BADB002 // Multiboot Magic value

.equ MBOOT_HEADER_FLAGS, MBOOT_MEM_INFO | MBOOT_ADDRESS_FIELD | MBOOT_PAGE_ALIGN
.equ MBOOT_CHECKSUM, -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)

.global mboot,bss,end,_start,start

.section .__mbHeader
.align 4
_start:
	jmp start
_mboot:
    .long MBOOT_HEADER_MAGIC
    .long MBOOT_HEADER_FLAGS
    .long MBOOT_CHECKSUM

    // AOUT kludge - must be physical addresses. Make a note of these:
    // The linker script fills in the data for these ones!
    .long _mboot
    .long _start
    .long _start_bss
    .long _end_bss
    .long start

.section .text
.func start
start:	

Thanks in advance. Any suggestion is appreciatted. I can succesfully boot the OS under Lguest. I have a "nice" command console... And I wanted to "port" it to a regular PC too. but I'm stuck at the very first step.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Multiboot checker says it's "ok" but grub complains

Post by gerryg400 »

Code: Select all

../build/toy: The Multiboot header is found at the offset 4101
The multiboot heeader must be long word aligned. You need something like this with the .align 4 in front of the magic number

Code: Select all

.code32
.text
		.globl	start
start:
		/*
		 * This is where GRUB drops us. We wont assume too much.
		 */
		jmp	over_multiboot_header

		.align	4
		.long	0x1badb002  /* multiboot magic number */
		.long	0x00000003  /* to grub we are an ELF file multiboot flags */
		.long	-(0x1badb002 + 0x00000003)  /* checksum */

over_multiboot_header:
If a trainstation is where trains stop, what is a workstation ?
User avatar
dc740
Member
Member
Posts: 40
Joined: Sat Jul 04, 2009 12:43 am
Location: Argentina

Re: Multiboot checker says it's "ok" but grub complains

Post by dc740 »

This is strange. Of course, you are right. The mb header is not aligned. I missed that detail. but if I do align it... the mbHeader gets placed anywere. Look:

Code: Select all

Sections:
Idx Name          Size      VMA       LMA       File off Algn
  0 .__mbHeader   00000028  c0100000  c0100000  00004000  2**2
File off
00004000

That's way beyond 8k :( so grub won't find it
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Multiboot checker says it's "ok" but grub complains

Post by gerryg400 »

Your linker script is wrong.
If a trainstation is where trains stop, what is a workstation ?
User avatar
dc740
Member
Member
Posts: 40
Joined: Sat Jul 04, 2009 12:43 am
Location: Argentina

Re: Multiboot checker says it's "ok" but grub complains

Post by dc740 »

gerryg400 wrote:Your linker script is wrong.

I'm sorry but I can't find the issue. What's wrong with the script?
User avatar
dc740
Member
Member
Posts: 40
Joined: Sat Jul 04, 2009 12:43 am
Location: Argentina

Re: Multiboot checker says it's "ok" but grub complains

Post by dc740 »

yeah, I just moved the header to the text section as sugested in the multiboot specification. I also removed the mnHeader section suggested in the wiki. It's a bad idea. testing different link files showed me that sometimes the linker makes optimizations that make the section be above the 8k, even when the link file doesn't say so. Yes, you can also use -n. But I didn't want to, cause I really didn't need to. I just needed to read every detail of what was happening. I was reading the wrong documentation last night. I started reading the "ld" docs to find the issue in my link file, but it was beyond that.

For begginers the best suggestion is to learn to use "objdump -x yourkernelimage" and the file offsets. (so you can discard an invalid header when in fact it's not aligned, or it's not in the first 8k)

Also, my kernel is an ELF file... so I didn't need the header at all. I was hitting my head against the wall for things that were not needed at all.

this things happen when you read some things quick cause you want to focus in something else... this is a copy paste from the MB especification:
If bit 16 in the ‘flags’ word is set, then the fields at offsets 12-28 in the Multiboot header are valid, and the boot loader should use them instead of the fields in the actual executable header to calculate where to load the OS image. This information does not need to be provided if the kernel image is in elf format, but it must be provided if the images is in a.out format or in some other format. Compliant boot loaders must be able to load images that either are in elf format or contain the load address information embedded in the Multiboot header; they may also directly support other executable formats, such as particular a.out variants, but are not required to.
See? I didn't need to set the bit 16 in the first place.

Thank you all for the responses.
TylerH
Member
Member
Posts: 285
Joined: Tue Apr 13, 2010 8:00 pm
Contact:

Re: Multiboot checker says it's "ok" but grub complains

Post by TylerH »

You might be able to use the LOADADDR(section) ld function to simplify things.
User avatar
dc740
Member
Member
Posts: 40
Joined: Sat Jul 04, 2009 12:43 am
Location: Argentina

Re: Multiboot checker says it's "ok" but grub complains

Post by dc740 »

LOADADDR(section)
Return the absolute load address of the named section. This is normally the same as ADDR, but it may be different if the AT keyword is used in the section definition (see section Optional Section Attributes).
AT ( ldadr )
The expression ldadr that follows the AT keyword specifies the load address of the section. The default (if you do not use the AT keyword) is to make the load address the same as the relocation address. This feature is designed to make it easy to build a ROM image. For example, this SECTIONS definition creates two output sections: one called `.text', which starts at 0x1000, and one called `.mdata', which is loaded at the end of the `.text' section even though its relocation address is 0x2000. The symbol _data is defined with the value 0x2000: SECTIONS
{
.text 0x1000 : { *(.text) _etext = . ; }
.mdata 0x2000 :
AT ( ADDR(.text) + SIZEOF ( .text ) )
{ _data = . ; *(.data); _edata = . ; }
.bss 0x3000 :
{ _bstart = . ; *(.bss) *(COMMON) ; _bend = . ;}
}

The run-time initialization code (for C programs, usually crt0) for use with a ROM generated this way has to include something like the following, to copy the initialized data from the ROM image to its runtime address:

char *src = _etext;
char *dst = _data;

/* ROM has data at end of text; copy it. */
while (dst < _edata) {
*dst++ = *src++;
}

/* Zero bss */
for (dst = _bstart; dst< _bend; dst++)
*dst = 0;

As far as I understand, LOADADDR doesn't have anything to do with the File Offset of the section, which was failing to be located below 8k when I aligned the multiboot header. I still don't trust the linker about this, but this is the .tet section in my link script if someone wants to learn from my mistakes:

Code: Select all

  .text VIRT_ADDR : AT(PHYS_ADDR) {
    *(.__mbHeader) // mb header must be inside .text, and above *(.text)
  	*(.text)
  	*(.rodata) *(.rodata.*)
  	. = ALIGN(4096);
  } = 0x90
If I don't use an entire section in the link file, the linker won't place the section in any place of the file (below 8k)... I know it sound weird.

But as you have seen in my previous posts, even if the linker lists the .__mbHeader section explicitly placed above .text and .data in the link scripts... the file offset of the section can end up above 8k cause the linker can relocate the section in the elf file. The safest way to "fix" it as far as I was able to test is to put the .__mbHeader "inside" the .text section in the linker.
This way the linker doesn't change where the code is stored in the file... And you are sure that the mbHeader will end up at the beginning of the .text.
Using the script above the final binary dump starts like this:

Code: Select all

build/toy:     file format elf32-i386


Disassembly of section .text:

c0100000 <_start>:
c0100000:       e9 23 04 00 00          jmp    c0100428 <start>

c0100005 <_mboot>:
c0100005:       00 00                   add    %al,(%eax)
c0100007:       00 02                   add    %al,(%edx)
c0100009:       b0 ad                   mov    $0xad,%al
c010000b:       1b 03                   sbb    (%ebx),%eax
c010000d:       00 00                   add    %al,(%eax)
c010000f:       00 fb                   add    %bh,%bl
c0100011:       4f                      dec    %edi
c0100012:       52                      push   %edx
c0100013:       e4
.......

Code: Select all

build/toy:     file format elf32-i386
build/toy
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0xc0100428

Program Header:
    LOAD off    0x00001000 vaddr 0xc0100000 paddr 0x00100000 align 2**12
         filesz 0x00003000 memsz 0x00004d00 flags rwx
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
         filesz 0x00000000 memsz 0x00000000 flags rwx

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00002000  c0100000  00100000  00001000  2**4
Finally, this is my header.

Code: Select all

.section .__mbHeader
_start:
	jmp start
_mboot:
.align 4
    .long MBOOT_HEADER_MAGIC
    .long MBOOT_HEADER_FLAGS
    .long MBOOT_CHECKSUM

/*
    // AOUT kludge - must be physical addresses. Make a note of these:
    // The linker script fills in the data for these ones if you use them.
    // Remember to set the bit 16 on in the multiboot flags.
    // And remember this is only needed if you are not using an ELF file format for your kernel
    .long _mboot
    .long _start
    .long _end_data
    .long _end_bss
    .long start*/
.section .text
.func start
start:	
I'm posting this so it helps the next guy with the error 13. I think this post has details on how to debug it (using objdump) and may come handy in the future.

Thank you all
Post Reply