Kernel too big? "relocation truncated to fit: R_386_16"

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
Avidanborisov
Posts: 10
Joined: Sat May 10, 2014 8:10 am

Kernel too big? "relocation truncated to fit: R_386_16"

Post by Avidanborisov »

Here I was adding new features to my kernel, when I started receiving the following errors:

Code: Select all

e820.o: In function `detectMemory':
boot/e820.cpp:16:(.text+0x16): relocation truncated to fit: R_386_16 against symbol `E820::count' defined in .bss section in e820.o
e820.o: In function `detectMemory':
boot/e820.cpp:16:(.text+0xc9): relocation truncated to fit: R_386_16 against symbol `E820::count' defined in .bss section in e820.o
...
I've been able to reproduce these by simply instantiating some templates, i.e. I believe the problem appears due to kernel code getting too big. It seems to be specifically related to code size only, as simply increasing the kernel binary (by creating a big static array for example) does not reproduce these errors.

I've determined that the largest possible size for kernel object file is 160692, and for the kernel binary (the one objcopied from the kernel object) 31820.

Anyone here familiar with this issue?
Thanks
Techel
Member
Member
Posts: 215
Joined: Fri Jan 30, 2015 4:57 pm
Location: Germany
Contact:

Re: Kernel too big? "relocation truncated to fit: R_386_16"

Post by Techel »

There's probably something wrong with your code: http://forum.osdev.org/viewtopic.php?f=1&t=13848
Avidanborisov
Posts: 10
Joined: Sat May 10, 2014 8:10 am

Re: Kernel too big? "relocation truncated to fit: R_386_16"

Post by Avidanborisov »

Roflo wrote:There's probably something wrong with your code: http://forum.osdev.org/viewtopic.php?f=1&t=13848
Perhaps, but it has to be due to a different reason than the one mentioned there (and the other threads I've found on relocation truncation errors).

Code size is clearly an issue though. I've been able to reproduce these errors by simply adding/removing one NOP.
Octocontrabass
Member
Member
Posts: 5587
Joined: Mon Mar 25, 2013 7:01 pm

Re: Kernel too big? "relocation truncated to fit: R_386_16"

Post by Octocontrabass »

There's definitely something wrong with your code.

Based on the name of your object file, I'm guessing you're doing something insane, like trying to make GCC emit code that can run in real mode.

You may get more insight into the problem by examining the assembly code generated by the compiler (with the -S flag) and the object code generated by the assembler (using objdump). Even if you can't figure out why it's generating 16-bit relocations, you should be able to narrow it down into something small you can post here.
Avidanborisov
Posts: 10
Joined: Sat May 10, 2014 8:10 am

Re: Kernel too big? "relocation truncated to fit: R_386_16"

Post by Avidanborisov »

Octocontrabass wrote:There's definitely something wrong with your code.
Based on the name of your object file, I'm guessing you're doing something insane, like trying to make GCC emit code that can run in real mode.
I am actually (using the .code16gcc directive). Here's the full source code for more info:
https://github.com/Avidanborisov/Bug/bl ... t/e820.cpp
Octocontrabass wrote: You may get more insight into the problem by examining the assembly code generated by the compiler (with the -S flag) and the object code generated by the assembler (using objdump). Even if you can't figure out why it's generating 16-bit relocations, you should be able to narrow it down into something small you can post here.
Here are the offending instructions:

Code: Select all

12:   66 0f b7 06 00 00       movzx  eax,WORD PTR ds:0x0
                     16: R_386_16    E820::count

c5:   66 0f b7 06 00 00       movzx  eax,WORD PTR ds:0x0
                     c9: R_386_16    E820::count

cf:   a3 00 00                mov    ds:0x0,ax
                     d0: R_386_16    E820::count

e4:   66 0f b7 06 00 00       movzx  eax,WORD PTR ds:0x0
                     e8: R_386_16    E820::count
Seems like the displacement of E820::count is too large to fit in 16-bit word.
Octocontrabass
Member
Member
Posts: 5587
Joined: Mon Mar 25, 2013 7:01 pm

Re: Kernel too big? "relocation truncated to fit: R_386_16"

Post by Octocontrabass »

GCC can't generate code that uses or understands segmentation, so it's limited to 64kB of addressable memory with 16-bit addressing. Since your kernel binary is linked to 0x9000, you end up with only 28kB of usable address space. (You also don't check the carry flag, so even if you do somehow manage to pack everything into that small space, it still won't work properly.)

The real problem here is that you're detecting memory, enabling A20, and switching to protected mode in your kernel. All of that only has to happen once, so why not put it in the bootloader?
User avatar
iansjack
Member
Member
Posts: 4706
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Kernel too big? "relocation truncated to fit: R_386_16"

Post by iansjack »

You are telling the assembler that you want to produce 16-bit code, but how is the C compiler supposed to know that? This is a recipe for disaster.
Avidanborisov
Posts: 10
Joined: Sat May 10, 2014 8:10 am

Re: Kernel too big? "relocation truncated to fit: R_386_16"

Post by Avidanborisov »

Octocontrabass wrote:GCC can't generate code that uses or understands segmentation, so it's limited to 64kB of addressable memory with 16-bit addressing. Since your kernel binary is linked to 0x9000, you end up with only 28kB of usable address space.
You're right, and I've already figured out how to solve it. Basically, the problems comes from the fact that the .data section, which was placed after .text, was used to store both real mode and protected mode data. Since the .text section grew too much, real mode data was placed beyond 64K, and therefore became unaddressable (not because memory beyond 64K is unaddressable in real mode, but because GCC only addresses data using the offset and never changes the segment pointer). The solution was to simply create a different section for real mode data and place it before .text, i.e. (.data16 is the new section for real mode data)

Code: Select all

ENTRY(entry)

SECTIONS {
    /**
     * This is the physical address the kernel will be loaded to
     * it must match the value of LOAD_SEGMENT in the bootloader
     */
    . = 0x9000;

    .boot : {
        *(.entry)
        *(.data16)
    }

    .text : {
        *(.text)
    }

    .rodata : {
        *(.rodata)
    }

    .data : {
        *(.data)
    }

    .bss : {
        *(COMMON)
        *(.bss)
    }

    kernelEnd = .;
}
Using GCC attributes, it's possible to specify the section in which C++ definitions should be placed by adding

Code: Select all

[[gnu::section(".data16")]];
to the definition.
Octocontrabass wrote:(You also don't check the carry flag, so even if you do somehow manage to pack everything into that small space, it still won't work properly.)
You're right. It didn't cause me any issues so far, but that's still a bug.
Octocontrabass wrote:The real problem here is that you're detecting memory, enabling A20, and switching to protected mode in your kernel. All of that only has to happen once, so why not put it in the bootloader?
That's more of a design decision rather than a problem. I didn't want to have a second stage bootloader, so I packed what I could into the boot-sector (basically finding and loading the kernel) and the rest is up to the kernel.
iansjack wrote:You are telling the assembler that you want to produce 16-bit code, but how is the C compiler supposed to know that? This is a recipe for disaster.
It's hacky, but it does work. Linux did it too.
Post Reply