Page 1 of 1

Relocation truncated to fit ...

Posted: Sat Oct 10, 2020 4:47 pm
by Doctor5555
This question has probably been answered in many places before, but none of the responses I have found have worked.
Currently, I use UEFI to launch a bootloader which loads my kernel. The kernel is in ELF64 format. Having finally figured out how to virtually map memory, I want to move the kernel to the higher half.
To do this, I've changed the virtual memory address in the linker script. Unfortunately, this causes a large number of R_X86_64_PC32, R_X86_64_32S and R_X86_64_PLT32 relocation truncated to fit errors.
According to every source I have been able to find, adding -mcmodel=large and -fPIC to the compiler/linker options should fix this. However, it does not. Removing -mcmodel=large reduces the errors to only the R_X86_64_PC32 variants in .start.
Linker script:

Code: Select all

ENTRY(_start)

KERNEL_PMA = 0x00080000;
KERNEL_VMA = 0xC0000000;

SECTIONS
{
    . = KERNEL_PMA;

    .start : {
        _start = .;
        *(.start)
        . = ALIGN(4096);
    }

    . += KERNEL_VMA;

    .text : AT(ADDR(.text) - KERNEL_VMA)
    {
        _code = .;
        *(.text)
        *(.rodata*)
        . = ALIGN(4096);
    }

   .data : AT(ADDR(.data) - KERNEL_VMA)
   {
        _data = .;
        *(.data)
        . = ALIGN(4096);
   }

   .eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA)
   {
       _ehframe = .;
       *(.eh_frame)
        . = ALIGN(4096);
   }

   .bss : AT(ADDR(.bss) - KERNEL_VMA)
   {
       _bss = .;
       *(.bss)

       /*
        * You usually need to include generated COMMON symbols
        * under kernel BSS section or use gcc's -fno-common
        */

        *(COMMON)
       . = ALIGN(4096);
   }

   _end = .;

   /DISCARD/ :
   {
        *(.comment)
   }
}
To GCC for compilation of all .c files, I pass:

Code: Select all

-O2 -g -ffreestanding -Wall -Wextra -fstack-protector -mno-red-zone -mno-mmx -mno-sse -mno-sse2 -mcmodel=large -D__is_kernel -Iinclude
And when linking:

Code: Select all

-ffreestanding -Wall -Wextra -fstack-protector -z max-page-size=0x1000 -static -mcmodel=large
boot.s:

Code: Select all

global _start

section .start

_start:
    mov rdi, rcx

    extern early_kmain
    call early_kmain

    extern _init
    call _init

    extern kmain
    call kmain

    extern _fini
    call _fini

.end:
    cli
.loop:
    jmp .loop

.size: equ $ - _start
And last, but not least, the error output:

Code: Select all

arch/x86_64/boot.o: in function `_start':
arch/x86_64/boot.s:(.start+0xb): relocation truncated to fit: R_X86_64_PC32 against symbol `_init' defined in .init section in arch/x86_64/crti.o
arch/x86_64/boot.s:(.start+0x10): relocation truncated to fit: R_X86_64_PC32 against symbol `kmain' defined in .text section in kernel/kernel.o
arch/x86_64/boot.s:(.start+0x15): relocation truncated to fit: R_X86_64_PC32 against symbol `_fini' defined in .fini section in arch/x86_64/crti.o
kernel/kernel.o:(.eh_frame+0x20): relocation truncated to fit: R_X86_64_PC32 against `.start'
arch/x86_64/crtbegin.o: in function `deregister_tm_clones':
crtstuff.c:(.text+0x7): relocation truncated to fit: R_X86_64_32S against `.tm_clone_table'
arch/x86_64/crtbegin.o: in function `register_tm_clones':
crtstuff.c:(.text+0x38): relocation truncated to fit: R_X86_64_32S against `.tm_clone_table'
arch/x86_64/crtbegin.o: in function `__do_global_dtors_aux':
crtstuff.c:(.text+0x95): relocation truncated to fit: R_X86_64_32S against `.dtors'
crtstuff.c:(.text+0xe0): relocation truncated to fit: R_X86_64_PLT32 against undefined symbol `__deregister_frame_info'
arch/x86_64/crtbegin.o: in function `frame_dummy':
crtstuff.c:(.text+0x119): relocation truncated to fit: R_X86_64_PLT32 against undefined symbol `__register_frame_info'
/Users/doctor5555/Dev/betelgeuse/sysroot/usr/lib/libk.a(printf.libk.o): in function `printf':
/Users/doctor5555/Dev/betelgeuse/libc/stdio/printf.c:69:(.text+0xf8): relocation truncated to fit: R_X86_64_32S against `.rodata'
/Users/doctor5555/Dev/betelgeuse/libc/stdio/printf.c:129:(.text+0x146): additional relocation overflows omitted from the output
If someone could help point me towards what obvious error I am missing, or perhaps hopefully not so obvious error, that would be much apperciated.

Re: Relocation truncated to fit ...

Posted: Sat Oct 10, 2020 11:06 pm
by sj95126
At some point, you have to explicitly jump to high-load space, and you can't do that directly with call as you've written it, because it only uses 32-bit displacements from your current location. Hence the truncation.

Here's how I trampoline into high-load space:
(Forgive the AT&T syntax, it's been too long since I've used Intel)

Code: Select all

	movabsq	$(KERNEL_BASE+trampoline), %rax
	jmp	*%rax
trampoline:
Alternatively, your calls could be rewritten as, e.g.:

Code: Select all

movabsq $early_kmain, %rax
call *%rax

Re: Relocation truncated to fit ...

Posted: Sun Oct 11, 2020 1:35 am
by bzt
Doctor5555 wrote:The kernel is in ELF64 format.

Code: Select all

KERNEL_PMA = 0x00080000;
KERNEL_VMA = 0xC0000000;

SECTIONS
{
    . = KERNEL_PMA;                  <-- this

    .start : {
        _start = .;
        *(.start)
        . = ALIGN(4096);
    }

    . += KERNEL_VMA;                  <-- this
The two addresses are both in lower mem in long mode. And using both addresses in the linker script will make your binary 3G in size (max_page_size won't always solve this, at least not with some versions of gcc, you'll need --nmagic or --omagic, to workaround the bug in ld).

In the _start section, use only absolute virtual (higher-half) addresses, as @sj95126 suggested, then it won't matter where that code is actually mapped in lower-half. Or even better, just link _start with the rest.

BTW, with UEFI, there's no need to put _start at a fixed address, just link your kernel at VMA (do not use PMA in the linker script at all). With UEFI there's no guarantee that address 0x80000 is going to be free anyway.

Cheers,
bzt

Re: Relocation truncated to fit ...

Posted: Sun Oct 11, 2020 4:58 am
by Doctor5555
Thanks to @sj95126 for the tip on how to jump to higher mem. Code is nasm that I used was:

Code: Select all

mov rax, QWORD _start_high
jmp [rax]
This removed the first 3 linker errors, but I also had to move the kernel to -2GiB and compile with -mcmodel=kernel, and recompile libgcc with -mcmodel=large, to actually remove all the errors.
I might also map the kernel to higher half in the bootloader before jumping to _start, and then map it all to vma.