64 Bit Kernel Linker Script

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
inixsoftware
Member
Member
Posts: 32
Joined: Fri Jan 31, 2014 8:21 am

64 Bit Kernel Linker Script

Post by inixsoftware »

I am trying to make a 64-bit kernel, and so, I looked at: http://wiki.osdev.org/Creating_a_64-bit_kernel
I am using GRUB2, so, I looked at the section about the 32-bit bootstrap, and it gave the following snippet for the linker script:

Code: Select all

...
ENTRY(bootstrap)
...
SECTIONS
{
    . = KERNEL_LMA;

    .bootstrap :
    {
        <path of bootstrap object> (.text)
    }

    . += KERNEL_VMA;

    .text : AT(ADDR(.text) - KERNEL_VMA)
    {
        _code = .;
        *(EXCLUDE_FILE(*<path of bootstrap object>) .text)
        *(.rodata*)
        . = ALIGN(4096);
    }
...
However, what exactly is <path of bootstrap object>?
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: 64 Bit Kernel Linker Script

Post by bluemoon »

You could have read the manual to learn the link script format.

Anyway, its *filename*, for example, my own link script I put the bootstrap on the beginning of text section:

Code: Select all

 .text : AT(ADDR(.text) - KERNEL_VMA)
    {
        *bootstrap*(.text)
        *(.text)
        *(.gnu.linkonce.t*)
        . = ALIGN(4096);
    }
and I have bootstrap.o to link with.
Synon
Member
Member
Posts: 169
Joined: Sun Sep 06, 2009 3:54 am
Location: Brighton, United Kingdom

Re: 64 Bit Kernel Linker Script

Post by Synon »

The ld scripting language is simpler than it looks.

You'll need to specify ENTRY(), which is your entry point (mine is a function called start32) and then possibly OUTPUT_FORMAT. The next thing you need is the SECTIONS. Here, you create the layout of your executable's segments and which sections to put in each segment. For example, my bootstrap header segment looks like this:

Code: Select all

. = PHYS_ADDR;
.boot : {
    *(.multiboot)
    __boot_start__ = .;
    *(.boot_text)
    *(.boot_rodata)
    *(.boot_data)
    . = ALIGN(4096);
    __pml4__ = .;
    . += 0x1000;
    __pdpt__ = .;
    . += 0x1000;
    __pdir__ = .;
    . += 0x2000;
    __boot_stack__ = .;
    __boot_end__  = .;
}
What this does is it tells ld to set the current relocation (the . variable) to PHYS_ADDR (which in my case, as with a lot of kernels, is 0x100000). You can think of this as similar to the ORG directive most assemblers have, except you can change it further down the file. It's also similar to NASM's $ variable except that it's writeable. Next I'm specifying that the .boot segment goes first. This is because my kernel is for x86_64 but GRUB 2 doesn't set up long mode, so I have to have 32-bit code first. In the .boot segment, the first section is .multiboot so that the multiboot header is right at the start of the file (immediately after the ELF header) because multiboot bootloaders only search the first 32 kiB of your file. The next line is defining a symbol which is placed at the given address (which here would be PHYS_ADDR plus the length of the .multiboot section). ld script symbols are different to variables in C because they're literally just named addresses; they don't have a value. When you want to use the symbol, you must take its address, e.g. &__boot_start__ is the address of the start of my .boot_text section. The value of __boot_start__ is whatever happens to be at that address. After that there are some more sections and then I tell ld to align the . variable to a page boundary. After that there are more symbols which are one page (4 kiB) apart from each other (except that the stack is 2 kiB after __pdir__).
Post Reply