Page 1 of 1

Is ELF64 in GRUB2 broken? [SOLVED]

Posted: Tue Sep 30, 2014 12:17 pm
by AndrewAPrice
I'm using GRUB 2.02 beta2.113, and once my kernel grows beyond a certain size (_loadEnd - _loadStart) > 64kb, things start going funny.

GRUB seems to be putting things in funny places.

Here's part of my kernel disassembled:

Code: Select all

ffffffff8010c2dc <kmain>:
ffffffff8010c2dc: 55 push rbp
ffffffff8010c2dd: 48 89 e5 mov rbp, rsp
ffffffff8010c2e0: fa cli
ffffffff8010c2e1: f4 hlt
It's a higher half kernel, so that function should be loaded by GRUB at 10c2dc. Here's a memory dump of 10c2dc after GRUB loads the kernel:

Code: Select all

000000000010c2dc: 0x40 0x60 0x48 0x8b 0x04 0xc5 0x00 0xf0
000000000010c2e4: 0x10 0x80 0x48 0x89 0xc7 0xe8 0x1b 0x04
000000000010c2ec: 0x00 0x00 0xfa 0xf4 0xeb 0xfd 0xc9 0xc3
000000000010c2f4: 0x55 0x48 0x89 0xe5 0xfa 0xf4 0x90 0x5d
The function was actually loaded at 10c2f4 - 24 bytes higher than where it should have been. This only occurs when (_loadEnd - _loadStart) > 64kb.

Here's my linker script:

Code: Select all

ENTRY(EntryPoint)

VIRT_BASE = 0xFFFFFFFF80000000;

SECTIONS
{
  . = 0x100000;

  .boot :
  {
    *(.mbhdr)
    _loadStart = .;
    *(.boot)
    . = ALIGN(4096);
    Pml4 = .;
    . += 0x1000;
    Pdpt = .;
    . += 0x1000;
    Pd = .;
    . += 0x1000;
    . += 0x8000;
    Stack = .;
  }

  . += VIRT_BASE;

  .text ALIGN(0x1000) : AT(ADDR(.text) - VIRT_BASE)
  {
    *(.text)
    *(.gnu.linkonce.t*)
  }
  
  .data ALIGN(0x1000) : AT(ADDR(.data) - VIRT_BASE)
  {
    *(.data)
    *(.gnu.linkonce.d*)
  }
  
  .rodata ALIGN(0x1000) : AT(ADDR(.rodata) - VIRT_BASE)
  {
    *(.rodata*)
    *(.gnu.linkonce.r*)
  }
  
  _loadEnd = . - VIRT_BASE;
  
  .bss ALIGN(0x1000) : AT(ADDR(.bss) - VIRT_BASE)
  {
    *(COMMON)
    *(.bss)
    *(.gnu.linkonce.b*)
  }
  
  _bssEnd = . - VIRT_BASE;
  
  /DISCARD/ :
  {
    *(.comment)
    *(.eh_frame)
  }
}
And my multiboot header:

Code: Select all

MbHdr:
	dd 0xE85250D6 ; magic
	dd 0 ; architecture
	dd HdrEnd - MbHdr ; length
	dd -(0xE85250D6 + 0 + (HdrEnd - MbHdr)) ; checksum

	; tags

	; sections override
	dw 2, 0 ; multiboot_header_tag_address
	dd 24
	dd MbHdr
	dd _loadStart
	dd _loadEnd
	dd _bssEnd

	; entry point override
	dw 3, 0 ; multiboot_header_tag_entry_address
	dd 12
	dd EntryPoint
	dd 0 ; align next tag to 8 byte boundry

	; request some information from GRUB for the kernel
	dw 1, 0 ; multiboot_header_tag_information_request
	dd 12
	dd 6 ; request multiboot_tag_type_mmap
	dd 0 ; align next tag to 8 byte boundry

	; end of tags
	dw 0, 0 ; MULTIBOOT_TAG_TYPE_END
	dd 8

	; hdr end mark

HdrEnd:
My whole .text section is being loaded 24 bytes above where it should be. Is this a bug in GRUB, or would something be purposely offsetting the load address?

Re: Is ELF64 in GRUB2 broken? [SOLVED]

Posted: Tue Sep 30, 2014 1:05 pm
by AndrewAPrice
I fixed this, I just had to get rid of this tag in my multiboot header:

Code: Select all

; sections override
   dw 2, 0 ; multiboot_header_tag_address
   dd 24
   dd MbHdr
   dd _loadStart
   dd _loadEnd
   dd _bssEnd
I'm not sure why it triggered a bug when (_loadEnd-_loadStart) > 64kb, but it loads correctly when I remove that tag.

Re: Is ELF64 in GRUB2 broken? [SOLVED]

Posted: Wed Oct 01, 2014 3:46 am
by Icee
MessiahAndrw wrote:I'm not sure why it triggered a bug when (_loadEnd-_loadStart) > 64kb, but it loads correctly when I remove that tag.
Because the _loadStart symbol should have been placed before the .mbhdr section.