There is a provision for the users to be able to provide their own memory layout
(linker scripts) if they choose to. One needs to simple set the USER_LAYOUT_FILE
in the project level makefile. This variable should point to the user defined
memory layout (linker script) file.
Consider for example the following preoject layout
examples/physical/hifive
├── Makefile
└── platform
├── layout.ld
├── platform.c
├── riscv32.S
└── uart
├── uart.c
└── uart.h
examples/physical/hifive/platform/layout.ld
is the user defined custom memory
layout file. The build system is instructed to pick this file over the inferred
layout file by setting the USER_LAYOUT_FILE
variable in the
examples/physical/hifive/Makefile
as below:
...
USER_LAYOUT_FILE = platform/layout.ld
...
In order for the shell to work correctly, the user memory layout file (linker script) must expose some symbols and define some sections. These symbols are essential for the shell to work correctly. Specifically because the shell depend on these to configure itself and find where the command table is.
Following sections are mandatory so the shell can find the commands:
*(.cmd_list*)
- The commands added using the ADD_CMD() macro go in this section. Shell looks for for a given string in this region to search the associated command to be execute on a string match.*(.auto_list*)
- The commands added using the AUTO_CMD() macro go in this section. Shell will automatically execute the commands in this section immediately after boot, before the prompt is presented for the user interactions.*(.cmd_end*)
- This section consist of12 Bytes
worth of0
s and marks the end of the command table. The shell infers the end of command table based on this section. This is important for the mechanics of the command search to work reliable.
Placement of the above sections must be made at the 8 Byte
aligned memory
address and these be placed together as follows:
. = ALIGN(8);
KEEP(*(.cmd_list*))
KEEP(*(.auto_list*))
KEEP(*(.cmd_end*))
The order is important! These must go in the exact same order!
Since the command table is supposed to be immutable during the runtime
these sections should be placed in the readonly sections of the memory. For
example the .rodata
section. Example placement is as below:
.rodata : {
...
. = ALIGN(8);
KEEP(*(.cmd_list*))
KEEP(*(.auto_list*))
KEEP(*(.cmd_end*))
...
Certain symbols orient the shell and let's it do
- The initialization correctly.
- Be aware of where the command database is.
- Initialize the stack correctly.
Complete list of symbols is as follows:
_text
: points to start of the.text
section._etext
: points to end of the.text
section._data
: points to the start of the.data
section.- preferrably this should start at an
8 Byte
boundary.
- preferrably this should start at an
_edata
: points to the end of the data sections._bss
: points to starts of the.bss
sections.- preferrably this should start at an
8 Byte
boundary.
- preferrably this should start at an
_ebss
: points to starts of the.bss
sections.__CMD_TABLE_START__
: points to the start of.cmd_list*
section.- Shell uses this symbol to infer the address at which the command database/table starts.
__AUTO_TABLE_START__
: points to the starts of.auto_list*
section.- After the boot, shell starts to execute all the commands placed in the
.auto_list*
section. It uses this symbol to infer the address of the start of the list of commands to be executed automatically.
- After the boot, shell starts to execute all the commands placed in the
_STACK_TOP_
: points to the start of the stack.
A typical linker script section placement would look as follows:
...
SECTIONS
{
.text : {
_text = .;
*(.init*)
*(.text*)
_etext = .;
} > ROM AT> ROM
.data : {
_data = .;
. = ALIGN(8);
*(.data*)
_edata = .;
} > RAM AT> ROM
.rodata : {
/* start: Command table for the shell */
. = ALIGN(8);
__CMD_TABLE_START__ = .;
KEEP(*(.cmd_list*))
__AUTO_TABLE_START__ = .;
KEEP(*(.auto_list*))
KEEP(*(.cmd_end*))
/* end: Command table for the shell */
*(.rodata*)
} > ROM AT> ROM
.bss : {
_bss = .;
. = ALIGN(8);
*(.bss*)
_ebss = .;
} > RAM AT> RAM
PROVIDE(_STACK_TOP_ = ORIGIN(RAM) + LENGTH(RAM) - 4);
}
An exmaple of a complete user defined linker file can look as follows (this can also be used as a template to start with and sections may be appended as needed):
MEMORY
{
ROM (rx) : ORIGIN = 0x0, LENGTH = 0x10000
RAM (rwx) : ORIGIN = 0x2000, LENGTH = 0x10000
}
SECTIONS
{
.text : {
_text = .;
*(.init*)
*(.text*)
_etext = .;
} > ROM AT> ROM
.data : {
_data = .;
. = ALIGN(8);
*(.data*)
_edata = .;
} > RAM AT> ROM
.rodata : {
/* start: Command table for the shell */
. = ALIGN(8);
__CMD_TABLE_START__ = .;
KEEP(*(.cmd_list*))
__AUTO_TABLE_START__ = .;
KEEP(*(.auto_list*))
KEEP(*(.cmd_end*))
/* end: Command table for the shell */
*(.rodata*)
} > ROM AT> ROM
.bss : {
_bss = .;
. = ALIGN(8);
*(.bss*)
_ebss = .;
} > RAM AT> RAM
PROVIDE(_STACK_TOP_ = ORIGIN(RAM) + LENGTH(RAM) - 4);
}
ROM
and RAM
origin and length can vary.