mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Part of #19063. Primarily, this moves Aro from deps/ to lib/compiler/ so that it can be lazily compiled from source. src/aro_translate_c.zig is moved to lib/compiler/aro_translate_c.zig and some of Zig CLI logic moved to a main() function there. aro_translate_c.zig becomes the "common" import for clang-based translate-c. Not all of the compiler was able to be detangled from Aro, however, so it still, for now, remains being compiled with the main compiler sources due to the clang-based translate-c depending on it. Once aro-based translate-c achieves feature parity with the clang-based translate-c implementation, the clang-based one can be removed from Zig. Aro made it unnecessarily difficult to depend on with these .def files and all these Zig module requirements. I looked at the .def files and made these observations: - The canonical source is llvm .def files. - Therefore there is an update process to sync with llvm that involves regenerating the .def files in Aro. - Therefore you might as well just regenerate the .zig files directly and check those into Aro. - Also with a small amount of tinkering, the file size on disk of these generated .zig files can be made many times smaller, without compromising type safety in the usage of the data. This would make things much easier on Zig as downstream project, particularly we could remove those pesky stubs when bootstrapping. I have gone ahead with these changes since they unblock me and I will have a chat with Vexu to see what he thinks.
483 lines
17 KiB
Zig
483 lines
17 KiB
Zig
const std = @import("std");
|
|
const mem = std.mem;
|
|
const Compilation = @import("../Compilation.zig");
|
|
const GCCDetector = @import("../Driver/GCCDetector.zig");
|
|
const Toolchain = @import("../Toolchain.zig");
|
|
const Driver = @import("../Driver.zig");
|
|
const Distro = @import("../Driver/Distro.zig");
|
|
const target_util = @import("../target.zig");
|
|
const system_defaults = @import("system_defaults");
|
|
|
|
const Linux = @This();
|
|
|
|
distro: Distro.Tag = .unknown,
|
|
extra_opts: std.ArrayListUnmanaged([]const u8) = .{},
|
|
gcc_detector: GCCDetector = .{},
|
|
|
|
pub fn discover(self: *Linux, tc: *Toolchain) !void {
|
|
self.distro = Distro.detect(tc.getTarget(), tc.filesystem);
|
|
try self.gcc_detector.discover(tc);
|
|
tc.selected_multilib = self.gcc_detector.selected;
|
|
|
|
try self.gcc_detector.appendToolPath(tc);
|
|
try self.buildExtraOpts(tc);
|
|
try self.findPaths(tc);
|
|
}
|
|
|
|
fn buildExtraOpts(self: *Linux, tc: *const Toolchain) !void {
|
|
const gpa = tc.driver.comp.gpa;
|
|
const target = tc.getTarget();
|
|
const is_android = target.isAndroid();
|
|
if (self.distro.isAlpine() or is_android) {
|
|
try self.extra_opts.ensureUnusedCapacity(gpa, 2);
|
|
self.extra_opts.appendAssumeCapacity("-z");
|
|
self.extra_opts.appendAssumeCapacity("now");
|
|
}
|
|
|
|
if (self.distro.isOpenSUSE() or self.distro.isUbuntu() or self.distro.isAlpine() or is_android) {
|
|
try self.extra_opts.ensureUnusedCapacity(gpa, 2);
|
|
self.extra_opts.appendAssumeCapacity("-z");
|
|
self.extra_opts.appendAssumeCapacity("relro");
|
|
}
|
|
|
|
if (target.cpu.arch.isARM() or target.cpu.arch.isAARCH64() or is_android) {
|
|
try self.extra_opts.ensureUnusedCapacity(gpa, 2);
|
|
self.extra_opts.appendAssumeCapacity("-z");
|
|
self.extra_opts.appendAssumeCapacity("max-page-size=4096");
|
|
}
|
|
|
|
if (target.cpu.arch == .arm or target.cpu.arch == .thumb) {
|
|
try self.extra_opts.append(gpa, "-X");
|
|
}
|
|
|
|
if (!target.cpu.arch.isMIPS() and target.cpu.arch != .hexagon) {
|
|
const hash_style = if (is_android) .both else self.distro.getHashStyle();
|
|
try self.extra_opts.append(gpa, switch (hash_style) {
|
|
inline else => |tag| "--hash-style=" ++ @tagName(tag),
|
|
});
|
|
}
|
|
|
|
if (system_defaults.enable_linker_build_id) {
|
|
try self.extra_opts.append(gpa, "--build-id");
|
|
}
|
|
}
|
|
|
|
fn addMultiLibPaths(self: *Linux, tc: *Toolchain, sysroot: []const u8, os_lib_dir: []const u8) !void {
|
|
if (!self.gcc_detector.is_valid) return;
|
|
const gcc_triple = self.gcc_detector.gcc_triple;
|
|
const lib_path = self.gcc_detector.parent_lib_path;
|
|
|
|
// Add lib/gcc/$triple/$version, with an optional /multilib suffix.
|
|
try tc.addPathIfExists(&.{ self.gcc_detector.install_path, tc.selected_multilib.gcc_suffix }, .file);
|
|
|
|
// Add lib/gcc/$triple/$libdir
|
|
// For GCC built with --enable-version-specific-runtime-libs.
|
|
try tc.addPathIfExists(&.{ self.gcc_detector.install_path, "..", os_lib_dir }, .file);
|
|
|
|
try tc.addPathIfExists(&.{ lib_path, "..", gcc_triple, "lib", "..", os_lib_dir, tc.selected_multilib.os_suffix }, .file);
|
|
|
|
// If the GCC installation we found is inside of the sysroot, we want to
|
|
// prefer libraries installed in the parent prefix of the GCC installation.
|
|
// It is important to *not* use these paths when the GCC installation is
|
|
// outside of the system root as that can pick up unintended libraries.
|
|
// This usually happens when there is an external cross compiler on the
|
|
// host system, and a more minimal sysroot available that is the target of
|
|
// the cross. Note that GCC does include some of these directories in some
|
|
// configurations but this seems somewhere between questionable and simply
|
|
// a bug.
|
|
if (mem.startsWith(u8, lib_path, sysroot)) {
|
|
try tc.addPathIfExists(&.{ lib_path, "..", os_lib_dir }, .file);
|
|
}
|
|
}
|
|
|
|
fn addMultiArchPaths(self: *Linux, tc: *Toolchain) !void {
|
|
if (!self.gcc_detector.is_valid) return;
|
|
const lib_path = self.gcc_detector.parent_lib_path;
|
|
const gcc_triple = self.gcc_detector.gcc_triple;
|
|
const multilib = self.gcc_detector.selected;
|
|
try tc.addPathIfExists(&.{ lib_path, "..", gcc_triple, "lib", multilib.os_suffix }, .file);
|
|
}
|
|
|
|
/// TODO: Very incomplete
|
|
fn findPaths(self: *Linux, tc: *Toolchain) !void {
|
|
const target = tc.getTarget();
|
|
const sysroot = tc.getSysroot();
|
|
|
|
var output: [64]u8 = undefined;
|
|
|
|
const os_lib_dir = getOSLibDir(target);
|
|
const multiarch_triple = getMultiarchTriple(target) orelse target_util.toLLVMTriple(target, &output);
|
|
|
|
try self.addMultiLibPaths(tc, sysroot, os_lib_dir);
|
|
|
|
try tc.addPathIfExists(&.{ sysroot, "/lib", multiarch_triple }, .file);
|
|
try tc.addPathIfExists(&.{ sysroot, "/lib", "..", os_lib_dir }, .file);
|
|
|
|
if (target.isAndroid()) {
|
|
// TODO
|
|
}
|
|
try tc.addPathIfExists(&.{ sysroot, "/usr", "lib", multiarch_triple }, .file);
|
|
try tc.addPathIfExists(&.{ sysroot, "/usr", "lib", "..", os_lib_dir }, .file);
|
|
|
|
try self.addMultiArchPaths(tc);
|
|
|
|
try tc.addPathIfExists(&.{ sysroot, "/lib" }, .file);
|
|
try tc.addPathIfExists(&.{ sysroot, "/usr", "lib" }, .file);
|
|
}
|
|
|
|
pub fn deinit(self: *Linux, allocator: std.mem.Allocator) void {
|
|
self.extra_opts.deinit(allocator);
|
|
}
|
|
|
|
fn isPIEDefault(self: *const Linux) bool {
|
|
_ = self;
|
|
return false;
|
|
}
|
|
|
|
fn getPIE(self: *const Linux, d: *const Driver) bool {
|
|
if (d.shared or d.static or d.relocatable or d.static_pie) {
|
|
return false;
|
|
}
|
|
return d.pie orelse self.isPIEDefault();
|
|
}
|
|
|
|
fn getStaticPIE(self: *const Linux, d: *Driver) !bool {
|
|
_ = self;
|
|
if (d.static_pie and d.pie != null) {
|
|
try d.err("cannot specify 'nopie' along with 'static-pie'");
|
|
}
|
|
return d.static_pie;
|
|
}
|
|
|
|
fn getStatic(self: *const Linux, d: *const Driver) bool {
|
|
_ = self;
|
|
return d.static and !d.static_pie;
|
|
}
|
|
|
|
pub fn getDefaultLinker(self: *const Linux, target: std.Target) []const u8 {
|
|
_ = self;
|
|
if (target.isAndroid()) {
|
|
return "ld.lld";
|
|
}
|
|
return "ld";
|
|
}
|
|
|
|
pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.ArrayList([]const u8)) Compilation.Error!void {
|
|
const d = tc.driver;
|
|
const target = tc.getTarget();
|
|
|
|
const is_pie = self.getPIE(d);
|
|
const is_static_pie = try self.getStaticPIE(d);
|
|
const is_static = self.getStatic(d);
|
|
const is_android = target.isAndroid();
|
|
const is_iamcu = target.os.tag == .elfiamcu;
|
|
const is_ve = target.cpu.arch == .ve;
|
|
const has_crt_begin_end_files = target.abi != .none; // TODO: clang checks for MIPS vendor
|
|
|
|
if (is_pie) {
|
|
try argv.append("-pie");
|
|
}
|
|
if (is_static_pie) {
|
|
try argv.appendSlice(&.{ "-static", "-pie", "--no-dynamic-linker", "-z", "text" });
|
|
}
|
|
|
|
if (d.rdynamic) {
|
|
try argv.append("-export-dynamic");
|
|
}
|
|
|
|
if (d.strip) {
|
|
try argv.append("-s");
|
|
}
|
|
|
|
try argv.appendSlice(self.extra_opts.items);
|
|
try argv.append("--eh-frame-hdr");
|
|
|
|
// Todo: Driver should parse `-EL`/`-EB` for arm to set endianness for arm targets
|
|
if (target_util.ldEmulationOption(d.comp.target, null)) |emulation| {
|
|
try argv.appendSlice(&.{ "-m", emulation });
|
|
} else {
|
|
try d.err("Unknown target triple");
|
|
return;
|
|
}
|
|
if (d.comp.target.cpu.arch.isRISCV()) {
|
|
try argv.append("-X");
|
|
}
|
|
if (d.shared) {
|
|
try argv.append("-shared");
|
|
}
|
|
if (is_static) {
|
|
try argv.append("-static");
|
|
} else {
|
|
if (d.rdynamic) {
|
|
try argv.append("-export-dynamic");
|
|
}
|
|
if (!d.shared and !is_static_pie and !d.relocatable) {
|
|
const dynamic_linker = d.comp.target.standardDynamicLinkerPath();
|
|
// todo: check for --dyld-prefix
|
|
if (dynamic_linker.get()) |path| {
|
|
try argv.appendSlice(&.{ "-dynamic-linker", try tc.arena.dupe(u8, path) });
|
|
} else {
|
|
try d.err("Could not find dynamic linker path");
|
|
}
|
|
}
|
|
}
|
|
|
|
try argv.appendSlice(&.{ "-o", d.output_name orelse "a.out" });
|
|
|
|
if (!d.nostdlib and !d.nostartfiles and !d.relocatable) {
|
|
if (!is_android and !is_iamcu) {
|
|
if (!d.shared) {
|
|
const crt1 = if (is_pie)
|
|
"Scrt1.o"
|
|
else if (is_static_pie)
|
|
"rcrt1.o"
|
|
else
|
|
"crt1.o";
|
|
try argv.append(try tc.getFilePath(crt1));
|
|
}
|
|
try argv.append(try tc.getFilePath("crti.o"));
|
|
}
|
|
if (is_ve) {
|
|
try argv.appendSlice(&.{ "-z", "max-page-size=0x4000000" });
|
|
}
|
|
|
|
if (is_iamcu) {
|
|
try argv.append(try tc.getFilePath("crt0.o"));
|
|
} else if (has_crt_begin_end_files) {
|
|
var path: []const u8 = "";
|
|
if (tc.getRuntimeLibKind() == .compiler_rt and !is_android) {
|
|
const crt_begin = try tc.getCompilerRt("crtbegin", .object);
|
|
if (tc.filesystem.exists(crt_begin)) {
|
|
path = crt_begin;
|
|
}
|
|
}
|
|
if (path.len == 0) {
|
|
const crt_begin = if (tc.driver.shared)
|
|
if (is_android) "crtbegin_so.o" else "crtbeginS.o"
|
|
else if (is_static)
|
|
if (is_android) "crtbegin_static.o" else "crtbeginT.o"
|
|
else if (is_pie or is_static_pie)
|
|
if (is_android) "crtbegin_dynamic.o" else "crtbeginS.o"
|
|
else if (is_android) "crtbegin_dynamic.o" else "crtbegin.o";
|
|
path = try tc.getFilePath(crt_begin);
|
|
}
|
|
try argv.append(path);
|
|
}
|
|
}
|
|
|
|
// TODO add -L opts
|
|
// TODO add -u opts
|
|
|
|
try tc.addFilePathLibArgs(argv);
|
|
|
|
// TODO handle LTO
|
|
|
|
try argv.appendSlice(d.link_objects.items);
|
|
|
|
if (!d.nostdlib and !d.relocatable) {
|
|
if (!d.nodefaultlibs) {
|
|
if (is_static or is_static_pie) {
|
|
try argv.append("--start-group");
|
|
}
|
|
try tc.addRuntimeLibs(argv);
|
|
|
|
// TODO: add pthread if needed
|
|
if (!d.nolibc) {
|
|
try argv.append("-lc");
|
|
}
|
|
if (is_iamcu) {
|
|
try argv.append("-lgloss");
|
|
}
|
|
if (is_static or is_static_pie) {
|
|
try argv.append("--end-group");
|
|
} else {
|
|
try tc.addRuntimeLibs(argv);
|
|
}
|
|
if (is_iamcu) {
|
|
try argv.appendSlice(&.{ "--as-needed", "-lsoftfp", "--no-as-needed" });
|
|
}
|
|
}
|
|
if (!d.nostartfiles and !is_iamcu) {
|
|
if (has_crt_begin_end_files) {
|
|
var path: []const u8 = "";
|
|
if (tc.getRuntimeLibKind() == .compiler_rt and !is_android) {
|
|
const crt_end = try tc.getCompilerRt("crtend", .object);
|
|
if (tc.filesystem.exists(crt_end)) {
|
|
path = crt_end;
|
|
}
|
|
}
|
|
if (path.len == 0) {
|
|
const crt_end = if (d.shared)
|
|
if (is_android) "crtend_so.o" else "crtendS.o"
|
|
else if (is_pie or is_static_pie)
|
|
if (is_android) "crtend_android.o" else "crtendS.o"
|
|
else if (is_android) "crtend_android.o" else "crtend.o";
|
|
path = try tc.getFilePath(crt_end);
|
|
}
|
|
try argv.append(path);
|
|
}
|
|
if (!is_android) {
|
|
try argv.append(try tc.getFilePath("crtn.o"));
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO add -T args
|
|
}
|
|
|
|
fn getMultiarchTriple(target: std.Target) ?[]const u8 {
|
|
const is_android = target.isAndroid();
|
|
const is_mips_r6 = std.Target.mips.featureSetHas(target.cpu.features, .mips32r6);
|
|
return switch (target.cpu.arch) {
|
|
.arm, .thumb => if (is_android) "arm-linux-androideabi" else if (target.abi == .gnueabihf) "arm-linux-gnueabihf" else "arm-linux-gnueabi",
|
|
.armeb, .thumbeb => if (target.abi == .gnueabihf) "armeb-linux-gnueabihf" else "armeb-linux-gnueabi",
|
|
.aarch64 => if (is_android) "aarch64-linux-android" else "aarch64-linux-gnu",
|
|
.aarch64_be => "aarch64_be-linux-gnu",
|
|
.x86 => if (is_android) "i686-linux-android" else "i386-linux-gnu",
|
|
.x86_64 => if (is_android) "x86_64-linux-android" else if (target.abi == .gnux32) "x86_64-linux-gnux32" else "x86_64-linux-gnu",
|
|
.m68k => "m68k-linux-gnu",
|
|
.mips => if (is_mips_r6) "mipsisa32r6-linux-gnu" else "mips-linux-gnu",
|
|
.mipsel => if (is_android) "mipsel-linux-android" else if (is_mips_r6) "mipsisa32r6el-linux-gnu" else "mipsel-linux-gnu",
|
|
.powerpcle => "powerpcle-linux-gnu",
|
|
.powerpc64 => "powerpc64-linux-gnu",
|
|
.powerpc64le => "powerpc64le-linux-gnu",
|
|
.riscv64 => "riscv64-linux-gnu",
|
|
.sparc => "sparc-linux-gnu",
|
|
.sparc64 => "sparc64-linux-gnu",
|
|
.s390x => "s390x-linux-gnu",
|
|
|
|
// TODO: expand this
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
fn getOSLibDir(target: std.Target) []const u8 {
|
|
switch (target.cpu.arch) {
|
|
.x86,
|
|
.powerpc,
|
|
.powerpcle,
|
|
.sparc,
|
|
.sparcel,
|
|
=> return "lib32",
|
|
else => {},
|
|
}
|
|
if (target.cpu.arch == .x86_64 and (target.abi == .gnux32 or target.abi == .muslx32)) {
|
|
return "libx32";
|
|
}
|
|
if (target.cpu.arch == .riscv32) {
|
|
return "lib32";
|
|
}
|
|
if (target.ptrBitWidth() == 32) {
|
|
return "lib";
|
|
}
|
|
return "lib64";
|
|
}
|
|
|
|
test Linux {
|
|
if (@import("builtin").os.tag == .windows) return error.SkipZigTest;
|
|
|
|
var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator);
|
|
defer arena_instance.deinit();
|
|
const arena = arena_instance.allocator();
|
|
|
|
var comp = Compilation.init(std.testing.allocator);
|
|
defer comp.deinit();
|
|
comp.environment = .{
|
|
.path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
};
|
|
defer comp.environment = .{};
|
|
|
|
const raw_triple = "x86_64-linux-gnu";
|
|
const cross = std.zig.CrossTarget.parse(.{ .arch_os_abi = raw_triple }) catch unreachable;
|
|
comp.target = cross.toTarget(); // TODO deprecated
|
|
comp.langopts.setEmulatedCompiler(.gcc);
|
|
|
|
var driver: Driver = .{ .comp = &comp };
|
|
defer driver.deinit();
|
|
driver.raw_target_triple = raw_triple;
|
|
|
|
const link_obj = try driver.comp.gpa.dupe(u8, "/tmp/foo.o");
|
|
try driver.link_objects.append(driver.comp.gpa, link_obj);
|
|
driver.temp_file_count += 1;
|
|
|
|
var toolchain: Toolchain = .{ .driver = &driver, .arena = arena, .filesystem = .{ .fake = &.{
|
|
.{ .path = "/tmp" },
|
|
.{ .path = "/usr" },
|
|
.{ .path = "/usr/lib64" },
|
|
.{ .path = "/usr/bin" },
|
|
.{ .path = "/usr/bin/ld", .executable = true },
|
|
.{ .path = "/lib" },
|
|
.{ .path = "/lib/x86_64-linux-gnu" },
|
|
.{ .path = "/lib/x86_64-linux-gnu/crt1.o" },
|
|
.{ .path = "/lib/x86_64-linux-gnu/crti.o" },
|
|
.{ .path = "/lib/x86_64-linux-gnu/crtn.o" },
|
|
.{ .path = "/lib64" },
|
|
.{ .path = "/usr/lib" },
|
|
.{ .path = "/usr/lib/gcc" },
|
|
.{ .path = "/usr/lib/gcc/x86_64-linux-gnu" },
|
|
.{ .path = "/usr/lib/gcc/x86_64-linux-gnu/9" },
|
|
.{ .path = "/usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o" },
|
|
.{ .path = "/usr/lib/gcc/x86_64-linux-gnu/9/crtend.o" },
|
|
.{ .path = "/usr/lib/x86_64-linux-gnu" },
|
|
.{ .path = "/etc/lsb-release", .contents =
|
|
\\DISTRIB_ID=Ubuntu
|
|
\\DISTRIB_RELEASE=20.04
|
|
\\DISTRIB_CODENAME=focal
|
|
\\DISTRIB_DESCRIPTION="Ubuntu 20.04.6 LTS"
|
|
\\
|
|
},
|
|
} } };
|
|
defer toolchain.deinit();
|
|
|
|
try toolchain.discover();
|
|
|
|
var argv = std.ArrayList([]const u8).init(driver.comp.gpa);
|
|
defer argv.deinit();
|
|
|
|
var linker_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
|
|
const linker_path = try toolchain.getLinkerPath(&linker_path_buf);
|
|
try argv.append(linker_path);
|
|
|
|
try toolchain.buildLinkerArgs(&argv);
|
|
|
|
const expected = [_][]const u8{
|
|
"/usr/bin/ld",
|
|
"-z",
|
|
"relro",
|
|
"--hash-style=gnu",
|
|
"--eh-frame-hdr",
|
|
"-m",
|
|
"elf_x86_64",
|
|
"-dynamic-linker",
|
|
"/lib64/ld-linux-x86-64.so.2",
|
|
"-o",
|
|
"a.out",
|
|
"/lib/x86_64-linux-gnu/crt1.o",
|
|
"/lib/x86_64-linux-gnu/crti.o",
|
|
"/usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o",
|
|
"-L/usr/lib/gcc/x86_64-linux-gnu/9",
|
|
"-L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib64",
|
|
"-L/lib/x86_64-linux-gnu",
|
|
"-L/lib/../lib64",
|
|
"-L/usr/lib/x86_64-linux-gnu",
|
|
"-L/usr/lib/../lib64",
|
|
"-L/lib",
|
|
"-L/usr/lib",
|
|
link_obj,
|
|
"-lgcc",
|
|
"--as-needed",
|
|
"-lgcc_s",
|
|
"--no-as-needed",
|
|
"-lc",
|
|
"-lgcc",
|
|
"--as-needed",
|
|
"-lgcc_s",
|
|
"--no-as-needed",
|
|
"/usr/lib/gcc/x86_64-linux-gnu/9/crtend.o",
|
|
"/lib/x86_64-linux-gnu/crtn.o",
|
|
};
|
|
try std.testing.expectEqual(expected.len, argv.items.len);
|
|
for (expected, argv.items) |expected_item, actual_item| {
|
|
try std.testing.expectEqualStrings(expected_item, actual_item);
|
|
}
|
|
}
|