Commit c668396941
Changed files (1)
lib
std
zig
system
lib/std/zig/system/NativeTargetInfo.zig
@@ -400,7 +400,86 @@ fn detectAbiAndDynamicLinker(
};
}
-const glibc_so_basename = "libc.so.6";
+fn glibcVerFromRPath(rpath: []const u8) !std.builtin.Version {
+ var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
+ error.NameTooLong => unreachable,
+ error.InvalidUtf8 => unreachable,
+ error.BadPathName => unreachable,
+ error.DeviceBusy => unreachable,
+
+ error.FileNotFound,
+ error.NotDir,
+ error.InvalidHandle,
+ error.AccessDenied,
+ error.NoDevice,
+ => return error.GLibCNotFound,
+
+ error.ProcessFdQuotaExceeded,
+ error.SystemFdQuotaExceeded,
+ error.SystemResources,
+ error.SymLinkLoop,
+ error.Unexpected,
+ => |e| return e,
+ };
+ defer dir.close();
+
+ // Now we have a candidate for the path to libc shared object. In
+ // the past, we used readlink() here because the link name would
+ // reveal the glibc version. However, in more recent GNU/Linux
+ // installations, there is no symlink. Thus we instead use a more
+ // robust check of opening the libc shared object and looking at the
+ // .dynstr section, and finding the max version number of symbols
+ // that start with "GLIBC_2.".
+ const glibc_so_basename = "libc.so.6";
+ var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {
+ error.NameTooLong => unreachable,
+ error.InvalidUtf8 => unreachable, // Windows only
+ error.BadPathName => unreachable, // Windows only
+ error.PipeBusy => unreachable, // Windows-only
+ error.SharingViolation => unreachable, // Windows-only
+ error.FileLocksNotSupported => unreachable, // No lock requested.
+ error.NoSpaceLeft => unreachable, // read-only
+ error.PathAlreadyExists => unreachable, // read-only
+ error.DeviceBusy => unreachable, // read-only
+ error.FileBusy => unreachable, // read-only
+ error.InvalidHandle => unreachable, // should not be in the error set
+ error.WouldBlock => unreachable, // not using O_NONBLOCK
+ error.NoDevice => unreachable, // not asking for a special device
+
+ error.AccessDenied,
+ error.FileNotFound,
+ error.NotDir,
+ error.IsDir,
+ => return error.GLibCNotFound,
+
+ error.FileTooBig => return error.Unexpected,
+
+ error.ProcessFdQuotaExceeded,
+ error.SystemFdQuotaExceeded,
+ error.SystemResources,
+ error.SymLinkLoop,
+ error.Unexpected,
+ => |e| return e,
+ };
+ defer f.close();
+
+ return glibcVerFromSoFile(f) catch |err| switch (err) {
+ error.InvalidElfMagic,
+ error.InvalidElfEndian,
+ error.InvalidElfClass,
+ error.InvalidElfFile,
+ error.InvalidElfVersion,
+ error.InvalidGnuLibCVersion,
+ error.UnexpectedEndOfFile,
+ => return error.GLibCNotFound,
+
+ error.SystemResources,
+ error.UnableToReadElfFile,
+ error.Unexpected,
+ error.FileSystem,
+ => |e| return e,
+ };
+}
fn glibcVerFromSoFile(file: fs.File) !std.builtin.Version {
var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
@@ -507,23 +586,6 @@ fn glibcVerFromSoFile(file: fs.File) !std.builtin.Version {
return max_ver;
}
-fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.builtin.Version {
- // example: "libc-2.3.4.so"
- // example: "libc-2.27.so"
- // example: "ld-2.33.so"
- const suffix = ".so";
- if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) {
- return error.UnrecognizedGnuLibCFileName;
- }
- // chop off "libc-" and ".so"
- const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len];
- return std.builtin.Version.parse(link_name_chopped) catch |err| switch (err) {
- error.Overflow => return error.InvalidGnuLibCVersion,
- error.InvalidCharacter => return error.InvalidGnuLibCVersion,
- error.InvalidVersion => return error.InvalidGnuLibCVersion,
- };
-}
-
pub const AbiAndDynamicLinkerFromFileError = error{
FileSystem,
SystemResources,
@@ -674,65 +736,65 @@ pub fn abiAndDynamicLinkerFromFile(
if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and
cross_target.glibc_version == null)
{
- if (rpath_offset) |rpoff| {
- const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
-
- var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
- const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
- const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
-
- var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
- if (sh_buf.len < shentsize) return error.InvalidElfFile;
-
- _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
- const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));
- const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));
- const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
- const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
- var strtab_buf: [4096:0]u8 = undefined;
- const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len);
- const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
- const shstrtab = strtab_buf[0..shstrtab_read_len];
-
- const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
- var sh_i: u16 = 0;
- const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
- // Reserve some bytes so that we can deref the 64-bit struct fields
- // even when the ELF file is 32-bits.
- const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
- const sh_read_byte_len = try preadMin(
- file,
- sh_buf[0 .. sh_buf.len - sh_reserve],
- shoff,
- shentsize,
+ const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
+
+ var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff);
+ const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize);
+ const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx);
+
+ var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
+ if (sh_buf.len < shentsize) return error.InvalidElfFile;
+
+ _ = try preadMin(file, &sh_buf, str_section_off, shentsize);
+ const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));
+ const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));
+ const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
+ const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
+ var strtab_buf: [4096:0]u8 = undefined;
+ const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len);
+ const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len);
+ const shstrtab = strtab_buf[0..shstrtab_read_len];
+
+ const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
+ var sh_i: u16 = 0;
+ const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) {
+ // Reserve some bytes so that we can deref the 64-bit struct fields
+ // even when the ELF file is 32-bits.
+ const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
+ const sh_read_byte_len = try preadMin(
+ file,
+ sh_buf[0 .. sh_buf.len - sh_reserve],
+ shoff,
+ shentsize,
+ );
+ var sh_buf_i: usize = 0;
+ while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
+ sh_i += 1;
+ shoff += shentsize;
+ sh_buf_i += shentsize;
+ }) {
+ const sh32 = @ptrCast(
+ *elf.Elf32_Shdr,
+ @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]),
);
- var sh_buf_i: usize = 0;
- while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({
- sh_i += 1;
- shoff += shentsize;
- sh_buf_i += shentsize;
- }) {
- const sh32 = @ptrCast(
- *elf.Elf32_Shdr,
- @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf[sh_buf_i]),
- );
- const sh64 = @ptrCast(
- *elf.Elf64_Shdr,
- @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]),
- );
- const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
- // TODO this pointer cast should not be necessary
- const sh_name = mem.sliceTo(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0), 0);
- if (mem.eql(u8, sh_name, ".dynstr")) {
- break :find_dyn_str .{
- .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
- .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
- };
- }
+ const sh64 = @ptrCast(
+ *elf.Elf64_Shdr,
+ @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf[sh_buf_i]),
+ );
+ const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name);
+ // TODO this pointer cast should not be necessary
+ const sh_name = mem.sliceTo(std.meta.assumeSentinel(shstrtab[sh_name_off..].ptr, 0), 0);
+ if (mem.eql(u8, sh_name, ".dynstr")) {
+ break :find_dyn_str .{
+ .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset),
+ .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size),
+ };
}
- } else null;
+ }
+ } else null;
- if (dynstr) |ds| {
+ if (dynstr) |ds| {
+ if (rpath_offset) |rpoff| {
// TODO this pointer cast should not be necessary
const rpoff_usize = std.math.cast(usize, rpoff) orelse return error.InvalidElfFile;
if (rpoff_usize > ds.size) return error.InvalidElfFile;
@@ -746,116 +808,26 @@ pub fn abiAndDynamicLinkerFromFile(
const rpath_list = mem.sliceTo(std.meta.assumeSentinel(strtab.ptr, 0), 0);
var it = mem.tokenize(u8, rpath_list, ":");
while (it.next()) |rpath| {
- var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
- error.NameTooLong => unreachable,
- error.InvalidUtf8 => unreachable,
- error.BadPathName => unreachable,
- error.DeviceBusy => unreachable,
-
- error.FileNotFound,
- error.NotDir,
- error.InvalidHandle,
- error.AccessDenied,
- error.NoDevice,
- => continue,
-
- error.ProcessFdQuotaExceeded,
- error.SystemFdQuotaExceeded,
- error.SystemResources,
- error.SymLinkLoop,
- error.Unexpected,
- => |e| return e,
- };
- defer dir.close();
-
- // Now we have a candidate for the path to libc shared object. In
- // the past, we used readlink() here because the link name would
- // reveal the glibc version. However, in more recent GNU/Linux
- // installations, there is no symlink. Thus we instead use a more
- // robust check of opening the libc shared object and looking at the
- // .dynstr section, and finding the max version number of symbols
- // that start with "GLIBC_2.".
- var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {
- error.NameTooLong => unreachable,
- error.InvalidUtf8 => unreachable, // Windows only
- error.BadPathName => unreachable, // Windows only
- error.PipeBusy => unreachable, // Windows-only
- error.SharingViolation => unreachable, // Windows-only
- error.FileLocksNotSupported => unreachable, // No lock requested.
- error.NoSpaceLeft => unreachable, // read-only
- error.PathAlreadyExists => unreachable, // read-only
- error.DeviceBusy => unreachable, // read-only
- error.FileBusy => unreachable, // read-only
- error.InvalidHandle => unreachable, // should not be in the error set
- error.WouldBlock => unreachable, // not using O_NONBLOCK
- error.NoDevice => unreachable, // not asking for a special device
-
- error.AccessDenied,
- error.FileNotFound,
- error.NotDir,
- => continue,
-
- error.IsDir => return error.InvalidElfFile,
- error.FileTooBig => return error.Unexpected,
-
- error.ProcessFdQuotaExceeded,
- error.SystemFdQuotaExceeded,
- error.SystemResources,
- error.SymLinkLoop,
- error.Unexpected,
- => |e| return e,
- };
- defer f.close();
-
- result.target.os.version_range.linux.glibc = glibcVerFromSoFile(f) catch |err| switch (err) {
- error.InvalidElfMagic,
- error.InvalidElfEndian,
- error.InvalidElfClass,
- error.InvalidElfFile,
- error.InvalidElfVersion,
- error.InvalidGnuLibCVersion,
- error.UnexpectedEndOfFile,
- => continue,
-
- error.SystemResources,
- error.UnableToReadElfFile,
- error.Unexpected,
- error.FileSystem,
- => |e| return e,
- };
- break;
+ if (glibcVerFromRPath(rpath)) |ver| {
+ result.target.os.version_range.linux.glibc = ver;
+ break;
+ } else |err| switch (err) {
+ error.GLibCNotFound => continue,
+ else => |e| return e,
+ }
+ }
+ } else if (result.dynamic_linker.get()) |dl_path| {
+ // 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;
+ } else |err| switch (err) {
+ error.GLibCNotFound => {},
+ else => |e| return e,
+ }
}
}
- } else if (result.dynamic_linker.get()) |dl_path| glibc_ver: {
- // There is no DT_RUNPATH but we can 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,
- };
}
}