Exported symbols removed unexpectedly in release mode

zig version: 0.14.0-dev.2435+7575f2121
target: riscv32

linkscript:

    . = ALIGN(4);
    _octopus_init_begin = .;
    KEEP(*(.octopus.init));
    _octopus_init_end = .;
    . = ALIGN(4);

code

fn MakeSymbols(comptime name: []const u8, comptime initfn: *const fn () callconv(.C) void) void {
    const foo = octopus.initm.OctopusInitElem{ .name = @ptrCast(name), .init = initfn };
    @export(&foo, .{
        .name = name,
        .section = ".octopus.init",
    });
}

comptime {
    MakeSymbols("A", _board_init);
    MakeSymbols("B", _board_init);
    MakeSymbols("C", _board_init);
}

When build in Debug mode, it works fine, which can be seen from the ‘readelf-a.out’:

 45451: 8004b6a0     0 NOTYPE  GLOBAL DEFAULT    2 _octopus_init_begin
 45452: 8004b6b8     0 NOTYPE  GLOBAL DEFAULT    2 _octopus_init_end
 45453: 8004b6a0     8 OBJECT  GLOBAL DEFAULT    2 A
 45454: 8004b6a8     8 OBJECT  GLOBAL DEFAULT    2 B
 45455: 8004b6b0     8 OBJECT  GLOBAL DEFAULT    2 C

But if try to build in release mode (ReleaseSafe), there are no symbols:

 10419: 80005ecc     0 NOTYPE  GLOBAL DEFAULT    2 _octopus_init_begin
 10420: 80005ecc     0 NOTYPE  GLOBAL DEFAULT    2 _octopus_init_end
 10421: 80000178    32 FUNC    WEAK   DEFAULT    2 memset

Why and how to solve ?

My guess would be because of this issue. You can try disabling LTO in your build script, or you can reference the symbols and force the compiler to include them as I’ve done in some of my bare metal code:

asm volatile("" :: [a] "s" (&stage2_data), [b] "s" (&vector_table));
2 Likes

You are right. After disabling LTO (by exe.want_lto = false;) symbols kept. Thanks!