Menu Close

RISC-V C Programming 1 (3)Linker Script

1.C Project Files Composition

Direct to Table of Contents:

 

RISC-V Syllabus

 

  • Test_dbg.cfg

It is mainly used to configure OpenOCD, and is not required for the normal project, only used for debugging. Generally, users do not need to modify it. It mainly includes jtag adapter interface clock configuration, jtag adapter interface chip configuration, CPU type, information of jtag module, and etc..

  • Entry.S

It is not used here, but will be used for interrupt project later. Detailed introduction will be present in C programming 2.

  • Startup.S

The first ever file will be executed in the project. It is assigned to the initial address space by the sys.lds file.

The main purpose is to initialize the registers, including the 31 registers ( X0 is hardwired to 0) and CSR registers. It also sets the stack initial position and call main function of the C source file.

  • Sys.lds

It is used to sort and classified store different file and variables under uniform address coding. See Figure 1 for a general idea of linker functionality. The code block is attached as well, and the detailed explanation is commended inside the code block.

Figure 1 Linker functionality [1]

 

2. sys.lds

OUTPUT_FORMAT( "elf32-littleriscv" )   /* specify the output as executable and linking format -> *.elf , and in 32 bits little endian*/
OUTPUT_ARCH( "riscv" )                 /*specify the output architecture is RISCV */
OUTPUT( "asm_temp.bin" )               /* specify the output file is asm_temp.bin, function the same as -o filename in the command line, if setting both at the same time, the command line has priority */
ENTRY(_start)                          /*set the start address as startup.s, function the same as --entry _filename in the command line 
                                         if setting both at the same time, the command line has priority */

_STACK_SIZE = 0x100;            /*define a macro */
_HEAP_SIZE  = 0x400;            /*define a macro */

/*
MEMORY: describe a memory block location and size 
syntax:
MEMORY
  {
    name [(attr)] : ORIGIN = origin, LENGTH = len
    …
  }

attr must consist only of the following characters:
R: read-only section
W: read/write section 
X: executable section
A: allocatable section
I: initialized section
L: same as 'I'
!: Invert the sense of any of the attributes that follow
origin is the start address of the memory region, it must evaluate to a constant and it cannot involve any symbols

len is an expression for the size in bytes of the memory region

*/

MEMORY
{
    rom (rx) :  ORIGIN = 0x80000000, LENGTH = 32K
    ram (wx!r) : ORIGIN = 0x90000000, LENGTH = 4K
}

SECTIONS                     /*section command */
{
    . = 0x80000000;          /*.address is set as 0x80000000 */
    .init  :                 /*initialization section */
    {
        KEEP (*(SORT_NONE(.init)))           /* put .init content into initialization, SORT_NONE means keeping the order*/
    }
    .text  :                /* program code*/
    { 
        *(.text)           /*put all the program code here */
    }
    .rodata :             /*read-only data*/
    {
        *(.rodata)        /* put all the read-only data here*/
    }
    . = 0x90000000;       /*.address is set as 0x90000000 */

    .data :               /* read-write initialized data*/
    { 
        *(.data)          /* put all the read-write initialized data here*/
    }

    .bss :               /* read-write zero initialized data*/
    { 
        . = ALIGN(4);    /* align with 4 bytes */
        *(.bss)         /* put all the read-write zero initialized data here*/
        *(COMMON)       /* put all the common symbols here*/
        . = ALIGN(4);   /*align with 4 bytes  */
        __bss_end = .;  /*assign __bss_end as the current address */
    }

    . = ALIGN(16);      /* align with 16 bytes*/

    .heap :             /*define heap section */
    { 
        _heap_begin = .;        /*define _heap_begin address*/
        _heap_start = .;        /*define _heap_start address*/
        . = . + _HEAP_SIZE;     /*define heap size*/
        . = ALIGN(16);          /*align with 16 bytes*/
        _heap_end = .;          /*define _heap_end address*/
    }

    . = ALIGN(8);              /*align with 8 bytes */

    /*provide is to define a symbol only if it is referenced and is not defined by any object included*/
    PROVIDE( _end = . );      /*define _end address */
    PROVIDE( end = . );       /*define end address */

    .stack ORIGIN(ram) + LENGTH(ram) - _STACK_SIZE :      /*define .stack address */
    {
        . = _STACK_SIZE;       /*_STACK_SIZE assign the address*/
        PROVIDE( _sp = . );     /*define _sp address */
    } 
}

 

  • Extra note

Right click test project, and go to Properties > Settings > Build Steps, as shown in Figure 2. Fill riscv64-unknown-elf-objdump.exe ${ProjName}.elf -d –full-contents -M no-aliases > fii.txt in the Post-build steps Command column, and compare with the result got when filling riscv64-unknown-elf-objdump.exe ${ProjName}.elf -d –full-contents -M no-aliases,numeric > fii.txt. See Table 1 for flag details.

Figure 2 Post-build steps setting

Option Description
-d Display assembler contents of executable sections
–full-contents Display the full contents of all sections requested
-M Pass text option on to the disassembler
No-aliases Disassemble only into canonical instructions, rather than into pseudo-instructions
numeric Print numeric register names, rather than ABI names

Table 1 Option description in riscv64-unknown-elf-objdump.exe

 

3.Reference

  1. “Linker Scripts for MSP430G2553 · MSP430-GCC”, Nhivp.github.io, 2020. [Online]. Available: https://nhivp.github.io/msp430-gcc/2018-07-19/linker-scripts. [Accessed: 27- Oct- 2020].
Posted in Application and Development, Articles, C Language, Embedded Programming Language, RISC-V, RISC-V Textbook, Textbook and Training Project

Related Articles

Leave a Reply

Your email address will not be published.

Leave the field below empty!