In this part I move on to describe the
GNU linker
script for a
bare-metal ARM project. The code accompanying this article is available
online at the
Embedded.com's Downloadable Code page
. The
recommended reading for this part includes "Embedded System Design on a
Shoestring" by Lewin Edwards [1],
specifically section "Ld—GNU Linker" in Chapter 3.
Linker Script
The linker script must match the startup code described in Part 2 of
this article for all the section names and other linker symbols. The
linker script cannot be generic, because it must define the specific
memory map of the target device, as well as other application-specific
information.
The linker script is therefore named here blinky.ld, which
corresponds to the Blinky example application that blinks the 4 user
LEDs of the AT91SAM7S-EK board. The C version of the example for this
article is located in the c_blinky directory, while the C++ version in
the cpp_blinky directory.


 |
| Listing
1 Linker script for the Blinky example application (AT91SAM7S64 MCU). |
Listing 1 above shows the
linker script for the Blinky example application. The script is almost
identical for C and C++ versions, with the minor differences discussed
later in this section. The highlights of the linker script are as
follows:
(1) The OUTPUT_FORMAT
directive specifies the format of the output image (elf32,
little-endian, ARM)
(2) OUTPUT_ARCH specifies
the target machine architecture.
(3) ENTRY explicitly
specifies the first instruction to execute in a program
(4) The MEMORY command
describes the location and size of blocks of memory in the target.
(5) The region ROM
corresponds to the on-chip flash of the AT91SAM7S64 device. It can
contain read-only and executable sections (rx), it starts at 0x00100000
and is 64KB in size.
(6) The region RAM
corresponds to the on-chip SRAM of the AT91SAM7S64 device. It can
contain read-only, read-write and executable sections (rwx), it starts
at 0x00200000 and is 16KB in size.
(7) The following symbols
denote the sizes of the ARM stacks. You need to adjust the sizes for
your particular application. The C-stack cannot be zero.
(8) The SECTIONS command
opens the definition of all the sections for the linker.
(9) The .reset section
contains the startup code (including the ARM vectors) and must be
located as the first section in ROM.
(10) This line locates all
.text sections from the startup.o object module.
(11) The section size is
aligned to the 4-byte boundary
(12) This section is loaded
directly to the ROM region defined in the MEMORY command.
(13) The .ramvect section
contains the RAM-based ARM vector table and the secondary jump table
and must be loaded as the first section in RAM
(14) The ARM vector table
and the secondary jump table have known size of 0x40 bytes. The current
location counter is simply incremented to reserve 0x40 bytes for the
section.
(15) The .ramvect section
goes into the RAM region.
(16) The .fastcode section
is used for RAM-based code, which needs to be loaded to ROM, but copied
and executed from RAM.
(17) The .fastcode section
has different load memory address (LMA) than the virtual memory address
(VMA). The symbol __fastcode_load corresponds to the LMA in ROM and is
needed by the startup code to copy the section from ROM to RAM.
(18) The __fastcode_start
symbol corresponds to the VMA of the .fastcode section and is needed by
the startup code to copy the section from ROM to RAM.
(19) The .glue_7t and
.glue_7 sections are synthesized by the compiler when you specify the
ARM-THUMB interworking option. The sections contain the "call veneers"
between THUMB and ARM code and are accessed frequently by every call
between ARM and THUMB. It's typically advantageous to place this small
amount of hot-spot code in RAM.
(20) The .text.fastcode
section is assigned explicitly to individual functions in the C/C++
code by means of the __attribute__ ((section (".text.fastcode")))
command.
(21) The GNU compiler is
also capable of placing each function in the separate section named
after the function (requires specifying the option
-ffunction-sections). This allows you to be very selective and to place
individual functions (e.g. the function Blinky_shift()) in RAM.
NOTE: The C++ compiler performs
function name-mangling and you need to consult the map file to figure
out the section name assigned to a given function. For example, the
class method Blinky::shift() is placed in the section
.text._ZN6Blinky5shiftEv)
(22) You can place more
hot-spot functions in RAM during the fine-tuning stage of the project.
(23) The .fastcode section
is located in RAM, but is loaded at the ROM address.
(24) The .text section is
for code and read-only data accessed in place.
(25) If you repeat sections
already located in the .fastcode section, the earlier location will
take precedence. However, if you decide to remove these sections from
.fastcode, they will be located per the second specification.
(26) The following sections
are synthesized by the GNU C++ compiler and are used for static
constructors and destructors.
(27) The section .rodata is
used for read-only (constant) data, such as look-up tables. Just as
code, you might choose to place some frequently accessed constants in
RAM by locating these sections in the .fastcode section.
(28) The .text section is
located and loaded to ROM.
(29) The .ARM.exidx section
is used for C++ exception handling. It is located here for
completeness. Bare-metal ARM projects typically cannot afford the
overhead associated with C++ exceptions handling.
(30) The .data section
contains initialized data.
(31) The .data section is
located in RAM, but is loaded to ROM and copied to RAM during startup.
(32) The .bss section
contains uninitialized data. The C/C++ standard requires that this
section must be cleared at startup.
(33) The .bss section is
located in RAM only.
(34) The .stack section
contains all the stacks. The section is initialized with a given
bit-pattern at startup.
(35) The ARM GNU toolset
uses full descending stack. Therefore the linker script provides only
the top of stack symbols to initialize the various ARM stack pointers.
In particular the C stack (SYS stack) is allocated at the end of the
.stack section.
(36) The .stack section is
located in RAM.
(37) The symbols _end,
__end, and end are used to set up the beginning of the heap, if the
heap is used.
(38) The following sections
are for the debugger only and are never loaded to the target.
Coming Up Next
Next in Part 4, I'll describe
the C and C++ compiler options as well as how to minimize the overhead
of C++ using the GNU toolchain. Stay tuned.
To read Part 1, go to What's
needed to get started.
To read Part 2, go to Startup
Code and the Low-level Initialization.
To download the C and C++
source code associated with this article series, go to Embedded.com's Downloadable Code page,
or go to Blinky for C and Blinky for C++ to download the
Zip files.
Miro
Samek, Ph.D., is president of Quantum
Leaps, LLC. He can be contacted at miro@quantum-leaps.com.
References
[1]
Lewin A.R.W. Edwards, "Embedded
System Design on a Shoestring", Elsevier 2003.
[2]
GNU Linker (ld) HTML documentation included in the CodeSourcery
Toolchain for ARM..
[3] ARM
Projects