From c7d6048081053c2852d4d3af5d549d83473f808c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Sep 2022 00:03:53 -0700 Subject: [PATCH] std.zig.system.NativeTargetInfo: add fallback check After failing to find RUNPATH in the ELF of /usr/bin/env, not finding the answer in a symlink of the dynamic interpreter, and not finding libc.so.6 in the same directory as the dynamic interpreter, Zig will check `/lib/$triple`. This fixes incorrect native glibc version detected on Debian bookworm. --- lib/std/zig/system/NativeTargetInfo.zig | 118 +++++++++++++++--------- 1 file changed, 75 insertions(+), 43 deletions(-) diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig index 11b5ec4191..a4d29c17b4 100644 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ b/lib/std/zig/system/NativeTargetInfo.zig @@ -820,56 +820,88 @@ pub fn abiAndDynamicLinkerFromFile( while (it.next()) |rpath| { if (glibcVerFromRPath(rpath)) |ver| { result.target.os.version_range.linux.glibc = ver; - break; + return result; } else |err| switch (err) { error.GLibCNotFound => continue, else => |e| return e, } } - } else if (result.dynamic_linker.get()) |dl_path| glibc_ver: { - // There is no DT_RUNPATH so we try to find libc.so.6 inside the same - // directory as the dynamic linker. - if (fs.path.dirname(dl_path)) |rpath| { - if (glibcVerFromRPath(rpath)) |ver| { - result.target.os.version_range.linux.glibc = ver; - break :glibc_ver; - } else |err| switch (err) { - error.GLibCNotFound => {}, - else => |e| return e, - } - } - - // So far, no luck. Next we try to see if the information is - // present in the symlink data for the dynamic linker path. - var link_buf: [std.os.PATH_MAX]u8 = undefined; - const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) { - error.NameTooLong => unreachable, - error.InvalidUtf8 => unreachable, // Windows only - error.BadPathName => unreachable, // Windows only - error.UnsupportedReparsePointType => unreachable, // Windows only - - error.AccessDenied, - error.FileNotFound, - error.NotLink, - error.NotDir, - => break :glibc_ver, - - error.SystemResources, - error.FileSystem, - error.SymLinkLoop, - error.Unexpected, - => |e| return e, - }; - result.target.os.version_range.linux.glibc = glibcVerFromLinkName( - fs.path.basename(link_name), - "ld-", - ) catch |err| switch (err) { - error.UnrecognizedGnuLibCFileName, - error.InvalidGnuLibCVersion, - => break :glibc_ver, - }; } } + + if (result.dynamic_linker.get()) |dl_path| glibc_ver: { + // There is no DT_RUNPATH so we try to find libc.so.6 inside the same + // directory as the dynamic linker. + if (fs.path.dirname(dl_path)) |rpath| { + if (glibcVerFromRPath(rpath)) |ver| { + result.target.os.version_range.linux.glibc = ver; + return result; + } else |err| switch (err) { + error.GLibCNotFound => {}, + else => |e| return e, + } + } + + // So far, no luck. Next we try to see if the information is + // present in the symlink data for the dynamic linker path. + var link_buf: [std.os.PATH_MAX]u8 = undefined; + const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) { + error.NameTooLong => unreachable, + error.InvalidUtf8 => unreachable, // Windows only + error.BadPathName => unreachable, // Windows only + error.UnsupportedReparsePointType => unreachable, // Windows only + + error.AccessDenied, + error.FileNotFound, + error.NotLink, + error.NotDir, + => break :glibc_ver, + + error.SystemResources, + error.FileSystem, + error.SymLinkLoop, + error.Unexpected, + => |e| return e, + }; + result.target.os.version_range.linux.glibc = glibcVerFromLinkName( + fs.path.basename(link_name), + "ld-", + ) catch |err| switch (err) { + error.UnrecognizedGnuLibCFileName, + error.InvalidGnuLibCVersion, + => break :glibc_ver, + }; + return result; + } + + // Nothing worked so far. Finally we fall back to hard-coded search paths. + // Some distros such as Debian keep their libc.so.6 in `/lib/$triple/`. + var path_buf: [std.os.PATH_MAX]u8 = undefined; + var index: usize = 0; + const prefix = "/lib/"; + const cpu_arch = @tagName(result.target.cpu.arch); + const os_tag = @tagName(result.target.os.tag); + const abi = @tagName(result.target.abi); + mem.copy(u8, path_buf[index..], prefix); + index += prefix.len; + mem.copy(u8, path_buf[index..], cpu_arch); + index += cpu_arch.len; + path_buf[index] = '-'; + index += 1; + mem.copy(u8, path_buf[index..], os_tag); + index += os_tag.len; + path_buf[index] = '-'; + index += 1; + mem.copy(u8, path_buf[index..], abi); + index += abi.len; + const rpath = path_buf[0..index]; + if (glibcVerFromRPath(rpath)) |ver| { + result.target.os.version_range.linux.glibc = ver; + return result; + } else |err| switch (err) { + error.GLibCNotFound => {}, + else => |e| return e, + } } return result;