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
Related
How do you get the size of an input section in ld? I would assume its just SIZEOF(.section); however ld gives an error upon trying to run that. Is there any way I can do this? .section would be defined in a .asm file like so
section .section
mov al, 15
Here's the linker script I have so far:
SECTIONS {
boot : {
*( .boot );
. += SECTOR_SIZE - SIZEOF( .boot_header );
*( .boot_header );
}
}
I have no problem with SIZEOF.
Just tested:
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_datasize = SIZEOF(.data);
Works as expected.
ok so, im linking a bootloader, kernel and linker (so i can make a functional os)
i keep getting this when im trying to link:
"ld:linker.ld:4: non constant or forward reference address expression for section .=0x0100000"
the linker:
ENTRY (loader)
SECTIONS {
.=0x00100000;
.text : {
*(.text)
}
.rodata ALIGN (0x1000): {
*(.data)
}
.bss : {
sbss = .;
*(COMMON)
*.(bss)
ebss = .;
}
}
my program is working on linux using gcc. Through the manual page, I find edata, which represent the first address past the end of the initialized data segment.
But I want know the first address of initialized data segment
How can I get it?
I have tried treating etext as the first address of initialized data segment. Then I got a segment fault when I increase the address and access the variable stored in it. I think some address space between etext and edata was not mapped into virtual memory. Is that right?
That depends on your linker scripts. For example on some platforms you have the symbol __bss_start at the beginning of BSS. It's a symbol without any data associated with it, you can get a pointer to it by extern declaring a variable with that name (only for the sake of taking the address of that variable). For example:
#include <stdio.h>
extern char __bss_start;
int main()
{
printf("%p\n", &__bss_start);
return 0;
}
You find this by looking in the linker script, for example in /usr/lib/ldscripts/elf_x64_64.x:
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
__bss_start = .; /* <<<<< this is what you're looking for /*
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
You can also see the edata you mentioned, but as edata is not reserved for the implementation (the PROVIDE means only to create this symbol if it otherwise isn't used) you should probably use _edata instead.
If you want the address to the start of the data section you could modify the linker script:
__data_start = . ;
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
__bss_start = .; /* <<<<< this is what you're looking for /*
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we don't
pad the .data section. */
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
You probably want to make a copy of the linker script (look for the right one in /usr/lib/ldscripts, they are different depending on what kind of output you're targeting) and supply it when you compile:
gcc -o execfile source.c -Wl,-T ldscript
Another option if you don't want to modify the linker script could be to use the __executable_start and parse the ELF headers (hoping that the executable is sufficiently linearly mapped)
As for _etext, it is the end of the text section (you can read that in the linker script as well, but I didn't include it in the excerpt), but the text section is followed by rodata, trying to write there is probably going to segfault.
You can use the linux tool size (binutils package in Debian/Ubuntu).
Example
size -A /usr/bin/gcc
results in
/usr/bin/gcc :
section size addr
.interp 28 4194928
.note.ABI-tag 32 4194956
.note.gnu.build-id 36 4194988
.gnu.hash 240 4195024
.dynsym 4008 4195264
.dynstr 2093 4199272
.gnu.version 334 4201366
.gnu.version_r 160 4201704
.rela.dyn 720 4201864
.rela.plt 3240 4202584
.init 14 4205824
.plt 2176 4205840
.text 384124 4208016
.fini 9 4592140
.rodata 303556 4592160
.eh_frame_hdr 8540 4895716
.eh_frame 50388 4904256
.gcc_except_table 264 4954644
.tbss 16 7052632
.init_array 16 7052632
.fini_array 8 7052648
.jcr 8 7052656
.data.rel.ro 3992 7052672
.dynamic 480 7056664
.got 216 7057144
.got.plt 1104 7057384
.data 2520 7058496
.bss 80976 7061024
.gnu_debuglink 12 0
Total 849310
I have an ARM board with ROM at 0x80000000 and RAM at 0x20000000. Board starts execution of raw binary code at 0x80000000.
I managed to get a simple ARM assembler program running on it, but I have to use C instead of ASM. I know I will need to use some kind of linker script, and then manually copy .data section to RAM and clear .bss, setup stack etc. but I haven't found a reliable solution how to do this yet, especially the linker script (it's very messy in my opinion).
Also, I can't get the linker to output a raw binary instead of ELF, but it's not a big deal as I can use objcopy later.
Thanks in advance.
This example is for STM32F051 MCU:
Very simple application as "blinky" (without delays):
// define used registers
#define RCC_AHB1 *(volatile unsigned int *)(0x40021014)
#define GPIOC_MODER *(volatile unsigned int *)(0x48000800)
#define GPIOC_BSRR *(volatile unsigned int *)(0x48000818)
// main program
void mainApp() {
RCC_AHB1 = 1 << 19; // enable clock for GPIOC
GPIOC_MODER = 1 << (9 * 2); // set output on GPIOC.P9
while (1) {
GPIOC_BSRR = 1 << 9; // set output on GPIOC.P9
GPIOC_BSRR = 1 << (9 + 16); // clear output on GPIOC.P9
}
}
// variables for testing memory initialisation
int x = 10;
int y = 0;
int z;
Here is also very simple startup file written in C (also will work in C++), which start application mainApp and initialise static variables .data initialised from ROM and .bss only set to zero, there are variables initialised to zero and uninitialised variables.
extern void mainApp();
// external variables defined in linker script
// address in FLASH where are stored initial data for .data section
extern unsigned int _data_load;
// defines start and end of .data section in RAM
extern unsigned int _data_start;
extern unsigned int _data_end;
// defines start and end of .bss section in RAM
extern unsigned int _bss_start;
extern unsigned int _bss_end;
void resetHandler() {
unsigned int *src, *dst;
// copy .data area
src = &_data_load;
dst = &_data_start;
while (dst < &_data_end) {
*dst++ = *src++;
}
// clear .bss area
dst = &_bss_start;
while (dst < &_bss_end) {
*dst++ = 0;
}
mainApp();
while(1);
}
// _stacktop is defined in linker script
extern unsigned int _stacktop;
// vector table, will be placed on begin of FLASH memory, is defined in linker script
// only reset vector defined, need add other used vectors (especially NMI)
__attribute__((section(".vectors"), used)) void *isr_vectors[] = {
&_stacktop, // first vector is not vector but initial stack position
(void *)resetHandler, // vector which is called after MCU start
};
And finaly linker script which define location and sizes of memories, sections for vector, program (text), data (.data and .bss) a stacktop
MEMORY {
FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 64K
SRAM(rwx) : ORIGIN = 0x20000000, LENGTH = 8K
}
SECTIONS {
. = ORIGIN(FLASH);
.text : {
*(.vectors)
*(.text)
} >FLASH
. = ORIGIN(SRAM);
.data ALIGN(4) : {
_data_start = .;
*(.data)
. = ALIGN(4);
_data_end = .;
} >SRAM AT >FLASH
.bss ALIGN(4) (NOLOAD) : {
_bss_start = .;
*(.bss)
. = ALIGN(4);
_bss_end = .;
} >SRAM
_stacktop = ORIGIN(SRAM) + LENGTH(SRAM);
_data_load = LOADADDR(.data);
}
Build this with these command (build and link at once):
$ arm-none-eabi-gcc -mcpu=cortex-m0 -mthumb -nostartfiles main.c startup.c -T stm32f051x8.ld -o main.elf
In symbol table is possible to see where are stored data:
$ arm-none-eabi-nm -C -l -n -S main.elf
08000000 00000008 T isr_vectors
08000008 00000034 T mainApp
0800003c 0000005c T resetHandler
08000098 A _data_load
20000000 D _data_start
20000000 00000004 D x
20000004 D _data_end
20000004 B _bss_start
20000004 00000004 B y
20000008 B _bss_end
20000008 00000004 B z
20002000 A _stacktop
Also you can look into listing:
arm-none-eabi-objdump -S main.elf
Raw binary:
arm-none-eabi-objcopy -O binary main.elf main.bin
MEMORY
{
bob : ORIGIN = 0x8000, LENGTH = 0x1000
ted : ORIGIN = 0xA000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > bob
__data_rom_start__ = .;
.data : {
__data_start__ = .;
*(.data*)
} > ted AT > bob
__data_end__ = .;
__data_size__ = __data_end__ - __data_start__;
.bss : {
__bss_start__ = .;
*(.bss*)
} > ted
__bss_end__ = .;
__bss_size__ = __bss_end__ - __bss_start__;
}
of course change the addresses as you see fit. clearly the names of the sections mean nothing, you might try rom and ram if it helps you.
If you were to do something like this
MEMORY
{
rom : ORIGIN = 0x80000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.bss : { *(.bss*) } > ram
.rodata : { *(.rodata*) } > rom
.data : { *(.data*) } > ram
}
and then objcopy it then you are going to end up with a huge file, even if you only have one instruction of .text and one byte of data. because a binary format has to cover everything as in the memory so it will make a file that is 0x80000000+sizeof(text)-0x20000000. If you had one instruction only in ram and one byte of data then the file would be 0x60000004 bytes or 1.6 gig. leave it as an elf and use objdump -D until you have your linker script worked out THEN make a .bin if you really need one. or make an intel hex or s record and again you can examine it to see if it is all in the same address space before trying a binary.
the first key to your problem is the AT
MEMORY
{
bob : ORIGIN = 0x8000, LENGTH = 0x1000
ted : ORIGIN = 0xA000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > bob
.data : { *(.data*) } > ted AT > bob
.bss : { *(.bss*) } > bob
}
it says I want the .data in the address space ted, but place it in the binary in the bob space. it compiles for the ted address space, but the bits are loaded from bob space. exactly what you want. except you dont know how much .data to copy from rom to ram. you have to be super careful where you place the variables in the more complicated one or it wont work right.
I am working on an embedded system (Stellaris Launchpad) and writing a simple OS (as a hobby project). The used toolchain is gcc-none-eabi.
My next step is to get used to the MPU to allow the kernel to prevent user programs from altering specific data. I have a bunch of C files and I splitted them in two parts: kernel and other.
I have the following linker script to start out with:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
SECTIONS
{
.text :
{
_text = .;
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
.data : /*AT(ADDR(.text) + SIZEOF(.text))*/ /*contains initialized data*/
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM AT > FLASH
.bss : AT (ADDR(.data) + SIZEOF(.data)) /*contains unitialized data (should be set to all zero's)*/
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
_start_heap = .;
} > SRAM
_stack_top = ORIGIN(SRAM) + LENGTH(SRAM) - 1; /*The starting point of the stack, at the very bottom of the RAM*/
}
And after reading up on linker scripts I know that I can replace the stars with filenames, and thus start splitting the flash in multiple parts. I would for example create a .kernel.bss section and put all of the kernel object files instead of the stars in that section.
My only problem left is that the kernel is not one file, it is a whole lot of files. And files might be added, removed etc. So how do I do this? How do I change my linker script so that a dynamic first group of files is mapped to the first place and a dynamic second group of files is mapped to a second place?
you know that you can specify what files are used as input for a section?
We use this for separating kernel and application code into fast internal flash, and slower external flash memory, like so:
.kernel_text :
{
build/kernel/*.o (.text*) /*text section from files in build/kernel*/
} > INT_FLASH
.app_text:
{
build/app/*.o(.text*)
} > EXT_FLASH
Section 4.6.4 might be helpful, (describes input sections in more detail)
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Using_ld_the_GNU_Linker/sections.html
I found a solution, allthough it feels a bit hacky. It does work though:
I found out that a linker script is OK with working on .a files if they are statically linked with ar. So lets say you have a buch of .o files that, together form the kernel: a.o, b.o, c.o. Use ar rcs kernel.a a.o, b.o, c.o. kernel.a is now your kernel, which you want to store seperately in memory.
The next thing you need to know is that the * in a linker script is actually a wildcard for everything not used yet. So we can create the following linker script:
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}
SECTIONS
{
.kernel.text :
{
_kernel_text = .;
KEEP(kernel.a(.isr_vector))
KEEP(kernel.a(_sbrk))
kernel.a(.text*)
kernel.a(.rodata*)
_kernel_etext = .;
_kernel_flash_data = ALIGN(0x4);
} > FLASH
.kernel.data : /*AT(ADDR(.text) + SIZEOF(.text))*/ /*contains initialized data*/
{
_kernel_data = .;
kernel.a(vtable)
kernel.a(.data*)
_kernel_edata = .;
} > SRAM AT > FLASH
.kernel.bss :
{
_kernel_bss = .;
kernel.a(.bss*)
kernel.a(COMMON)
_kernel_ebss = .;
} > SRAM
.text : /*AT (ADDR(.core.text) + SIZEOF(.core.text) + SIZEOF(.core.data))*/
{
_text = .;
*(.text*)
*(.rodata*)
_etext = .;
_flash_data = ALIGN(0x4);
} > FLASH
.data :
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM AT > FLASH
.bss : AT (ADDR(.data) + SIZEOF(.data)) /*contains unitialized data (should be set to all zero's)*/
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
_start_heap = .;
} > SRAM
}
This works but will probably lead to a new problem: the linker treats libraries as.. well, libraries. So if they contain the program start (as in my case) the linker does not actually look for it, the linker only looks trough the library for functions refered to by the actual o files. The solution I found for this is to add the -u <name> flag to the linker invocation. This flag causes a symbol to become undefined, so the linker will look for this symbol plus all symbols that are needed by this synbol.
My invocation, for references sake:
arm-none-eabi-ld -Tlinker_script.ld -nostdlib --entry ResetISR
--gc-sections -u _sbrk -u .isr_vector
-L./lib//hardfp
-L/home/me/gcc-arm-none-eabi/gcc-arm-none-eabi-4_9-2015q1/arm-none-eabi/lib/armv7e-m/fpu
-L/home/me/gcc-arm-none-eabi/gcc-arm-none-eabi-4_9-2015q1/lib/gcc/arm-none-eabi/4.9.3/armv7e-m/fpu
-Lrelease/
-o release/os
./user/obj/release/ledsDance.c.o ./user/obj/release/main.c.o ./validation/obj/release/val_floattest.c.o ./validation/obj/release/val_genTest.c.o ./validation/obj/release/val_gpiotest.c.o ./validation/obj/release/val_iotest.c.o ./validation/obj/release/val_proctest.c.o ./validation/obj/release/val_schedTest.c.o release/kernel.a release/core.a
-ldriver-cm4f
-luartstdio
-lm
-lc
-lgcc