Reputation: 5384
I have an ARM project, where I would like to keep certain unused variables and their data, until the time they are used.
I have seen prevent gcc from removing an unused variable :
__attribute__((used))
did not work for me with a global variable (the documentation does imply it only works on functions) (arm-none-eabi gcc 7), but putting the symbol in a different section via__attribute__((section(".data")))
did work. This is presumably because the linker's is only able to strip symbols when they are given their own section via-fdata-sections
. I do not like it, but it worked.
So, I tried this approach, but the variables were not kept - and I think this is because something in that project enables -Wl,--gc-sections
during linking. Here is a minimal example showing what I've tried to do (basically the main file only refers to the header where the variables to be "kept" are declared as extern - and other than that, main program has does not use these variables; and then those same variables are defined in a separate .c file):
test.c
#include <stdio.h>
#include "test_opt.h"
const char greeting[] = "Hello World - am used";
int main(void) {
printf("%s!\n", greeting);
return 0;
}
test_opt.h
#include <stdint.h>
extern const char mystring[];
struct MyStruct {
uint16_t param_one;
uint8_t param_two;
unsigned char param_three[32];
};
typedef struct MyStruct MyStruct_t;
extern const MyStruct_t mystruct;
mystruct.c
#include "test_opt.h"
const char __attribute__((section(".MYSTRING"))) mystring[] = "Me, mystring, I am not being used";
const MyStruct_t __attribute__((section(".MYSTRUCT"))) mystruct = {
.param_one = 65535,
.param_two = 42,
.param_three = "myStructer here",
};
Let's first try without -Wl,--gc-sections
:
$ gcc -Wall -g mystruct.c test_opt.c -o test_opt.exe
$ strings ./test_opt.exe | grep -i 'mystring\|mystruct'
Me, mystring, I am not being used
*myStructer here
mystring
MyStruct
MyStruct_t
mystruct
mystruct.c
mystruct.c
mystruct.c
mystruct.c
mystring
mystruct
.MYSTRING
.MYSTRUCT
.MYSTRING
.MYSTRUCT
Clearly, variables and content are visible here.
Now let's try -Wl,--gc-sections
:
$ gcc -Wall -g -Wl,--gc-sections mystruct.c test_opt.c -o test_opt.exe
$ strings ./test_opt.exe | grep -i 'mystring\|mystruct'
mystring
MyStruct
MyStruct_t
mystruct
mystruct.c
mystruct.c
mystruct.c
mystruct.c
mystring
mystruct
Apparently, here we still have some symbol debugging info left - but there are no sections, nor data being reported.
Let's re-do same experiment with ARM gcc - first without -Wl,--gc-sections
:
$ arm-none-eabi-gcc -Wall -g test_opt.c mystruct.c -o test_opt.elf -lc -lnosys
$ arm-none-eabi-strings ./test_opt.elf | grep -i 'mystring\|mystruct'
Me, mystring, I am not being used
*myStructer here
mystruct.c
MyStruct_t
MyStruct
mystruct
mystruct.c
mystring
mystruct.c
mystring
mystruct
.MYSTRING
.MYSTRUCT
Same as before, variables, content and section names are visible.
Now let's try with -Wl,--gc-sections
:
$ arm-none-eabi-gcc -Wall -g -Wl,--gc-sections test_opt.c mystruct.c -o test_opt.elf -lc -lnosys
$ arm-none-eabi-strings ./test_opt.elf | grep -i 'mystring\|mystruct'
Note that, unlike the previous case, here there is neither any data content left, nor any debugging info/symbol names!
So, my question is: assuming that -Wl,--gc-sections
is enabled in the project, and I otherwise do not want to remove it (because I like the functionality otherwise), can I somehow specify in code for some special variables, "keep these variables even if the are unused/unreferenced", in such a way that they are kept even with -Wl,--gc-sections
enabled?
Note that adding keep
to attributes, say:
const char __attribute__((keep,section(".MYSTRING"))) mystring[] = "Me, mystring, I am not being used";
... and compiling with (or without) -Wl,--gc-sections
typically results with compiler warning:
mystruct.c:3:1: warning: 'keep' attribute directive ignored [-Wattributes]
3 | const char __attribute__((keep,section(".MYSTRING"))) mystring[] = "Me, mystring, I am not being used";
| ^~~~~
... I guess, because the variables are already declared const
if I read that arrow correctly (or maybe because a section is already assumed to be "kept")? So attribute keep
is definitely not the answer here ...
Upvotes: 3
Views: 970
Reputation: 11
I've compared how attributes "used" and "retain" affect the content of an assembler file generated by arm-none-eabi-gcc (Arm GNU Toolchain 13.2 rel.1), which, as I thought, must somehow instruct the linker on what should not be discarded.
// Source code part, attributes comparison
const uint8_t A1[100] __attribute__((__used__));
const uint8_t A2[100] __attribute__((__retain__));
const uint8_t A3[100];
Compiled with options -fdata-sections -ffunction-sections -save-temps
.
Linked with options --gc-sections
.
And it seems that gcc just ignores all those attributes.
The picture 1 shows, that unused arrays A1,A2,A3 go into asm-file all in the same way and are placed in sections .rodata.A1...A3 . But on the linking stage, the garbage collector discards those sections. Though "retain" is supposed to retain A2 as GCC doc says.
The possible solution I see to protect the arrays from discarding is to put them into a dedicated section (say .rodata_nogc for constants), and handle this section in linker_script with KEEP command 2.
// Source code part, array protection
const uint8_t A1[100] __attribute__((section(".rodata_nogc")));
const uint8_t A2[100] __attribute__((section(".rodata_nogc")));
const uint8_t A3[100] __attribute__((section(".rodata_nogc")));
/* Linker script part, .rodata section */
.rodata :
{
. = ALIGN(4);
KEEP(*(.rodata_nogc))
*(.rodata)
*(.rodata*)
. = ALIGN(4);
} >FLASH
Upvotes: 1
Reputation: 354
--require-defined=symbol
worked for me.
Require that symbol is defined in the output file. This option is the same as option --undefined except that if symbol is not defined in the output file then the linker will issue an error and exit. The same effect can be achieved in a linker script by using "EXTERN", "ASSERT" and "DEFINED" together. This option can be used multiple times to require additional symbols.
(https://man.archlinux.org/man/ld.1.en)
Upvotes: 0
Reputation: 21878
To inform linker that some variable needs to be preserved you should use the -Wl,--undefined=XXX
option:
gcc ... -Wl,--undefined=greeting
Note that __attribute__((used))
is a compiler-only flag to suppress -Wunused-variable
warning.
Upvotes: 2
Reputation: 5384
OK - I found something; not ideal, but at least its just a "syntax hack", and I don't have to come up with stupid stuff to do with the structs just so they show up in the executable (and usually even the code I come up with in that case, gets optimized away :)
).
I first tried the (void) varname;
hack used for How can I suppress "unused parameter" warnings in C? - I left it below just to show it doesn't work.
What ended up working is: basically, just have a static const void*
where the main()
is, and assign a pointer to the struct to it (EDIT: in the main()
!); I guess because of "static const", the compiler will not remove the variable and its section, even with -Wl,--gc-sections
. So test_opt.c
now becomes:
#include <stdio.h>
#include "test_opt.h"
const char greeting[] = "Hello World - am used";
static const void *fake; //, *fakeB;
int main(void) {
fake = &mystruct;
(void) &mystring; //fakeB = &mystring;
printf("%s!\n", greeting);
return 0;
}
... and we can test with:
$ arm-none-eabi-gcc -Wall -g -Wl,--gc-sections test_opt.c mystruct.c -o test_opt.elf -lc -lnosys
$ arm-none-eabi-readelf -a ./test_opt.elf | grep -i 'mystring\|mystruct'
[ 5] .MYSTRUCT PROGBITS 00013780 013780 000024 00 A 0 0 4
01 .init .text .fini .rodata .MYSTRUCT .ARM.exidx .eh_frame
5: 00013780 0 SECTION LOCAL DEFAULT 5 .MYSTRUCT
379: 00000000 0 FILE LOCAL DEFAULT ABS mystruct.c
535: 00013780 36 OBJECT GLOBAL DEFAULT 5 mystruct
$ arm-none-eabi-strings ./test_opt.elf | grep -i 'mystring\|mystruct'
*myStructer here
mystruct.c
MyStruct_t
mystruct
MyStruct
mystruct.c
mystring
mystruct.c
mystruct
.MYSTRUCT
Note that only mystruct
in above example ended up being preserved - mystring
still got optimized away.
EDIT: note that if you try to cheat and move the assignment outside of main:
static const void *fake = &mystruct, *fakeB = &mystring;
int main(void) {
...
... then the compiler will see through your shenanigans, and greet you with:
test_opt.c:6:39: warning: 'fakeB' defined but not used [-Wunused-variable]
6 | static const void *fake = &mystruct, *fakeB = &mystring;
| ^~~~~
test_opt.c:6:20: warning: 'fake' defined but not used [-Wunused-variable]
6 | static const void *fake = &mystruct, *fakeB = &mystring;
| ^~~~
... and you're none the better off still.
Upvotes: -1