使用 -Wl,--gc-sections 时防止 gcc 优化/删除变量?

问题描述 投票:0回答:3

我有一个 ARM 项目,我想在其中保留某些未使用的变量及其数据,直到使用它们为止。

我已经看到阻止 gcc 删除未使用的变量

__attribute__((used))
对于全局变量不起作用(文档确实暗示它仅适用于函数)(arm-none-eabi gcc 7),但通过
__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",
};

使用常用的 MINGW64 gcc 进行测试

我们先尝试一下没有

-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 进行测试

让我们用 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
绝对不是这里的答案......

c gcc linker arm
3个回答
2
投票

要通知链接器需要保留某些变量,您应该使用

-Wl,--undefined=XXX
选项:

gcc ... -Wl,--undefined=greeting

请注意,

__attribute__((used))
是一个仅限编译器的标志,用于抑制
-Wunused-variable
警告。


0
投票

--require-defined=symbol
为我工作。

要求在输出文件中定义该符号。 此选项与选项 --undefined 相同,除了 if 输出文件中未定义符号,则链接器将发出错误并退出。 同样的效果也可以 通过一起使用“EXTERN”、“ASSERT”和“DEFINED”在链接描述文件中实现。 该选项可以多次使用 有时需要额外的符号。

https://man.archlinux.org/man/ld.1.en


-1
投票

好的 - 我发现了一些东西;并不理想,但至少它只是一个“语法黑客”,而且我不必想出与结构有关的愚蠢的东西,这样它们就会出现在可执行文件中(通常甚至是我在在这种情况下,就会得到优化

:)
)。

我首先尝试了

(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;
      |                    ^~~~

...你的情况仍然没有好转。

© www.soinside.com 2019 - 2024. All rights reserved.