| |||
| Home > Using scatter files > Placing functions and data at specific addresses | |||
Normally, the compiler produces RO, RW and ZI sections from a single source file. These regions contain all the code and data from the source file. To place a single function or data item at a fixed address, you must enable the linker to process the function or data separately from the rest of the input files.
The linker has two methods that enable you to place a section at a specific address:
You can create a scatter file that defines an execution region at the required address with a section description that selects only one section.
For a specially-named section the linker can get
the placement address from the section name. These specially-named
sections are called __at sections.
To place a function or variable at a specific address it must be placed in its own section. There are several ways to do this:
Place the function or data item in its own source file.
Use __attribute__((at( to
place variables in a separate section at a specific address.address)))
Use __attribute__((section(" to
place functions and variables in a named section.name")))
Use the AREA directive from assembly
language. In assembly code, the smallest locatable unit is an AREA.
Use the --split_sections compiler
option to generate one ELF section for each function in the source
file.
This option results in a small increase in code size for some
functions because it reduces the potential for sharing addresses,
data, and string literals between functions. However, this can help
to reduce the final image size overall by enabling the linker to
remove unused functions when you specify armlink --remove.
This example shows how to modify your source code to place code and data at specific addresses, and does not require a scatter file:
Create the source file main.c containing
the following code:
#include <stdio.h>
extern int sqr(int n1);
int gSquared __attribute__((at(0x5000))); // Place at 0x5000
int main()
{
gSquared=sqr(3);
printf("Value squared is: %d\n", gSquared);
}
Create the source file function.c containing
the following code:
int sqr(int n1)
{
return n1*n1;
}
Compile and link the sources:
armcc -c -g function.c armcc -c -g main.c armlink --map function.o main.o -o squared.axf
The --map option displays the memory map
of the image. Also, --autoat is the default.
In this example, __attribute__((at(0x5000))) specifies
that the global variable gSquared is to be placed
at the absolute address 0x20000. gSquared is
placed in the execution region ER$$.ARM.__AT_0x00005000 and
load region LR$$.ARM.__AT_0x00005000.
The memory map shows:
...
Load Region LR$$.ARM.__AT_0x00005000 (Base: 0x00005000, Size: 0x00000000, Max: 0x00000004, ABSOLUTE)
Execution Region ER$$.ARM.__AT_0x00005000 (Base: 0x00005000, Size: 0x00000004, Max: 0x00000004, ABSOLUTE, UNINIT)
Base Addr Size Type Attr Idx E Section Name Object
0x00005000 0x00000004 Zero RW 15 .ARM.__AT_0x00005000 main.o
This example shows how to modify your source code to place code and data in a specific section using a scatter file:
Create the source file main.c containing
the following code:
#include <stdio.h>
extern int sqr(int n1);
int gSquared __attribute__((section("foo"))); // Place in section foo
int main()
{
gSquared=sqr(3);
printf("Value squared is: %d\n", gSquared);
}
Create the source file function.c containing
the following code:
int sqr(int n1)
{
return n1*n1;
}
Create the scatter file scatter.scat containing
the following load region:
LR1 0x0000 0x20000
{
ER1 0x0 0x2000
{
*(+RO) ; rest of code and read-only data
}
ER2 0x8000 0x2000
{
main.o
}
ER3 0x10000 0x2000
{
function.o
*(foo) ; Place gSquared in ER3
}
RAM 0x200000 (0x1FF00-0x2000) ; RW & ZI data to be placed at 0x200000
{
*(+RW, +ZI)
}
ARM_LIB_STACK 0x800000 EMPTY -0x10000
{
}
ARM_LIB_HEAP +0 EMPTY 0x10000
{
}
}
The ARM_LIB_STACK and ARM_LIB_HEAP regions
are required because the program is being linked with the semihosting
libraries.
Compile and link the sources:
armcc -c -g function.c armcc -c -g main.c aarmlink --map --scatter=scatter.scat function.o main.o -o squared.axf
The --map option displays the memory map
of the image. Also, --autoat is the default.
In this example, __attribute__((section("foo"))) specifies
that the global variable gSquared is to be placed
in a section called foo. The scatter file specifies
that the section foo is to be placed in the ER3 execution
region.
The memory map shows:
Load Region LR1 (Base: 0x00000000, Size: 0x00001778, Max: 0x00020000, ABSOLUTE)
...
Execution Region ER3 (Base: 0x00010000, Size: 0x00000004, Max: 0x00002000, ABSOLUTE)
Base Addr Size Type Attr Idx E Section Name Object
0x00010000 0x00000004 Data RW 15 foo main.o
...
If you omit *(foo) from the scatter file,
the section is placed in the region of the same type. That is RAM in
this example.
This example shows how to modify your source code to place code and data at a specific address using a scatter file:
Create the source file main.c containing
the following code:
#include <stdio.h>
extern int sqr(int n1);
// Place at address 0x10000
const int gValue __attribute__((section(".ARM.__at_0x10000"))) = 3;
int main()
{
int squared;
squared=sqr(gValue);
printf("Value squared is: %d\n", squared);
}
Create the source file function.c containing
the following code:
int sqr(int n1)
{
return n1*n1;
}
Create the scatter file scatter.scat containing
the following load region:
LR1 0x0
{
ER1 0x0
{
*(+RO) ; rest of code and read-only data
}
ER2 +0
{
function.o
*(.ARM.__at_0x10000) ; Place gValue at 0x10000
}
RAM 0x200000 (0x1FF00-0x2000) ; RW & ZI data to be placed at 0x200000
{
*(+RW, +ZI)
}
ARM_LIB_STACK 0x800000 EMPTY -0x10000
{
}
ARM_LIB_HEAP +0 EMPTY 0x10000
{
}
}
The ARM_LIB_STACK and ARM_LIB_HEAP regions
are required because the program is being linked with the semihosting
libraries.
Compile and link the sources:
armcc -c -g function.c armcc -c -g main.c armlink --no_autoat --scatter=scatter.scat --map function.o main.o -o squared.axf
The --map option displays the memory map
of the image.
The memory map shows that the variable is placed in the ER2 execution
region at address 0x11000:
...
Execution Region ER2 (Base: 0x00001598, Size: 0x0000ea6c, Max: 0xffffffff, ABSOLUTE)
Base Addr Size Type Attr Idx E Section Name Object
0x00001598 0x0000000c Code RO 3 .text function.o
0x000015a4 0x0000ea5c PAD
0x00010000 0x00000004 Data RO 15 .ARM.__at_0x10000 main.o...
In this example, the size of ER1 is uknown.
Therefore, gValue might be placed in ER1 or ER2.
To make sure that gValue is placed in ER2,
you must include the corresponding selector in ER2 and link
with the --no_autoat command-line option. If you
omit --no_autoat, gValue is
to placed in a separate load region LR$$.ARM.__AT_0x00010000 that
contains the execution region ER$$.ARM.__AT_0x00020000.