You may assign a value to a symbol in a linker script. This will define the symbol and place it into the symbol table with a global scope.
3.5.1 Simple Assignments
You may assign to a symbol using any of the C assignment operators:
symbol = expression ; symbol += expression ; symbol -= expression ; symbol *= expression ; symbol /= expression ; symbol <<= expression ; symbol >>= expression ; symbol &= expression ; symbol |= expression ;
The first case will define symbol to the value of expression. In the other cases, symbol must already be defined, and the value will be adjusted accordingly.
The special symbol name ‘.’ indicates the location counter. You may only use this within a SECTIONS command. See Section 3.10.5 [Location Counter], page 85.
The semicolon after expression is required.
Expressions are defined below; seeSection 3.10 [Expressions], page 84.
You may write symbol assignments as commands in their own right, or as statements within a SECTIONS command, or as part of an output section description in a SECTIONS command.
The section of the symbol will be set from the section of the expression; for more information, seeSection 3.10.8 [Expression Section], page 88.
Here is an example showing the three different places that symbol assignments may be used:
floating_point = 0;
SECTIONS {
.text : {
*(.text) _etext = .;
}
_bdata = (. + 3) & ~ 3;
.data : { *(.data) } }
In this example, the symbol ‘floating_point’ will be defined as zero. The symbol ‘_etext’
will be defined as the address following the last ‘.text’ input section. The symbol ‘_bdata’
will be defined as the address following the ‘.text’ output section aligned upward to a 4 byte boundary.
3.5.2 HIDDEN
For ELF targeted ports, define a symbol that will be hidden and won’t be exported. The syntax is HIDDEN(symbol = expression ).
Here is the example from Section 3.5.1 [Simple Assignments], page 60, rewritten to use HIDDEN:
HIDDEN(floating_point = 0);
SECTIONS {
.text : {
*(.text)
HIDDEN(_etext = .);
}
HIDDEN(_bdata = (. + 3) & ~ 3);
.data : { *(.data) } }
In this case none of the three symbols will be visible outside this module.
3.5.3 PROVIDE
In some cases, it is desirable for a linker script to define a symbol only if it is referenced and is not defined by any object included in the link. For example, traditional linkers defined the symbol ‘etext’. However, ANSI C requires that the user be able to use ‘etext’ as a function name without encountering an error. The PROVIDE keyword may be used to define a symbol, such as ‘etext’, only if it is referenced but not defined. The syntax is PROVIDE(symbol = expression ).
Here is an example of using PROVIDE to define ‘etext’:
SECTIONS {
.text : {
*(.text) _etext = .;
PROVIDE(etext = .);
} }
In this example, if the program defines ‘_etext’ (with a leading underscore), the linker will give a multiple definition diagnostic. If, on the other hand, the program defines ‘etext’
(with no leading underscore), the linker will silently use the definition in the program. If the program references ‘etext’ but does not define it, the linker will use the definition in the linker script.
Note - the PROVIDE directive considers a common symbol to be defined, even though such a symbol could be combined with the symbol that the PROVIDE would create. This is particularly important when considering constructor and destructor list symbols such as
‘__CTOR_LIST__’ as these are often defined as common symbols.
3.5.4 PROVIDE HIDDEN
Similar to PROVIDE. For ELF targeted ports, the symbol will be hidden and won’t be exported.
3.5.5 Source Code Reference
Accessing a linker script defined variable from source code is not intuitive. In particular a linker script symbol is not equivalent to a variable declaration in a high level language, it is instead a symbol that does not have a value.
Before going further, it is important to note that compilers often transform names in the source code into different names when they are stored in the symbol table. For example, Fortran compilers commonly prepend or append an underscore, and C++ performs extensive
‘name mangling’. Therefore there might be a discrepancy between the name of a variable as it is used in source code and the name of the same variable as it is defined in a linker script. For example in C a linker script variable might be referred to as:
extern int foo;
But in the linker script it might be defined as:
_foo = 1000;
In the remaining examples however it is assumed that no name transformation has taken place.
When a symbol is declared in a high level language such as C, two things happen. The first is that the compiler reserves enough space in the program’s memory to hold the value of the symbol. The second is that the compiler creates an entry in the program’s symbol table which holds the symbol’s address. ie the symbol table contains the address of the block of memory holding the symbol’s value. So for example the following C declaration, at file scope:
int foo = 1000;
creates an entry called ‘foo’ in the symbol table. This entry holds the address of an ‘int’
sized block of memory where the number 1000 is initially stored.
When a program references a symbol the compiler generates code that first accesses the symbol table to find the address of the symbol’s memory block and then code to read the value from that memory block. So:
foo = 1;
looks up the symbol ‘foo’ in the symbol table, gets the address associated with this symbol and then writes the value 1 into that address. Whereas:
int * a = & foo;
looks up the symbol ‘foo’ in the symbol table, gets its address and then copies this address into the block of memory associated with the variable ‘a’.
Linker scripts symbol declarations, by contrast, create an entry in the symbol table but do not assign any memory to them. Thus they are an address without a value. So for example the linker script definition:
foo = 1000;
creates an entry in the symbol table called ‘foo’ which holds the address of memory location 1000, but nothing special is stored at address 1000. This means that you cannot access the value of a linker script defined symbol - it has no value - all you can do is access the address of a linker script defined symbol.
Hence when you are using a linker script defined symbol in source code you should always take the address of the symbol, and never attempt to use its value. For example suppose you want to copy the contents of a section of memory called .ROM into a section called .FLASH and the linker script contains these declarations:
start_of_ROM = .ROM;
end_of_ROM = .ROM + sizeof (.ROM);
start_of_FLASH = .FLASH;
Then the C source code to perform the copy would be:
extern char start_of_ROM, end_of_ROM, start_of_FLASH;
memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);
Note the use of the ‘&’ operators. These are correct. Alternatively the symbols can be treated as the names of vectors or arrays and then the code will again work as expected:
extern char start_of_ROM[], end_of_ROM[], start_of_FLASH[];
memcpy (start_of_FLASH, start_of_ROM, end_of_ROM - start_of_ROM);
Note how using this method does not require the use of ‘&’ operators.