DurandA
DurandA

Reputation: 1550

How to prevent GCC from inserting memset during link-time optimization?

While developping a bare metal firmware in C for a RV32IM target (RISC-V), I encountered a linking error when LTO is enabled:

/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'

There are however no call to memset in my firmware. The memset is inserted by GCC during optimization as described here. The build is optimized for size using GCC -Os and -flto -fuse-linker-plugin flags. In addition, the -fno-builtin-memset -nostdinc -fno-tree-loop-distribute-patterns -nostdlib -ffreestanding flags are used to prevent the use of memset during optimization and to not include standard libs.

How to prevent memset insertion during LTO? Note that the firmware should not be linked against libc. I also tried providing a custom implementation of memset but the linker does not want to use it for memset inserted during optimization (still throws undefined reference).

Upvotes: 4

Views: 3306

Answers (2)

Kito Cheng
Kito Cheng

Reputation: 46

I hit similar issue servers years ago and tried to fixed that, but it turns out I misunderstanding the meaning of -fno-builtin[1], -fno-builtin not guaranteed GCC won't call memcpy, memmove or memset implicitly.

I guess the simplest solution is, DO NOT compile your libc.c with -flto, or in another word, compile libc.c with -fno-lto.

That's my guess about what happen, I don't have know how to reproduce what you see, so it might incorrect,

  • During the first phase of LTO, LTO will collect any symbol you used in program
  • And then ask linker to provide those files, and discard any unused symbol.
  • Then read those files into GCC and optimize again, in this moment gcc using some built-in function to optimize or code gen, but it not pull-in before.
  • The symbol reference is created at LTO stage, which is too late pull in any symbol in current GCC LTO flow, and in this case, memset is discard in earlier stage...

So you might have question about why compile libc.c with -fno-lto will work? because if it didn't involved into LTO flow, which means it won't be discarded in the LTO flow.

Some sample program to show the gcc will call memset even you compile with -fno-builtin, aarch64 gcc and riscv gcc will generate a function call to 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;
}

Here is the corresponding gcc source code[2] for this case.

[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

Upvotes: 3

Nikos C.
Nikos C.

Reputation: 51832

I'm not sure -fno-builtin-* does what you think it does. If you use those flags, then GCC will try to call an external function. If you don't use those flags, GCC will instead just insert inline code instead of relying on the library.

So it would appear to me you should simply not use any -fno-builtin flags.

Upvotes: 1

Related Questions