Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
SamWindell opened this issue Jun 4, 2023 · 10 comments
Labels
backend-llvm The LLVM backend outputs an LLVM IR Module. bug Observed behavior contradicts documented or intended behavior os-windows
Milestone

Comments

@SamWindell
Copy link

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 SamWindell added the bug Observed behavior contradicts documented or intended behavior label Jun 4, 2023
@ghost
Copy link

ghost commented Jun 29, 2023

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

@andrewrk andrewrk added this to the 0.12.0 milestone Jul 23, 2023
@andrewrk andrewrk added os-windows backend-llvm The LLVM backend outputs an LLVM IR Module. labels Jul 23, 2023
@ThePotatoChronicler
Copy link
Contributor

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?

@ghost
Copy link

ghost commented Aug 7, 2023

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
Copy link

ghost commented Aug 9, 2023

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.

@britown88
Copy link

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

@okvik
Copy link

okvik commented Dec 5, 2023

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.

@e253
Copy link

e253 commented Mar 14, 2024

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;
}

@alexrp
Copy link
Member

alexrp commented Apr 20, 2024

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

@ThePotatoChronicler
Copy link
Contributor

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

@SamWindell
Copy link
Author

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);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend-llvm The LLVM backend outputs an LLVM IR Module. bug Observed behavior contradicts documented or intended behavior os-windows
Projects
None yet
Development

No branches or pull requests

7 participants