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

c86crjj0  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(478)

我有一个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",
};

使用常规MINGW 64 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肯定不是这里的答案......

new9mtju

new9mtju1#

要通知链接器需要保留某些变量,应使用-Wl,--undefined=XXX选项:

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

请注意,__attribute__((used))是一个仅用于编译器的标志,用于禁止-Wunused-variable警告。

6rqinv9w

6rqinv9w2#

OK -我发现了一些东西;不理想,但至少它只是一个“语法黑客”,我不必想出愚蠢的东西来处理结构体,只是为了让它们出现在可执行文件中(通常甚至我在这种情况下想出的代码,得到优化了:))。
我第一次尝试了(void) varname; hack用于How can I suppress "unused parameter" warnings in C?-我把它留在下面只是为了表明它不工作。
最后奏效的是:基本上,只需在main()所在的位置创建一个static const void*,并将指向该结构体的指针分配给它(编辑:*中的!);我猜由于“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仍然被优化掉了。
EDIT:请注意,如果你试图作弊并将任务移到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;
      |                    ^~~~

......而你的情况也没有好转。

相关问题