Linker blows up x86-64 Kernel

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
Andy1988
Member
Member
Posts: 50
Joined: Fri Aug 22, 2008 12:01 pm
Location: Germany - Aachen

Linker blows up x86-64 Kernel

Post by Andy1988 »

Hi there,
I'm just trying to code a startup environment for some 64 bit code, because I want to play with the virtualization extensions.

Unfortunately the linker always blows up my kernel with zeros.
The boostrapping code (from 32 bit to 64 bit) should be located at 1MB. This code sets up 4MB identity mapped memory starting at 0x0 and jumps to the 64 bit C-code. At least this is what it is supposed to do ;)

I tried elf64-x86-64 and elf64-little as a target binary in the linker script. Both results are the same.
GCC is the newest 4.4.2, Binutils: newest 2.20.

If I look at the produced ELF File with a Hex-Editor, I can see the ELF header and then a padding until 0x100000.
Why does the linker do this? It's not necessary.
If I modify the linker script so that the location is 0xC000000, the file is not 3GB big. It's just 2MB.

readelf says this:

Code: Select all

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .bootstrap        PROGBITS         0000000000100000  00100000
       000000000000008a  0000000000000000  AX       0     0     16
  [ 2] .rela.bootstrap   RELA             0000000000000000  001030a8
       0000000000000000  0000000000000018          26     1     8
  [ 3] .text             PROGBITS         000000000010008c  0010008c
       0000000000000f74  0000000000000000  AX       0     0     4
  [ 4] .eh_frame         PROGBITS         0000000000101000  00101000
       0000000000000040  0000000000000000   A       0     0     8
  [ 5] .rela.eh_frame    RELA             0000000000000000  001030a8
       0000000000000000  0000000000000018          26     4     8
  [ 6] .data             PROGBITS         0000000000101040  00101040
       0000000000000fc0  0000000000000000  WA       0     0     4
  [ 7] .rela.data        RELA             0000000000000000  001030a8
       0000000000000000  0000000000000018          26     6     8
  [ 8] .bss              NOBITS           0000000000102000  00102000
       0000000000004000  0000000000000000  WA       0     0     4
  [ 9] .stab             PROGBITS         0000000000000000  00102000
       0000000000000198  0000000000000014          11     0     4
  [10] .rel.stab         REL              0000000000000000  001030a8
       0000000000000000  0000000000000010          26     9     8
  [11] .stabstr          STRTAB           0000000000000000  00102198
       000000000000000d  0000000000000000           0     0     4
  [12] .debug_abbrev     PROGBITS         0000000000000000  001021a5
       0000000000000036  0000000000000000           0     0     1
  [13] .debug_info       PROGBITS         0000000000000000  001021db
       0000000000000056  0000000000000000           0     0     1
  [14] .rela.debug_info  RELA             0000000000000000  001030a8
       0000000000000000  0000000000000018          26    13     8
  [15] .debug_line       PROGBITS         0000000000000000  00102231
       000000000000003d  0000000000000000           0     0     1
  [16] .rela.debug_line  RELA             0000000000000000  001030a8
       0000000000000000  0000000000000018          26    15     8
  [17] .debug_frame      PROGBITS         0000000000000000  00102270
       0000000000000040  0000000000000000           0     0     8
  [18] .rela.debug_frame RELA             0000000000000000  001030a8
       0000000000000000  0000000000000018          26    17     8
  [19] .debug_loc        PROGBITS         0000000000000000  001022b0
       000000000000004c  0000000000000000           0     0     1
  [20] .debug_pubnames   PROGBITS         0000000000000000  001022fc
       000000000000001b  0000000000000000           0     0     1
  [21] .rela.debug_pubna RELA             0000000000000000  001030a8
       0000000000000000  0000000000000018          26    20     8
  [22] .debug_aranges    PROGBITS         0000000000000000  00102317
       0000000000000030  0000000000000000           0     0     1
  [23] .rela.debug_arang RELA             0000000000000000  001030a8
       0000000000000000  0000000000000018          26    22     8
  [24] .debug_str        PROGBITS         0000000000000000  00102347
       0000000000000042  0000000000000001  MS       0     0     1
  [25] .shstrtab         STRTAB           0000000000000000  00102389
       00000000000000e4  0000000000000000           0     0     1
  [26] .symtab           SYMTAB           0000000000000000  00102b70
       0000000000000450  0000000000000018          27    38     8
  [27] .strtab           STRTAB           0000000000000000  00102fc0
       00000000000000e5  0000000000000000           0     0     1
The first section should not be there.

