From 0a35c1fbc636efb3a1707cac587c538cafe307ad Mon Sep 17 00:00:00 2001 From: mjonaitis Date: Tue, 26 Oct 2021 11:16:59 +0000 Subject: [PATCH] add glibc abilist files versioning Currently, zig cc has a flaw: it cannot detect when a symbol is moved between libraries. Example: pthread_sigmask was moved from libpthread to libc since glibc-2.32. As symbol mapping files that are generated using update_glibc.zig tool are based on latest glibc version, compiling for targets with lower that glibc-2.32 version results in pthread_sigmask being linked with libc instead of libpthread. This causes a relocation error #7667 This commit adds versioning for (fns|abi|vers).txt based on glibc version. --- src/glibc.zig | 55 ++++++++++++++++++++++++++++++++++++--- src/print_targets.zig | 7 ++++- tools/update_glibc.zig | 58 ++++++++++++++++++++++++++++++++---------- 3 files changed, 102 insertions(+), 18 deletions(-) diff --git a/src/glibc.zig b/src/glibc.zig index aed1a244377d..832b41e85ea0 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -57,9 +57,48 @@ pub const LoadMetaDataError = error{ OutOfMemory, }; +// Gets appropriate lowest available glibc version symbol mapping directory for given target version. +pub fn appropriateSymbolsDir(zig_lib_dir: std.fs.Dir, target_version: std.builtin.Version) ![]const u8{ + var d = try zig_lib_dir.openDir("libc"++path.sep_str++"glibc"++path.sep_str++"symbols", .{.iterate = true}); + defer d.close(); + var it = d.iterate(); + var lowest_compatible_version: ?std.builtin.Version = null; + var lowest_dir_name: []const u8 = undefined; + + while(it.next()) |dir|{ + if(dir) |entry|{ + var parsed = std.build.Version.parse(entry.name) catch continue; + if (lowest_compatible_version) |lcv|{ + switch(target_version.order(parsed)){ + .lt, .eq => { + if (parsed.order(lcv) == .lt){ + lowest_compatible_version = parsed; + lowest_dir_name = entry.name; + } + }, + else => continue, + } + }else{ + switch(target_version.order(parsed)){ + .lt, .eq => { + lowest_compatible_version = parsed; + lowest_dir_name = entry.name; + }, + else => continue, + } + } + }else{ + break; + } + }else |err|{ + return err; + } + return lowest_dir_name; +} + /// This function will emit a log error when there is a problem with the zig installation and then return /// `error.ZigInstallationCorrupt`. -pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI { +pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir, target_version: std.builtin.Version) LoadMetaDataError!*ABI { const tracy = trace(@src()); defer tracy.end(); @@ -72,7 +111,15 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! var version_table = std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList){}; errdefer version_table.deinit(gpa); - var glibc_dir = zig_lib_dir.openDir("libc" ++ path.sep_str ++ "glibc", .{}) catch |err| { + const version_directory = appropriateSymbolsDir(zig_lib_dir, target_version) catch |err|{ + std.log.err("unable to open glibc symbols dir: {s}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }; + const glibc_dir_path = path.join(arena, &[_][]const u8{"libc", "glibc", "symbols", version_directory}) catch |err|{ + std.log.err("unable to open glibc symbols dir: {s}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }; + var glibc_dir = zig_lib_dir.openDir(glibc_dir_path, .{.iterate = true}) catch |err| { std.log.err("unable to open glibc dir: {s}", .{@errorName(err)}); return error.ZigInstallationCorrupt; }; @@ -210,6 +257,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! std.log.err("abi.txt:{d}: too many versions", .{line_i}); return error.ZigInstallationCorrupt; } + const version_index = std.fmt.parseInt(u8, version_index_string, 10) catch |err| { // If this happens with legit data, increase the size of the integer type in the struct. std.log.err("abi.txt:{d}: unable to parse version: {s}", .{ line_i, @errorName(err) }); @@ -753,9 +801,8 @@ pub fn buildSharedObjects(comp: *Compilation) !void { } else false; if (!actual_hit) { - const metadata = try loadMetaData(comp.gpa, comp.zig_lib_directory.handle); + const metadata = try loadMetaData(comp.gpa, comp.zig_lib_directory.handle, target_version); defer metadata.destroy(comp.gpa); - const ver_list_base = metadata.version_table.get(.{ .arch = target.cpu.arch, .os = target.os.tag, diff --git a/src/print_targets.zig b/src/print_targets.zig index d0a1d5167a86..1361fb067b31 100644 --- a/src/print_targets.zig +++ b/src/print_targets.zig @@ -24,7 +24,12 @@ pub fn cmdTargets( defer zig_lib_directory.handle.close(); defer allocator.free(zig_lib_directory.path.?); - const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_directory.handle); + const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_directory.handle, std.builtin.Version{ + .major = 2, + .minor = 34, + .patch = 0, + }); + defer glibc_abi.destroy(allocator); var bw = io.bufferedWriter(stdout); diff --git a/tools/update_glibc.zig b/tools/update_glibc.zig index 0b25d9757274..ec5362413df6 100644 --- a/tools/update_glibc.zig +++ b/tools/update_glibc.zig @@ -100,11 +100,11 @@ const abi_lists = [_]AbiList{ }, AbiList{ .targets = &[_]ZigTarget{ZigTarget{ .arch = .powerpc64le, .abi = .gnu }}, - .path = "powerpc/powerpc64/le", + .path = "powerpc/powerpc64", }, AbiList{ .targets = &[_]ZigTarget{ZigTarget{ .arch = .powerpc64, .abi = .gnu }}, - .path = "powerpc/powerpc64/be", + .path = "powerpc/powerpc64", }, AbiList{ .targets = &[_]ZigTarget{ @@ -137,9 +137,23 @@ pub fn main() !void { const args = try std.process.argsAlloc(allocator); const in_glibc_dir = args[1]; // path to the unzipped tarball of glibc, e.g. ~/downloads/glibc-2.25 const zig_src_dir = args[2]; // path to the source checkout of zig, lib dir, e.g. ~/zig-src/lib + const glibc_version = args[3]; // glibc version that will be updated, e.g. 2.32; 2.19; 2.2.5 etc. + const glibc_version_target = try std.builtin.Version.parse(glibc_version); const prefix = try fs.path.join(allocator, &[_][]const u8{ in_glibc_dir, "sysdeps", "unix", "sysv", "linux" }); - const glibc_out_dir = try fs.path.join(allocator, &[_][]const u8{ zig_src_dir, "libc", "glibc" }); + var glibc_out_dir = try fs.path.join(allocator, &[_][]const u8{ zig_src_dir, "libc", "glibc", "symbols"}); + + // Check if version directory is already created, and create if it is not. + var symbols_dir = try fs.cwd().openDir(glibc_out_dir, .{}); + var out_dir: std.fs.Dir = symbols_dir.openDir(glibc_version, .{}) catch |err| switch(err){ + error.FileNotFound => blk: { + try symbols_dir.makeDir(glibc_version); + break :blk try symbols_dir.openDir(glibc_version, .{}); + }, + else => return err, + }; + symbols_dir.close(); + defer out_dir.close(); var global_fn_set = std.StringHashMap(Function).init(allocator); var global_ver_set = std.StringHashMap(usize).init(allocator); @@ -155,6 +169,18 @@ pub fn main() !void { } const fn_set = &target_funcs_gop.value_ptr.list; + // glibc 2.31 added sysdeps/unix/sysv/linux/arm/le and sysdeps/unix/sysv/linux/arm/be + // Before these directories did not exist. + const ver30 = std.builtin.Version{ + .major = 2, + .minor = 30, + }; + // Similarly, powerpc64 le and be were introduced in glibc 2.29 + const ver28 = std.builtin.Version{ + .major = 2, + .minor = 28, + }; + for (lib_names) |lib_name| { const lib_prefix = if (std.mem.eql(u8, lib_name, "ld")) "" else "lib"; const basename = try fmt.allocPrint(allocator, "{s}{s}.abilist", .{ lib_prefix, lib_name }); @@ -176,11 +202,20 @@ pub fn main() !void { (is_c or (is_m and abi_list.targets[0].arch == .powerpc))) { break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "nofpu", basename }); - } else if (abi_list.targets[0].arch == .arm) { - break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "le", basename }); - } else if (abi_list.targets[0].arch == .armeb) { - break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, "be", basename }); + } else if ((abi_list.targets[0].arch == .armeb or abi_list.targets[0].arch == .arm) and glibc_version_target.order(ver30) == .gt){ + var le_be = "le"; + if (abi_list.targets[0].arch == .armeb){ + le_be = "be"; + } + break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, le_be, basename }); + } else if ((abi_list.targets[0].arch == .powerpc64le or abi_list.targets[0].arch == .powerpc64) and glibc_version_target.order(ver28) == .gt){ + var le_be = "le"; + if (abi_list.targets[0].arch == .powerpc64){ + le_be = "be"; + } + break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, le_be, basename }); } + break :blk try fs.path.join(allocator, &[_][]const u8{ prefix, abi_list.path, basename }); }; const max_bytes = 10 * 1024 * 1024; @@ -236,8 +271,7 @@ pub fn main() !void { break :blk list.items; }; { - const vers_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "vers.txt" }); - const vers_txt_file = try fs.cwd().createFile(vers_txt_path, .{}); + const vers_txt_file = try out_dir.createFile("vers.txt", .{}); defer vers_txt_file.close(); var buffered = std.io.bufferedWriter(vers_txt_file.writer()); const vers_txt = buffered.writer(); @@ -248,8 +282,7 @@ pub fn main() !void { try buffered.flush(); } { - const fns_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "fns.txt" }); - const fns_txt_file = try fs.cwd().createFile(fns_txt_path, .{}); + const fns_txt_file = try out_dir.createFile("fns.txt", .{}); defer fns_txt_file.close(); var buffered = std.io.bufferedWriter(fns_txt_file.writer()); const fns_txt = buffered.writer(); @@ -279,8 +312,7 @@ pub fn main() !void { } { - const abilist_txt_path = try fs.path.join(allocator, &[_][]const u8{ glibc_out_dir, "abi.txt" }); - const abilist_txt_file = try fs.cwd().createFile(abilist_txt_path, .{}); + const abilist_txt_file = try out_dir.createFile("abi.txt", .{}); defer abilist_txt_file.close(); var buffered = std.io.bufferedWriter(abilist_txt_file.writer()); const abilist_txt = buffered.writer();