我有一个 ARM 项目,我想在其中保留某些未使用的变量及其数据,直到使用它们为止。
我已经看到阻止 gcc 删除未使用的变量:
对于全局变量不起作用(文档确实暗示它仅适用于函数)(arm-none-eabi gcc 7),但通过__attribute__((used))
将符号放在不同的部分确实有效。这可能是因为链接器只能在通过__attribute__((section(".data")))
给出自己的部分时才能删除符号。我不喜欢它,但它有效。-fdata-sections
所以,我尝试了这种方法,但变量没有保留 - 我认为这是因为该项目中的某些内容在链接过程中启用了
-Wl,--gc-sections
。这是一个最小的示例,显示了我尝试做的事情(基本上主文件仅引用要“保留”的变量声明为 extern 的标头 - 除此之外,主程序不使用这些变量;然后在单独的 .c 文件中定义这些相同的变量):
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",
};
我们先尝试一下没有
-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
显然,变量和内容在这里都是可见的。
现在让我们尝试一下
-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
显然,这里我们仍然剩下一些符号调试信息 - 但没有任何部分,也没有报告数据。
让我们用 ARM gcc 重新做同样的实验 - 首先不使用
-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
和以前一样,变量、内容和节名称都是可见的。
现在让我们尝试一下
-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'
请注意,与之前的情况不同,这里没有留下任何数据内容,也没有任何调试信息/符号名称!
所以,我的问题是:假设在项目中启用了
-Wl,--gc-sections
,并且我不想删除它(因为我喜欢其他功能),我可以以某种方式在代码中指定一些特殊变量,“保留这些即使未使用/未引用变量”,即使启用了 -Wl,--gc-sections
也能保留它们?
请注意,将
keep
添加到属性中,例如:
const char __attribute__((keep,section(".MYSTRING"))) mystring[] = "Me, mystring, I am not being used";
...并且使用(或不使用)
-Wl,--gc-sections
进行编译通常会出现编译器警告:
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";
| ^~~~~
...我想,因为变量已经声明了
const
,如果我正确地阅读了该箭头(或者可能是因为已经假设某个部分被“保留”)?所以属性keep
绝对不是这里的答案......
要通知链接器需要保留某些变量,您应该使用
-Wl,--undefined=XXX
选项:
gcc ... -Wl,--undefined=greeting
请注意,
__attribute__((used))
是一个仅限编译器的标志,用于抑制-Wunused-variable
警告。
--require-defined=symbol
为我工作。
要求在输出文件中定义该符号。 此选项与选项 --undefined 相同,除了 if 输出文件中未定义符号,则链接器将发出错误并退出。 同样的效果也可以 通过一起使用“EXTERN”、“ASSERT”和“DEFINED”在链接描述文件中实现。 该选项可以多次使用 有时需要额外的符号。
好的 - 我发现了一些东西;并不理想,但至少它只是一个“语法黑客”,而且我不必想出与结构有关的愚蠢的东西,这样它们就会出现在可执行文件中(通常甚至是我在在这种情况下,就会得到优化
:)
)。
我首先尝试了
(void) varname;
hack 用于如何在 C 中抑制“未使用的参数”警告? - 我将其留在下面只是为了表明它不起作用。
最终的工作是:基本上,只需在
static const void*
所在的位置有一个 main()
,然后为其分配一个指向结构的指针(编辑:in main()
!);我想由于“static const”,编译器不会删除变量及其部分,即使使用-Wl,--gc-sections
。所以 test_opt.c
现在变成:
#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;
}
...我们可以测试:
$ 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
请注意,上面示例中只有
mystruct
最终被保留 - mystring
仍然得到了优化。
编辑:请注意,如果您尝试作弊并将作业移到 main 之外:
static const void *fake = &mystruct, *fakeB = &mystring;
int main(void) {
...
...然后编译器就会看穿你的把戏,并用以下方式问候你:
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;
| ^~~~
...你的情况仍然没有好转。