Skip to content

Commit

Permalink
add glibc abilist files versioning
Browse files Browse the repository at this point in the history
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 ziglang#7667

This commit adds versioning for (fns|abi|vers).txt based on glibc version.
  • Loading branch information
mjonaitis1 committed Oct 26, 2021
1 parent cde3dd3 commit 0a35c1f
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 18 deletions.
55 changes: 51 additions & 4 deletions src/glibc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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;
};
Expand Down Expand Up @@ -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) });
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 6 additions & 1 deletion src/print_targets.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
58 changes: 45 additions & 13 deletions tools/update_glibc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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);
Expand All @@ -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 });
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 0a35c1f

Please sign in to comment.