Does anybody have a clue why this is done? Any help would be great.

Thanks and merry christmas ;)

Some code:
start.S

Code: Select all

PAGE_SIZE	equ	0x1000
STACK_SIZE	equ	16*1024

PML2		equ	0x1000
PML3		equ	PML2+PAGE_SIZE
PML4		equ	PML3+PAGE_SIZE

;---------------------------------------------------------------------------------------------
MODULEALIGN        equ        1<<0
MEMINFO            equ        1<<1
FLAGS              equ        MODULEALIGN | MEMINFO
MAGIC              equ        0x1BADB002
CHECKSUM           equ        -(MAGIC + FLAGS)

[extern main]
[global bootstrap]

;---------------------------------------------------------------------------------------------
section .text

[BITS 32]
align 4
MultiBootHeader:
	dd MAGIC
    dd FLAGS
    dd CHECKSUM
    
bootstrap:
    cli

    ; clear 3 pages of pagetables
    mov edi, PML2
    xor eax, eax
    mov ecx, 3*4096/4
    rep stosd

    ; set up pagetables
    mov dword [PML2], 0x87		; single 4 MB id mapping PML2
    mov dword [PML3], PML2 | 7	; single entry at PML3
    mov dword [PML4], PML3 | 7	; single entry at PML4

    ; load the GDT
    lgdt [gdt_desc]

    ; set PSE,  PAE
    mov eax, 0x30
    mov cr4, eax

    ; long mode
    mov ecx, 0xc0000080
    rdmsr
    or eax, 0x100
    wrmsr

    ; enable pagetables
    mov eax, PML4
    mov cr3, eax

    ; turn on long mode and paging
    mov eax, 0x80010001
    mov cr0, eax

    jmp SEL_CS:code64

[BITS 64]
code64:
    mov ax, SEL_DS
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax

    mov esp, bootStack
    call main

inf:
    jmp inf
    

;---------------------------------------------------------------------------------------------
section .data

gdt_desc:
	dw  GDT_LEN-1
	dd  gdt

align 8
gdt	    db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; 0x00 dummy
gdt_cs	db 0xff, 0xff, 0x00, 0x00, 0x00, 0x9b, 0xaf, 0x00 ; 0x08 code64
gdt_ds	db 0xff, 0xff, 0x00, 0x00, 0x00, 0x93, 0xaf, 0x00 ; 0x18 data64

GDT_LEN equ $ - gdt
SEL_CS equ gdt_cs - gdt
SEL_DS equ gdt_ds - gdt

;---------------------------------------------------------------------------------------------
section .bss
align 32

global bootStack
bootStack:
	resb      STACK_SIZE
main.c

Code: Select all

int main(void);

int main()
{
    for(;;);
    return 0;
}
linker.ld

Code: Select all

OUTPUT_FORMAT(elf64-x86-64)
ENTRY (bootstrap)

SECTIONS{
    . = 0x100000;
    .bootstrap :
    {
        obj/start.o (.text)
    }
    
    
    . += 0x0;
    .text : AT(ADDR(.text) - 0x0)
    {
        _code = .;
        *(.text)
        *(.rodata)
        . = ALIGN(4096);
    }

    .data : AT(ADDR(.data) - 0x0)
    {
        _data = .;
        *(.data)
        . = ALIGN(4096);
    }
    
    .ehframe : AT(ADDR(.ehframe) - 0x0)
    {
        _ehframe = .;
        *(.ehframe)
        . = ALIGN(4096);
    }

    .bss : AT(ADDR(.data) - 0x0)
    {
        _bss = .;
        *(.bss)
        . = ALIGN(4096);
    }
    
    _end = .;
    
    /DISCARD/ :
    {
        *(.comment)
    }
}
edit:
Just saw that the PML2 constant was set to 0x1000.
I was pretty sure that this didn't this behaviour since it is not a symbol, but I changed it anyway. As expected, the output remains the same :(
User avatar
quanganht
Member
Member
Posts: 301
Joined: Fri May 16, 2008 7:13 pm
Location: Hanoi, Vietnam

Re: Linker blows up x86-64 Kernel

Post by quanganht »

First, posting a bunch of code is just helpless. At least try to find a good starting point.

Second, notice the linker.ld:

Code: Select all

. = ALIGN(4096);
Those 4kb align will make your kernel big like an elephant. Try using ALIGN(64) instead. :wink:

