mirror of
https://github.com/ziglang/zig.git
synced 2025-12-21 21:53:08 +00:00
* caching system: use 16 bytes siphash final(), there was a bug in the std lib that wasn't catching undefined values for 18 bytes. fixed in master branch. * fix caching system unit test logic to not cause error.TextBusy on windows * port the logic from stage1 for building glibc shared objects * add is_native_os to the base cache hash * fix incorrectly freeing crt_files key (which is always a reference to global static constant data) * fix 2 use-after-free in loading glibc metadata * fix memory leak in buildCRTFile (errdefer instead of defer on arena)
984 lines
40 KiB
Zig
984 lines
40 KiB
Zig
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const mem = std.mem;
|
|
const path = std.fs.path;
|
|
const assert = std.debug.assert;
|
|
|
|
const target_util = @import("target.zig");
|
|
const Compilation = @import("Compilation.zig");
|
|
const build_options = @import("build_options");
|
|
const trace = @import("tracy.zig").trace;
|
|
const Cache = @import("Cache.zig");
|
|
const Package = @import("Package.zig");
|
|
|
|
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 {
|
|
const tracy = trace(@src());
|
|
defer tracy.end();
|
|
|
|
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" ++ 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);
|
|
|
|
// Arena allocated because the result contains references to function names.
|
|
const fns_txt_contents = glibc_dir.readFileAlloc(arena, "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;
|
|
},
|
|
};
|
|
|
|
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(gpa, 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;
|
|
}
|
|
|
|
pub const CRTFile = enum {
|
|
crti_o,
|
|
crtn_o,
|
|
scrt1_o,
|
|
libc_nonshared_a,
|
|
};
|
|
|
|
pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
|
|
if (!build_options.have_llvm) {
|
|
return error.ZigCompilerNotBuiltWithLLVMExtensions;
|
|
}
|
|
const gpa = comp.gpa;
|
|
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
|
defer arena_allocator.deinit();
|
|
const arena = &arena_allocator.allocator;
|
|
|
|
switch (crt_file) {
|
|
.crti_o => {
|
|
var args = std.ArrayList([]const u8).init(arena);
|
|
try add_include_dirs(comp, arena, &args);
|
|
try args.appendSlice(&[_][]const u8{
|
|
"-D_LIBC_REENTRANT",
|
|
"-include",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
|
|
"-DMODULE_NAME=libc",
|
|
"-Wno-nonportable-include-path",
|
|
"-include",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
|
|
"-DTOP_NAMESPACE=glibc",
|
|
"-DASSEMBLER",
|
|
"-g",
|
|
"-Wa,--noexecstack",
|
|
});
|
|
return build_crt_file(comp, "crti.o", .Obj, &[1]Compilation.CSourceFile{
|
|
.{
|
|
.src_path = try start_asm_path(comp, arena, "crti.S"),
|
|
.extra_flags = args.items,
|
|
},
|
|
});
|
|
},
|
|
.crtn_o => {
|
|
var args = std.ArrayList([]const u8).init(arena);
|
|
try add_include_dirs(comp, arena, &args);
|
|
try args.appendSlice(&[_][]const u8{
|
|
"-D_LIBC_REENTRANT",
|
|
"-DMODULE_NAME=libc",
|
|
"-DTOP_NAMESPACE=glibc",
|
|
"-DASSEMBLER",
|
|
"-g",
|
|
"-Wa,--noexecstack",
|
|
});
|
|
return build_crt_file(comp, "crtn.o", .Obj, &[1]Compilation.CSourceFile{
|
|
.{
|
|
.src_path = try start_asm_path(comp, arena, "crtn.S"),
|
|
.extra_flags = args.items,
|
|
},
|
|
});
|
|
},
|
|
.scrt1_o => {
|
|
const start_os: Compilation.CSourceFile = blk: {
|
|
var args = std.ArrayList([]const u8).init(arena);
|
|
try add_include_dirs(comp, arena, &args);
|
|
try args.appendSlice(&[_][]const u8{
|
|
"-D_LIBC_REENTRANT",
|
|
"-include",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
|
|
"-DMODULE_NAME=libc",
|
|
"-Wno-nonportable-include-path",
|
|
"-include",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
|
|
"-DPIC",
|
|
"-DSHARED",
|
|
"-DTOP_NAMESPACE=glibc",
|
|
"-DASSEMBLER",
|
|
"-g",
|
|
"-Wa,--noexecstack",
|
|
});
|
|
break :blk .{
|
|
.src_path = try start_asm_path(comp, arena, "start.S"),
|
|
.extra_flags = args.items,
|
|
};
|
|
};
|
|
const abi_note_o: Compilation.CSourceFile = blk: {
|
|
var args = std.ArrayList([]const u8).init(arena);
|
|
try args.appendSlice(&[_][]const u8{
|
|
"-I",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "csu"),
|
|
});
|
|
try add_include_dirs(comp, arena, &args);
|
|
try args.appendSlice(&[_][]const u8{
|
|
"-D_LIBC_REENTRANT",
|
|
"-DMODULE_NAME=libc",
|
|
"-DTOP_NAMESPACE=glibc",
|
|
"-DASSEMBLER",
|
|
"-g",
|
|
"-Wa,--noexecstack",
|
|
});
|
|
break :blk .{
|
|
.src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"),
|
|
.extra_flags = args.items,
|
|
};
|
|
};
|
|
return build_crt_file(comp, "Scrt1.o", .Obj, &[_]Compilation.CSourceFile{ start_os, abi_note_o });
|
|
},
|
|
.libc_nonshared_a => {
|
|
const deps = [_][]const u8{
|
|
lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "atexit.c",
|
|
lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "at_quick_exit.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat64.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat64.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat64.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat64.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknod.c",
|
|
lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknodat.c",
|
|
lib_libc_glibc ++ "nptl" ++ path.sep_str ++ "pthread_atfork.c",
|
|
lib_libc_glibc ++ "debug" ++ path.sep_str ++ "stack_chk_fail_local.c",
|
|
};
|
|
|
|
var c_source_files: [deps.len + 1]Compilation.CSourceFile = undefined;
|
|
|
|
c_source_files[0] = blk: {
|
|
var args = std.ArrayList([]const u8).init(arena);
|
|
try args.appendSlice(&[_][]const u8{
|
|
"-std=gnu11",
|
|
"-fgnu89-inline",
|
|
"-g",
|
|
"-O2",
|
|
"-fmerge-all-constants",
|
|
"-fno-stack-protector",
|
|
"-fmath-errno",
|
|
"-fno-stack-protector",
|
|
"-I",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "csu"),
|
|
});
|
|
try add_include_dirs(comp, arena, &args);
|
|
try args.appendSlice(&[_][]const u8{
|
|
"-DSTACK_PROTECTOR_LEVEL=0",
|
|
"-fPIC",
|
|
"-fno-stack-protector",
|
|
"-ftls-model=initial-exec",
|
|
"-D_LIBC_REENTRANT",
|
|
"-include",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
|
|
"-DMODULE_NAME=libc",
|
|
"-Wno-nonportable-include-path",
|
|
"-include",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
|
|
"-DPIC",
|
|
"-DLIBC_NONSHARED=1",
|
|
"-DTOP_NAMESPACE=glibc",
|
|
});
|
|
break :blk .{
|
|
.src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "elf-init.c"),
|
|
.extra_flags = args.items,
|
|
};
|
|
};
|
|
|
|
for (deps) |dep, i| {
|
|
var args = std.ArrayList([]const u8).init(arena);
|
|
try args.appendSlice(&[_][]const u8{
|
|
"-std=gnu11",
|
|
"-fgnu89-inline",
|
|
"-g",
|
|
"-O2",
|
|
"-fmerge-all-constants",
|
|
"-fno-stack-protector",
|
|
"-fmath-errno",
|
|
"-ftls-model=initial-exec",
|
|
"-Wno-ignored-attributes",
|
|
});
|
|
try add_include_dirs(comp, arena, &args);
|
|
try args.appendSlice(&[_][]const u8{
|
|
"-D_LIBC_REENTRANT",
|
|
"-include",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"),
|
|
"-DMODULE_NAME=libc",
|
|
"-Wno-nonportable-include-path",
|
|
"-include",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
|
|
"-DPIC",
|
|
"-DLIBC_NONSHARED=1",
|
|
"-DTOP_NAMESPACE=glibc",
|
|
});
|
|
c_source_files[i + 1] = .{
|
|
.src_path = try lib_path(comp, arena, dep),
|
|
.extra_flags = args.items,
|
|
};
|
|
}
|
|
return build_crt_file(comp, "libc_nonshared.a", .Lib, &c_source_files);
|
|
},
|
|
}
|
|
}
|
|
|
|
fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 {
|
|
const arch = comp.getTarget().cpu.arch;
|
|
const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
|
|
const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
|
|
const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;
|
|
const is_64 = arch.ptrBitWidth() == 64;
|
|
|
|
const s = path.sep_str;
|
|
|
|
var result = std.ArrayList(u8).init(arena);
|
|
try result.appendSlice(comp.zig_lib_directory.path.?);
|
|
try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s);
|
|
if (is_sparc) {
|
|
if (is_64) {
|
|
try result.appendSlice("sparc" ++ s ++ "sparc64");
|
|
} else {
|
|
try result.appendSlice("sparc" ++ s ++ "sparc32");
|
|
}
|
|
} else if (arch.isARM()) {
|
|
try result.appendSlice("arm");
|
|
} else if (arch.isMIPS()) {
|
|
try result.appendSlice("mips");
|
|
} else if (arch == .x86_64) {
|
|
try result.appendSlice("x86_64");
|
|
} else if (arch == .i386) {
|
|
try result.appendSlice("i386");
|
|
} else if (is_aarch64) {
|
|
try result.appendSlice("aarch64");
|
|
} else if (arch.isRISCV()) {
|
|
try result.appendSlice("riscv");
|
|
} else if (is_ppc) {
|
|
if (is_64) {
|
|
try result.appendSlice("powerpc" ++ s ++ "powerpc64");
|
|
} else {
|
|
try result.appendSlice("powerpc" ++ s ++ "powerpc32");
|
|
}
|
|
}
|
|
|
|
try result.appendSlice(s);
|
|
try result.appendSlice(basename);
|
|
return result.items;
|
|
}
|
|
|
|
fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void {
|
|
const target = comp.getTarget();
|
|
const arch = target.cpu.arch;
|
|
const opt_nptl: ?[]const u8 = if (target.os.tag == .linux) "nptl" else "htl";
|
|
const glibc = try lib_path(comp, arena, lib_libc ++ "glibc");
|
|
|
|
const s = path.sep_str;
|
|
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "include"));
|
|
|
|
if (target.os.tag == .linux) {
|
|
try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux"));
|
|
}
|
|
|
|
if (opt_nptl) |nptl| {
|
|
try add_include_dirs_arch(arena, args, arch, nptl, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
|
|
}
|
|
|
|
if (target.os.tag == .linux) {
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
|
|
"unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic"));
|
|
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
|
|
"unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include"));
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++
|
|
"unix" ++ s ++ "sysv" ++ s ++ "linux"));
|
|
}
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc_glibc ++ "sysdeps", nptl }));
|
|
}
|
|
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread"));
|
|
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv"));
|
|
|
|
try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
|
|
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix"));
|
|
|
|
try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps"));
|
|
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic"));
|
|
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc ++ "glibc" }));
|
|
|
|
try args.append("-I");
|
|
try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", .{
|
|
comp.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi),
|
|
}));
|
|
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc"));
|
|
|
|
try args.append("-I");
|
|
try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-linux-any", .{
|
|
comp.zig_lib_directory.path.?, @tagName(arch),
|
|
}));
|
|
|
|
try args.append("-I");
|
|
try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "any-linux-any"));
|
|
}
|
|
|
|
fn add_include_dirs_arch(
|
|
arena: *Allocator,
|
|
args: *std.ArrayList([]const u8),
|
|
arch: std.Target.Cpu.Arch,
|
|
opt_nptl: ?[]const u8,
|
|
dir: []const u8,
|
|
) error{OutOfMemory}!void {
|
|
const is_x86 = arch == .i386 or arch == .x86_64;
|
|
const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
|
|
const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
|
|
const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9;
|
|
const is_64 = arch.ptrBitWidth() == 64;
|
|
|
|
const s = path.sep_str;
|
|
|
|
if (is_x86) {
|
|
if (arch == .x86_64) {
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", nptl }));
|
|
} else {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64" }));
|
|
}
|
|
} else if (arch == .i386) {
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "i386", nptl }));
|
|
} else {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "i386" }));
|
|
}
|
|
}
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "x86", nptl }));
|
|
} else {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "x86" }));
|
|
}
|
|
} else if (arch.isARM()) {
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "arm", nptl }));
|
|
} else {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "arm" }));
|
|
}
|
|
} else if (arch.isMIPS()) {
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "mips", nptl }));
|
|
} else {
|
|
if (is_64) {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips64" }));
|
|
} else {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips32" }));
|
|
}
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" }));
|
|
}
|
|
} else if (is_sparc) {
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc", nptl }));
|
|
} else {
|
|
if (is_64) {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc64" }));
|
|
} else {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc32" }));
|
|
}
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" }));
|
|
}
|
|
} else if (is_aarch64) {
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64", nptl }));
|
|
} else {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64" }));
|
|
}
|
|
} else if (is_ppc) {
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc", nptl }));
|
|
} else {
|
|
if (is_64) {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc64" }));
|
|
} else {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc32" }));
|
|
}
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" }));
|
|
}
|
|
} else if (arch.isRISCV()) {
|
|
if (opt_nptl) |nptl| {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv", nptl }));
|
|
} else {
|
|
try args.append("-I");
|
|
try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv" }));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn path_from_lib(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 {
|
|
return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path });
|
|
}
|
|
|
|
const lib_libc = "libc" ++ path.sep_str;
|
|
const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str;
|
|
|
|
fn lib_path(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 {
|
|
return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path });
|
|
}
|
|
|
|
fn build_crt_file(
|
|
comp: *Compilation,
|
|
basename: []const u8,
|
|
output_mode: std.builtin.OutputMode,
|
|
c_source_files: []const Compilation.CSourceFile,
|
|
) !void {
|
|
const tracy = trace(@src());
|
|
defer tracy.end();
|
|
|
|
// TODO: This is extracted into a local variable to work around a stage1 miscompilation.
|
|
const emit_bin = Compilation.EmitLoc{
|
|
.directory = null, // Put it in the cache directory.
|
|
.basename = basename,
|
|
};
|
|
const sub_compilation = try Compilation.create(comp.gpa, .{
|
|
// TODO use the global cache directory here
|
|
.zig_cache_directory = comp.zig_cache_directory,
|
|
.zig_lib_directory = comp.zig_lib_directory,
|
|
.target = comp.getTarget(),
|
|
.root_name = mem.split(basename, ".").next().?,
|
|
.root_pkg = null,
|
|
.output_mode = output_mode,
|
|
.rand = comp.rand,
|
|
.libc_installation = comp.bin_file.options.libc_installation,
|
|
.emit_bin = emit_bin,
|
|
.optimize_mode = comp.bin_file.options.optimize_mode,
|
|
.want_sanitize_c = false,
|
|
.want_stack_check = false,
|
|
.want_valgrind = false,
|
|
.want_pic = comp.bin_file.options.pic,
|
|
.emit_h = null,
|
|
.strip = comp.bin_file.options.strip,
|
|
.is_native_os = comp.bin_file.options.is_native_os,
|
|
.self_exe_path = comp.self_exe_path,
|
|
.c_source_files = c_source_files,
|
|
.debug_cc = comp.debug_cc,
|
|
.debug_link = comp.bin_file.options.debug_link,
|
|
.clang_passthrough_mode = comp.clang_passthrough_mode,
|
|
});
|
|
defer sub_compilation.destroy();
|
|
|
|
try updateSubCompilation(sub_compilation);
|
|
|
|
try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1);
|
|
const artifact_path = if (sub_compilation.bin_file.options.directory.path) |p|
|
|
try path.join(comp.gpa, &[_][]const u8{ p, basename })
|
|
else
|
|
try comp.gpa.dupe(u8, basename);
|
|
|
|
comp.crt_files.putAssumeCapacityNoClobber(basename, .{
|
|
.full_object_path = artifact_path,
|
|
.lock = sub_compilation.bin_file.toOwnedLock(),
|
|
});
|
|
}
|
|
|
|
pub const BuiltSharedObjects = struct {
|
|
lock: Cache.Lock,
|
|
dir_path: []u8,
|
|
|
|
pub fn deinit(self: *BuiltSharedObjects, gpa: *Allocator) void {
|
|
self.lock.release();
|
|
gpa.free(self.dir_path);
|
|
self.* = undefined;
|
|
}
|
|
};
|
|
|
|
const all_map_basename = "all.map";
|
|
|
|
pub fn buildSharedObjects(comp: *Compilation) !void {
|
|
const tracy = trace(@src());
|
|
defer tracy.end();
|
|
|
|
var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
|
|
defer arena_allocator.deinit();
|
|
const arena = &arena_allocator.allocator;
|
|
|
|
const target = comp.getTarget();
|
|
const target_version = target.os.version_range.linux.glibc;
|
|
|
|
// TODO use the global cache directory here
|
|
var cache_parent: Cache = .{
|
|
.gpa = comp.gpa,
|
|
.manifest_dir = comp.cache_parent.manifest_dir,
|
|
};
|
|
var cache = cache_parent.obtain();
|
|
defer cache.deinit();
|
|
cache.hash.addBytes(build_options.version);
|
|
cache.hash.addBytes(comp.zig_lib_directory.path orelse ".");
|
|
cache.hash.add(target.cpu.arch);
|
|
cache.hash.addBytes(target.cpu.model.name);
|
|
cache.hash.add(target.cpu.features.ints);
|
|
cache.hash.add(target.abi);
|
|
cache.hash.add(target_version);
|
|
|
|
const hit = try cache.hit();
|
|
const digest = cache.final();
|
|
const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest });
|
|
if (!hit) {
|
|
var o_directory: Compilation.Directory = .{
|
|
.handle = try comp.zig_cache_directory.handle.makeOpenPath(o_sub_path, .{}),
|
|
.path = try path.join(arena, &[_][]const u8{ comp.zig_cache_directory.path.?, o_sub_path }),
|
|
};
|
|
defer o_directory.handle.close();
|
|
|
|
const metadata = try loadMetaData(comp.gpa, comp.zig_lib_directory.handle);
|
|
defer metadata.destroy(comp.gpa);
|
|
|
|
const ver_list_base = metadata.version_table.get(.{
|
|
.arch = target.cpu.arch,
|
|
.os = target.os.tag,
|
|
.abi = target.abi,
|
|
}) orelse return error.GLibCUnavailableForThisTarget;
|
|
const target_ver_index = for (metadata.all_versions) |ver, i| {
|
|
switch (ver.order(target_version)) {
|
|
.eq => break i,
|
|
.lt => continue,
|
|
.gt => {
|
|
// TODO Expose via compile error mechanism instead of log.
|
|
std.log.warn("invalid target glibc version: {}", .{target_version});
|
|
return error.InvalidTargetGLibCVersion;
|
|
},
|
|
}
|
|
} else blk: {
|
|
const latest_index = metadata.all_versions.len - 1;
|
|
std.log.warn("zig cannot build new glibc version {}; providing instead {}", .{
|
|
target_version, metadata.all_versions[latest_index],
|
|
});
|
|
break :blk latest_index;
|
|
};
|
|
{
|
|
var map_contents = std.ArrayList(u8).init(arena);
|
|
for (metadata.all_versions) |ver| {
|
|
if (ver.patch == 0) {
|
|
try map_contents.writer().print("GLIBC_{d}.{d} {{ }};\n", .{ ver.major, ver.minor });
|
|
} else {
|
|
try map_contents.writer().print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch });
|
|
}
|
|
}
|
|
try o_directory.handle.writeFile(all_map_basename, map_contents.items);
|
|
map_contents.deinit(); // The most recent allocation of an arena can be freed :)
|
|
}
|
|
var zig_body = std.ArrayList(u8).init(comp.gpa);
|
|
defer zig_body.deinit();
|
|
var zig_footer = std.ArrayList(u8).init(comp.gpa);
|
|
defer zig_footer.deinit();
|
|
for (libs) |*lib| {
|
|
zig_body.shrinkRetainingCapacity(0);
|
|
zig_footer.shrinkRetainingCapacity(0);
|
|
|
|
try zig_body.appendSlice(
|
|
\\comptime {
|
|
\\ asm (
|
|
\\
|
|
);
|
|
for (metadata.all_functions) |*libc_fn, fn_i| {
|
|
if (libc_fn.lib != lib) continue;
|
|
|
|
const ver_list = ver_list_base[fn_i];
|
|
// Pick the default symbol version:
|
|
// - If there are no versions, don't emit it
|
|
// - Take the greatest one <= than the target one
|
|
// - If none of them is <= than the
|
|
// specified one don't pick any default version
|
|
if (ver_list.len == 0) continue;
|
|
var chosen_def_ver_index: u8 = 255;
|
|
{
|
|
var ver_i: u8 = 0;
|
|
while (ver_i < ver_list.len) : (ver_i += 1) {
|
|
const ver_index = ver_list.versions[ver_i];
|
|
if ((chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) and
|
|
target_ver_index >= ver_index)
|
|
{
|
|
chosen_def_ver_index = ver_index;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
var ver_i: u8 = 0;
|
|
while (ver_i < ver_list.len) : (ver_i += 1) {
|
|
const ver_index = ver_list.versions[ver_i];
|
|
const ver = metadata.all_versions[ver_index];
|
|
const sym_name = libc_fn.name;
|
|
const stub_name = if (ver.patch == 0)
|
|
try std.fmt.allocPrint(arena, "{s}_{d}_{d}", .{ sym_name, ver.major, ver.minor })
|
|
else
|
|
try std.fmt.allocPrint(arena, "{s}_{d}_{d}_{d}", .{ sym_name, ver.major, ver.minor, ver.patch });
|
|
|
|
try zig_footer.writer().print("export fn {s}() void {{}}\n", .{stub_name});
|
|
|
|
// Default symbol version definition vs normal symbol version definition
|
|
const want_two_ats = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index;
|
|
const at_sign_str = "@@"[0 .. @boolToInt(want_two_ats) + @as(usize, 1)];
|
|
if (ver.patch == 0) {
|
|
try zig_body.writer().print(" \\\\ .symver {s}, {s}{s}GLIBC_{d}.{d}\n", .{
|
|
stub_name, sym_name, at_sign_str, ver.major, ver.minor,
|
|
});
|
|
} else {
|
|
try zig_body.writer().print(" \\\\ .symver {s}, {s}{s}GLIBC_{d}.{d}.{d}\n", .{
|
|
stub_name, sym_name, at_sign_str, ver.major, ver.minor, ver.patch,
|
|
});
|
|
}
|
|
// Hide the stub to keep the symbol table clean
|
|
try zig_body.writer().print(" \\\\ .hidden {s}\n", .{stub_name});
|
|
}
|
|
}
|
|
}
|
|
|
|
try zig_body.appendSlice(
|
|
\\ );
|
|
\\}
|
|
\\
|
|
);
|
|
try zig_body.appendSlice(zig_footer.items);
|
|
|
|
var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
|
|
const zig_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.zig", .{lib.name}) catch unreachable;
|
|
try o_directory.handle.writeFile(zig_file_basename, zig_body.items);
|
|
|
|
try buildSharedLib(comp, arena, comp.zig_cache_directory, o_directory, zig_file_basename, lib);
|
|
}
|
|
cache.writeManifest() catch |err| {
|
|
std.log.warn("glibc shared objects: failed to write cache manifest: {}", .{@errorName(err)});
|
|
};
|
|
}
|
|
|
|
assert(comp.glibc_so_files == null);
|
|
comp.glibc_so_files = BuiltSharedObjects{
|
|
.lock = cache.toOwnedLock(),
|
|
.dir_path = try path.join(comp.gpa, &[_][]const u8{ comp.zig_cache_directory.path.?, o_sub_path }),
|
|
};
|
|
}
|
|
|
|
fn buildSharedLib(
|
|
comp: *Compilation,
|
|
arena: *Allocator,
|
|
zig_cache_directory: Compilation.Directory,
|
|
bin_directory: Compilation.Directory,
|
|
zig_file_basename: []const u8,
|
|
lib: *const Lib,
|
|
) !void {
|
|
const tracy = trace(@src());
|
|
defer tracy.end();
|
|
|
|
const emit_bin = Compilation.EmitLoc{
|
|
.directory = bin_directory,
|
|
.basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }),
|
|
};
|
|
const version: std.builtin.Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
|
|
const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
|
|
const override_soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else null;
|
|
const map_file_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, all_map_basename });
|
|
// TODO we should be able to just give the open directory to Package
|
|
const root_pkg = try Package.create(comp.gpa, std.fs.cwd(), bin_directory.path.?, zig_file_basename);
|
|
defer root_pkg.destroy(comp.gpa);
|
|
const sub_compilation = try Compilation.create(comp.gpa, .{
|
|
.zig_cache_directory = zig_cache_directory,
|
|
.zig_lib_directory = comp.zig_lib_directory,
|
|
.target = comp.getTarget(),
|
|
.root_name = lib.name,
|
|
.root_pkg = null,
|
|
.output_mode = .Lib,
|
|
.link_mode = .Dynamic,
|
|
.rand = comp.rand,
|
|
.libc_installation = comp.bin_file.options.libc_installation,
|
|
.emit_bin = emit_bin,
|
|
.optimize_mode = comp.bin_file.options.optimize_mode,
|
|
.want_sanitize_c = false,
|
|
.want_stack_check = false,
|
|
.want_valgrind = false,
|
|
.emit_h = null,
|
|
.strip = comp.bin_file.options.strip,
|
|
.is_native_os = false,
|
|
.self_exe_path = comp.self_exe_path,
|
|
.debug_cc = comp.debug_cc,
|
|
.debug_link = comp.bin_file.options.debug_link,
|
|
.clang_passthrough_mode = comp.clang_passthrough_mode,
|
|
.version = version,
|
|
.stage1_is_dummy_so = true,
|
|
.version_script = map_file_path,
|
|
.override_soname = override_soname,
|
|
});
|
|
defer sub_compilation.destroy();
|
|
|
|
try updateSubCompilation(sub_compilation);
|
|
}
|
|
|
|
fn updateSubCompilation(sub_compilation: *Compilation) !void {
|
|
try sub_compilation.update();
|
|
|
|
// Look for compilation errors in this sub_compilation
|
|
var errors = try sub_compilation.getAllErrorsAlloc();
|
|
defer errors.deinit(sub_compilation.gpa);
|
|
|
|
if (errors.list.len != 0) {
|
|
for (errors.list) |full_err_msg| {
|
|
std.log.err("{}:{}:{}: error: {}\n", .{
|
|
full_err_msg.src_path,
|
|
full_err_msg.line + 1,
|
|
full_err_msg.column + 1,
|
|
full_err_msg.msg,
|
|
});
|
|
}
|
|
return error.BuildingLibCObjectFailed;
|
|
}
|
|
}
|