Commit 8cf40f3445
Changed files (4)
src-self-hosted
src-self-hosted/glibc.zig
@@ -0,0 +1,231 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const target_util = @import("target.zig");
+const mem = std.mem;
+
+pub const Lib = struct {
+ name: []const u8,
+ sover: u8,
+};
+
+pub const Fn = struct {
+ name: []const u8,
+ lib: *const Lib,
+};
+
+pub const VerList = struct {
+ /// 7 is just the max number, we know statically it's big enough.
+ versions: [7]u8,
+ len: u8,
+};
+
+pub const ABI = struct {
+ all_versions: []const std.builtin.Version,
+ all_functions: []const Fn,
+ /// The value is a pointer to all_functions.len items and each item is an index into all_functions.
+ version_table: std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList),
+ arena_state: std.heap.ArenaAllocator.State,
+
+ pub fn destroy(abi: *ABI, gpa: *Allocator) void {
+ abi.version_table.deinit(gpa);
+ abi.arena_state.promote(gpa).deinit(); // Frees the ABI memory too.
+ }
+};
+
+pub const libs = [_]Lib{
+ .{ .name = "c", .sover = 6 },
+ .{ .name = "m", .sover = 6 },
+ .{ .name = "pthread", .sover = 0 },
+ .{ .name = "dl", .sover = 2 },
+ .{ .name = "rt", .sover = 1 },
+ .{ .name = "ld", .sover = 2 },
+ .{ .name = "util", .sover = 1 },
+};
+
+pub const LoadMetaDataError = error{
+ /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data.
+ ZigInstallationCorrupt,
+ OutOfMemory,
+};
+
+/// 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 {
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
+ errdefer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ var all_versions = std.ArrayListUnmanaged(std.builtin.Version){};
+ var all_functions = std.ArrayListUnmanaged(Fn){};
+ var version_table = std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList){};
+ errdefer version_table.deinit(gpa);
+
+ var glibc_dir = zig_lib_dir.openDir("libc" ++ std.fs.path.sep_str ++ "glibc", .{}) catch |err| {
+ std.log.err("unable to open glibc dir: {}", .{@errorName(err)});
+ return error.ZigInstallationCorrupt;
+ };
+ defer glibc_dir.close();
+
+ const max_txt_size = 500 * 1024; // Bigger than this and something is definitely borked.
+ const vers_txt_contents = glibc_dir.readFileAlloc(gpa, "vers.txt", max_txt_size) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => {
+ std.log.err("unable to read vers.txt: {}", .{@errorName(err)});
+ return error.ZigInstallationCorrupt;
+ },
+ };
+ defer gpa.free(vers_txt_contents);
+
+ const fns_txt_contents = glibc_dir.readFileAlloc(gpa, "fns.txt", max_txt_size) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => {
+ std.log.err("unable to read fns.txt: {}", .{@errorName(err)});
+ return error.ZigInstallationCorrupt;
+ },
+ };
+ defer gpa.free(fns_txt_contents);
+
+ const abi_txt_contents = glibc_dir.readFileAlloc(gpa, "abi.txt", max_txt_size) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => {
+ std.log.err("unable to read abi.txt: {}", .{@errorName(err)});
+ return error.ZigInstallationCorrupt;
+ },
+ };
+ defer gpa.free(abi_txt_contents);
+
+ {
+ var it = mem.tokenize(vers_txt_contents, "\r\n");
+ var line_i: usize = 1;
+ while (it.next()) |line| : (line_i += 1) {
+ const prefix = "GLIBC_";
+ if (!mem.startsWith(u8, line, prefix)) {
+ std.log.err("vers.txt:{}: expected 'GLIBC_' prefix", .{line_i});
+ return error.ZigInstallationCorrupt;
+ }
+ const adjusted_line = line[prefix.len..];
+ const ver = std.builtin.Version.parse(adjusted_line) catch |err| {
+ std.log.err("vers.txt:{}: unable to parse glibc version '{}': {}", .{ line_i, line, @errorName(err) });
+ return error.ZigInstallationCorrupt;
+ };
+ try all_versions.append(arena, ver);
+ }
+ }
+ {
+ var file_it = mem.tokenize(fns_txt_contents, "\r\n");
+ var line_i: usize = 1;
+ while (file_it.next()) |line| : (line_i += 1) {
+ var line_it = mem.tokenize(line, " ");
+ const fn_name = line_it.next() orelse {
+ std.log.err("fns.txt:{}: expected function name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const lib_name = line_it.next() orelse {
+ std.log.err("fns.txt:{}: expected library name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const lib = findLib(lib_name) orelse {
+ std.log.err("fns.txt:{}: unknown library name: {}", .{ line_i, lib_name });
+ return error.ZigInstallationCorrupt;
+ };
+ try all_functions.append(arena, .{
+ .name = fn_name,
+ .lib = lib,
+ });
+ }
+ }
+ {
+ var file_it = mem.split(abi_txt_contents, "\n");
+ var line_i: usize = 0;
+ while (true) {
+ const ver_list_base: []VerList = blk: {
+ const line = file_it.next() orelse break;
+ if (line.len == 0) break;
+ line_i += 1;
+ const ver_list_base = try arena.alloc(VerList, all_functions.items.len);
+ var line_it = mem.tokenize(line, " ");
+ while (line_it.next()) |target_string| {
+ var component_it = mem.tokenize(target_string, "-");
+ const arch_name = component_it.next() orelse {
+ std.log.err("abi.txt:{}: expected arch name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const os_name = component_it.next() orelse {
+ std.log.err("abi.txt:{}: expected OS name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const abi_name = component_it.next() orelse {
+ std.log.err("abi.txt:{}: expected ABI name", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse {
+ std.log.err("abi.txt:{}: unrecognized arch: '{}'", .{ line_i, arch_name });
+ return error.ZigInstallationCorrupt;
+ };
+ if (!mem.eql(u8, os_name, "linux")) {
+ std.log.err("abi.txt:{}: expected OS 'linux', found '{}'", .{ line_i, os_name });
+ return error.ZigInstallationCorrupt;
+ }
+ const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
+ std.log.err("abi.txt:{}: unrecognized ABI: '{}'", .{ line_i, abi_name });
+ return error.ZigInstallationCorrupt;
+ };
+
+ const triple = target_util.ArchOsAbi{
+ .arch = arch_tag,
+ .os = .linux,
+ .abi = abi_tag,
+ };
+ try version_table.put(arena, triple, ver_list_base.ptr);
+ }
+ break :blk ver_list_base;
+ };
+ for (ver_list_base) |*ver_list| {
+ const line = file_it.next() orelse {
+ std.log.err("abi.txt:{}: missing version number line", .{line_i});
+ return error.ZigInstallationCorrupt;
+ };
+ line_i += 1;
+
+ ver_list.* = .{
+ .versions = undefined,
+ .len = 0,
+ };
+ var line_it = mem.tokenize(line, " ");
+ while (line_it.next()) |version_index_string| {
+ if (ver_list.len >= ver_list.versions.len) {
+ // If this happens with legit data, increase the array len in the type.
+ std.log.err("abi.txt:{}: 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:{}: unable to parse version: {}", .{ line_i, @errorName(err) });
+ return error.ZigInstallationCorrupt;
+ };
+
+ ver_list.versions[ver_list.len] = version_index;
+ ver_list.len += 1;
+ }
+ }
+ }
+ }
+
+ const abi = try arena.create(ABI);
+ abi.* = .{
+ .all_versions = all_versions.items,
+ .all_functions = all_functions.items,
+ .version_table = version_table,
+ .arena_state = arena_allocator.state,
+ };
+ return abi;
+}
+
+fn findLib(name: []const u8) ?*const Lib {
+ for (libs) |*lib| {
+ if (mem.eql(u8, lib.name, name)) {
+ return lib;
+ }
+ }
+ return null;
+}
src-self-hosted/main.zig
@@ -16,7 +16,7 @@ const warn = std.log.warn;
const introspect = @import("introspect.zig");
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
-fn fatal(comptime format: []const u8, args: anytype) noreturn {
+pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
std.log.emerg(format, args);
process.exit(1);
}
src-self-hosted/Module.zig
@@ -1256,6 +1256,14 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Module {
mod.c_object_table.putAssumeCapacityNoClobber(c_object, {});
}
+ // If we need to build glibc for the target, add work items for it.
+ if (mod.bin_file.options.link_libc and
+ mod.bin_file.options.libc_installation == null and
+ mod.bin_file.options.target.isGnuLibC())
+ {
+ try mod.addBuildingGLibCWorkItems();
+ }
+
return mod;
}
@@ -4495,3 +4503,8 @@ pub fn get_libc_crt_file(mod: *Module, arena: *Allocator, basename: []const u8)
const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
return full_path;
}
+
+fn addBuildingGLibCWorkItems(mod: *Module) !void {
+ // crti.o, crtn.o, start.os, abi-note.o, Scrt1.o, libc_nonshared.a
+ try mod.work_queue.ensureUnusedCapacity(6);
+}
src-self-hosted/print_targets.zig
@@ -6,8 +6,9 @@ const Allocator = mem.Allocator;
const Target = std.Target;
const target = @import("target.zig");
const assert = std.debug.assert;
-
+const glibc = @import("glibc.zig");
const introspect = @import("introspect.zig");
+const fatal = @import("main.zig").fatal;
pub fn cmdTargets(
allocator: *Allocator,
@@ -16,33 +17,16 @@ pub fn cmdTargets(
stdout: anytype,
native_target: Target,
) !void {
- const available_glibcs = blk: {
- const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch |err| {
- std.debug.print("unable to find zig installation directory: {}\n", .{@errorName(err)});
- std.process.exit(1);
- };
- defer allocator.free(zig_lib_dir);
-
- var dir = try std.fs.cwd().openDir(zig_lib_dir, .{});
- defer dir.close();
-
- const vers_txt = try dir.readFileAlloc(allocator, "libc" ++ std.fs.path.sep_str ++ "glibc" ++ std.fs.path.sep_str ++ "vers.txt", 10 * 1024);
- defer allocator.free(vers_txt);
-
- var list = std.ArrayList(std.builtin.Version).init(allocator);
- defer list.deinit();
-
- var it = mem.tokenize(vers_txt, "\r\n");
- while (it.next()) |line| {
- const prefix = "GLIBC_";
- assert(mem.startsWith(u8, line, prefix));
- const adjusted_line = line[prefix.len..];
- const ver = try std.builtin.Version.parse(adjusted_line);
- try list.append(ver);
- }
- break :blk list.toOwnedSlice();
+ const zig_lib_dir_path = introspect.resolveZigLibDir(allocator) catch |err| {
+ fatal("unable to find zig installation directory: {}\n", .{@errorName(err)});
};
- defer allocator.free(available_glibcs);
+ defer allocator.free(zig_lib_dir_path);
+
+ var zig_lib_dir = try fs.cwd().openDir(zig_lib_dir_path, .{});
+ defer zig_lib_dir.close();
+
+ const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_dir);
+ errdefer glibc_abi.destroy(allocator);
var bos = io.bufferedOutStream(stdout);
const bos_stream = bos.outStream();
@@ -90,10 +74,10 @@ pub fn cmdTargets(
try jws.objectField("glibc");
try jws.beginArray();
- for (available_glibcs) |glibc| {
+ for (glibc_abi.all_versions) |ver| {
try jws.arrayElem();
- const tmp = try std.fmt.allocPrint(allocator, "{}", .{glibc});
+ const tmp = try std.fmt.allocPrint(allocator, "{}", .{ver});
defer allocator.free(tmp);
try jws.emitString(tmp);
}
@@ -170,7 +154,6 @@ pub fn cmdTargets(
try jws.emitString(@tagName(native_target.os.tag));
try jws.objectField("abi");
try jws.emitString(@tagName(native_target.abi));
- // TODO implement native glibc version detection in self-hosted
try jws.endObject();
try jws.endObject();