I have a linker script for a kernel with two absolute symbols: _kernel_start
and _kernel_end
. However, I get a linker relocation error for only _kernel_end
:
In function `kernel::mem::mm::setup_memorymap':
/home/virtlink/kernel/src/mem/mm.rs:25:(.text._ZN3mem2mm15setup_memorymap):
relocation truncated to fit: R_X86_64_PC32 against symbol `_kernel_end'
defined in *ABS* section in ./kernel.bin
There have been many questions here on SO about this error, but I found none that solves my particular problem.
Apparently, this:
_kernel_start = .;
...is treated as 32-bit, whereas this:
. += KERNEL_BASE;
_kernel_end = . - KERNEL_BASE;
...is treated as 64-bit. If I move the _kernel_end
symbol above the . += KERNEL_BASE
line like this:
_kernel_end = .;
. += KERNEL_BASE;
...then it works again. But I want _kernel_end
at the end of my linker script.
The linker script puts the boot code at the start of memory, and the rest of the code in the higher-half of the 64-bit virtual memory space. It looks like this:
OUTPUT_FORMAT(elf64-x86-64)
KERNEL_BASE = 0xFFFFFFFF80000000;
SECTIONS
{
/* Boot code at 1 MiB */
. = 1M;
_kernel_start = .;
.boot :
{
KEEP( *(.multiboot) )
*(.boot)
*(.bootdata)
}
/* Kernel code at high virtual address. */
. += KERNEL_BASE;
.text ALIGN(4K) : AT(ADDR(.text) - KERNEL_BASE)
{
*(.text)
*(.gnu.linkonce.t*)
}
.data ALIGN(4K) : AT(ADDR(.data) - KERNEL_BASE)
{
*(.data)
*(.gnu.linkonce.d*)
}
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_BASE)
{
*(.rodata)
*(.gnu.linkonce.r*)
}
.bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_BASE)
{
*(COMMON)
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b*)
}
_kernel_end = . - KERNEL_BASE;
/DISCARD/ :
{
*(.comment)
*(.eh_frame)
}
}
The kernel is really small, so _kernel_start = 0x00100000
and _kernel_end = 0x00142000
. It shouldn't give me relocation errors.
How can I rewrite the linker script such that _kernel_end
give me no more relocation errors? I don't want to use mcmodel=large
just for this one symbol.
Here's the code in which I'm using the symbols. It's Rust.
fn get_kernel_location() -> (*const u8, *const u8) {
extern {
static _kernel_start: u8;
static _kernel_end: u8;
}
let kernel_start: *const u8 = &_kernel_start;
let kernel_end: *const u8 = &_kernel_end;
println!("{:p}", kernel_start);
println!("{:p}", kernel_end);
(kernel_start, kernel_end)
}
Here are the entries in the relocation table of the Rust compiled object file:
Offset Info Type Sym. Value Sym. Name + Addend
000000000012 058800000009 R_X86_64_GOTPCREL 0000000000000000 _kernel_end - 4
000000000019 058900000009 R_X86_64_GOTPCREL 0000000000000000 _kernel_start - 4