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",
};
Test with usual MINGW64 gcc
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.
Test with ARM gcc
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 ...
CodePudding user response:
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.
CodePudding user response:
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.