From: Eric Joldasov Based on https://github.com/ziglang/zig/pull/12567 and https://github.com/ziglang/zig/pull/17671 with small fixes, all ported to 0.11.0. First try `getconf GNU_LIBC_VERSION` to detect glibc version, If there are any errors, skip to the upstream logic. Also fix glibc version parsing: if version string does not contain third (patch) component, "std.SemanticVersion.parse" returns parsing error. For example, this currently happens with "GLIBC_2.37" or "glibc 2.37" inputs. To fix this, we use copy-pasted "std.zig.CrossTarget.parse" function here, that sets omitted patch component to 0. After applying this patch, both `zig build-exe --show-builtin` and `zig env` show correct version on my default/linux/amd64/17.1/desktop/plasma : glibc 2.37. Bug: https://bugs.gentoo.org/914731 Bug: https://bugs.gentoo.org/914101 diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index 99a1a8f2e..0250db968 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -19,6 +19,32 @@ dynamic_linker: DynamicLinker = DynamicLinker{}, pub const DynamicLinker = Target.DynamicLinker; +// Copy-pasted from `std.zig.CrossTarget.parse` to avoid introducing unexpected new public function as part of standard library. +/// Parses a version with an omitted patch component, such as "1.0", +/// which SemanticVersion.parse is not capable of. +fn parseWithOptionalPatchField(ver: []const u8) error{ InvalidVersion, Overflow }!std.SemanticVersion { + const parseVersionComponent = struct { + fn parseVersionComponent(component: []const u8) !usize { + return std.fmt.parseUnsigned(usize, component, 10) catch |err| { + switch (err) { + error.InvalidCharacter => return error.InvalidVersion, + error.Overflow => return error.Overflow, + } + }; + } + }.parseVersionComponent; + var version_components = mem.splitScalar(u8, ver, '.'); + const major = version_components.first(); + const minor = version_components.next() orelse return error.InvalidVersion; + const patch = version_components.next() orelse "0"; + if (version_components.next() != null) return error.InvalidVersion; + return .{ + .major = try parseVersionComponent(major), + .minor = try parseVersionComponent(minor), + .patch = try parseVersionComponent(patch), + }; +} + pub const DetectError = error{ FileSystem, SystemResources, @@ -307,6 +333,39 @@ fn detectAbiAndDynamicLinker( } const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; + if (is_linux and !os_is_non_native and cross_target.glibc_version == null) try_getconf: { + var buf: [4096]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&buf); + const allocator = fba.allocator(); + + const getconf = std.process.Child.exec(.{ + .allocator = allocator, + .argv = &.{ "getconf", "GNU_LIBC_VERSION" }, + .max_output_bytes = 1024, + }) catch break :try_getconf; + if (!std.mem.startsWith(u8, getconf.stdout, "glibc ")) break :try_getconf; + const version_string = std.mem.trim(u8, getconf.stdout["glibc ".len..], &std.ascii.whitespace); + const glibc_version = parseWithOptionalPatchField(version_string) catch break :try_getconf; + + var os_with_glibc = os; + os_with_glibc.version_range.linux.glibc = glibc_version; + + const target: Target = .{ + .cpu = cpu, + .os = os_with_glibc, + .abi = .gnu, + .ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os_with_glibc.tag, cpu.arch), + }; + const result: NativeTargetInfo = .{ + .target = target, + .dynamic_linker = if (cross_target.dynamic_linker.get() == null) + target.standardDynamicLinkerPath() + else + cross_target.dynamic_linker, + }; + return result; + } + // Best case scenario: the executable is dynamically linked, and we can iterate // over our own shared objects and find a dynamic linker. const elf_file = blk: { @@ -563,7 +622,7 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion { while (it.next()) |s| { if (mem.startsWith(u8, s, "GLIBC_2.")) { const chopped = s["GLIBC_".len..]; - const ver = std.SemanticVersion.parse(chopped) catch |err| switch (err) { + const ver = parseWithOptionalPatchField(chopped) catch |err| switch (err) { error.Overflow => return error.InvalidGnuLibCVersion, error.InvalidVersion => return error.InvalidGnuLibCVersion, }; @@ -586,7 +645,7 @@ fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.Semantic } // chop off "libc-" and ".so" const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len]; - return std.SemanticVersion.parse(link_name_chopped) catch |err| switch (err) { + return parseWithOptionalPatchField(link_name_chopped) catch |err| switch (err) { error.Overflow => return error.InvalidGnuLibCVersion, error.InvalidVersion => return error.InvalidGnuLibCVersion, };