This article enables you to understand or create linker script using linker directives. The directives and examples used in the article are written for GHS/RH850 Toolchain. However, the basics are same other linkers too and the learning could be applied.
What goes into the Target?
When we compile an Embedded program, the code is transformed into a binary file, a format that can be downloaded into the target hardware for execution.
The transformed binary code usually contains 4 types of code items in it, arranged together as specified by the developer.
The 4 different program sections in a binary file (or a program) are,
.text - segment for executable instructions. Also known as Code segment.
.bss - segment for uninitialized data . The stationery init code zeroes this area at startup.
.data - segment for initialized data.
.rodata - segment for constant data.
Note : The data referred in the .bss/.data/.rodata are the global variables. The local variables are "executable instructions" and they are part of .text section.
Where it goes into the Target?
After the compilation is successfully completed, different code sections are still available as fragments.
The Linker binds these compiled fragments together and assigns them with the target hardware's address. This operation is called Linking. This is established with the help of the Linker Directives(.ld) file which instructs the linker on how to perform the linking operation.
Therefore the linker directives usually contain the different code segments and their respective memory addresses. The linker directives shall also include other information that is specific to the compiler or hardware architecture or the application.
This page provide information on what is a linker directive file, its contents, and details about each directive based on the Green hills (GHS) toolchain and the memory models based on Renesas RH850.
Linker Directives - GHS
Linker directives file is used to define program sections, and to assign them to specific addresses in memory. In this section, the structure and the contents of the GHS linker directive are explained. These files may contain the following directives (sections):
OPTION - Specifies Linker options
CONSTANTS - Sets constant values for use in memory and section maps.
MEMORY - Defines a memory map by specifying the name, starting address, and size of memory regions.
SECTIONS - Defines a section map by specifying the name, attributes, and address (memory locations) of program sections.
Below is an example of how the different sections in a linker directives file look. It also shows the default program sections that shall be present in a program. Custom linker directives shall be created based on the default linker directive file provided by the IDE.
Note : Comment lines are prefixed by a hash symbol (#)
Next, let's get to the directives in detail.
The OPTION Directive
This directive is used to include command-line options in a linker directives file. The syntax is,
Example 1. Using the OPTION Directive
To specify the -checksum and -map options in a linker directives file, add the following line:
To use the OPTION directive to specify files to be linked, using the following syntax:
The CONSTANTS Directive
This directive is to specify the constants for use in a linker directives file. The syntax is,
Example 2. Using the CONSTANTS Directive
To set the constant foo to the value 0x2000 in a .ld file, add the following line:
Multiple constants can be specified, separated by a semicolon,
Constants specified in this manner can be used in place of absolute values in section and memory maps.
The MEMORY Directive (Memory Maps)
The linker uses a memory map to define regions of memory, to which the sections of the executable can be assigned. A memory map is formatted as follows:
where:
memname - Specifies the name of a memory region.
origin_expression - Specifies the starting address of the memory region.
length_expression - Specifies the length of the memory region.
All three of these elements must be specified for each region to produce a valid memory map. If a memory map is passed to the linker, only the memory regions named and defined in it can be referenced in an associated section map.
Example 3. Format of a Memory Map
The linker directives file, memory.ld contains the following memory map:
Using “M” (meaning megabyte) and “K” (meaning kilobyte ) in memory maps as shown in the first two lines of the above example is accepted.
The SECTIONS Directive (Section Maps)
The linker uses a section map to write program sections in the executable to particular memory regions. A section map is formatted as follows:
where,
secname - Specifies the name of a section
start_expression -
Specifies the starting address of the section.
If omitted, the section starts at the next available address. Usually, this is the address immediately after the previous section. However, if a memory map has been specified and there is not enough space to hold the section in the current memory region, the linker will search for the next valid area of memory (specified in the memory map) into which the section will fit.
The starting address may be further modified to fit the alignment constraints of the subsections included by contents.
attributes -
Specifies the attributes of the section.
Any number of attributes shall be included. The list of attributes is detailed separately on this page.
contents - Specifies section inclusion commands and assignments.
There is no limit to the number of commands and assignments.
Section inclusion commands are of the form filename (secname), which directs that the section secname from filename should be included.
To include the section secname from all files, use {*(secname)}.
Leaving the {contents} section empty is also equivalent to adding {*(secname)}.
The special variable dot (“.”), represents the current offset within the section. You can assign symbols to dot using the syntax “symbol=.;”. You can create holes in the section using the dot. For example, “.+=0x100;” would create a 0x100 byte hole or a gap inside a section.
memname
Allocates the section to memory region memname, which is pre-defined in the memory map.
If both a start_expression and a memname memory region are specified in a line in the section map, then the start_expression takes precedence. If more than one section is assigned to a memname memory region, the section that appears first in the section map will be written to that region first.
All sections in input files that participate in the memory layout must be referenced in the section map.
Example 4. Format of a Section Map
The linker directives file, sections.ld contains the following section map, which references certain of the named memory regions.
In this example, .data is written to the memory region dram_memory. Since no memory region is specified for .bss, this section will be written to dram_memory, immediately after .data.
Example 5. Including and Renaming Sections The section map lines given in this example demonstrate the syntax of the most commonly used forms of section inclusion. For the sake of clarity, only the executable section name and the {contents } argument are given.
The first line above includes the .data sections from all object files in the .data section of the executable. Since the executable section and the object file sections have the same name, it is not necessary to specify a { contents } argument in this case, and the syntax given in the second line is sufficient.
The line includes the .data sections from all object files in the .newdata section of the executable.
The line includes the .data sections from foo.o and then bar.o in the .newdata section of the executable. The .data sections from all other object files are included, by default, in the .data section of the executable.
The line includes the .text section from the object file foo.o contained within lib.a in the .newtext section of the executable. The .text sections from all other object files are included, by default, in the .text section of the executable.
The line includes the .bss section from all object files contained within lib.a in the .newbss section of the executable. The .bss sections from all other object files are included, by default, in the .bss section of the executable.
Library filenames are case-sensitive. You may specify just the library name, rather than a full path.
SECTION ATTRIBUTES
The following section attributes can be used to configure the section behavior:
ABS
Sets a flag in the output file that indicates that this section has an absolute address and should not be moved. Program loaders and other utilities, that manipulate the output-image should not include this section in any movement related to position independent code or position independent data.
CLEAR / NOCLEAR
Sets or removes the CLEAR attribute of the section. If CLEAR is present, an entry is made in the run-time clear table, which is often used by startup code to initialize memory regions to a particular value.
The CLEAR attribute is set by default for .bss section or other such data sections (we will see other sections similar to .bss later).
PAD (value)
Places value bytes of padding at the beginning of the section. This is equivalent to specifying padding at the beginning of the section contents.
Example: The following two definitions are equivalent:
These attributes allocate the contents of a section, sect_name, to ROM at link time, while reserving space for the data to be copied to RAM at startup.
MIN_SIZE (size)
Instructs the linker to pad, if necessary, to ensure that the section is at least size bytes in length.
MIN_ENDADDRESS (address)
Instructs the linker to pad, if necessary, to ensure that the section extends to at least address.
MAX_SIZE (size)
Indicates that an error should be generated if the section exceeds size bytes in length.
MAX_ENDADDRESS (address)
Indicates that an error should be generated if the section extends beyond address during final layout.
Example 6. Using CLEAR and NOCLEAR
The section map is by default set to CLEAR :
This line above overrides the default CLEAR attribute of .sbss.
The line above disables the default clearing that results for SMALLCOMMONS:
The line above causes the heap, which is normally uninitialized, to be cleared at startup.
Example 7. Using ROM
A section created with this attribute will inherit the attributes and contents of the original section. The original section is marked uninitialized; that is, it is modified to reserve address space only, as if it were all padding with no contents.
To place the .data section in ROM with instructions to have it copied to RAM at startup, you would include, as a minimum, the following lines in your section map:
An entry is automatically made in the ROM copy table to ensure that during program startup the ROM image of .ROM.data is copied into the space reserved for .data.
Memory Model - Renesas/RH850
The Renesas RH850 and the V850 architectures, support the following memory models,
Normal data: The default memory model, where all data is placed within the data area and is accessed using normal load and store operations.
Small data: Data is assigned either automatically by the compiler or manually by the user (or both) into a small data area and is referenced using a reserved register (r4) as a base pointer, allowing for smaller code for data accesses. The total size of the Small is limited to 64 Kbytes.
Zero data: Similar to Small data, although the zero-register (r0) is used as the base register to access data within 16 bits of address 0. The total size of the Tiny Data Area is limited to 4 Kbytes.
Tiny data: Data is assigned by the user into small sections accessed using the Tiny Data Area (TDA) base register (ep) and the V850 short load/store operations providing for the smallest possible code for data accesses. The total size of the Tiny Data Area is limited to 4 Kbytes.
The usage of Small Data Area (SDA), Zero Data Area (ZDA), and Tiny Data Area (TDA) improves the program size and speed because addressing objects in this area requires fewer instructions.
The developer can assign different initialized, non-initialized, and constant data to each of these small/zero/tiny data areas. To accommodate these memory models and assigning different data into these Data areas, the linker directive file is prepared with specific information for each data area.
GHS program sections for RH850 memory models
Based on the RH850 memory model, the various program sections for the data are given below,
Memory Allocation for Initialized Variables - data, sdata, tdata, zdata.
Memory Allocation for Un-initialized Variables - bss, sbss, zbss.
Memory Allocation for Constant Variables - rodata, rosdata, rozdata.
Only initialized data are placed in the TDA. Therefore only tdata section can be added in the linker directives file.
The developer shall include any or all these program sections in their linker directives file and assign them memory locations.
I hope this post helped you to understand about linker directives. Happy Learning !
Comments