Page 1 of 1
Kernel too big? "relocation truncated to fit: R_386_16"
Posted: Sat Feb 13, 2016 11:55 am
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
Re: Kernel too big? "relocation truncated to fit: R_386_16"
Posted: Sat Feb 13, 2016 12:15 pm
by Techel
There's probably something wrong with your code:
http://forum.osdev.org/viewtopic.php?f=1&t=13848
Re: Kernel too big? "relocation truncated to fit: R_386_16"
Posted: Sat Feb 13, 2016 12:53 pm
by Avidanborisov
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.
Re: Kernel too big? "relocation truncated to fit: R_386_16"
Posted: Sat Feb 13, 2016 6:17 pm
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.
Re: Kernel too big? "relocation truncated to fit: R_386_16"
Posted: Sun Feb 14, 2016 1:05 pm
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.
Re: Kernel too big? "relocation truncated to fit: R_386_16"
Posted: Sun Feb 14, 2016 5:57 pm
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?
Re: Kernel too big? "relocation truncated to fit: R_386_16"
Posted: Mon Feb 15, 2016 5:03 am
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.
Re: Kernel too big? "relocation truncated to fit: R_386_16"
Posted: Mon Feb 15, 2016 11:12 am
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
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.