2

在为 RV32IM 目标 (RISC-V) 用 C 语言开发裸机固件时,我在启用 LTO 时遇到了链接错误:

/home/duranda/riscv/lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/bin/ld: /tmp/firmware.elf.5cZNyC.ltrans0.ltrans.o: in function `.L0 ':
/home/duranda/whatever/firmware.c:493: undefined reference to `memset'

但是,我的固件中没有调用memset。由memsetGCC 在优化期间插入,如此处所述-Os该构建使用 GCC和-flto -fuse-linker-plugin标志针对大小进行了优化。此外,这些-fno-builtin-memset -nostdinc -fno-tree-loop-distribute-patterns -nostdlib -ffreestanding标志用于防止memset在优化期间使用,并且不包括标准库。

memsetLTO期间如何防止插入?请注意,固件不应与 libc 链接。我还尝试提供自定义实现,memset但链接器不想memset在优化期间将其用于插入(仍然抛出未定义的引用)。

4

2 回答 2

1

我不确定-fno-builtin-*你认为它会做什么。如果您使用这些标志,那么 GCC 将尝试调用外部函数。如果您不使用这些标志,GCC 将只插入内联代码而不是依赖于库。

所以在我看来,你不应该使用任何-fno-builtin标志。

于 2020-11-02T15:33:39.127 回答
1

几年前我遇到了类似的问题服务器并试图解决这个问题,但事实证明我误解了-fno-builtin[1] 的含义,-fno-builtin不能保证 GCC 不会调用memcpymemmove或者memset隐式调用。

我想最简单的解决方案是,不要编译你的libc.cwith -flto,或者换句话说,编译你libc.c-fno-lto.

这是我对发生的事情的猜测,我不知道如何重现你所看到的,所以它可能不正确,

  • 在 LTO 的第一阶段,LTO 将收集您在程序中使用的任何符号
  • 然后要求链接器提供这些文件,并丢弃任何未使用的符号。
  • 然后将这些文件读入 GCC 并再次优化,此时 gcc 使用一些内置函数进行优化或代码生成,但之前没有引入。
  • 符号引用是在 LTO 阶段创建的,在当前的 GCC LTO 流程中拉入任何符号都为时已晚,在这种情况下,memset 在早期阶段被丢弃......

所以你可能会问为什么 compile libc.cwith-fno-lto会起作用?因为如果它没有参与到 LTO 流程中,这意味着它不会在 LTO 流程中被丢弃。

即使您使用 -fno-builtin 编译,一些显示 gcc 的示例程序也会调用 memset,aarch64 gcc 和 riscv gcc 将生成对 memset 的函数调用。

// $ riscv64-unknown-elf-gcc x.c -o - -O3  -S -fno-builtin
struct bar {
    int a[100];
};

struct bar y;

void foo(){
  struct bar x = {{0}};
  y = x;
}

这是本案例对应的 gcc 源代码[2]。

[1] https://gcc.gnu.org/pipermail/gcc-patches/2014-August/397382.html

[2] https://github.com/riscv/riscv-gcc/blob/riscv-gcc-10.2.0/gcc/expr.c#L3143

于 2020-11-03T02:08:57.360 回答