Missing symbols when using LTO on Windows (LLD): _create_locale, _free_locale and _rand_s
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.
It seems at the moment the only workaround is to pass -fno-lto >.>
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?
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.
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.
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
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.
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;
}
FWIW, I'm not able to reproduce this on Zig 0.12.0. Can anyone else confirm that it's fixed?
Enabled LTO for everything in my project, also not getting it anymore on 0.13.0-dev.28+3c5e84073
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);
}