mirror of
https://github.com/ziglang/zig.git
synced 2026-01-04 20:43:19 +00:00
* CLI: change to -mred-zone and -mno-red-zone to match gcc/clang.
* build.zig: remove the double negative and make it an optional bool.
This follows precedent from other flags, allowing the compiler CLI to
be the decider of what is default instead of duplicating the default
value into the build system code.
* Compilation: make it an optional `want_red_zone` instead of a
`no_red_zone` bool. The default is decided by a call to
`target_util.hasRedZone`.
* When creating a Clang command line, put -mred-zone on the command
line if we are forcing it to be enabled.
* Update update_clang_options.zig with respect to the recent {s}/{} format changes.
* `zig cc` integration with red zone preference.
964 lines
40 KiB
Zig
964 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: {s}", .{@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: {s}", .{@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: {s}", .{@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: {s}", .{@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:{d}: 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:{d}: unable to parse glibc version '{s}': {s}", .{ 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:{d}: expected function name", .{line_i});
|
|
return error.ZigInstallationCorrupt;
|
|
};
|
|
const lib_name = line_it.next() orelse {
|
|
std.log.err("fns.txt:{d}: expected library name", .{line_i});
|
|
return error.ZigInstallationCorrupt;
|
|
};
|
|
const lib = findLib(lib_name) orelse {
|
|
std.log.err("fns.txt:{d}: unknown library name: {s}", .{ 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:{d}: expected arch name", .{line_i});
|
|
return error.ZigInstallationCorrupt;
|
|
};
|
|
const os_name = component_it.next() orelse {
|
|
std.log.err("abi.txt:{d}: expected OS name", .{line_i});
|
|
return error.ZigInstallationCorrupt;
|
|
};
|
|
const abi_name = component_it.next() orelse {
|
|
std.log.err("abi.txt:{d}: 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:{d}: unrecognized arch: '{s}'", .{ line_i, arch_name });
|
|
return error.ZigInstallationCorrupt;
|
|
};
|
|
if (!mem.eql(u8, os_name, "linux")) {
|
|
std.log.err("abi.txt:{d}: expected OS 'linux', found '{s}'", .{ line_i, os_name });
|
|
return error.ZigInstallationCorrupt;
|
|
}
|
|
const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse {
|
|
std.log.err("abi.txt:{d}: unrecognized ABI: '{s}'", .{ 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:{d}: 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:{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) });
|
|
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",
|
|
"-Wa,--noexecstack",
|
|
});
|
|
return comp.build_crt_file("crti", .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",
|
|
"-include",
|
|
try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"),
|
|
"-DTOP_NAMESPACE=glibc",
|
|
"-DASSEMBLER",
|
|
"-Wa,--noexecstack",
|
|
});
|
|
return comp.build_crt_file("crtn", .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",
|
|
"-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",
|
|
"-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 comp.build_crt_file("Scrt1", .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",
|
|
"-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",
|
|
"-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 comp.build_crt_file("c_nonshared", .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}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{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}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{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 });
|
|
}
|
|
|
|
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();
|
|
|
|
if (!build_options.have_llvm) {
|
|
return error.ZigCompilerNotBuiltWithLLVMExtensions;
|
|
}
|
|
|
|
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;
|
|
|
|
// Use the global cache directory.
|
|
var cache_parent: Cache = .{
|
|
.gpa = comp.gpa,
|
|
.manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}),
|
|
};
|
|
defer cache_parent.manifest_dir.close();
|
|
|
|
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.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 });
|
|
|
|
// Even if we get a hit, it doesn't guarantee that we finished the job last time.
|
|
// We use the presence of an "ok" file to determine if it is a true hit.
|
|
|
|
var o_directory: Compilation.Directory = .{
|
|
.handle = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}),
|
|
.path = try path.join(arena, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }),
|
|
};
|
|
defer o_directory.handle.close();
|
|
|
|
const ok_basename = "ok";
|
|
const actual_hit = if (hit) blk: {
|
|
o_directory.handle.access(ok_basename, .{}) catch |err| switch (err) {
|
|
error.FileNotFound => break :blk false,
|
|
else => |e| return e,
|
|
};
|
|
break :blk true;
|
|
} else false;
|
|
|
|
if (!actual_hit) {
|
|
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();
|
|
for (libs) |*lib| {
|
|
zig_body.shrinkRetainingCapacity(0);
|
|
|
|
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) {
|
|
// Example:
|
|
// .globl _Exit_2_2_5
|
|
// .type _Exit_2_2_5, %function;
|
|
// .symver _Exit_2_2_5, _Exit@@GLIBC_2.2.5
|
|
// .hidden _Exit_2_2_5
|
|
// _Exit_2_2_5:
|
|
const ver_index = ver_list.versions[ver_i];
|
|
const ver = metadata.all_versions[ver_index];
|
|
const sym_name = libc_fn.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) {
|
|
const sym_plus_ver = try std.fmt.allocPrint(
|
|
arena,
|
|
"{s}_{d}_{d}",
|
|
.{ sym_name, ver.major, ver.minor },
|
|
);
|
|
try zig_body.writer().print(
|
|
\\.globl {s}
|
|
\\.type {s}, %function;
|
|
\\.symver {s}, {s}{s}GLIBC_{d}.{d}
|
|
\\.hidden {s}
|
|
\\{s}:
|
|
\\
|
|
, .{
|
|
sym_plus_ver,
|
|
sym_plus_ver,
|
|
sym_plus_ver,
|
|
sym_name,
|
|
at_sign_str,
|
|
ver.major,
|
|
ver.minor,
|
|
sym_plus_ver,
|
|
sym_plus_ver,
|
|
});
|
|
} else {
|
|
const sym_plus_ver = try std.fmt.allocPrint(
|
|
arena,
|
|
"{s}_{d}_{d}_{d}",
|
|
.{ sym_name, ver.major, ver.minor, ver.patch },
|
|
);
|
|
try zig_body.writer().print(
|
|
\\.globl {s}
|
|
\\.type {s}, %function;
|
|
\\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d}
|
|
\\.hidden {s}
|
|
\\{s}:
|
|
\\
|
|
, .{
|
|
sym_plus_ver,
|
|
sym_plus_ver,
|
|
sym_plus_ver,
|
|
sym_name,
|
|
at_sign_str,
|
|
ver.major,
|
|
ver.minor,
|
|
ver.patch,
|
|
sym_plus_ver,
|
|
sym_plus_ver,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc.
|
|
const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable;
|
|
try o_directory.handle.writeFile(asm_file_basename, zig_body.items);
|
|
|
|
try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib);
|
|
}
|
|
// No need to write the manifest because there are no file inputs associated with this cache hash.
|
|
// However we do need to write the ok file now.
|
|
if (o_directory.handle.createFile(ok_basename, .{})) |file| {
|
|
file.close();
|
|
} else |err| {
|
|
std.log.warn("glibc shared objects: failed to mark completion: {s}", .{@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.global_cache_directory.path.?, o_sub_path }),
|
|
};
|
|
}
|
|
|
|
// zig fmt: on
|
|
|
|
fn buildSharedLib(
|
|
comp: *Compilation,
|
|
arena: *Allocator,
|
|
zig_cache_directory: Compilation.Directory,
|
|
bin_directory: Compilation.Directory,
|
|
asm_file_basename: []const u8,
|
|
lib: *const Lib,
|
|
) !void {
|
|
const tracy = trace(@src());
|
|
defer tracy.end();
|
|
|
|
const basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover });
|
|
const emit_bin = Compilation.EmitLoc{
|
|
.directory = bin_directory,
|
|
.basename = basename,
|
|
};
|
|
const version: std.builtin.Version = .{ .major = lib.sover, .minor = 0, .patch = 0 };
|
|
const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?);
|
|
const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename;
|
|
const map_file_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, all_map_basename });
|
|
const c_source_files = [1]Compilation.CSourceFile{
|
|
.{
|
|
.src_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, asm_file_basename }),
|
|
},
|
|
};
|
|
const sub_compilation = try Compilation.create(comp.gpa, .{
|
|
.local_cache_directory = zig_cache_directory,
|
|
.global_cache_directory = comp.global_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,
|
|
.thread_pool = comp.thread_pool,
|
|
.libc_installation = comp.bin_file.options.libc_installation,
|
|
.emit_bin = emit_bin,
|
|
.optimize_mode = comp.compilerRtOptMode(),
|
|
.want_sanitize_c = false,
|
|
.want_stack_check = false,
|
|
.want_red_zone = comp.bin_file.options.red_zone,
|
|
.want_valgrind = false,
|
|
.want_tsan = false,
|
|
.emit_h = null,
|
|
.strip = comp.compilerRtStrip(),
|
|
.is_native_os = false,
|
|
.is_native_abi = false,
|
|
.self_exe_path = comp.self_exe_path,
|
|
.verbose_cc = comp.verbose_cc,
|
|
.verbose_link = comp.bin_file.options.verbose_link,
|
|
.verbose_tokenize = comp.verbose_tokenize,
|
|
.verbose_ast = comp.verbose_ast,
|
|
.verbose_ir = comp.verbose_ir,
|
|
.verbose_llvm_ir = comp.verbose_llvm_ir,
|
|
.verbose_cimport = comp.verbose_cimport,
|
|
.verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
|
|
.clang_passthrough_mode = comp.clang_passthrough_mode,
|
|
.version = version,
|
|
.version_script = map_file_path,
|
|
.soname = soname,
|
|
.c_source_files = &c_source_files,
|
|
.skip_linker_dependencies = true,
|
|
});
|
|
defer sub_compilation.destroy();
|
|
|
|
try sub_compilation.updateSubCompilation();
|
|
}
|