authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-09-08 20:49:16-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-09-08 20:52:49-07:00
log9f40f34501ef086d18e4570416c078a7ad508628
treed4c24be3d5c7fa50f96fc54e7880f0c37bba422f
parentc668396941f19a5c015014822d980bbe9f2bc585

std.zig.system.NativeTargetInfo: restore symlink logic

This is a partial revert of the previous commit, fixing a regression on Debian. However, the commit additionally improves the detectAbiAndDynamicLinker function to read more than 1 byte at a time when detecting a shebang line.

1 files changed, 57 insertions(+), 16 deletions(-)

lib/std/zig/system/NativeTargetInfo.zig+57-16
......@@ -347,26 +347,17 @@ fn detectAbiAndDynamicLinker(
347347 };
348348 errdefer file.close();
349349
350 const line = file.reader().readUntilDelimiter(&buffer, '\n') catch |err| switch (err) {
351 error.IsDir => unreachable, // Handled before
352 error.AccessDenied => unreachable,
353 error.WouldBlock => unreachable, // Did not request blocking mode
354 error.OperationAborted => unreachable, // Windows-only
355 error.BrokenPipe => unreachable,
356 error.ConnectionResetByPeer => unreachable,
357 error.ConnectionTimedOut => unreachable,
358 error.InputOutput => unreachable,
359 error.Unexpected => unreachable,
360
361 error.StreamTooLong,
362 error.EndOfStream,
363 error.NotOpenForReading,
350 const len = preadMin(file, &buffer, 0, buffer.len) catch |err| switch (err) {
351 error.UnexpectedEndOfFile,
352 error.UnableToReadElfFile,
364353 => break :blk file,
365354
366355 else => |e| return e,
367356 };
357 const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file;
358 const line = buffer[0..newline];
368359 if (!mem.startsWith(u8, line, "#!")) break :blk file;
369 var it = std.mem.tokenize(u8, line[2..], " ");
360 var it = mem.tokenize(u8, line[2..], " ");
370361 file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, cross_target);
371362 file.close();
372363 }
......@@ -375,6 +366,8 @@ fn detectAbiAndDynamicLinker(
375366
376367 // If Zig is statically linked, such as via distributed binary static builds, the above
377368 // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file.
369 // TODO: inline this function and combine the buffer we already read above to find
370 // the possible shebang line with the buffer we use for the ELF header.
378371 return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) {
379372 error.FileSystem,
380373 error.SystemResources,
......@@ -586,6 +579,23 @@ fn glibcVerFromSoFile(file: fs.File) !std.builtin.Version {
586579 return max_ver;
587580}
588581
582fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) !std.builtin.Version {
583 // example: "libc-2.3.4.so"
584 // example: "libc-2.27.so"
585 // example: "ld-2.33.so"
586 const suffix = ".so";
587 if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) {
588 return error.UnrecognizedGnuLibCFileName;
589 }
590 // chop off "libc-" and ".so"
591 const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len];
592 return std.builtin.Version.parse(link_name_chopped) catch |err| switch (err) {
593 error.Overflow => return error.InvalidGnuLibCVersion,
594 error.InvalidCharacter => return error.InvalidGnuLibCVersion,
595 error.InvalidVersion => return error.InvalidGnuLibCVersion,
596 };
597}
598
589599pub const AbiAndDynamicLinkerFromFileError = error{
590600 FileSystem,
591601 SystemResources,
......@@ -816,17 +826,48 @@ pub fn abiAndDynamicLinkerFromFile(
816826 else => |e| return e,
817827 }
818828 }
819 } else if (result.dynamic_linker.get()) |dl_path| {
829 } else if (result.dynamic_linker.get()) |dl_path| glibc_ver: {
820830 // There is no DT_RUNPATH so we try to find libc.so.6 inside the same
821831 // directory as the dynamic linker.
822832 if (fs.path.dirname(dl_path)) |rpath| {
823833 if (glibcVerFromRPath(rpath)) |ver| {
824834 result.target.os.version_range.linux.glibc = ver;
835 break :glibc_ver;
825836 } else |err| switch (err) {
826837 error.GLibCNotFound => {},
827838 else => |e| return e,
828839 }
829840 }
841
842 // So far, no luck. Next we try to see if the information is
843 // present in the symlink data for the dynamic linker path.
844 var link_buf: [std.os.PATH_MAX]u8 = undefined;
845 const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) {
846 error.NameTooLong => unreachable,
847 error.InvalidUtf8 => unreachable, // Windows only
848 error.BadPathName => unreachable, // Windows only
849 error.UnsupportedReparsePointType => unreachable, // Windows only
850
851 error.AccessDenied,
852 error.FileNotFound,
853 error.NotLink,
854 error.NotDir,
855 => break :glibc_ver,
856
857 error.SystemResources,
858 error.FileSystem,
859 error.SymLinkLoop,
860 error.Unexpected,
861 => |e| return e,
862 };
863 result.target.os.version_range.linux.glibc = glibcVerFromLinkName(
864 fs.path.basename(link_name),
865 "ld-",
866 ) catch |err| switch (err) {
867 error.UnrecognizedGnuLibCFileName,
868 error.InvalidGnuLibCVersion,
869 => break :glibc_ver,
870 };
830871 }
831872 }
832873 }