EDIT:
Andy1988 wrote: SECTIONS{
. = 0x100000;
Did you consider this? Maybe it will pad 0x100000 zeros.
"Programmers are tools for converting caffeine into code."
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Linker blows up x86-64 Kernel

Post by Owen »

quanganht wrote:First, posting a bunch of code is just helpless. At least try to find a good starting point.

Second, notice the linker.ld:

Code: Select all

. = ALIGN(4096);
Those 4kb align will make your kernel big like an elephant. Try using ALIGN(64) instead. :wink:

EDIT:
Andy1988 wrote: SECTIONS{
. = 0x100000;
Did you consider this? Maybe it will pad 0x100000 zeros.
Please try to understand code before you critique it. For example, there are very good reasons to pad things out to page sizes; I, for example, would want my kernel to be able to take advantage of the NX and W bits as a layer of defense in depth
Andy1988
Member
Member
Posts: 50
Joined: Fri Aug 22, 2008 12:01 pm
Location: Germany - Aachen

Re: Linker blows up x86-64 Kernel

Post by Andy1988 »

quanganht wrote:First, posting a bunch of code is just helpless. At least try to find a good starting point.
Oh, I've got much more code than this ;) It's just not for a 64 bit architecture.
And as you can read I already did my own investigations. I just don't have any experience with x86-64. That's the reason I'm messing around with this in addition to the virtualization stuff.
If I would have been able to solve this problem on my own, I wouldn't have asked for help.
quanganht wrote: Second, notice the linker.ld:

Code: Select all

. = ALIGN(4096);
Those 4kb align will make your kernel big like an elephant. Try using ALIGN(64) instead. :wink:
Hmm... An elephant is 20KB for you?
I've got 5 sections each aligned at 4KB. Assuming that each of these sections are not bigger than 4KB, I end up with a total size of 20KB without any headers. Doesn't sound like an elephant to me ;)

And yes, Owen is perfectly right about the reason for the alignment. If you don't have your sections page aligned, you cant use any specific flags in your paging subsystem. The data inside the sections has a different behaviour. That's the reason because they exist.
You don't need to write into your .text section if you don't have any selfmodifying code. And you don't need to exectute any code in your .data or .bss sections except for a malicious attacker who want's to own your software with exploiting a buffer overflow ;)
Unfortunately all those flags can only be set with page granularity. That's the reason for the alignment.
quanganht wrote: EDIT:
Andy1988 wrote: SECTIONS{
. = 0x100000;
Did you consider this? Maybe it will pad 0x100000 zeros.
Yes, that may be the reason. But it doesn't help me very much, because I need this line.
It basically tells the linker on which address the following stuff will be located. My bootloader will load the kernel at 1MB. So all the addresses of any symbol must be after that address. That's exactly what this instruction does.
It's the same as the "ORG" instruction (it's not a real instruction) in any assembler.
User avatar
ucosty
Member
Member
Posts: 271
Joined: Tue Aug 08, 2006 7:43 am
Location: Sydney, Australia

Re: Linker blows up x86-64 Kernel

Post by ucosty »

I had this very problem myself and found I had to force the linker to use 4kb pages. This reduced the binary down the the desired size while maintaining 4kb alignment of sections.

The following linker option does this:

Code: Select all

-z max-page-size=0x1000
The cake is a lie | rackbits.com
User avatar
quanganht
Member
Member
Posts: 301
Joined: Fri May 16, 2008 7:13 pm
Location: Hanoi, Vietnam

Re: Linker blows up x86-64 Kernel

Post by quanganht »

Owen wrote:Please try to understand code before you critique it. For example, there are very good reasons to pad things out to page sizes; I, for example, would want my kernel to be able to take advantage of the NX and W bits as a layer of defense in depth
I did not say it is bad at all.
"Programmers are tools for converting caffeine into code."
Andy1988
Member
Member
Posts: 50
Joined: Fri Aug 22, 2008 12:01 pm
Location: Germany - Aachen

Re: Linker blows up x86-64 Kernel

Post by Andy1988 »

ucosty wrote:I had this very problem myself and found I had to force the linker to use 4kb pages. This reduced the binary down the the desired size while maintaining 4kb alignment of sections.

The following linker option does this:

Code: Select all

-z max-page-size=0x1000
That's it. Thank you!

Perhaps we should add this to the Wiki?
http://wiki.osdev.org/Creating_a_64-bit ... g.21.21.21
User avatar
ucosty
Member
Member
Posts: 271
Joined: Tue Aug 08, 2006 7:43 am
Location: Sydney, Australia

Re: Linker blows up x86-64 Kernel

Post by ucosty »

You're welcome. I've added it to the wiki though it can (should) probably be reworded as it's probably a little vague.
The cake is a lie | rackbits.com
Post Reply