On ARM GCC (plain C code), when I declare a constant as in
__attribute__((used,section(".rodata.$AppID")))
const uint8_t ApplicationID[16] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
and I don't refer to it in the code, it's optimized out, and listed in Discarded input sections on map file. It's included in binary output only if I refer to it, somewhere else in the sources.
Shouldn't the "used
" tag alone be enough? In the GCC manual (6.34.1 Common Variable Attributes) I read:
used
This attribute, attached to a variable with static storage, means that the variable must be emitted even if it appears that the variable is not referenced.
The meaning is to have it at a fixed memory address, in the specified section, for a separate application to check for it
I'm running ARM GCC as provided with NXP MCUXpresso 11.1, reporting verbose version as
GNU C17 (GNU Tools for Arm Embedded Processors 8-2019-q3-update) version 8.3.1 20190703 (release) [gcc-8-branch revision 273027] (arm-none-eabi)
compiled by GNU C version 5.3.1 20160211, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3, isl version isl-0.18-GMP
Shouldn't the "used" tag alone be enough?
It is not sufficient, and it is not necessary. It is not relevant.
As per the GCC documentation that you have quoted, attribute used
is
applicable to definitions of static variables. And as an answer that is now
deleted by the author pointed out, your ApplicationID
is not static, so attribute used
has no effect.
Here:
/* app_id_extern.c */
#include <stdint.h>
const uint8_t ApplicationID[16] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
we have ApplicationID
defined, by default, as an extern
variable. The default storage class
of a filescope variable, like ApplicationID
, is extern
. The compiler will
accordingly generate an object file in which the definition of ApplicationID
is exposed for linkage, as we can see:
$ gcc -c app_id_extern.c
$ readelf -s app_id_extern.o
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
...
9: 0000000000000000 16 OBJECT GLOBAL DEFAULT 4 ApplicationID
In the object file, ApplicationID
is a 16-byte GLOBAL
symbol in section #4
(which in this case happens to be .rodata
). The GLOBAL
binding means that the static linker can see this symbol.
And here:
/* app_id_static.c */
#include <stdint.h>
static const uint8_t ApplicationID[16] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
we have ApplicationID
explicitly defined as a static
variable. The compiler
will accordingly generate an object file in which the definition of ApplicationID
is not exposed for linkage, as we can also see:
$ gcc -c app_id_static.c
$ readelf -s app_id_static.o
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
...
6: 0000000000000000 16 OBJECT LOCAL DEFAULT 4 ApplicationID
...
In this object file, ApplicationID
is a 16-byte LOCAL
symbol in the .rodata
section
The LOCAL
binding means that the static linker cannot see this symbol.
The compiler will always emit in the object file a definition of an extern
variable,
like that of ApplicationID
in app_id_extern.c
, even if that definition is not
referenced in the object file, because the external definition will be available to
the linker, and therefore might by be referenced at linktime from other object files, for all that
the compiler can possibly know.
But if a variable is static
, then the compiler knows that its definition is unavailable
for linkage. So if it can determine that the definition is not referenced within the
object file itself, it may conclude that the definition is redundant and not emit it
in the object file at all. Like so:
$ gcc -O1 -c app_id_static.c
This time, we ask the compiler to perform minimal optimizations. And then
$ readelf -s app_id_static.o
Symbol table '.symtab' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS app_id_static.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 2
4: 0000000000000000 0 SECTION LOCAL DEFAULT 3
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 4
the unreferenced definition of ApplicationID
is no longer present in the object file
at all. It was optimized out.
Now for certain unusual applications we may want the compiler to emit the definition
of a symbol in an object file that does not refer to it, and conceal it from the static linker. That is
where attribute used
comes into play:
/* app_id_static_used .c */
#include <stdint.h>
static const uint8_t ApplicationID[16] __attribute__((used,section(".rodata.$AppID"))) = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
Once again we compile with -O1 optimization:
$ gcc -O1 -c app_id_static_used.c
$ readelf -s app_id_static_used.o
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
...
6: 0000000000000000 16 OBJECT LOCAL DEFAULT 4 ApplicationID
...
But this time, thanks to attribute used
, the LOCAL
definition of ApplicationID
reappears in section #4 (which in this object file is .rodata.$AppID
)
That is how attribute used
works. It affects the behaviour of the compiler: it has no
influence on the linker.
We haven't done any linkage yet. Let's do some now.
/* hello_world.c */
#include <stdio.h>
int main(void)
{
puts("Hello world!")
return 0;
}
This program makes no reference to ApplicationID
, but we'll input app_id_static_used.o
to the linkage regardless:
$ gcc -O1 -c hello_world.c
$ gcc -o hello hello_world.o app_id_static_used.o -Wl,-gc-sections,-Map=mapfile.txt
In the linkage, I have asked for unused input sections to be dropped, and for a mapfile
to be output (-Wl,-gc-sections,-Map=mapfile.txt
)
In the mapfile we find:
Mapfile.txt
...
Discarded input sections
...
.rodata.$AppID
0x0000000000000000 0x10 app_id_static_used.o
...
The linker has discarded section .rodata.$AppID
input from app_id_static_used.o
because no symbol defined in that section is referenced in the program. With
attribute used
, we compelled the compiler to emit the definition of that static
symbol
in app_id_static_used.o
. That doesn't compell the linker to need it, or keep
it in the executable.
In we switch from app_id_static_used.c
to:
/* app_id_extern_used.c */
#include <stdint.h>
const uint8_t ApplicationID[16] __attribute__((used,section(".rodata.$AppID"))) = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
then we're doing what you did, applying attribute used
to an extern
definition. Attribute
used
has no effect in that case, because the compiler is bound to emit the extern
definition in any case. And the linker will still discard the .rodata.$AppID
input section from the
executable if the program does not refer to anything in it.
So far, your app-id source file might as well be:
/* app_id_extern_section.c */
#include <stdint.h>
const uint8_t ApplicationID[16] __attribute__((section(".rodata.$AppID"))) = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x12, 0x34, 0x00, 0x00
};
And what you then need to do is inform the linker that you want the definition of
the symbol ApplicationID
kept, even if it is not referenced by your program, and
if even if unused sections are dropped.
To achieve that, use the linker option --undefined=ApplicationID
. This will
direct the linker to assume from the start that the linkage of your program
has encountered an undefined reference to ApplicationID
and compel the linker
to find and link its definition, if any input file provides one. Thus:
$ gcc -O1 -c app_id_extern_section.c
$ gcc -o hello hello_world.o app_id_extern_section.o -Wl,-gc-sections,--undefined=ApplicationID
Now the program contains the definition of ApplicationID
, despite not referring to it:
$ readelf -s hello | grep ApplicationID
58: 0000000000002010 16 OBJECT GLOBAL DEFAULT 18 ApplicationID
Section #18 is the .rodata
section of the program:
$ readelf --sections hello | grep '.rodata'
[18] .rodata PROGBITS 0000000000002000 00002000
Lastly, note that the input section .rodata.$AppID
from app_id_extern_section.o
has been merged into the output section .rodata
, because the linker's default
linker script specifies:
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
i.e. all input sections matching .rodata
, .rodata.*
or .gnu.linkonce.r.*
will be output to .rodata
. This means that even:
__attribute__((section(".rodata.$AppID")))
is redundant. So the app-id source file might as well simply be the one
I started with, app_id_extern.c
, and the linkage option --undefined=ApplicationID
is all that is necessary to keep the unreferenced symbol in the program. Unless
your linker is different in that respect you will find the same.