authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-13 18:05:02-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-13 18:05:02-07:00
log5d1aab72d92d0ab512464af0d3ffc036467ac011
treeee6af9d8d7f8d4a49bbb8cc2ed7b0c2af0f33469
parent1442aa7dc039fd77c483862e37b099fe6d43f369

std.zig.system: improve native glibc version detection

When the Zig compiler is statically linked, it inspects the /usr/bin/env ELF file to determine the native glibc version, by checking the DT_RUNPATH, and then calling readlink() on the libc.so file, because typically the symlink will have e.g. libc-2.33.so in the name, revealing the glibc version. Fortunately, this information is also in readlink() of ld.so, which is available as the "INTERP" file path. This commit looks for e.g. `ld-2.33.so` on the symlink data for the dynamic linker. In theory a more complete solution would also look at `/etc/ld.so.cache` if necessary, and finally fall back to some hard coded paths, in order to resolve the location of libc.so, in order to do this readlink() trick on the resulting path. You can find that flow chart with `man ld.so`. But I think this logic will be enough to get a correct answer in all real world cases. This has been tested on Debian Buster and glibc-based Void Linux. Fixes #6469

1 files changed, 37 insertions(+), 4 deletions(-)

lib/std/zig/system/NativeTargetInfo.zig+37-4
...@@ -424,13 +424,13 @@ fn glibcVerFromSO(so_path: [:0]const u8) !std.builtin.Version {...@@ -424,13 +424,13 @@ fn glibcVerFromSO(so_path: [:0]const u8) !std.builtin.Version {
424 error.BadPathName => unreachable, // Windows only424 error.BadPathName => unreachable, // Windows only
425 error.UnsupportedReparsePointType => unreachable, // Windows only425 error.UnsupportedReparsePointType => unreachable, // Windows only
426 };426 };
427 return glibcVerFromLinkName(link_name);427 return glibcVerFromLinkName(link_name, "libc-");
428}428}
429429
430fn glibcVerFromLinkName(link_name: []const u8) !std.builtin.Version {430fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.builtin.Version {
431 // example: "libc-2.3.4.so"431 // example: "libc-2.3.4.so"
432 // example: "libc-2.27.so"432 // example: "libc-2.27.so"
433 const prefix = "libc-";433 // example: "ld-2.33.so"
434 const suffix = ".so";434 const suffix = ".so";
435 if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) {435 if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) {
436 return error.UnrecognizedGnuLibCFileName;436 return error.UnrecognizedGnuLibCFileName;
...@@ -590,7 +590,9 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -590,7 +590,9 @@ pub fn abiAndDynamicLinkerFromFile(
590 }590 }
591 }591 }
592592
593 if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and cross_target.glibc_version == null) {593 if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and
594 cross_target.glibc_version == null)
595 {
594 if (rpath_offset) |rpoff| {596 if (rpath_offset) |rpoff| {
595 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);597 const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
596598
...@@ -706,6 +708,7 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -706,6 +708,7 @@ pub fn abiAndDynamicLinkerFromFile(
706 };708 };
707 result.target.os.version_range.linux.glibc = glibcVerFromLinkName(709 result.target.os.version_range.linux.glibc = glibcVerFromLinkName(
708 link_name,710 link_name,
711 "libc-",
709 ) catch |err| switch (err) {712 ) catch |err| switch (err) {
710 error.UnrecognizedGnuLibCFileName,713 error.UnrecognizedGnuLibCFileName,
711 error.InvalidGnuLibCVersion,714 error.InvalidGnuLibCVersion,
...@@ -714,6 +717,36 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -714,6 +717,36 @@ pub fn abiAndDynamicLinkerFromFile(
714 break;717 break;
715 }718 }
716 }719 }
720 } else if (result.dynamic_linker.get()) |dl_path| glibc_ver: {
721 // There is no DT_RUNPATH but we can try to see if the information is
722 // present in the symlink data for the dynamic linker path.
723 var link_buf: [std.os.PATH_MAX]u8 = undefined;
724 const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) {
725 error.NameTooLong => unreachable,
726 error.InvalidUtf8 => unreachable, // Windows only
727 error.BadPathName => unreachable, // Windows only
728 error.UnsupportedReparsePointType => unreachable, // Windows only
729
730 error.AccessDenied,
731 error.FileNotFound,
732 error.NotLink,
733 error.NotDir,
734 => break :glibc_ver,
735
736 error.SystemResources,
737 error.FileSystem,
738 error.SymLinkLoop,
739 error.Unexpected,
740 => |e| return e,
741 };
742 result.target.os.version_range.linux.glibc = glibcVerFromLinkName(
743 fs.path.basename(link_name),
744 "ld-",
745 ) catch |err| switch (err) {
746 error.UnrecognizedGnuLibCFileName,
747 error.InvalidGnuLibCVersion,
748 => break :glibc_ver,
749 };
717 }750 }
718 }751 }
719752