zig icon indicating copy to clipboard operation
zig copied to clipboard

Missing symbols when using LTO on Windows (LLD): _create_locale, _free_locale and _rand_s

Open SamWindell opened this issue 2 years ago • 8 comments

Zig Version

0.11.0-dev.3298+5744ceedb

Steps to Reproduce and Observed Behavior

I have found some more cases of link errors following on from this closed issue #8531. As it looks like there might be some unresolved parts, I thought it warrants this new issue.

Repro based on @ThePotatoChronicler code:

src/main.c:

#include <locale.h>
#define _CRT_RAND_S
#include <stdlib.h>
int main() {
  _locale_t locale = _create_locale(LC_ALL, "de-CH");
  _free_locale(locale);
  unsigned int r;
  rand_s(&r);
}

build.zig:

const std = @import("std");
pub fn build(b: *std.Build) void {
    const target = std.zig.CrossTarget.parse(.{ .arch_os_abi = "x86_64-windows-gnu" }) catch unreachable;
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "test",
        .root_source_file = .{ .path = "src/main.c" },
        .target = target,
        .optimize = optimize,
    });

    exe.linkLibC();
    b.installArtifact(exe);

    // Change to false and it will compile
    exe.want_lto = true;

    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}

result:

zig build-exe test Debug x86_64-windows-gnu: error: the following command failed with 3 compilation errors:
C:\Users\Sam\Projects\zig\zig.exe build-exe C:\Users\Sam\Projects\test\src\main.c -lc --cache-dir C:\Users\Sam\Projects\test\zig-cache --global-cache-dir C:\Users\Sam\AppData\Local\zig --name test -target x86_64-windows-gnu -mcpu x86_64 -flto --listen=-
Build Summary: 0/3 steps succeeded; 1 failed (disable with -fno-summary)
install transitive failure
+- install test transitive failure
   +- zig build-exe test Debug x86_64-windows-gnu 3 errors
error: lld-link: undefined symbol: __declspec(dllimport) _create_locale
    note: referenced by C:\Users\Sam\Projects\test\src\main.c:5
    note:               C:\Users\Sam\Projects\test\zig-cache\o\62fe3e51ac093baf35703f258d18efcd\test.exe.lto.obj:(main)
error: lld-link: undefined symbol: __declspec(dllimport) _free_locale
    note: referenced by C:\Users\Sam\Projects\test\src\main.c:6
    note:               C:\Users\Sam\Projects\test\zig-cache\o\62fe3e51ac093baf35703f258d18efcd\test.exe.lto.obj:(main)
error: lld-link: undefined symbol: __declspec(dllimport) rand_s
    note: referenced by C:\Users\Sam\Projects\test\src\main.c:8
    note:               C:\Users\Sam\Projects\test\zig-cache\o\62fe3e51ac093baf35703f258d18efcd\test.exe.lto.obj:(main)

Expected Behavior

No link errors.

SamWindell avatar Jun 04 '23 20:06 SamWindell

It seems at the moment the only workaround is to pass -fno-lto >.>

ghost avatar Jun 29 '23 22:06 ghost

Is there a known workaround for this, other than no LTO? I feel like I've seen someone before mention in another similar issue that there is a way to force the linker to not remove those functions if they're written down manually?

ThePotatoChronicler avatar Aug 04 '23 10:08 ThePotatoChronicler

Is there a known workaround for this, other than no LTO? I feel like I've seen someone before mention in another similar issue that there is a way to force the linker to not remove those functions if they're written down manually?

You can preface the definition with __attribute__((used)). Related: https://sourceforge.net/p/mingw-w64/mailman/mingw-w64-public/thread/778a777e-edff-35b6-a30c-85ccbf536ade%40126.com/ I've confirmed this to fix the three examples given in this thread, e.g. by editing lib/libc/mingw/misc/_create_locale.c etc. I am not sure whether this is a mingw bug or an llvm/lld bug but at any rate it doesn't seem to be a zig bug. I'm not gonna spend 72 hours bootstrapping a llvm-mingw toolchain with LTO to check whether it exhibits the same behavior.

ghost avatar Aug 07 '23 23:08 ghost

Sorry, I forgot it did that. Here you can see how easy the fix is for a particular case, although I imagine in the general case the problem probably has something to do with lld discarding "unused" aliases to function pointers. Still probably worth poking MinGW guys about. Zig doesn't preprocess MinGW headers as I understand so almost certainly not Zig's bug anyway.

ghost avatar Aug 09 '23 10:08 ghost

I was getting the same issues described when building ReleaseFast on windows, unresolved externals for _free_locale and _create_locale, as stated, adding __attribute__((used)) tp lib/libc/minw/misc/_free_locale.c and lib/libc/minw/misc/_create_locale.c resolved the issue.

Alternatively, .want_lto = false gets around the issue completely

britown88 avatar Oct 16 '23 17:10 britown88

Have also hit this issue while building (cross-compiling from Linux to Windows) a program depending on zig-gamedev. Disabling LTO fixes the issue.

The /weirdest/ thing is that everything worked just fine the entire day while I was experimenting, then once I did:

exe.subsystem = .Windows

It worked fine the first time, but later I couldn't build in release modes anymore due to the linking error, even with the above line removed.

okvik avatar Dec 05 '23 15:12 okvik

Quick Fix! I'm on zig 0.11.

if (optimize == .ReleaseFast and builtin.target.os.tag == .windows) {
        // LTO breaks build of libcpp on windows in ReleastFast
        exe.want_lto = false;
}

e253 avatar Mar 14 '24 06:03 e253

FWIW, I'm not able to reproduce this on Zig 0.12.0. Can anyone else confirm that it's fixed?

alexrp avatar Apr 20 '24 12:04 alexrp

Enabled LTO for everything in my project, also not getting it anymore on 0.13.0-dev.28+3c5e84073

ThePotatoChronicler avatar Apr 22 '24 07:04 ThePotatoChronicler

Yep works here too. I updated the exact example in my original post to work with Zig 0.12.0 and ran the same command. It works now.

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.resolveTargetQuery(std.Target.Query.parse(.{
        .arch_os_abi = "x86_64-windows-gnu",
    }) catch @panic("err"));
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "test",
        .target = target,
        .optimize = optimize,
    });
    exe.addCSourceFile(.{ .file = .{ .path = "src/main.c" } });

    exe.linkLibC();
    b.installArtifact(exe);

    exe.want_lto = true; // LTO works!

    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}

SamWindell avatar Apr 22 '24 10:04 SamWindell