Engineer-to-Engineer Note EE-069
a
Technical notes on using Analog Devices DSPs, processors and development tools
Visit our Web resources http://www.analog.com/ee-notes and http://www.analog.com/processors or
e-mail processor.support@analog.com or processor.tools.support@analog.com for technical support.
Understanding and Using Linker Description Files on SHARC®
Processors
Contributed by Matt Walsh Rev 2 – January 17, 2007
Introduction
Often, a programmer wants to control where a
particular piece of code or data resides in a
SHARC® processor’s memory space. For
example, on ADSP-2106x, ADSP-2116x, and
ADSP-2137x processors, it is possible to execute
from external memory; however, execution is
performed at a reduced rate. In this case, the
program will need to store and execute
frequently used code internally and store rarely
used code externally.
Regardless whether you want to relocate a
C function or an assembly routine, the
mechanism is the same. To map portions of code
or data to specific memory sections, it is
necessary to use the Linker Description File
(referred to as the .LDF file). This EE-Note
explains the functionality of the .LDF file and
demonstrates its features through an example.
The implementation details of specific
applications that require relatively complex
files (such as the external code-execution
example previously mentioned) are beyond the
scope of this document.
This EE-Note applies to all SHARC
L
The first step toward gaining an understanding of
the .LDF file is to understand the makeup of the
files involved in building a processor executable
(.DXE).
processors.
.LDF
Source Files
Source files contain code written in C or
assembly. The first step toward producing an
executable is to compile and/or assemble these
source files. The assembler outputs files called
object files. (The compiler outputs assembly files
that are then fed to the assembler.) The
VisualDSP++® assembler produces object files
that have a .DOJ extension.
Typically, these object files are
L
output to the ~/YourProject/debug
directory.
Object Files
Object files produced by the compiler and
assembler are divided into various sections
(referred to as object sections). Each of these
objects section holds a particular type of
compiled source code. For example, an object
section may hold program opcodes (48-bits
wide) or data such as variables (16, 32, or 40 bits
wide). Some object sections
information not pertinent to this discussion
(since the information is not important to the
user, such as debug-information, and so on).
Each object section has a different name that is
specified in the source code. Depending on
whether the source is C or assembly, a different
convention is used to specify the object section
name.
also hold
Copyright 2002-2007, Analog Devices, Inc. All rights reserved. Analog Devices assumes no responsibility for customer product design or the use or application of
customers’ products or for any infringements of patents or rights of others which may result from Analog Devices assistance. All trademarks and logos are property
of their respective holders. Information furnished by Analog Devices applications and development tools engineers is believed to be accurate and reliable, however
no responsibility is assumed by Analog Devices regarding technical accuracy and topicality of the content provided in Analog Devices Engineer-to-Engineer Notes.
a
In an assembly source, the code and/or data is
placed below the .SECTION segment_name.
Refer to the following e
.SECTION /dm seg_dmda
.VAR foo[3];
.SECTION /pm seg_pmco
r0 = 0x1234;
r1 = 0x4567;
r2 = r1 + r2;
In the example above, the seg_dmda object
section would contain the foo array, and the
three lines of code would be located in the
seg_pmco object section.
In a C source file, the programmer would use the
section("segment_name") directive, for
example:
section("ext_data") int temp;
section("ext_code") void func1(void)
{
int x = 1;
}
void func2(void)
{
int i = 0;
}
When the C file above is compiled, the code
generated from func1 will be stored in its own
separate object section of the of the .DOJ file
named
separately in ext_data.
So what happens to func2? If an object section
name is not specified, the compiler will use a
default name. In this case, the object file would
hold the code from
for program code with a default object section
name of seg_pmco.
ext_code. The temp variable will reside
xample.
func2 in an object section
The way that these object sections and object
section names relate
memory will be illustrated later.
to code placement in
Executable Files
Once the source files have been assembled
and/or compiled into their respective object files,
the linker combines these object files into one
integrated executable, which has a .DXE
extension.
Similar to object files, an executable is split into
different sections. All .DXE files produced by
the VisualDSP++ linker adhere to the splitting
rules as dictated by the ELF (Executable and
Linking Format) file standard. These sections are
referred to as DXE sections, and have DXE
section names.
DXE section names are completely independent
of object section names. They exist in different
namespaces, which means that a DXE section
name can be identical to an object section name.
(Unfortunately, in most distributed examples,
this is the case. Using the same name for
different items, although valid, is poor
programming style and makes the .LDF file look
confusing.)
The .DXE file is not loaded into the processor,
and it is not burned into an EPROM. A
contains extra information (in addition to the raw
code and data from the object files), which is
used by down-stream tools such as the loader
(to populate an EPROM) and the debugger
(to simulate or emulate a processor) in locating
code to the processor.
.DXE file
For the list of default object section names, refer
to the VisualDSP++ C/C++ Compiler and
Library Manual for SHARC Processors.
There are no default object section
L
Understanding and Using Linker Description Files (LDFs) on SHARC (EE-069) Page 2 of 7
names for assembly source files, and
.SECTION/ statements must be used.
.LDF Files vs. the Other Files
The linker’s job is reasonably straightforward.
Based on commands in the .LDF file, the linker
collects the object sections from the object files
and combines them into a single executable
a
(.DXE) file using the memory model declared in
.LDF file.
the
Let’s look at an example
.LDF file to understand
how this process works. We will examine an
.LDF file used to link assembly-only source code.
This is the simplest type of
.LDF file, because
C source code must be supported in the .LDF file
with additional information about the C run-time
header and other library information.
The following section of this document discusses
test.ldf (Listing 1). Each section below
includes a number that matches it to a
corresponding portion of the
.LDF file, identified
with [#:] adjacent to the .LDF feature being
described.
[1:] Memory{}
The first thing of interest in the .LDF file is the
Memory{} command, which defines the memory
model. It informs the linker as to the available
memory spaces and gives them names called
memory space names. It’s important to
understand that the memory space names
declared here have absolutely no relation to DXE
object section names. The next notable feature of
the .LDF file is used to bind (link) all of these
various sections and name spaces together.
When the .DXE file created by the linker is
loaded into the debugger or made ready for
EPROM via the loader, each of these downstream utilities will know where the different
DXE sections reside on-chip.
[3:] $OBJECTS and $COMMAND_LINE_OBJECTS
A minor thing to note is that we have used
$OBJECTS interchangeably with
$COMMAND_LINE_OBJECTS. This is because the $
symbol in the .LDF file is a macro definition. It
works much like a #define statement in C.
These macro definitions, though unnecessary in
this example, are quite useful when you have
multiple object files. For instance, there are two
options if you want to place all of the code
portions (object sections) of multiple files into
the same memory section. The first way is to
explicitly list each object file, as follows:
seg_pmco
{
INPUT_SECTIONS(
main.doj(seg_pmco)
config.doj(seg_pmco)
dsp.doj(seg_pmco)
)
}> seg_pmco
The second way is to define a macro (as follows)
[2:] Sections{}
$OBJECTS = main.doj, config.doj,
dsp.doj;
As mentioned, the second and perhaps most
important piece of the
Sections{} portion, where the linker does the
.LDF file is the
and place the macro in the INPUT_SECTIONS{}
portion of the .LDF file as follows:
real work. Based on three arguments to the
Sections{} command, the linker takes object
sections as inputs, places them in a DXE section,
and then maps each DXE section to the specified
memory space.
In test.ldf, for example, the first line in the
Sections{} command takes the object section
named seg_rth created in main.doj, places it in
the DXE section named seg_rth, and maps that
DXE section to the
Understanding and Using Linker Description Files (LDFs) on SHARC (EE-069) Page 3 of 7
seg_rth memory space.
seg_pmco
{
INPUT_SECTIONS(
$OBJECTS (seg_pmco)
)
} > seg_pmco
Both of these syntaxes are equivalent. They
cause the linker to go through all of the object
files listed, and if any object sections named
seg_pmco are found, it puts them into the same
DXE section (here
“seg_pmco”), and then links