aboutsummaryrefslogtreecommitdiff
path: root/deps/aro/toolchains/Linux.zig
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2023-10-02 07:08:53 +0300
committerGitHub <noreply@github.com>2023-10-02 07:08:53 +0300
commitfc4d53e2ea6b41440e37caf32d2fd236d0f58c93 (patch)
treebe400bc7033d3f198978ad04c05c14f15b8c5324 /deps/aro/toolchains/Linux.zig
parent0f1652dc603ad43be733cfdd721cedf38d9e45d9 (diff)
parent5792570197f44b2c7599fb756f5c1e9d59bd0a9a (diff)
downloadzig-fc4d53e2ea6b41440e37caf32d2fd236d0f58c93.tar.gz
zig-fc4d53e2ea6b41440e37caf32d2fd236d0f58c93.zip
Merge pull request #17221 from Vexu/aro-translate-c
Aro translate-c
Diffstat (limited to 'deps/aro/toolchains/Linux.zig')
-rw-r--r--deps/aro/toolchains/Linux.zig482
1 files changed, 482 insertions, 0 deletions
diff --git a/deps/aro/toolchains/Linux.zig b/deps/aro/toolchains/Linux.zig
new file mode 100644
index 0000000000..7ec73204a7
--- /dev/null
+++ b/deps/aro/toolchains/Linux.zig
@@ -0,0 +1,482 @@
+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",
+ };
+
+ 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);
+ }
+}