From 766ed25e2b6126e35316718b4b198c37a02180c0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 14 Oct 2024 07:21:06 +0200 Subject: [PATCH 01/13] nix: add basic flake --- .envrc | 5 + flake.lock | 328 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 49 ++++++++ shell.nix | 12 ++ 4 files changed, 394 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 shell.nix diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..24e67d2e --- /dev/null +++ b/.envrc @@ -0,0 +1,5 @@ +# If we are a computer with nix-shell available, then use that to setup +# the build environment with exactly what we need. +if has nix; then + use nix +fi diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..a0465abd --- /dev/null +++ b/flake.lock @@ -0,0 +1,328 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_3": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_4": { + "inputs": { + "systems": "systems_4" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "zls", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1704290814, + "narHash": "sha256-LWvKHp7kGxk/GEtlrGYV68qIvPHkU9iToomNFGagixU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1708161998, + "narHash": "sha256-6KnemmUorCvlcAvGziFosAVkrlWZGIc6UNT9GUYr0jQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "84d981bae8b5e783b3b548de505b22880559515f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1726320982, + "narHash": "sha256-RuVXUwcYwaUeks6h3OLrEmg14z9aFXdWppTWPMTwdQw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "8f7492cce28977fbf8bd12c72af08b1f6c7c3e49", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "zig": "zig", + "zls": "zls" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_4": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "zig": { + "inputs": { + "flake-compat": "flake-compat_2", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1728865848, + "narHash": "sha256-QpNTFnPHsYH2JKEeCYIvbzLkqDR0h2AcksW9h4v1NEQ=", + "owner": "mitchellh", + "repo": "zig-overlay", + "rev": "a36eaabc36435ef64dc6b86d4e523234d091ca89", + "type": "github" + }, + "original": { + "owner": "mitchellh", + "repo": "zig-overlay", + "type": "github" + } + }, + "zig-overlay": { + "inputs": { + "flake-compat": "flake-compat_3", + "flake-utils": "flake-utils_4", + "nixpkgs": [ + "zls", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1726402199, + "narHash": "sha256-Q1NZ72dNZivH7KFkzW3wN1AHdAier+Xido+7XDzc++I=", + "owner": "mitchellh", + "repo": "zig-overlay", + "rev": "ca646ec2a1eebe15b2b6bd33811de6f12aa576c4", + "type": "github" + }, + "original": { + "owner": "mitchellh", + "repo": "zig-overlay", + "type": "github" + } + }, + "zls": { + "inputs": { + "flake-utils": "flake-utils_3", + "gitignore": "gitignore", + "nixpkgs": "nixpkgs_3", + "zig-overlay": "zig-overlay" + }, + "locked": { + "lastModified": 1728753797, + "narHash": "sha256-VRqZE/2pItRwHdAMNvVJC9zy8FpROMQ3whps4m8tea8=", + "owner": "zigtools", + "repo": "zls", + "rev": "66f0f90ec5468fbea1f7b65451eff5ca70d8f305", + "type": "github" + }, + "original": { + "owner": "zigtools", + "repo": "zls", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..bbf9ba8f --- /dev/null +++ b/flake.nix @@ -0,0 +1,49 @@ +{ + description = "An empty project that uses Zig."; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05"; + flake-utils.url = "github:numtide/flake-utils"; + zig.url = "github:mitchellh/zig-overlay"; + zls.url = "github:zigtools/zls"; + + # Used for shell.nix + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + ... + } @ inputs: let + overlays = [ + # Other overlays + (final: prev: { + zigpkgs = inputs.zig.packages.${prev.system}; + zlspkgs = inputs.zls.packages.${prev.system}; + }) + ]; + + # Our supported systems are the same supported systems as the Zig binaries + systems = builtins.attrNames inputs.zig.packages; + in + flake-utils.lib.eachSystem systems ( + system: let + pkgs = import nixpkgs {inherit overlays system;}; + in rec { + devShells.default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + zigpkgs.master-2024-10-08 + zlspkgs.default + ]; + }; + + # For compatibility with older versions of the `nix` binary + devShell = self.devShells.${system}.default; + } + ); +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..a15057aa --- /dev/null +++ b/shell.nix @@ -0,0 +1,12 @@ +(import + ( + let + flake-compat = (builtins.fromJSON (builtins.readFile ./flake.lock)).nodes.flake-compat; + in + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/${flake-compat.locked.rev}.tar.gz"; + sha256 = flake-compat.locked.narHash; + } + ) + {src = ./.;}) +.shellNix From 8d8ba038fdb84ed839863697cf4b8626b0279627 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 14 Oct 2024 11:07:20 +0200 Subject: [PATCH 02/13] macho: handle strip locals -x flag --- src/MachO.zig | 4 ++++ src/MachO/InternalObject.zig | 1 + src/MachO/Object.zig | 42 +++++++++++++++++++++++++++++------- src/MachO/Options.zig | 10 ++++++--- src/MachO/relocatable.zig | 29 ++++++++++++++++++++++++- 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/MachO.zig b/src/MachO.zig index 4143de04..05f7dd80 100644 --- a/src/MachO.zig +++ b/src/MachO.zig @@ -67,6 +67,10 @@ binds_to_weak: AtomicBool = AtomicBool.init(false), weak_defines: AtomicBool = AtomicBool.init(false), has_errors: AtomicBool = AtomicBool.init(false), +/// Atomic counter used only in relocatable (-r) mode when -x flag (strip locals) +/// was specified. See Object.stripLocalsRelocatable for usage. +strip_locals_counter: std.atomic.Value(u32) = std.atomic.Value(u32).init(0), + pub fn openPath(allocator: Allocator, options: Options, thread_pool: *ThreadPool) !*MachO { const file = try options.emit.directory.createFile(options.emit.sub_path, .{ .truncate = true, diff --git a/src/MachO/InternalObject.zig b/src/MachO/InternalObject.zig index 552480a4..74c87fc0 100644 --- a/src/MachO/InternalObject.zig +++ b/src/MachO/InternalObject.zig @@ -583,6 +583,7 @@ pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) void { const file = ref.getFile(macho_file) orelse continue; if (file.getIndex() != self.index) continue; if (sym.getName(macho_file).len == 0) continue; + if (macho_file.options.strip_locals and sym.isLocal()) continue; sym.flags.output_symtab = true; if (sym.isLocal()) { sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index 61ec3393..b7a2546d 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -1703,11 +1703,35 @@ fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname return n_sect; } -pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void { +/// If -x flag was passed to the linker, we were asked to strip local symbols. +/// However, in -r mode, we cannot really strip locals yet since this would potentially +/// completely eliminate ability to dead strip by the linker at a later stage. +/// Therefore, following Apple ld, we rename every local symbol to a unique +/// temp name lxxx where xxx is a global integer id. +pub fn stripLocalsRelocatable(self: *Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - const is_relocatable = macho_file.options.relocatable; + assert(macho_file.options.strip_locals); + const gpa = macho_file.base.allocator; + + for (self.symbols.items, 0..) |*sym, i| { + const ref = self.getSymbolRef(@intCast(i), macho_file); + const file = ref.getFile(macho_file) orelse continue; + if (file.getIndex() != self.index) continue; + if (sym.getAtom(macho_file)) |atom| if (!atom.alive.load(.seq_cst)) continue; + if (sym.isSymbolStab(macho_file)) continue; + if (!sym.isLocal()) continue; + // Pad to + const name = try std.fmt.allocPrintZ(gpa, "l{d:0>3}", .{macho_file.strip_locals_counter.fetchAdd(1, .seq_cst)}); + defer gpa.free(name); + sym.name = try self.addString(gpa, name); + } +} + +pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); for (self.symbols.items, 0..) |*sym, i| { const ref = self.getSymbolRef(@intCast(i), macho_file); @@ -1717,12 +1741,14 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void { if (sym.isSymbolStab(macho_file)) continue; const name = sym.getName(macho_file); if (name.len == 0) continue; - // TODO in -r mode, we actually want to merge symbol names and emit only one - // work it out when emitting relocs - if ((name[0] == 'L' or name[0] == 'l' or - mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_")) and - !is_relocatable) - continue; + if (!macho_file.options.relocatable) { + // If -x flag was passed and we are not emitting a relocatable object file, + // we simply skip every local symbol. + if (macho_file.options.strip_locals and sym.isLocal()) continue; + // TODO in -r mode, we actually want to merge symbol names and emit only one + // work it out when emitting relocs + if ((name[0] == 'L' or name[0] == 'l' or mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_"))) continue; + } sym.flags.output_symtab = true; if (sym.isLocal()) { sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file); diff --git a/src/MachO/Options.zig b/src/MachO/Options.zig index 6f558b15..6c77e161 100644 --- a/src/MachO/Options.zig +++ b/src/MachO/Options.zig @@ -65,7 +65,7 @@ const usage = \\-reexport-l[name] Link against library and re-export it for the clients \\ -reexport_library [name] \\-rpath [path] Specify runtime path - \\-S Do not put debug information (STABS or DWARF) in the output file + \\-S Do not put debug information (STABS or DWARF) in the symbol table \\-search_paths_first Search each dir in library search paths for `libx.dylib` then `libx.a` \\-search_dylibs_first Search `libx.dylib` in each dir in library search paths, then `libx.a` \\-stack_size [value] Size of the default stack in hexadecimal notation @@ -74,11 +74,12 @@ const usage = \\-u [name] Specifies symbol which has to be resolved at link time for the link to succeed \\-undefined [value] Specify how undefined symbols are to be treated: \\ error (default), warning, suppress, or dynamic_lookup. + \\-v Print version + \\--verbose Print full linker invocation to stderr \\-weak_framework [name] Link against framework and mark it and all referenced symbols as weak \\-weak-l[name] Link against library and mark it and all referenced symbols as weak \\ -weak_library [name] - \\-v Print version - \\--verbose Print full linker invocation to stderr + \\-x Do not put non-global symbols in the symbol table \\ \\ld64.zld: supported targets: macho-x86-64, macho-arm64 \\ld64.zld: supported emulations: macho_x86_64, macho_arm64 @@ -105,6 +106,7 @@ rpath_list: []const []const u8, syslibroot: ?[]const u8 = null, stack_size: ?u64 = null, strip: bool = false, +strip_locals: bool = false, entry: ?[]const u8 = null, force_undefined_symbols: []const []const u8 = &[0][]const u8{}, current_version: ?Version = null, @@ -240,6 +242,8 @@ pub fn parse(arena: Allocator, args: []const []const u8, ctx: anytype) !Options try force_undefined_symbols.put(name, {}); } else if (p.flag1("S")) { opts.strip = true; + } else if (p.flag1("x")) { + opts.strip_locals = true; } else if (p.flag1("r")) { opts.relocatable = true; } else if (p.flag1("all_load")) { diff --git a/src/MachO/relocatable.zig b/src/MachO/relocatable.zig index 442500b8..8436fa16 100644 --- a/src/MachO/relocatable.zig +++ b/src/MachO/relocatable.zig @@ -129,8 +129,12 @@ fn calcSectionSizes(macho_file: *MachO) !void { }); } } + for (macho_file.objects.items) |index| { - macho_file.base.thread_pool.spawnWg(&wg, File.calcSymtabSize, .{ macho_file.getFile(index).?, macho_file }); + macho_file.base.thread_pool.spawnWg(&wg, stripLocalsWorker, .{ + macho_file.getFile(index).?.object, + macho_file, + }); } macho_file.base.thread_pool.spawnWg(&wg, MachO.updateLinkeditSizeWorker, .{ macho_file, .data_in_code }); @@ -221,10 +225,33 @@ fn calcEhFrameSizeWorker(macho_file: *MachO) void { }; } +fn stripLocalsWorker(object: *Object, macho_file: *MachO) void { + const tracy = trace(@src()); + defer tracy.end(); + object.stripLocalsRelocatable(macho_file) catch |err| { + macho_file.base.fatal("failed to strip local symbols in object {}: {s}", .{ + object.fmtPath(), + @errorName(err), + }); + _ = macho_file.has_errors.swap(true, .seq_cst); + }; +} + fn calcSymtabSize(macho_file: *MachO) void { const tracy = trace(@src()); defer tracy.end(); + var wg: WaitGroup = .{}; + + { + wg.reset(); + defer wg.wait(); + + for (macho_file.objects.items) |index| { + macho_file.base.thread_pool.spawnWg(&wg, File.calcSymtabSize, .{ macho_file.getFile(index).?, macho_file }); + } + } + var nlocals: u32 = 0; var nstabs: u32 = 0; var nexports: u32 = 0; From 7f4eb2a5e866ee42edd1f7a3bbb335c444f5405d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 14 Oct 2024 11:24:03 +0200 Subject: [PATCH 03/13] macho: handle and ignore optimisation levels -Ox --- src/MachO/Options.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/MachO/Options.zig b/src/MachO/Options.zig index 6c77e161..99b6005e 100644 --- a/src/MachO/Options.zig +++ b/src/MachO/Options.zig @@ -57,6 +57,8 @@ const usage = \\-o [path] Specify output path for the final artifact \\-ObjC Force load all members of static archives that implement an \\ Objective-C class or category + \\-Ox Set optimisation level from values: [0, 1, 2]. + \\ This option is currently unused. \\-pagezero_size [value] Size of the __PAGEZERO segment in hexademical notation \\-platform_version [platform] [min_version] [sdk_version] \\ Sets the platform, oldest supported version of that platform and @@ -155,6 +157,14 @@ pub fn parse(arena: Allocator, args: []const []const u8, ctx: anytype) !Options ctx.fatal(usage ++ "\n", .{cmd}); } else if (p.arg2("debug-log")) |scope| { try ctx.log_scopes.append(scope); + } else if (p.arg1("O")) |level_string| { + // Purposely ignored but still validate for acceptable values. + const level = std.fmt.parseUnsigned(u8, level_string, 0) catch + ctx.fatal("Could not parse value '{s}' into integer\n", .{level_string}); + switch (level) { + 0...2 => {}, + else => ctx.fatal("Invalid optimisation level value '{d}', allowed [0, 1, 2]", .{level}), + } } else if (p.arg2("entitlements")) |path| { opts.entitlements = path; } else if (p.flag1("v")) { From 2da0ef790f08cf64e7789008fc485b3b4e5c2d3c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 14 Oct 2024 13:11:46 +0200 Subject: [PATCH 04/13] macho: handle and ignore -mllvm options Do not strip locals in relocatable mode unless requested to do so. --- src/MachO/Options.zig | 31 ++++++++++++++++++++++--------- src/MachO/relocatable.zig | 4 ++-- src/Zld.zig | 7 +++++++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/MachO/Options.zig b/src/MachO/Options.zig index 99b6005e..5da80f04 100644 --- a/src/MachO/Options.zig +++ b/src/MachO/Options.zig @@ -149,6 +149,7 @@ pub fn parse(arena: Allocator, args: []const []const u8, ctx: anytype) !Options .framework_dirs = undefined, .rpath_list = undefined, }; + var unknown_options = std.ArrayList(u8).init(arena); var it = Zld.Options.ArgsIterator{ .args = args }; var p = Zld.ArgParser(@TypeOf(ctx)){ .it = &it, .ctx = ctx }; @@ -157,14 +158,6 @@ pub fn parse(arena: Allocator, args: []const []const u8, ctx: anytype) !Options ctx.fatal(usage ++ "\n", .{cmd}); } else if (p.arg2("debug-log")) |scope| { try ctx.log_scopes.append(scope); - } else if (p.arg1("O")) |level_string| { - // Purposely ignored but still validate for acceptable values. - const level = std.fmt.parseUnsigned(u8, level_string, 0) catch - ctx.fatal("Could not parse value '{s}' into integer\n", .{level_string}); - switch (level) { - 0...2 => {}, - else => ctx.fatal("Invalid optimisation level value '{d}', allowed [0, 1, 2]", .{level}), - } } else if (p.arg2("entitlements")) |path| { opts.entitlements = path; } else if (p.flag1("v")) { @@ -329,10 +322,24 @@ pub fn parse(arena: Allocator, args: []const []const u8, ctx: anytype) !Options opts.namespace = .flat; } else if (p.flag1("ObjC")) { opts.force_load_objc = true; + } else if (p.arg1("O")) |level_string| { + // Purposely ignored but still validate for acceptable values. + const level = std.fmt.parseUnsigned(u8, level_string, 0) catch + ctx.fatal("Could not parse value '{s}' into integer\n", .{level_string}); + switch (level) { + 0...2 => {}, + else => ctx.fatal("Invalid optimisation level value '{d}', allowed [0, 1, 2]", .{level}), + } } else if (p.flag1("adhoc_codesign")) { opts.adhoc_codesign = true; } else if (p.flag1("no_adhoc_codesign")) { opts.adhoc_codesign = false; + } else if (p.argLLVM()) |opt| { + // Skip any LTO flags since we cannot handle it at the moment anyhow. + std.log.debug("TODO unimplemented -mllvm {s} option", .{opt}); + } else if (mem.startsWith(u8, p.arg, "-")) { + try unknown_options.appendSlice(p.arg); + try unknown_options.append('\n'); } else { try positionals.append(.{ .path = p.arg, .tag = .obj }); } @@ -354,7 +361,13 @@ pub fn parse(arena: Allocator, args: []const []const u8, ctx: anytype) !Options ctx.print("{s}\n", .{version}); } - if (positionals.items.len == 0) ctx.fatal("Expected at least one positional argument\n", .{}); + if (unknown_options.items.len > 0) { + ctx.fatal("Unknown options:\n{s}", .{unknown_options.items}); + } + + if (positionals.items.len == 0) { + ctx.fatal("Expected at least one positional argument\n", .{}); + } if (opts.namespace == .two_level) switch (opts.undefined_treatment) { .warn, .suppress => |x| ctx.fatal("illegal flags: '-undefined {s}' with '-two_levelnamespace'\n", .{ diff --git a/src/MachO/relocatable.zig b/src/MachO/relocatable.zig index 8436fa16..f2c8e21d 100644 --- a/src/MachO/relocatable.zig +++ b/src/MachO/relocatable.zig @@ -130,12 +130,12 @@ fn calcSectionSizes(macho_file: *MachO) !void { } } - for (macho_file.objects.items) |index| { + if (macho_file.options.strip_locals) for (macho_file.objects.items) |index| { macho_file.base.thread_pool.spawnWg(&wg, stripLocalsWorker, .{ macho_file.getFile(index).?.object, macho_file, }); - } + }; macho_file.base.thread_pool.spawnWg(&wg, MachO.updateLinkeditSizeWorker, .{ macho_file, .data_in_code }); } diff --git a/src/Zld.zig b/src/Zld.zig index 6b895e5a..a4e3b942 100644 --- a/src/Zld.zig +++ b/src/Zld.zig @@ -150,6 +150,13 @@ pub fn ArgParser(comptime Ctx: type) type { return null; } + pub fn argLLVM(p: *Self) ?[]const u8 { + if (p.flag1("mllvm")) { + return p.it.nextOrFatal(p.ctx); + } + return null; + } + fn argPrefix(p: *Self, comptime pat: []const u8, comptime prefix: []const u8) ?[]const u8 { if (mem.startsWith(u8, p.arg, prefix)) { const actual_arg = p.arg[prefix.len..]; From aced53ee1f2ce4d9ba2e3203e769d9191227df9b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 14 Oct 2024 13:37:38 +0200 Subject: [PATCH 05/13] macho: handle DWARFv5 Changes in the header, and additional forms. --- src/MachO/Object.zig | 266 +++++++++++++++++++-------------- src/MachO/dwarf.zig | 341 +++++++++++++++++++++++++++++++------------ src/main.zig | 2 +- 3 files changed, 399 insertions(+), 210 deletions(-) diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index b7a2546d..b6166b78 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -1354,146 +1354,188 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { const gpa = macho_file.base.allocator; - var debug_info_index: ?usize = null; - var debug_abbrev_index: ?usize = null; - var debug_str_index: ?usize = null; + var dw_info = Dwarf.Sections{}; + defer dw_info.deinit(gpa); - for (self.sections.items(.header), 0..) |sect, index| { + for (self.sections.items(.header), 0..) |sect, i| { if (sect.attrs() & macho.S_ATTR_DEBUG == 0) continue; - if (mem.eql(u8, sect.sectName(), "__debug_info")) debug_info_index = index; - if (mem.eql(u8, sect.sectName(), "__debug_abbrev")) debug_abbrev_index = index; - if (mem.eql(u8, sect.sectName(), "__debug_str")) debug_str_index = index; + const index: u32 = @intCast(i); + if (mem.eql(u8, sect.sectName(), "__debug_info")) { + try dw_info.sections.putNoClobber(gpa, .debug_info, .{ .index = index }); + } + if (mem.eql(u8, sect.sectName(), "__debug_abbrev")) { + try dw_info.sections.putNoClobber(gpa, .debug_abbrev, .{ .index = index }); + } + if (mem.eql(u8, sect.sectName(), "__debug_str")) { + try dw_info.sections.putNoClobber(gpa, .debug_str, .{ .index = index }); + } + if (mem.eql(u8, sect.sectName(), "__debug_str_offs")) { + try dw_info.sections.putNoClobber(gpa, .debug_str_offsets, .{ .index = index }); + } } - if (debug_info_index == null or debug_abbrev_index == null) return; + if (dw_info.sections.keys().len == 0) return; - const slice = self.sections.slice(); - const file = macho_file.getFileHandle(self.file_handle); - const debug_info = blk: { - const sect = slice.items(.header)[debug_info_index.?]; - const data = try gpa.alloc(u8, sect.size); - const amt = try file.preadAll(data, sect.offset + self.offset); - if (amt != data.len) return error.InputOutput; - break :blk data; - }; - defer gpa.free(debug_info); - const debug_abbrev = blk: { - const sect = slice.items(.header)[debug_abbrev_index.?]; - const data = try gpa.alloc(u8, sect.size); - const amt = try file.preadAll(data, sect.offset + self.offset); - if (amt != data.len) return error.InputOutput; - break :blk data; - }; - defer gpa.free(debug_abbrev); - const debug_str = if (debug_str_index) |sid| blk: { - const sect = slice.items(.header)[sid]; - const data = try gpa.alloc(u8, sect.size); - const amt = try file.preadAll(data, sect.offset + self.offset); - if (amt != data.len) return error.InputOutput; - break :blk data; - } else &[0]u8{}; - defer gpa.free(debug_str); + for (dw_info.sections.values()) |*sect| { + try sect.readData(gpa, macho_file.getFileHandle(self.file_handle), self.*); + } self.compile_unit = self.findCompileUnit(.{ .gpa = gpa, - .debug_info = debug_info, - .debug_abbrev = debug_abbrev, - .debug_str = debug_str, - }) catch null; // TODO figure out what errors are fatal, and when we silently fail + .dw_info = dw_info, + }, macho_file) catch |err| switch (err) { + error.ParseFailed => return error.ParseFailed, + else => |e| { + macho_file.base.fatal("{}: unexpected error when parsing DWARF info: {s}", .{ self.fmtPath(), @errorName(e) }); + return error.ParseFailed; + }, + }; } fn findCompileUnit(self: *Object, args: struct { gpa: Allocator, - debug_info: []const u8, - debug_abbrev: []const u8, - debug_str: []const u8, -}) !CompileUnit { - var cu_wip: struct { - comp_dir: ?[:0]const u8 = null, - tu_name: ?[:0]const u8 = null, - } = .{}; - + dw_info: Dwarf.Sections, +}, macho_file: *MachO) !CompileUnit { const gpa = args.gpa; - var info_reader = dwarf.InfoReader{ .bytes = args.debug_info, .strtab = args.debug_str }; - var abbrev_reader = dwarf.AbbrevReader{ .bytes = args.debug_abbrev }; + var info_reader = Dwarf.InfoReader{ .ctx = args.dw_info }; + var abbrev_reader = Dwarf.AbbrevReader{ .ctx = args.dw_info }; - const cuh = try info_reader.readCompileUnitHeader(); + const cuh = switch (try info_reader.readCompileUnitHeader()) { + .ok => |cuh| cuh, + .wrong_version => |ver| { + macho_file.base.fatal("{}: unhandled or unknown DWARF version detected: {d}", .{ self.fmtPath(), ver }); + return error.ParseFailed; + }, + }; try abbrev_reader.seekTo(cuh.debug_abbrev_offset); - const cu_decl = (try abbrev_reader.readDecl()) orelse return error.Eof; - if (cu_decl.tag != dwarf.TAG.compile_unit) return error.UnexpectedTag; - - try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader); + const cu_decl = (try abbrev_reader.readDecl()) orelse return error.UnexpectedEndOfFile; + if (cu_decl.tag != Dwarf.TAG.compile_unit) { + macho_file.base.fatal("{}: unexpected DW_TAG_xxx value detected as the first decl: expected 0x{x}, found 0x{x}", .{ + self.fmtPath(), + Dwarf.TAG.compile_unit, + cu_decl.tag, + }); + return error.ParseFailed; + } - while (try abbrev_reader.readAttr()) |attr| switch (attr.at) { - dwarf.AT.name => { - cu_wip.tu_name = try info_reader.readString(attr.form, cuh); - }, - dwarf.AT.comp_dir => { - cu_wip.comp_dir = try info_reader.readString(attr.form, cuh); + switch (try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader)) { + .ok => {}, + .unknown_form => |form| { + macho_file.base.fatal("{}: unexpected DW_FORM_xxx value detected: 0x{x}", .{ self.fmtPath(), form }); + return error.ParseFailed; }, - else => switch (attr.form) { - dwarf.FORM.sec_offset, - dwarf.FORM.ref_addr, - => { - _ = try info_reader.readOffset(cuh.format); - }, + } - dwarf.FORM.addr => { - _ = try info_reader.readNBytes(cuh.address_size); - }, + const Pos = struct { + pos: usize, + form: Dwarf.Form, + }; - dwarf.FORM.block1, - dwarf.FORM.block2, - dwarf.FORM.block4, - dwarf.FORM.block, - => { - _ = try info_reader.readBlock(attr.form); - }, + var saved: struct { + tu_name: ?Pos, + comp_dir: ?Pos, + str_offsets_base: ?Pos, + } = .{ + .tu_name = null, + .comp_dir = null, + .str_offsets_base = null, + }; - dwarf.FORM.exprloc => { - _ = try info_reader.readExprLoc(); + while (try abbrev_reader.readAttr()) |attr| { + switch (attr.at) { + Dwarf.AT.name => saved.tu_name = .{ .pos = info_reader.pos, .form = attr.form }, + Dwarf.AT.comp_dir => saved.comp_dir = .{ .pos = info_reader.pos, .form = attr.form }, + Dwarf.AT.str_offsets_base => saved.str_offsets_base = .{ .pos = info_reader.pos, .form = attr.form }, + else => {}, + } + switch (try info_reader.skip(attr.form, cuh)) { + .ok => {}, + .unknown_form => |form| { + macho_file.base.fatal("{}: unexpected DW_FORM_xxx value detected: 0x{x}", .{ self.fmtPath(), form }); + return error.ParseFailed; }, + } + } - dwarf.FORM.flag_present => {}, - - dwarf.FORM.data1, - dwarf.FORM.ref1, - dwarf.FORM.flag, - dwarf.FORM.data2, - dwarf.FORM.ref2, - dwarf.FORM.data4, - dwarf.FORM.ref4, - dwarf.FORM.data8, - dwarf.FORM.ref8, - dwarf.FORM.ref_sig8, - dwarf.FORM.udata, - dwarf.FORM.ref_udata, - dwarf.FORM.sdata, - => { - _ = try info_reader.readConstant(attr.form); - }, + const readDwarfString = struct { + fn readDwarfString( + reader: *Dwarf.InfoReader, + header: Dwarf.CompileUnitHeader, + pos: Pos, + base: u64, + ) !union(enum) { + ok: [:0]const u8, + invalid_form: Dwarf.Form, + } { + try reader.seekTo(pos.pos); + switch (pos.form) { + Dwarf.FORM.strp, + Dwarf.FORM.string, + => return .{ .ok = try reader.readString(pos.form, header) }, + Dwarf.FORM.strx, + Dwarf.FORM.strx1, + Dwarf.FORM.strx2, + Dwarf.FORM.strx3, + Dwarf.FORM.strx4, + => return .{ .ok = try reader.readStringIndexed(pos.form, header, base) }, + else => return .{ .invalid_form = pos.form }, + } + } + }.readDwarfString; - dwarf.FORM.strp, - dwarf.FORM.string, - => { - _ = try info_reader.readString(attr.form, cuh); - }, + if (saved.comp_dir == null) { + macho_file.base.fatal("{}: missing DW_AT_comp_dir attribute", .{self.fmtPath()}); + return error.ParseFailed; + } + if (saved.tu_name == null) { + macho_file.base.fatal("{}: missing DW_AT_name attribute", .{self.fmtPath()}); + return error.ParseFailed; + } - else => { - // TODO actual errors? - log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form}); - return error.UnhandledForm; - }, + const str_offsets_base: ?u64 = if (saved.str_offsets_base) |str_offsets_base| str_offsets_base: { + if (cuh.version < 5) break :str_offsets_base null; + try info_reader.seekTo(str_offsets_base.pos); + break :str_offsets_base try info_reader.readOffset(cuh.format); + } else null; + + for (&[_]Pos{ saved.comp_dir.?, saved.tu_name.? }) |pos| { + if (cuh.version >= 5 and needsStrOffsetsBase(pos.form) and str_offsets_base == null) { + macho_file.base.fatal("{}: missing DW_AT_str_offsets_base attribute", .{self.fmtPath()}); + return error.ParseFailed; + } + } + + const comp_dir = switch (try readDwarfString(&info_reader, cuh, saved.comp_dir.?, str_offsets_base orelse 0)) { + .ok => |str| str, + .invalid_form => |form| { + macho_file.base.fatal("{}: invalid form when parsing DWARF string: 0x{x}", .{ self.fmtPath(), form }); + return error.ParseFailed; + }, + }; + const tu_name = switch (try readDwarfString(&info_reader, cuh, saved.tu_name.?, str_offsets_base orelse 0)) { + .ok => |str| str, + .invalid_form => |form| { + macho_file.base.fatal("{}: invalid form when parsing DWARF string: 0x{x}", .{ self.fmtPath(), form }); + return error.ParseFailed; }, }; - - if (cu_wip.comp_dir == null) return error.MissingCompDir; - if (cu_wip.tu_name == null) return error.MissingTuName; return .{ - .comp_dir = try self.addString(gpa, cu_wip.comp_dir.?), - .tu_name = try self.addString(gpa, cu_wip.tu_name.?), + .comp_dir = try self.addString(gpa, comp_dir), + .tu_name = try self.addString(gpa, tu_name), + }; +} + +fn needsStrOffsetsBase(form: Dwarf.Form) bool { + return switch (form) { + Dwarf.FORM.strx, + Dwarf.FORM.strx1, + Dwarf.FORM.strx2, + Dwarf.FORM.strx3, + Dwarf.FORM.strx4, + => true, + else => false, }; } @@ -3127,7 +3169,6 @@ const aarch64 = struct { }; const assert = std.debug.assert; -const dwarf = @import("dwarf.zig"); const eh_frame = @import("eh_frame.zig"); const log = std.log.scoped(.link); const macho = std.macho; @@ -3139,6 +3180,7 @@ const std = @import("std"); const Allocator = mem.Allocator; const Atom = @import("Atom.zig"); const Cie = eh_frame.Cie; +const Dwarf = @import("Dwarf.zig"); const Fde = eh_frame.Fde; const File = @import("file.zig").File; const LoadCommandIterator = macho.LoadCommandIterator; diff --git a/src/MachO/dwarf.zig b/src/MachO/dwarf.zig index 2b4977ae..0d121a02 100644 --- a/src/MachO/dwarf.zig +++ b/src/MachO/dwarf.zig @@ -1,25 +1,96 @@ +index: u32, +data: []u8 = &[0]u8{}, + +fn deinit(dwarf: *Dwarf, allocator: Allocator) void { + allocator.free(dwarf.data); +} + +pub fn readData(dwarf: *Dwarf, allocator: Allocator, file: File.Handle, object: Object) !void { + const header = object.sections.items(.header)[dwarf.index]; + const data = try allocator.alloc(u8, header.size); + const amt = try file.preadAll(data, header.offset + object.offset); + errdefer allocator.free(data); + if (amt != data.len) return error.InputOutput; + dwarf.data = data; +} + +/// Pulls an offset into __debug_str section from a __debug_str_offs section. +/// This is new in DWARFv5 and requires the producer to specify DW_FORM_strx* (`index` arg) +/// but also DW_AT_str_offsets_base with DW_FORM_sec_offset (`base` arg) in the opening header +/// of a "referencing entity" such as DW_TAG_compile_unit. +fn getOffset(dwarf: Dwarf, base: u64, index: u64, dw_fmt: DwarfFormat) u64 { + return switch (dw_fmt) { + .dwarf32 => @as(*align(1) const u32, @ptrCast(dwarf.data.ptr + base + index * @sizeOf(u32))).*, + .dwarf64 => @as(*align(1) const u64, @ptrCast(dwarf.data.ptr + base + index * @sizeOf(u64))).*, + }; +} + +const Index = enum { + debug_info, + debug_abbrev, + debug_str, + debug_str_offsets, +}; + +pub const Sections = struct { + sections: std.AutoArrayHashMapUnmanaged(Index, Dwarf) = .{}, + + pub fn deinit(self: *Sections, allocator: Allocator) void { + for (self.sections.values()) |*sect| { + sect.deinit(allocator); + } + } +}; + pub const InfoReader = struct { - bytes: []const u8, - strtab: []const u8, + ctx: Sections, pos: usize = 0, - pub fn readCompileUnitHeader(p: *InfoReader) !CompileUnitHeader { + fn bytes(p: InfoReader) []const u8 { + return p.ctx.sections.get(.debug_info).?.data; + } + + pub fn readCompileUnitHeader(p: *InfoReader) !union(enum) { + ok: CompileUnitHeader, + wrong_version: Version, + } { var length: u64 = try p.readInt(u32); const is_64bit = length == 0xffffffff; if (is_64bit) { length = try p.readInt(u64); } const dw_fmt: DwarfFormat = if (is_64bit) .dwarf64 else .dwarf32; - return .{ + const version = try p.readInt(u16); + const other: struct { + debug_abbrev_offset: u64, + address_size: u8, + unit_type: u8, + } = switch (version) { + 4 => .{ + .debug_abbrev_offset = try p.readOffset(dw_fmt), + .address_size = try p.readByte(), + .unit_type = 0, + }, + 5 => .{ + // According to the spec, version 5 introduced .unit_type field in the header, and + // it reordered .debug_abbrev_offset with .address_size fields. + .unit_type = try p.readByte(), + .address_size = try p.readByte(), + .debug_abbrev_offset = try p.readOffset(dw_fmt), + }, + else => return .{ .wrong_version = version }, + }; + return .{ .ok = .{ .format = dw_fmt, .length = length, - .version = try p.readInt(u16), - .debug_abbrev_offset = try p.readOffset(dw_fmt), - .address_size = try p.readByte(), - }; + .version = version, + .debug_abbrev_offset = other.debug_abbrev_offset, + .address_size = other.address_size, + .unit_type = other.unit_type, + } }; } - pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader) !void { + pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader) !SkipResult { const cuh_length = math.cast(usize, cuh.length) orelse return error.Overflow; const end_pos = p.pos + switch (cuh.format) { .dwarf32 => @as(usize, 4), @@ -27,72 +98,109 @@ pub const InfoReader = struct { } + cuh_length; while (p.pos < end_pos) { const di_code = try p.readULEB128(u64); - if (di_code == 0) return error.Eof; - if (di_code == code) return; + if (di_code == 0) return error.UnexpectedEndOfFile; + if (di_code == code) return .ok; + + while (try abbrev_reader.readAttr()) |attr| { + const res = try p.skip(attr.form, cuh); + switch (res) { + .ok => {}, + .unknown_form => return res, + } + } + } + return error.UnexpectedEndOfFile; + } - while (try abbrev_reader.readAttr()) |attr| switch (attr.at) { - dwarf.FORM.sec_offset, - dwarf.FORM.ref_addr, - => { - _ = try p.readOffset(cuh.format); - }, + const SkipResult = union(enum) { + ok, + unknown_form: Form, + }; - dwarf.FORM.addr => { - _ = try p.readNBytes(cuh.address_size); - }, + /// When skipping attributes, we don't really need to be able to handle them all + /// since we only ever care about the DW_TAG_compile_unit. + pub fn skip(p: *InfoReader, form: Form, cuh: CompileUnitHeader) !SkipResult { + switch (form) { + dw.FORM.sec_offset, + dw.FORM.ref_addr, + => { + _ = try p.readOffset(cuh.format); + }, - dwarf.FORM.block1, - dwarf.FORM.block2, - dwarf.FORM.block4, - dwarf.FORM.block, - => { - _ = try p.readBlock(attr.form); - }, + dw.FORM.addr => { + _ = try p.readNBytes(cuh.address_size); + }, - dwarf.FORM.exprloc => { - _ = try p.readExprLoc(); - }, + dw.FORM.block1, + dw.FORM.block2, + dw.FORM.block4, + dw.FORM.block, + => { + _ = try p.readBlock(form); + }, + + dw.FORM.exprloc => { + _ = try p.readExprLoc(); + }, + + dw.FORM.flag_present => {}, + + dw.FORM.data1, + dw.FORM.ref1, + dw.FORM.flag, + dw.FORM.data2, + dw.FORM.ref2, + dw.FORM.data4, + dw.FORM.ref4, + dw.FORM.data8, + dw.FORM.ref8, + dw.FORM.ref_sig8, + dw.FORM.udata, + dw.FORM.ref_udata, + dw.FORM.sdata, + => { + _ = try p.readConstant(form); + }, + + dw.FORM.strp, + dw.FORM.string, + => { + _ = try p.readString(form, cuh); + }, - dwarf.FORM.flag_present => {}, - - dwarf.FORM.data1, - dwarf.FORM.ref1, - dwarf.FORM.flag, - dwarf.FORM.data2, - dwarf.FORM.ref2, - dwarf.FORM.data4, - dwarf.FORM.ref4, - dwarf.FORM.data8, - dwarf.FORM.ref8, - dwarf.FORM.ref_sig8, - dwarf.FORM.udata, - dwarf.FORM.ref_udata, - dwarf.FORM.sdata, + else => if (cuh.version >= 5) switch (form) { + dw.FORM.strx, + dw.FORM.strx1, + dw.FORM.strx2, + dw.FORM.strx3, + dw.FORM.strx4, => { - _ = try p.readConstant(attr.form); + // We are just iterating over the __debug_info data, so we don't care about an actual + // string, therefore we set the `base = 0`. + _ = try p.readStringIndexed(form, cuh, 0); }, - dwarf.FORM.strp, - dwarf.FORM.string, + dw.FORM.addrx, + dw.FORM.addrx1, + dw.FORM.addrx2, + dw.FORM.addrx3, + dw.FORM.addrx4, => { - _ = try p.readString(attr.form, cuh); + _ = try p.readIndex(form); }, - else => { - // TODO better errors - log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form}); - return error.UnhandledDwFormValue; - }, - }; + else => return .{ .unknown_form = form }, + } else return .{ .unknown_form = form }, } + return .ok; } pub fn readBlock(p: *InfoReader, form: Form) ![]const u8 { const len: u64 = switch (form) { - dwarf.FORM.block1 => try p.readByte(), - dwarf.FORM.block2 => try p.readInt(u16), - dwarf.FORM.block4 => try p.readInt(u32), - dwarf.FORM.block => try p.readULEB128(u64), + dw.FORM.block1 => try p.readByte(), + dw.FORM.block2 => try p.readInt(u16), + dw.FORM.block4 => try p.readInt(u32), + dw.FORM.block => try p.readULEB128(u64), else => unreachable, }; return p.readNBytes(len); @@ -105,51 +213,80 @@ pub const InfoReader = struct { pub fn readConstant(p: *InfoReader, form: Form) !u64 { return switch (form) { - dwarf.FORM.data1, dwarf.FORM.ref1, dwarf.FORM.flag => try p.readByte(), - dwarf.FORM.data2, dwarf.FORM.ref2 => try p.readInt(u16), - dwarf.FORM.data4, dwarf.FORM.ref4 => try p.readInt(u32), - dwarf.FORM.data8, dwarf.FORM.ref8, dwarf.FORM.ref_sig8 => try p.readInt(u64), - dwarf.FORM.udata, dwarf.FORM.ref_udata => try p.readULEB128(u64), - dwarf.FORM.sdata => @bitCast(try p.readILEB128(i64)), + dw.FORM.data1, dw.FORM.ref1, dw.FORM.flag => try p.readByte(), + dw.FORM.data2, dw.FORM.ref2 => try p.readInt(u16), + dw.FORM.data4, dw.FORM.ref4 => try p.readInt(u32), + dw.FORM.data8, dw.FORM.ref8, dw.FORM.ref_sig8 => try p.readInt(u64), + dw.FORM.udata, dw.FORM.ref_udata => try p.readULEB128(u64), + dw.FORM.sdata => @bitCast(try p.readILEB128(i64)), else => return error.UnhandledConstantForm, }; } + pub fn readIndex(p: *InfoReader, form: Form) !u64 { + return switch (form) { + dw.FORM.strx1, dw.FORM.addrx1 => try p.readByte(), + dw.FORM.strx2, dw.FORM.addrx2 => try p.readInt(u16), + dw.FORM.strx3, dw.FORM.addrx3 => error.UnhandledDwForm, + dw.FORM.strx4, dw.FORM.addrx4 => try p.readInt(u32), + dw.FORM.strx, dw.FORM.addrx => try p.readULEB128(u64), + else => return error.UnhandledIndexForm, + }; + } + pub fn readString(p: *InfoReader, form: Form, cuh: CompileUnitHeader) ![:0]const u8 { switch (form) { - dwarf.FORM.strp => { + dw.FORM.strp => { + const strtab = p.ctx.sections.get(.debug_str).?.data; const off = try p.readOffset(cuh.format); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.strtab.ptr + off)), 0); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0); }, - dwarf.FORM.string => { + dw.FORM.string => { const start = p.pos; - while (p.pos < p.bytes.len) : (p.pos += 1) { - if (p.bytes[p.pos] == 0) break; + while (p.pos < p.bytes().len) : (p.pos += 1) { + if (p.bytes()[p.pos] == 0) break; } - if (p.bytes[p.pos] != 0) return error.Eof; - return p.bytes[start..p.pos :0]; + if (p.bytes()[p.pos] != 0) return error.UnexpectedEndOfFile; + return p.bytes()[start..p.pos :0]; + }, + else => unreachable, + } + } + + pub fn readStringIndexed(p: *InfoReader, form: Form, cuh: CompileUnitHeader, base: u64) ![:0]const u8 { + switch (form) { + dw.FORM.strx, + dw.FORM.strx1, + dw.FORM.strx2, + dw.FORM.strx3, + dw.FORM.strx4, + => { + const index = try p.readIndex(form); + const off = p.ctx.sections.get(.debug_str_offsets).?.getOffset(base, index, cuh.format); + const strtab = p.ctx.sections.get(.debug_str).?.data; + return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0); }, else => unreachable, } } pub fn readByte(p: *InfoReader) !u8 { - if (p.pos + 1 > p.bytes.len) return error.Eof; + if (p.pos + 1 > p.bytes().len) return error.UnexpectedEndOfFile; defer p.pos += 1; - return p.bytes[p.pos]; + return p.bytes()[p.pos]; } pub fn readNBytes(p: *InfoReader, num: u64) ![]const u8 { const num_usize = math.cast(usize, num) orelse return error.Overflow; - if (p.pos + num_usize > p.bytes.len) return error.Eof; + if (p.pos + num_usize > p.bytes().len) return error.UnexpectedEndOfFile; defer p.pos += num_usize; - return p.bytes[p.pos..][0..num_usize]; + return p.bytes()[p.pos..][0..num_usize]; } pub fn readInt(p: *InfoReader, comptime Int: type) !Int { - if (p.pos + @sizeOf(Int) > p.bytes.len) return error.Eof; + if (p.pos + @sizeOf(Int) > p.bytes().len) return error.UnexpectedEndOfFile; defer p.pos += @sizeOf(Int); - return mem.readInt(Int, p.bytes[p.pos..][0..@sizeOf(Int)], .little); + return mem.readInt(Int, p.bytes()[p.pos..][0..@sizeOf(Int)], .little); } pub fn readOffset(p: *InfoReader, dw_fmt: DwarfFormat) !u64 { @@ -160,7 +297,7 @@ pub const InfoReader = struct { } pub fn readULEB128(p: *InfoReader, comptime Type: type) !Type { - var stream = std.io.fixedBufferStream(p.bytes[p.pos..]); + var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]); var creader = std.io.countingReader(stream.reader()); const value: Type = try leb.readULEB128(Type, creader.reader()); p.pos += creader.bytes_read; @@ -168,7 +305,7 @@ pub const InfoReader = struct { } pub fn readILEB128(p: *InfoReader, comptime Type: type) !Type { - var stream = std.io.fixedBufferStream(p.bytes[p.pos..]); + var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]); var creader = std.io.countingReader(stream.reader()); const value: Type = try leb.readILEB128(Type, creader.reader()); p.pos += creader.bytes_read; @@ -181,11 +318,15 @@ pub const InfoReader = struct { }; pub const AbbrevReader = struct { - bytes: []const u8, + ctx: Sections, pos: usize = 0, + fn bytes(p: AbbrevReader) []const u8 { + return p.ctx.sections.get(.debug_abbrev).?.data; + } + pub fn hasMore(p: AbbrevReader) bool { - return p.pos < p.bytes.len; + return p.pos < p.bytes().len; } pub fn readDecl(p: *AbbrevReader) !?AbbrevDecl { @@ -217,13 +358,13 @@ pub const AbbrevReader = struct { } pub fn readByte(p: *AbbrevReader) !u8 { - if (p.pos + 1 > p.bytes.len) return error.Eof; + if (p.pos + 1 > p.bytes().len) return error.UnexpectedEndOfFile; defer p.pos += 1; - return p.bytes[p.pos]; + return p.bytes()[p.pos]; } pub fn readULEB128(p: *AbbrevReader, comptime Type: type) !Type { - var stream = std.io.fixedBufferStream(p.bytes[p.pos..]); + var stream = std.io.fixedBufferStream(p.bytes()[p.pos..]); var creader = std.io.countingReader(stream.reader()); const value: Type = try leb.readULEB128(Type, creader.reader()); p.pos += creader.bytes_read; @@ -250,12 +391,13 @@ const AbbrevAttr = struct { len: usize, }; -const CompileUnitHeader = struct { +pub const CompileUnitHeader = struct { format: DwarfFormat, length: u64, - version: u16, + version: Version, debug_abbrev_offset: u64, address_size: u8, + unit_type: u8, }; const Die = struct { @@ -268,18 +410,23 @@ const DwarfFormat = enum { dwarf64, }; -const dwarf = std.dwarf; +const dw = std.dwarf; const leb = std.leb; const log = std.log.scoped(.link); const math = std.math; const mem = std.mem; const std = @import("std"); - -const At = u64; -const Code = u64; -const Form = u64; -const Tag = u64; - -pub const AT = dwarf.AT; -pub const FORM = dwarf.FORM; -pub const TAG = dwarf.TAG; +const Allocator = mem.Allocator; +const Dwarf = @This(); +const File = @import("file.zig").File; +const Object = @import("Object.zig"); + +pub const At = u64; +pub const Code = u64; +pub const Form = u64; +pub const Tag = u64; +pub const Version = u16; + +pub const TAG = dw.TAG; +pub const FORM = dw.FORM; +pub const AT = dw.AT; diff --git a/src/main.zig b/src/main.zig index 16013e21..0f4a57e3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -43,7 +43,7 @@ fn logFn( const scope_name = @tagName(scope); for (log_scopes.items) |log_scope| { if (mem.eql(u8, log_scope, scope_name)) break; - } else return; + } else if (scope != .default) return; } // We only recognize 4 log levels in this application. From 6a766657fc493020b18fe33e8d28bca926e1b85e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 15 Oct 2024 13:01:28 +0200 Subject: [PATCH 06/13] flake: change from mkShell to mkDerivation --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index bbf9ba8f..002fa5b0 100644 --- a/flake.nix +++ b/flake.nix @@ -35,7 +35,8 @@ system: let pkgs = import nixpkgs {inherit overlays system;}; in rec { - devShells.default = pkgs.mkShell { + devShells.default = pkgs.stdenvNoCC.mkDerivation { + name = "emerald"; nativeBuildInputs = with pkgs; [ zigpkgs.master-2024-10-08 zlspkgs.default From 330cf28b853498cf83e942b5f3a6fe8c114ace65 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 15 Oct 2024 13:28:05 +0200 Subject: [PATCH 07/13] macho: create dummy section-end atom when object has no subsections too If MH_SUBSECTIONS_VIA_SYMBOLS flag is not set on the input relocatable object file, we should also handle the situation where the compiler may emit a symbol attached to the end of the section. --- src/MachO/Object.zig | 68 ++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index b6166b78..86129da9 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -366,18 +366,7 @@ fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void { } // Create a dummy atom marking the end of a section. - // Some compilers including Go may emit symbols that are zero-sized and lie at the section - // boundary. In order to properly attach such a symbol to an atom (in this case, a dummy - // zero-sized atom), we need to create it first. - const name = try std.fmt.allocPrintZ(allocator, "{s}${s}$stop", .{ sect.segName(), sect.sectName() }); - defer allocator.free(name); - const atom_index = try self.addAtom(allocator, .{ - .name = try self.addString(allocator, name), - .n_sect = @intCast(n_sect), - .off = sect.size, - .size = 0, - .alignment = sect.@"align", - }); + const atom_index = try self.addSectionStopAtom(allocator, @intCast(n_sect)); try self.atoms_indexes.append(allocator, atom_index); try subsections.append(allocator, .{ .atom = atom_index, @@ -399,18 +388,22 @@ fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void { if (isFixedSizeLiteral(sect)) continue; if (isPtrLiteral(sect)) continue; - const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() }); - defer allocator.free(name); + const subsections = &slice.items(.subsections)[n_sect]; - const atom_index = try self.addAtom(allocator, .{ - .name = try self.addString(allocator, name), - .n_sect = @intCast(n_sect), - .off = 0, - .size = sect.size, - .alignment = sect.@"align", - }); - try self.atoms_indexes.append(allocator, atom_index); - try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = 0 }); + { + const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() }); + defer allocator.free(name); + + const atom_index = try self.addAtom(allocator, .{ + .name = try self.addString(allocator, name), + .n_sect = @intCast(n_sect), + .off = 0, + .size = sect.size, + .alignment = sect.@"align", + }); + try self.atoms_indexes.append(allocator, atom_index); + try subsections.append(allocator, .{ .atom = atom_index, .off = 0 }); + } const nlist_start = for (nlists, 0..) |nlist, i| { if (nlist.nlist.n_sect - 1 == n_sect) break i; @@ -436,6 +429,14 @@ fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void { self.symtab.items(.size)[nlists[i].idx] = size; } } + + // Create a dummy atom marking the end of a section. + const atom_index = try self.addSectionStopAtom(allocator, @intCast(n_sect)); + try self.atoms_indexes.append(allocator, atom_index); + try subsections.append(allocator, .{ + .atom = atom_index, + .off = sect.size, + }); } } @@ -1786,7 +1787,9 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) void { if (!macho_file.options.relocatable) { // If -x flag was passed and we are not emitting a relocatable object file, // we simply skip every local symbol. - if (macho_file.options.strip_locals and sym.isLocal()) continue; + // TODO we currently skip any local that was assigned a GOT entry for the simple + // reason we don't yet relax GOT accesses for locals on aarch64. + if (macho_file.options.strip_locals and sym.isLocal() and !sym.getSectionFlags().got) continue; // TODO in -r mode, we actually want to merge symbol names and emit only one // work it out when emitting relocs if ((name[0] == 'L' or name[0] == 'l' or mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_"))) continue; @@ -2398,6 +2401,23 @@ fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index { return atom_index; } +/// Creates a dummy atom marking the end of a section. +/// Some compilers including Go may emit symbols that are zero-sized and lie at the section +/// boundary. In order to properly attach such a symbol to an atom (in this case, a dummy +/// zero-sized atom), we need to create it first. +fn addSectionStopAtom(self: *Object, allocator: Allocator, n_sect: u8) !Atom.Index { + const sect = self.sections.items(.header)[n_sect]; + const name = try std.fmt.allocPrintZ(allocator, "{s}${s}$stop", .{ sect.segName(), sect.sectName() }); + defer allocator.free(name); + return self.addAtom(allocator, .{ + .name = try self.addString(allocator, name), + .n_sect = @intCast(n_sect), + .off = sect.size, + .size = 0, + .alignment = sect.@"align", + }); +} + pub fn getAtom(self: *Object, atom_index: Atom.Index) ?*Atom { if (atom_index == 0) return null; assert(atom_index < self.atoms.items.len); From c6c9f1484f6e8e4ca65c9a944195f4ab2690da23 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 15 Oct 2024 13:45:02 +0200 Subject: [PATCH 08/13] macho: rename dwarf.zig into Dwarf.zig --- src/MachO/{dwarf.zig => Dwarf.zig} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/MachO/{dwarf.zig => Dwarf.zig} (100%) diff --git a/src/MachO/dwarf.zig b/src/MachO/Dwarf.zig similarity index 100% rename from src/MachO/dwarf.zig rename to src/MachO/Dwarf.zig From 5d018ce2f0339f6ce79488c8586239ed2d0d322c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 16 Oct 2024 16:18:19 +0200 Subject: [PATCH 09/13] macho: cleanup tracking of DWARF sections in Object --- src/MachO/Dwarf.zig | 63 ++++++++++++++------------------------------ src/MachO/Object.zig | 46 ++++++++++++++++---------------- 2 files changed, 43 insertions(+), 66 deletions(-) diff --git a/src/MachO/Dwarf.zig b/src/MachO/Dwarf.zig index 0d121a02..30100d8b 100644 --- a/src/MachO/Dwarf.zig +++ b/src/MachO/Dwarf.zig @@ -1,53 +1,32 @@ -index: u32, -data: []u8 = &[0]u8{}, - -fn deinit(dwarf: *Dwarf, allocator: Allocator) void { - allocator.free(dwarf.data); -} - -pub fn readData(dwarf: *Dwarf, allocator: Allocator, file: File.Handle, object: Object) !void { - const header = object.sections.items(.header)[dwarf.index]; - const data = try allocator.alloc(u8, header.size); - const amt = try file.preadAll(data, header.offset + object.offset); - errdefer allocator.free(data); - if (amt != data.len) return error.InputOutput; - dwarf.data = data; +debug_info: []u8 = &[0]u8{}, +debug_abbrev: []u8 = &[0]u8{}, +debug_str: []u8 = &[0]u8{}, +debug_str_offsets: []u8 = &[0]u8{}, + +pub fn deinit(dwarf: *Dwarf, allocator: Allocator) void { + allocator.free(dwarf.debug_info); + allocator.free(dwarf.debug_abbrev); + allocator.free(dwarf.debug_str); + allocator.free(dwarf.debug_str_offsets); } /// Pulls an offset into __debug_str section from a __debug_str_offs section. /// This is new in DWARFv5 and requires the producer to specify DW_FORM_strx* (`index` arg) /// but also DW_AT_str_offsets_base with DW_FORM_sec_offset (`base` arg) in the opening header /// of a "referencing entity" such as DW_TAG_compile_unit. -fn getOffset(dwarf: Dwarf, base: u64, index: u64, dw_fmt: DwarfFormat) u64 { +fn getOffset(debug_str_offsets: []const u8, base: u64, index: u64, dw_fmt: DwarfFormat) u64 { return switch (dw_fmt) { - .dwarf32 => @as(*align(1) const u32, @ptrCast(dwarf.data.ptr + base + index * @sizeOf(u32))).*, - .dwarf64 => @as(*align(1) const u64, @ptrCast(dwarf.data.ptr + base + index * @sizeOf(u64))).*, + .dwarf32 => @as(*align(1) const u32, @ptrCast(debug_str_offsets.ptr + base + index * @sizeOf(u32))).*, + .dwarf64 => @as(*align(1) const u64, @ptrCast(debug_str_offsets.ptr + base + index * @sizeOf(u64))).*, }; } -const Index = enum { - debug_info, - debug_abbrev, - debug_str, - debug_str_offsets, -}; - -pub const Sections = struct { - sections: std.AutoArrayHashMapUnmanaged(Index, Dwarf) = .{}, - - pub fn deinit(self: *Sections, allocator: Allocator) void { - for (self.sections.values()) |*sect| { - sect.deinit(allocator); - } - } -}; - pub const InfoReader = struct { - ctx: Sections, + ctx: Dwarf, pos: usize = 0, fn bytes(p: InfoReader) []const u8 { - return p.ctx.sections.get(.debug_info).?.data; + return p.ctx.debug_info; } pub fn readCompileUnitHeader(p: *InfoReader) !union(enum) { @@ -237,9 +216,8 @@ pub const InfoReader = struct { pub fn readString(p: *InfoReader, form: Form, cuh: CompileUnitHeader) ![:0]const u8 { switch (form) { dw.FORM.strp => { - const strtab = p.ctx.sections.get(.debug_str).?.data; const off = try p.readOffset(cuh.format); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.ctx.debug_str.ptr + off)), 0); }, dw.FORM.string => { const start = p.pos; @@ -262,9 +240,8 @@ pub const InfoReader = struct { dw.FORM.strx4, => { const index = try p.readIndex(form); - const off = p.ctx.sections.get(.debug_str_offsets).?.getOffset(base, index, cuh.format); - const strtab = p.ctx.sections.get(.debug_str).?.data; - return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0); + const off = getOffset(p.ctx.debug_str_offsets, base, index, cuh.format); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(p.ctx.debug_str.ptr + off)), 0); }, else => unreachable, } @@ -318,11 +295,11 @@ pub const InfoReader = struct { }; pub const AbbrevReader = struct { - ctx: Sections, + ctx: Dwarf, pos: usize = 0, fn bytes(p: AbbrevReader) []const u8 { - return p.ctx.sections.get(.debug_abbrev).?.data; + return p.ctx.debug_abbrev; } pub fn hasMore(p: AbbrevReader) bool { diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index 86129da9..01c4fd8c 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -1354,37 +1354,31 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { defer tracy.end(); const gpa = macho_file.base.allocator; + const file = macho_file.getFileHandle(self.file_handle); - var dw_info = Dwarf.Sections{}; - defer dw_info.deinit(gpa); + var dwarf: Dwarf = .{}; + defer dwarf.deinit(gpa); for (self.sections.items(.header), 0..) |sect, i| { if (sect.attrs() & macho.S_ATTR_DEBUG == 0) continue; - const index: u32 = @intCast(i); + const index: u8 = @intCast(i); if (mem.eql(u8, sect.sectName(), "__debug_info")) { - try dw_info.sections.putNoClobber(gpa, .debug_info, .{ .index = index }); + dwarf.debug_info = try self.readSectionData(gpa, file, index); } if (mem.eql(u8, sect.sectName(), "__debug_abbrev")) { - try dw_info.sections.putNoClobber(gpa, .debug_abbrev, .{ .index = index }); + dwarf.debug_abbrev = try self.readSectionData(gpa, file, index); } if (mem.eql(u8, sect.sectName(), "__debug_str")) { - try dw_info.sections.putNoClobber(gpa, .debug_str, .{ .index = index }); + dwarf.debug_str = try self.readSectionData(gpa, file, index); } if (mem.eql(u8, sect.sectName(), "__debug_str_offs")) { - try dw_info.sections.putNoClobber(gpa, .debug_str_offsets, .{ .index = index }); + dwarf.debug_str_offsets = try self.readSectionData(gpa, file, index); } } - if (dw_info.sections.keys().len == 0) return; - - for (dw_info.sections.values()) |*sect| { - try sect.readData(gpa, macho_file.getFileHandle(self.file_handle), self.*); - } + if (dwarf.debug_info.len == 0) return; - self.compile_unit = self.findCompileUnit(.{ - .gpa = gpa, - .dw_info = dw_info, - }, macho_file) catch |err| switch (err) { + self.compile_unit = self.findCompileUnit(gpa, dwarf, macho_file) catch |err| switch (err) { error.ParseFailed => return error.ParseFailed, else => |e| { macho_file.base.fatal("{}: unexpected error when parsing DWARF info: {s}", .{ self.fmtPath(), @errorName(e) }); @@ -1393,13 +1387,9 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { }; } -fn findCompileUnit(self: *Object, args: struct { - gpa: Allocator, - dw_info: Dwarf.Sections, -}, macho_file: *MachO) !CompileUnit { - const gpa = args.gpa; - var info_reader = Dwarf.InfoReader{ .ctx = args.dw_info }; - var abbrev_reader = Dwarf.AbbrevReader{ .ctx = args.dw_info }; +fn findCompileUnit(self: *Object, gpa: Allocator, dwarf: Dwarf, macho_file: *MachO) !CompileUnit { + var info_reader = Dwarf.InfoReader{ .ctx = dwarf }; + var abbrev_reader = Dwarf.AbbrevReader{ .ctx = dwarf }; const cuh = switch (try info_reader.readCompileUnitHeader()) { .ok => |cuh| cuh, @@ -2548,6 +2538,16 @@ pub fn getUnwindRecord(self: *Object, index: UnwindInfo.Record.Index) *UnwindInf return &self.unwind_records.items[index]; } +/// Caller owns the memory. +fn readSectionData(self: Object, allocator: Allocator, file: File.Handle, n_sect: u8) ![]u8 { + const header = self.sections.items(.header)[n_sect]; + const data = try allocator.alloc(u8, header.size); + const amt = try file.preadAll(data, header.offset + self.offset); + errdefer allocator.free(data); + if (amt != data.len) return error.InputOutput; + return data; +} + pub fn format( self: *Object, comptime unused_fmt_string: []const u8, From 3ad45dc64db65968ae0784cc1978941a7a68f3eb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 16 Oct 2024 16:24:28 +0200 Subject: [PATCH 10/13] macho: refactor how we fetch section's data from file --- src/MachO/Object.zig | 63 +++++++++++++------------------------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index 01c4fd8c..a23e3d11 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -449,10 +449,8 @@ fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, m for (slice.items(.header), 0..) |sect, n_sect| { if (!isCstringLiteral(sect)) continue; - const data = try allocator.alloc(u8, sect.size); + const data = try self.readSectionData(allocator, file, @intCast(n_sect)); defer allocator.free(data); - const amt = try file.preadAll(data, sect.offset + self.offset); - if (amt != data.len) return error.InputOutput; var count: u32 = 0; var start: u32 = 0; @@ -651,12 +649,10 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO } const slice = self.sections.slice(); - for (slice.items(.header), slice.items(.subsections)) |header, subs| { + for (slice.items(.header), slice.items(.subsections), 0..) |header, subs, n_sect| { if (isCstringLiteral(header) or isFixedSizeLiteral(header)) { - const data = try gpa.alloc(u8, header.size); + const data = try self.readSectionData(gpa, file, @intCast(n_sect)); defer gpa.free(data); - const amt = try file.preadAll(data, header.offset + self.offset); - if (amt != data.len) return error.InputOutput; for (subs.items) |sub| { const atom = self.getAtom(sub.atom).?; @@ -687,11 +683,7 @@ pub fn resolveLiterals(self: *Object, lp: *MachO.LiteralPool, macho_file: *MachO buffer.resize(target.size) catch unreachable; const gop = try sections_data.getOrPut(target.n_sect); if (!gop.found_existing) { - const target_sect = slice.items(.header)[target.n_sect]; - const data = try gpa.alloc(u8, target_sect.size); - const amt = try file.preadAll(data, target_sect.offset + self.offset); - if (amt != data.len) return error.InputOutput; - gop.value_ptr.* = data; + gop.value_ptr.* = try self.readSectionData(gpa, file, @intCast(target.n_sect)); } const data = gop.value_ptr.*; @memcpy(buffer.items, data[target.off..][0..target.size]); @@ -997,7 +989,7 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m defer tracy.end(); const slice = self.sections.slice(); - for (slice.items(.header), slice.items(.relocs)) |sect, *out| { + for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| { if (sect.nreloc == 0) continue; // We skip relocs for __DWARF since even in -r mode, the linker is expected to emit // debug symbol stabs in the relocatable. This made me curious why that is. For now, @@ -1006,8 +998,8 @@ fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, m !mem.eql(u8, sect.sectName(), "__compact_unwind")) continue; switch (cpu_arch) { - .x86_64 => try x86_64.parseRelocs(self, sect, out, file, macho_file), - .aarch64 => try aarch64.parseRelocs(self, sect, out, file, macho_file), + .x86_64 => try x86_64.parseRelocs(self, @intCast(n_sect), sect, out, file, macho_file), + .aarch64 => try aarch64.parseRelocs(self, @intCast(n_sect), sect, out, file, macho_file), else => unreachable, } @@ -1141,11 +1133,8 @@ fn initUnwindRecords(self: *Object, allocator: Allocator, sect_id: u8, file: Fil } }; - const sect = self.sections.items(.header)[sect_id]; - const data = try allocator.alloc(u8, sect.size); + const data = try self.readSectionData(allocator, file, sect_id); defer allocator.free(data); - const amt = try file.preadAll(data, sect.offset + self.offset); - if (amt != data.len) return error.InputOutput; const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry)); const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs]; @@ -1875,10 +1864,7 @@ pub fn writeAtoms(self: *Object, macho_file: *MachO) !void { for (headers, 0..) |header, n_sect| { if (header.isZerofill()) continue; - const data = try gpa.alloc(u8, header.size); - const amt = try file.preadAll(data, header.offset + self.offset); - if (amt != data.len) return error.InputOutput; - sections_data[n_sect] = data; + sections_data[n_sect] = try self.readSectionData(gpa, file, @intCast(n_sect)); } for (self.getAtoms()) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; @@ -1911,10 +1897,7 @@ pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void { for (headers, 0..) |header, n_sect| { if (header.isZerofill()) continue; - const data = try gpa.alloc(u8, header.size); - const amt = try file.preadAll(data, header.offset + self.offset); - if (amt != data.len) return error.InputOutput; - sections_data[n_sect] = data; + sections_data[n_sect] = try self.readSectionData(gpa, file, @intCast(n_sect)); } for (self.getAtoms()) |atom_index| { const atom = self.getAtom(atom_index) orelse continue; @@ -2829,6 +2812,7 @@ const CompactUnwindCtx = struct { const x86_64 = struct { fn parseRelocs( self: *Object, + n_sect: u8, sect: macho.section_64, out: *std.ArrayListUnmanaged(Relocation), file: File.Handle, @@ -2838,18 +2822,12 @@ const x86_64 = struct { const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); defer gpa.free(relocs_buffer); - { - const amt = try file.preadAll(relocs_buffer, sect.reloff + self.offset); - if (amt != relocs_buffer.len) return error.InputOutput; - } + const amt = try file.preadAll(relocs_buffer, sect.reloff + self.offset); + if (amt != relocs_buffer.len) return error.InputOutput; const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; - const code = try gpa.alloc(u8, sect.size); + const code = try self.readSectionData(gpa, file, n_sect); defer gpa.free(code); - { - const amt = try file.preadAll(code, sect.offset + self.offset); - if (amt != code.len) return error.InputOutput; - } try out.ensureTotalCapacityPrecise(gpa, relocs.len); @@ -2997,6 +2975,7 @@ const x86_64 = struct { const aarch64 = struct { fn parseRelocs( self: *Object, + n_sect: u8, sect: macho.section_64, out: *std.ArrayListUnmanaged(Relocation), file: File.Handle, @@ -3006,18 +2985,12 @@ const aarch64 = struct { const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); defer gpa.free(relocs_buffer); - { - const amt = try file.preadAll(relocs_buffer, sect.reloff + self.offset); - if (amt != relocs_buffer.len) return error.InputOutput; - } + const amt = try file.preadAll(relocs_buffer, sect.reloff + self.offset); + if (amt != relocs_buffer.len) return error.InputOutput; const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; - const code = try gpa.alloc(u8, sect.size); + const code = try self.readSectionData(gpa, file, n_sect); defer gpa.free(code); - { - const amt = try file.preadAll(code, sect.offset + self.offset); - if (amt != code.len) return error.InputOutput; - } try out.ensureTotalCapacityPrecise(gpa, relocs.len); From 1547173da212c0a6c63b8ac4034476d97dd92a3b Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 16 Oct 2024 16:42:51 +0200 Subject: [PATCH 11/13] macho: simplify errors arising from parsing DWARF --- src/MachO/Dwarf.zig | 61 +++++++++-------- src/MachO/Object.zig | 152 ++++++++++++++++--------------------------- 2 files changed, 90 insertions(+), 123 deletions(-) diff --git a/src/MachO/Dwarf.zig b/src/MachO/Dwarf.zig index 30100d8b..828b319f 100644 --- a/src/MachO/Dwarf.zig +++ b/src/MachO/Dwarf.zig @@ -21,6 +21,11 @@ fn getOffset(debug_str_offsets: []const u8, base: u64, index: u64, dw_fmt: Dwarf }; } +const ErrCtx = struct { + object: Object, + macho_file: *MachO, +}; + pub const InfoReader = struct { ctx: Dwarf, pos: usize = 0, @@ -29,10 +34,7 @@ pub const InfoReader = struct { return p.ctx.debug_info; } - pub fn readCompileUnitHeader(p: *InfoReader) !union(enum) { - ok: CompileUnitHeader, - wrong_version: Version, - } { + pub fn readCompileUnitHeader(p: *InfoReader, err_ctx: ErrCtx) !CompileUnitHeader { var length: u64 = try p.readInt(u32); const is_64bit = length == 0xffffffff; if (is_64bit) { @@ -57,19 +59,25 @@ pub const InfoReader = struct { .address_size = try p.readByte(), .debug_abbrev_offset = try p.readOffset(dw_fmt), }, - else => return .{ .wrong_version = version }, + else => { + err_ctx.macho_file.base.fatal("{}: unhandled DWARF version: expected 4 or 5, got {d}", .{ + err_ctx.object.fmtPath(), + version, + }); + return error.InvalidVersion; + }, }; - return .{ .ok = .{ + return .{ .format = dw_fmt, .length = length, .version = version, .debug_abbrev_offset = other.debug_abbrev_offset, .address_size = other.address_size, .unit_type = other.unit_type, - } }; + }; } - pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader) !SkipResult { + pub fn seekToDie(p: *InfoReader, code: Code, cuh: CompileUnitHeader, abbrev_reader: *AbbrevReader, err_ctx: ErrCtx) !void { const cuh_length = math.cast(usize, cuh.length) orelse return error.Overflow; const end_pos = p.pos + switch (cuh.format) { .dwarf32 => @as(usize, 4), @@ -78,27 +86,28 @@ pub const InfoReader = struct { while (p.pos < end_pos) { const di_code = try p.readULEB128(u64); if (di_code == 0) return error.UnexpectedEndOfFile; - if (di_code == code) return .ok; + if (di_code == code) return; while (try abbrev_reader.readAttr()) |attr| { - const res = try p.skip(attr.form, cuh); - switch (res) { - .ok => {}, - .unknown_form => return res, - } + try p.skip(attr.form, cuh, err_ctx); } } return error.UnexpectedEndOfFile; } - const SkipResult = union(enum) { - ok, - unknown_form: Form, - }; - /// When skipping attributes, we don't really need to be able to handle them all /// since we only ever care about the DW_TAG_compile_unit. - pub fn skip(p: *InfoReader, form: Form, cuh: CompileUnitHeader) !SkipResult { + pub fn skip(p: *InfoReader, form: Form, cuh: CompileUnitHeader, err_ctx: ErrCtx) !void { + p.skipInner(form, cuh) catch |err| switch (err) { + error.UnhandledForm => { + err_ctx.macho_file.base.fatal("{}: unhandled DW_FORM_* 0x{x}", .{ err_ctx.object.fmtPath(), form }); + return error.UnhandledForm; + }, + else => |e| return e, + }; + } + + fn skipInner(p: *InfoReader, form: Form, cuh: CompileUnitHeader) !void { switch (form) { dw.FORM.sec_offset, dw.FORM.ref_addr, @@ -168,10 +177,9 @@ pub const InfoReader = struct { _ = try p.readIndex(form); }, - else => return .{ .unknown_form = form }, - } else return .{ .unknown_form = form }, + else => return error.UnhandledForm, + } else return error.UnhandledForm, } - return .ok; } pub fn readBlock(p: *InfoReader, form: Form) ![]const u8 { @@ -198,7 +206,7 @@ pub const InfoReader = struct { dw.FORM.data8, dw.FORM.ref8, dw.FORM.ref_sig8 => try p.readInt(u64), dw.FORM.udata, dw.FORM.ref_udata => try p.readULEB128(u64), dw.FORM.sdata => @bitCast(try p.readILEB128(i64)), - else => return error.UnhandledConstantForm, + else => return error.UnhandledForm, }; } @@ -206,10 +214,10 @@ pub const InfoReader = struct { return switch (form) { dw.FORM.strx1, dw.FORM.addrx1 => try p.readByte(), dw.FORM.strx2, dw.FORM.addrx2 => try p.readInt(u16), - dw.FORM.strx3, dw.FORM.addrx3 => error.UnhandledDwForm, + dw.FORM.strx3, dw.FORM.addrx3 => error.UnhandledForm, dw.FORM.strx4, dw.FORM.addrx4 => try p.readInt(u32), dw.FORM.strx, dw.FORM.addrx => try p.readULEB128(u64), - else => return error.UnhandledIndexForm, + else => return error.UnhandledForm, }; } @@ -396,6 +404,7 @@ const std = @import("std"); const Allocator = mem.Allocator; const Dwarf = @This(); const File = @import("file.zig").File; +const MachO = @import("../MachO.zig"); const Object = @import("Object.zig"); pub const At = u64; diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index a23e3d11..aa633a7e 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -1367,12 +1367,21 @@ fn parseDebugInfo(self: *Object, macho_file: *MachO) !void { if (dwarf.debug_info.len == 0) return; - self.compile_unit = self.findCompileUnit(gpa, dwarf, macho_file) catch |err| switch (err) { - error.ParseFailed => return error.ParseFailed, - else => |e| { - macho_file.base.fatal("{}: unexpected error when parsing DWARF info: {s}", .{ self.fmtPath(), @errorName(e) }); - return error.ParseFailed; - }, + self.compile_unit = self.findCompileUnit(gpa, dwarf, macho_file) catch |err| { + switch (err) { + error.UnhandledForm, + error.UnexpectedTag, + error.MissingCompDir, + error.MissingTuName, + error.MissingStrOffsetsBase, + error.InvalidForm, + => {}, + else => |e| macho_file.base.fatal("{}: unexpected error when parsing DWARF info: {s}", .{ + self.fmtPath(), + @errorName(e), + }), + } + return error.ParseFailed; }; } @@ -1380,32 +1389,20 @@ fn findCompileUnit(self: *Object, gpa: Allocator, dwarf: Dwarf, macho_file: *Mac var info_reader = Dwarf.InfoReader{ .ctx = dwarf }; var abbrev_reader = Dwarf.AbbrevReader{ .ctx = dwarf }; - const cuh = switch (try info_reader.readCompileUnitHeader()) { - .ok => |cuh| cuh, - .wrong_version => |ver| { - macho_file.base.fatal("{}: unhandled or unknown DWARF version detected: {d}", .{ self.fmtPath(), ver }); - return error.ParseFailed; - }, - }; + const cuh = try info_reader.readCompileUnitHeader(.{ .object = self.*, .macho_file = macho_file }); try abbrev_reader.seekTo(cuh.debug_abbrev_offset); const cu_decl = (try abbrev_reader.readDecl()) orelse return error.UnexpectedEndOfFile; if (cu_decl.tag != Dwarf.TAG.compile_unit) { - macho_file.base.fatal("{}: unexpected DW_TAG_xxx value detected as the first decl: expected 0x{x}, found 0x{x}", .{ + macho_file.base.fatal("{}: unexpected DW_TAG_* value detected as the first decl: expected 0x{x}, got 0x{x}", .{ self.fmtPath(), Dwarf.TAG.compile_unit, cu_decl.tag, }); - return error.ParseFailed; + return error.UnexpectedTag; } - switch (try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader)) { - .ok => {}, - .unknown_form => |form| { - macho_file.base.fatal("{}: unexpected DW_FORM_xxx value detected: 0x{x}", .{ self.fmtPath(), form }); - return error.ParseFailed; - }, - } + try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader, .{ .object = self.*, .macho_file = macho_file }); const Pos = struct { pos: usize, @@ -1423,100 +1420,61 @@ fn findCompileUnit(self: *Object, gpa: Allocator, dwarf: Dwarf, macho_file: *Mac }; while (try abbrev_reader.readAttr()) |attr| { + const pos: Pos = .{ .pos = info_reader.pos, .form = attr.form }; switch (attr.at) { - Dwarf.AT.name => saved.tu_name = .{ .pos = info_reader.pos, .form = attr.form }, - Dwarf.AT.comp_dir => saved.comp_dir = .{ .pos = info_reader.pos, .form = attr.form }, - Dwarf.AT.str_offsets_base => saved.str_offsets_base = .{ .pos = info_reader.pos, .form = attr.form }, + Dwarf.AT.name => saved.tu_name = pos, + Dwarf.AT.comp_dir => saved.comp_dir = pos, + Dwarf.AT.str_offsets_base => saved.str_offsets_base = pos, else => {}, } - switch (try info_reader.skip(attr.form, cuh)) { - .ok => {}, - .unknown_form => |form| { - macho_file.base.fatal("{}: unexpected DW_FORM_xxx value detected: 0x{x}", .{ self.fmtPath(), form }); - return error.ParseFailed; - }, - } + try info_reader.skip(attr.form, cuh, .{ .object = self.*, .macho_file = macho_file }); } - const readDwarfString = struct { - fn readDwarfString( - reader: *Dwarf.InfoReader, - header: Dwarf.CompileUnitHeader, - pos: Pos, - base: u64, - ) !union(enum) { - ok: [:0]const u8, - invalid_form: Dwarf.Form, - } { - try reader.seekTo(pos.pos); - switch (pos.form) { - Dwarf.FORM.strp, - Dwarf.FORM.string, - => return .{ .ok = try reader.readString(pos.form, header) }, - Dwarf.FORM.strx, - Dwarf.FORM.strx1, - Dwarf.FORM.strx2, - Dwarf.FORM.strx3, - Dwarf.FORM.strx4, - => return .{ .ok = try reader.readStringIndexed(pos.form, header, base) }, - else => return .{ .invalid_form = pos.form }, - } - } - }.readDwarfString; - if (saved.comp_dir == null) { macho_file.base.fatal("{}: missing DW_AT_comp_dir attribute", .{self.fmtPath()}); - return error.ParseFailed; + return error.MissingCompDir; } if (saved.tu_name == null) { macho_file.base.fatal("{}: missing DW_AT_name attribute", .{self.fmtPath()}); - return error.ParseFailed; + return error.MissingTuName; } const str_offsets_base: ?u64 = if (saved.str_offsets_base) |str_offsets_base| str_offsets_base: { - if (cuh.version < 5) break :str_offsets_base null; try info_reader.seekTo(str_offsets_base.pos); break :str_offsets_base try info_reader.readOffset(cuh.format); } else null; - for (&[_]Pos{ saved.comp_dir.?, saved.tu_name.? }) |pos| { - if (cuh.version >= 5 and needsStrOffsetsBase(pos.form) and str_offsets_base == null) { - macho_file.base.fatal("{}: missing DW_AT_str_offsets_base attribute", .{self.fmtPath()}); - return error.ParseFailed; - } + var cu: CompileUnit = .{ .comp_dir = .{}, .tu_name = .{} }; + for (&[_]struct { Pos, *MachO.String }{ + .{ saved.comp_dir.?, &cu.comp_dir }, + .{ saved.tu_name.?, &cu.tu_name }, + }) |tuple| { + const pos, const str_offset_ptr = tuple; + try info_reader.seekTo(pos.pos); + str_offset_ptr.* = switch (pos.form) { + Dwarf.FORM.strp, + Dwarf.FORM.string, + => try self.addString(gpa, try info_reader.readString(pos.form, cuh)), + Dwarf.FORM.strx, + Dwarf.FORM.strx1, + Dwarf.FORM.strx2, + Dwarf.FORM.strx3, + Dwarf.FORM.strx4, + => blk: { + const base = str_offsets_base orelse { + macho_file.base.fatal("{}: missing DW_AT_str_offsets_base attribute", .{self.fmtPath()}); + return error.MissingStrOffsetsBase; + }; + break :blk try self.addString(gpa, try info_reader.readStringIndexed(pos.form, cuh, base)); + }, + else => |form| { + macho_file.base.fatal("{}: invalid DW_FORM_* when parsing string: 0x{x}", .{ self.fmtPath(), form }); + return error.InvalidForm; + }, + }; } - const comp_dir = switch (try readDwarfString(&info_reader, cuh, saved.comp_dir.?, str_offsets_base orelse 0)) { - .ok => |str| str, - .invalid_form => |form| { - macho_file.base.fatal("{}: invalid form when parsing DWARF string: 0x{x}", .{ self.fmtPath(), form }); - return error.ParseFailed; - }, - }; - const tu_name = switch (try readDwarfString(&info_reader, cuh, saved.tu_name.?, str_offsets_base orelse 0)) { - .ok => |str| str, - .invalid_form => |form| { - macho_file.base.fatal("{}: invalid form when parsing DWARF string: 0x{x}", .{ self.fmtPath(), form }); - return error.ParseFailed; - }, - }; - - return .{ - .comp_dir = try self.addString(gpa, comp_dir), - .tu_name = try self.addString(gpa, tu_name), - }; -} - -fn needsStrOffsetsBase(form: Dwarf.Form) bool { - return switch (form) { - Dwarf.FORM.strx, - Dwarf.FORM.strx1, - Dwarf.FORM.strx2, - Dwarf.FORM.strx3, - Dwarf.FORM.strx4, - => true, - else => false, - }; + return cu; } pub fn resolveSymbols(self: *Object, macho_file: *MachO) !void { From 15adb1bab3d807de155a45e594a8888def148b05 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 21 Oct 2024 12:33:06 +0200 Subject: [PATCH 12/13] Pin to Zig 0.13.0 --- .github/workflows/main.yml | 8 ++++---- build.zig | 3 +-- build.zig.zon | 8 ++++---- flake.nix | 2 +- src/Coff.zig | 14 +++++++------- src/Coff/Symbol.zig | 2 +- src/Elf.zig | 6 +++--- src/Elf/Atom.zig | 2 +- src/Elf/InternalObject.zig | 8 ++++---- src/Elf/LdScript.zig | 2 +- src/Elf/Object.zig | 16 ++++++++-------- src/Elf/SharedObject.zig | 16 ++++++++-------- src/Elf/Symbol.zig | 2 +- src/Elf/Thunk.zig | 2 +- src/Elf/merge_section.zig | 4 ++-- src/MachO/Atom.zig | 2 +- src/MachO/Dylib.zig | 8 ++++---- src/MachO/InternalObject.zig | 16 ++++++++-------- src/MachO/Object.zig | 16 ++++++++-------- src/MachO/Symbol.zig | 2 +- src/Wasm/Object.zig | 4 ++-- src/riscv.zig | 2 +- test/macho.zig | 2 +- test/test.zig | 4 ++-- 24 files changed, 75 insertions(+), 76 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3fe743dc..b6222a3e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v3 - uses: mlugg/setup-zig@v1 with: - version: master + version: 0.13.0 - run: zig version - run: zig fmt --check src - run: zig build test -Dhas-static -Dhas-zig -Dhas-objc-msgsend-stubs @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v3 - uses: mlugg/setup-zig@v1 with: - version: master + version: 0.13.0 - uses: ilammy/msvc-dev-cmd@v1 - run: zig version - run: zig fmt --check src @@ -41,7 +41,7 @@ jobs: - uses: actions/checkout@v3 - uses: mlugg/setup-zig@v1 with: - version: master + version: 0.13.0 - run: sudo apt-get install -y musl-tools - run: zig version - run: CC=musl-gcc zig build test -Dhas-static -Dmusl @@ -53,7 +53,7 @@ jobs: - uses: actions/checkout@v3 - uses: mlugg/setup-zig@v1 with: - version: master + version: 0.13.0 - run: sudo apt-get install -y clang - run: zig version - run: CC=clang zig build test -Dhas-static -Dsystem-compiler=clang diff --git a/build.zig b/build.zig index 55de246b..02542104 100644 --- a/build.zig +++ b/build.zig @@ -155,8 +155,7 @@ const CreateSymlinksStep = struct { return self; } - fn make(step: *std.Build.Step, options: std.Build.Step.MakeOptions) anyerror!void { - const prog_node = options.progress_node; + fn make(step: *std.Build.Step, prog_node: std.Progress.Node) anyerror!void { const self: *CreateSymlinksStep = @fieldParentPtr("step", step); const install_path = self.install.artifact.getEmittedBin().getPath(self.builder); const rel_source = fs.path.basename(install_path); diff --git a/build.zig.zon b/build.zig.zon index c4ce7435..3419dc5b 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -4,12 +4,12 @@ .dependencies = .{ .@"zig-yaml" = .{ - .url = "https://github.com/kubkon/zig-yaml/archive/a551d00ab041f4799420ab224cdc3efdf978092c.tar.gz", - .hash = "12208398e1393f83a39d03f3ef4893607650b6227dc7f1eee3db4d163fbc2c0c37ca", + .url = "https://github.com/kubkon/zig-yaml/archive/325dbdd276604dccf184c32fef9600b0ac48343d.tar.gz", + .hash = "1220e8870ca83e47b98807e89b5b636072413f6c09f9b26037e4c98c55e4960ac55a", }, .@"zig-dis-x86_64" = .{ - .url = "https://github.com/kubkon/zig-dis-x86_64/archive/53b2e2dae9e824d7b4994e767c2cbb4f39e443a9.tar.gz", - .hash = "1220f2b9588352067d5b6f3b68d5461cea52f4425c9144f6757db775cf2f19cb5d26", + .url = "https://github.com/kubkon/zig-dis-x86_64/archive/5203b9affc5045e000ae7963d988e155e98e396d.tar.gz", + .hash = "12207252f0592e53e8794d5a41409791d5c8c70e0de67bfba48844406619847cc971", }, }, diff --git a/flake.nix b/flake.nix index 002fa5b0..cbc2e2ec 100644 --- a/flake.nix +++ b/flake.nix @@ -38,7 +38,7 @@ devShells.default = pkgs.stdenvNoCC.mkDerivation { name = "emerald"; nativeBuildInputs = with pkgs; [ - zigpkgs.master-2024-10-08 + zigpkgs."0.13.0" zlspkgs.default ]; }; diff --git a/src/Coff.zig b/src/Coff.zig index 8ee69be8..572fdd88 100644 --- a/src/Coff.zig +++ b/src/Coff.zig @@ -1293,14 +1293,14 @@ fn writeHeader(self: *Coff) !void { pub fn isCoffObj(buffer: *const [@sizeOf(coff.CoffHeader)]u8) bool { const header = @as(*align(1) const coff.CoffHeader, @ptrCast(buffer)).*; - if (header.machine == .UNKNOWN and header.number_of_sections == 0xffff) return false; + if (header.machine == .Unknown and header.number_of_sections == 0xffff) return false; if (header.size_of_optional_header != 0) return false; return true; } pub fn isImportLib(buffer: *const [@sizeOf(coff.ImportHeader)]u8) bool { const header = @as(*align(1) const coff.ImportHeader, @ptrCast(buffer)).*; - return header.sig1 == .UNKNOWN and header.sig2 == 0xffff; + return header.sig1 == .Unknown and header.sig2 == 0xffff; } pub fn isArchive(data: *const [Archive.magic.len]u8) bool { @@ -1502,14 +1502,14 @@ pub fn getSymbol(self: *Coff, index: Symbol.Index) *Symbol { } pub fn addSymbolExtra(self: *Coff, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; try self.symbols_extra.ensureUnusedCapacity(self.base.allocator, fields.len); return self.addSymbolExtraAssumeCapacity(extra); } pub fn addSymbolExtraAssumeCapacity(self: *Coff, extra: Symbol.Extra) u32 { const index = @as(u32, @intCast(self.symbols_extra.items.len)); - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields) |field| { self.symbols_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -1521,7 +1521,7 @@ pub fn addSymbolExtraAssumeCapacity(self: *Coff, extra: Symbol.Extra) u32 { pub fn getSymbolExtra(self: Coff, index: u32) ?Symbol.Extra { if (index == 0) return null; - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; var i: usize = index; var result: Symbol.Extra = undefined; inline for (fields) |field| { @@ -1536,7 +1536,7 @@ pub fn getSymbolExtra(self: Coff, index: u32) ?Symbol.Extra { pub fn setSymbolExtra(self: *Coff, index: u32, extra: Symbol.Extra) void { assert(index > 0); - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.symbols_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), @@ -1645,7 +1645,7 @@ fn formatSectionFlags( ) !void { _ = options; _ = unused_fmt_string; - inline for (@typeInfo(coff.SectionHeaderFlags).@"struct".fields) |field| { + inline for (@typeInfo(coff.SectionHeaderFlags).Struct.fields) |field| { if (@field(flags, field.name) == 0b1) { try writer.writeAll(field.name ++ " "); } diff --git a/src/Coff/Symbol.zig b/src/Coff/Symbol.zig index 4d392f57..a87bccc7 100644 --- a/src/Coff/Symbol.zig +++ b/src/Coff/Symbol.zig @@ -119,7 +119,7 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, coff_file: *Coff) !void { symbol.extra = try coff_file.addSymbolExtra(.{}); } var extra = symbol.getExtra(coff_file).?; - inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| { + inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extra, field.name) = x; } diff --git a/src/Elf.zig b/src/Elf.zig index 7ff921bf..0751fa14 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -41,11 +41,11 @@ resolver: SymbolResolver = .{}, undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{}, dupes: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{}, -shstrtab: std.ArrayListUnmanaged(u8) = .empty, +shstrtab: std.ArrayListUnmanaged(u8) = .{}, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -strtab: std.ArrayListUnmanaged(u8) = .empty, +strtab: std.ArrayListUnmanaged(u8) = .{}, dynsym: DynsymSection = .{}, -dynstrtab: std.ArrayListUnmanaged(u8) = .empty, +dynstrtab: std.ArrayListUnmanaged(u8) = .{}, versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, verneed: VerneedSection = .{}, diff --git a/src/Elf/Atom.zig b/src/Elf/Atom.zig index 8ac00777..8d7fdcd8 100644 --- a/src/Elf/Atom.zig +++ b/src/Elf/Atom.zig @@ -125,7 +125,7 @@ const AddExtraOpts = struct { pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) void { const object = atom.getObject(elf_file); var extras = object.getAtomExtra(atom.extra); - inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| { + inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extras, field.name) = x; } diff --git a/src/Elf/InternalObject.zig b/src/Elf/InternalObject.zig index a299b6c2..5efd95e2 100644 --- a/src/Elf/InternalObject.zig +++ b/src/Elf/InternalObject.zig @@ -399,14 +399,14 @@ fn addSymbolAssumeCapacity(self: *InternalObject) Symbol.Index { } pub fn addSymbolExtra(self: *InternalObject, allocator: Allocator, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); return self.addSymbolExtraAssumeCapacity(extra); } pub fn addSymbolExtraAssumeCapacity(self: *InternalObject, extra: Symbol.Extra) u32 { const index = @as(u32, @intCast(self.symbols_extra.items.len)); - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields) |field| { self.symbols_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -417,7 +417,7 @@ pub fn addSymbolExtraAssumeCapacity(self: *InternalObject, extra: Symbol.Extra) } pub fn getSymbolExtra(self: *InternalObject, index: u32) Symbol.Extra { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; var i: usize = index; var result: Symbol.Extra = undefined; inline for (fields) |field| { @@ -431,7 +431,7 @@ pub fn getSymbolExtra(self: *InternalObject, index: u32) Symbol.Extra { } pub fn setSymbolExtra(self: *InternalObject, index: u32, extra: Symbol.Extra) void { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.symbols_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), diff --git a/src/Elf/LdScript.zig b/src/Elf/LdScript.zig index 12baeb65..ca95ad27 100644 --- a/src/Elf/LdScript.zig +++ b/src/Elf/LdScript.zig @@ -105,7 +105,7 @@ const Command = enum { as_needed, fn fromString(s: []const u8) ?Command { - inline for (@typeInfo(Command).@"enum".fields) |field| { + inline for (@typeInfo(Command).Enum.fields) |field| { const upper_name = n: { comptime var buf: [field.name.len]u8 = undefined; inline for (field.name, 0..) |c, i| { diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index 20721672..6466b8af 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -1038,14 +1038,14 @@ fn addSymbolAssumeCapacity(self: *Object) Symbol.Index { } pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); return self.addSymbolExtraAssumeCapacity(extra); } pub fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 { const index = @as(u32, @intCast(self.symbols_extra.items.len)); - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields) |field| { self.symbols_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -1056,7 +1056,7 @@ pub fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 { } pub fn getSymbolExtra(self: *Object, index: u32) Symbol.Extra { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; var i: usize = index; var result: Symbol.Extra = undefined; inline for (fields) |field| { @@ -1070,7 +1070,7 @@ pub fn getSymbolExtra(self: *Object, index: u32) Symbol.Extra { } pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.symbols_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), @@ -1149,14 +1149,14 @@ pub fn getAtom(self: *Object, atom_index: Atom.Index) ?*Atom { } pub fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 { - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len); return self.addAtomExtraAssumeCapacity(extra); } pub fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 { const index = @as(u32, @intCast(self.atoms_extra.items.len)); - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; inline for (fields) |field| { self.atoms_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -1167,7 +1167,7 @@ pub fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 { } pub fn getAtomExtra(self: *Object, index: u32) Atom.Extra { - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; var i: usize = index; var result: Atom.Extra = undefined; inline for (fields) |field| { @@ -1181,7 +1181,7 @@ pub fn getAtomExtra(self: *Object, index: u32) Atom.Extra { } pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void { - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.atoms_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), diff --git a/src/Elf/SharedObject.zig b/src/Elf/SharedObject.zig index 82802c3e..86bd608d 100644 --- a/src/Elf/SharedObject.zig +++ b/src/Elf/SharedObject.zig @@ -10,12 +10,12 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, verstrings: std.ArrayListUnmanaged(u32) = .{}, -symbols: std.ArrayListUnmanaged(Symbol) = .empty, -symbols_extra: std.ArrayListUnmanaged(u32) = .empty, -symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .empty, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{}, aliases: ?std.ArrayListUnmanaged(u32) = null, -dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .empty, +dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .{}, needed: bool, alive: bool, @@ -420,14 +420,14 @@ fn addSymbolAssumeCapacity(self: *SharedObject) Symbol.Index { } pub fn addSymbolExtra(self: *SharedObject, allocator: Allocator, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); return self.addSymbolExtraAssumeCapacity(extra); } pub fn addSymbolExtraAssumeCapacity(self: *SharedObject, extra: Symbol.Extra) u32 { const index = @as(u32, @intCast(self.symbols_extra.items.len)); - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields) |field| { self.symbols_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -438,7 +438,7 @@ pub fn addSymbolExtraAssumeCapacity(self: *SharedObject, extra: Symbol.Extra) u3 } pub fn getSymbolExtra(self: *SharedObject, index: u32) Symbol.Extra { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; var i: usize = index; var result: Symbol.Extra = undefined; inline for (fields) |field| { @@ -452,7 +452,7 @@ pub fn getSymbolExtra(self: *SharedObject, index: u32) Symbol.Extra { } pub fn setSymbolExtra(self: *SharedObject, index: u32, extra: Symbol.Extra) void { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.symbols_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), diff --git a/src/Elf/Symbol.zig b/src/Elf/Symbol.zig index 44905e08..b742427c 100644 --- a/src/Elf/Symbol.zig +++ b/src/Elf/Symbol.zig @@ -242,7 +242,7 @@ const AddExtraOpts = struct { pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void { var extra = symbol.getExtra(elf_file); - inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| { + inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extra, field.name) = x; } diff --git a/src/Elf/Thunk.zig b/src/Elf/Thunk.zig index 3315a1ca..6a4d1806 100644 --- a/src/Elf/Thunk.zig +++ b/src/Elf/Thunk.zig @@ -1,6 +1,6 @@ value: i64 = 0, out_shndx: u32 = 0, -symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .empty, +symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{}, output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(thunk: *Thunk, allocator: Allocator) void { diff --git a/src/Elf/merge_section.zig b/src/Elf/merge_section.zig index 917122d7..38b30f9d 100644 --- a/src/Elf/merge_section.zig +++ b/src/Elf/merge_section.zig @@ -14,8 +14,8 @@ pub const MergeSection = struct { IndexContext, std.hash_map.default_max_load_percentage, ) = .{}, - subsections: std.ArrayListUnmanaged(MergeSubsection) = .empty, - finalized_subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .empty, + subsections: std.ArrayListUnmanaged(MergeSubsection) = .{}, + finalized_subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{}, pub fn deinit(msec: *MergeSection, allocator: Allocator) void { msec.bytes.deinit(allocator); diff --git a/src/MachO/Atom.zig b/src/MachO/Atom.zig index 480317c4..7803fb02 100644 --- a/src/MachO/Atom.zig +++ b/src/MachO/Atom.zig @@ -116,7 +116,7 @@ const AddExtraOpts = struct { pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) void { const file = atom.getFile(macho_file); var extra = file.getAtomExtra(atom.extra); - inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| { + inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extra, field.name) = x; } diff --git a/src/MachO/Dylib.zig b/src/MachO/Dylib.zig index c3678486..0dcb82b7 100644 --- a/src/MachO/Dylib.zig +++ b/src/MachO/Dylib.zig @@ -695,14 +695,14 @@ pub fn getSymbolRef(self: Dylib, index: Symbol.Index, macho_file: *MachO) MachO. } pub fn addSymbolExtra(self: *Dylib, allocator: Allocator, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); return self.addSymbolExtraAssumeCapacity(extra); } fn addSymbolExtraAssumeCapacity(self: *Dylib, extra: Symbol.Extra) u32 { const index = @as(u32, @intCast(self.symbols_extra.items.len)); - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields) |field| { self.symbols_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -713,7 +713,7 @@ fn addSymbolExtraAssumeCapacity(self: *Dylib, extra: Symbol.Extra) u32 { } pub fn getSymbolExtra(self: Dylib, index: u32) Symbol.Extra { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; var i: usize = index; var result: Symbol.Extra = undefined; inline for (fields) |field| { @@ -727,7 +727,7 @@ pub fn getSymbolExtra(self: Dylib, index: u32) Symbol.Extra { } pub fn setSymbolExtra(self: *Dylib, index: u32, extra: Symbol.Extra) void { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.symbols_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), diff --git a/src/MachO/InternalObject.zig b/src/MachO/InternalObject.zig index 74c87fc0..74acf70e 100644 --- a/src/MachO/InternalObject.zig +++ b/src/MachO/InternalObject.zig @@ -702,14 +702,14 @@ pub fn getAtoms(self: InternalObject) []const Atom.Index { } fn addAtomExtra(self: *InternalObject, allocator: Allocator, extra: Atom.Extra) !u32 { - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len); return self.addAtomExtraAssumeCapacity(extra); } fn addAtomExtraAssumeCapacity(self: *InternalObject, extra: Atom.Extra) u32 { const index = @as(u32, @intCast(self.atoms_extra.items.len)); - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; inline for (fields) |field| { self.atoms_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -720,7 +720,7 @@ fn addAtomExtraAssumeCapacity(self: *InternalObject, extra: Atom.Extra) u32 { } pub fn getAtomExtra(self: InternalObject, index: u32) Atom.Extra { - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; var i: usize = index; var result: Atom.Extra = undefined; inline for (fields) |field| { @@ -735,7 +735,7 @@ pub fn getAtomExtra(self: InternalObject, index: u32) Atom.Extra { pub fn setAtomExtra(self: *InternalObject, index: u32, extra: Atom.Extra) void { assert(index > 0); - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.atoms_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), @@ -783,14 +783,14 @@ pub fn getSymbolRef(self: InternalObject, index: Symbol.Index, macho_file: *Mach } pub fn addSymbolExtra(self: *InternalObject, allocator: Allocator, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); return self.addSymbolExtraAssumeCapacity(extra); } fn addSymbolExtraAssumeCapacity(self: *InternalObject, extra: Symbol.Extra) u32 { const index = @as(u32, @intCast(self.symbols_extra.items.len)); - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields) |field| { self.symbols_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -801,7 +801,7 @@ fn addSymbolExtraAssumeCapacity(self: *InternalObject, extra: Symbol.Extra) u32 } pub fn getSymbolExtra(self: InternalObject, index: u32) Symbol.Extra { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; var i: usize = index; var result: Symbol.Extra = undefined; inline for (fields) |field| { @@ -815,7 +815,7 @@ pub fn getSymbolExtra(self: InternalObject, index: u32) Symbol.Extra { } pub fn setSymbolExtra(self: *InternalObject, index: u32, extra: Symbol.Extra) void { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.symbols_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), diff --git a/src/MachO/Object.zig b/src/MachO/Object.zig index aa633a7e..73028a83 100644 --- a/src/MachO/Object.zig +++ b/src/MachO/Object.zig @@ -2360,14 +2360,14 @@ pub fn getAtoms(self: *Object) []const Atom.Index { } fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 { - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len); return self.addAtomExtraAssumeCapacity(extra); } fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 { const index = @as(u32, @intCast(self.atoms_extra.items.len)); - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; inline for (fields) |field| { self.atoms_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -2378,7 +2378,7 @@ fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 { } pub fn getAtomExtra(self: Object, index: u32) Atom.Extra { - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; var i: usize = index; var result: Atom.Extra = undefined; inline for (fields) |field| { @@ -2393,7 +2393,7 @@ pub fn getAtomExtra(self: Object, index: u32) Atom.Extra { pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void { assert(index > 0); - const fields = @typeInfo(Atom.Extra).@"struct".fields; + const fields = @typeInfo(Atom.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.atoms_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), @@ -2421,14 +2421,14 @@ pub fn getSymbolRef(self: Object, index: Symbol.Index, macho_file: *MachO) MachO } pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len); return self.addSymbolExtraAssumeCapacity(extra); } fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 { const index = @as(u32, @intCast(self.symbols_extra.items.len)); - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields) |field| { self.symbols_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), @@ -2439,7 +2439,7 @@ fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 { } pub fn getSymbolExtra(self: Object, index: u32) Symbol.Extra { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; var i: usize = index; var result: Symbol.Extra = undefined; inline for (fields) |field| { @@ -2453,7 +2453,7 @@ pub fn getSymbolExtra(self: Object, index: u32) Symbol.Extra { } pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; + const fields = @typeInfo(Symbol.Extra).Struct.fields; inline for (fields, 0..) |field, i| { self.symbols_extra.items[index + i] = switch (field.type) { u32 => @field(extra, field.name), diff --git a/src/MachO/Symbol.zig b/src/MachO/Symbol.zig index 6646da3b..e06acd43 100644 --- a/src/MachO/Symbol.zig +++ b/src/MachO/Symbol.zig @@ -198,7 +198,7 @@ const AddExtraOpts = struct { pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void { var extra = symbol.getExtra(macho_file); - inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| { + inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { if (@field(opts, field.name)) |x| { @field(extra, field.name) = x; } diff --git a/src/Wasm/Object.zig b/src/Wasm/Object.zig index 439b449f..8a256ea5 100644 --- a/src/Wasm/Object.zig +++ b/src/Wasm/Object.zig @@ -867,7 +867,7 @@ fn ElementType(comptime ptr: type) type { /// signedness of the given type `T`. /// Asserts `T` is an integer. fn readLeb(comptime T: type, reader: anytype) !T { - return switch (@typeInfo(T).int.signedness) { + return switch (@typeInfo(T).Int.signedness) { .signed => try leb.readILEB128(T, reader), .unsigned => try leb.readULEB128(T, reader), }; @@ -877,7 +877,7 @@ fn readLeb(comptime T: type, reader: anytype) !T { /// Asserts `T` is an enum fn readEnum(comptime T: type, reader: anytype) !T { switch (@typeInfo(T)) { - .@"enum" => |enum_type| return @as(T, @enumFromInt(try readLeb(enum_type.tag_type, reader))), + .Enum => |enum_type| return @as(T, @enumFromInt(try readLeb(enum_type.tag_type, reader))), else => @compileError("T must be an enum. Instead was given type " ++ @typeName(T)), } } diff --git a/src/riscv.zig b/src/riscv.zig index 0923a25a..ebf0351c 100644 --- a/src/riscv.zig +++ b/src/riscv.zig @@ -419,7 +419,7 @@ pub fn writeSetSub6(comptime op: enum { set, sub }, code: *[1]u8, addend: anytyp pub fn writeAddend( comptime Int: type, comptime op: enum { add, sub }, - code: *[@typeInfo(Int).int.bits / 8]u8, + code: *[@typeInfo(Int).Int.bits / 8]u8, value: anytype, ) void { var V: Int = mem.readInt(Int, code, .little); diff --git a/test/macho.zig b/test/macho.zig index 989b77ff..60b57bbe 100644 --- a/test/macho.zig +++ b/test/macho.zig @@ -2516,7 +2516,7 @@ fn testReexportsZig(b: *Build, opts: Options) *Step { \\ return x; \\} \\comptime { - \\ @export(&foo, .{ .name = "bar", .linkage = .strong }); + \\ @export(foo, .{ .name = "bar", .linkage = .strong }); \\} ); diff --git a/test/test.zig b/test/test.zig index f93e6364..2c04d365 100644 --- a/test/test.zig +++ b/test/test.zig @@ -235,9 +235,9 @@ pub const SkipTestStep = struct { return self; } - fn make(step: *Step, options: Step.MakeOptions) anyerror!void { + fn make(step: *Step, prog_node: std.Progress.Node) anyerror!void { _ = step; - _ = options; + _ = prog_node; return error.MakeSkipped; } }; From 674150aa2d560e620bc31ca927a3f45674411f2f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 21 Oct 2024 12:36:57 +0200 Subject: [PATCH 13/13] Update Zig requirements in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a40a3970..8fec6e9f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ### Building -You will need latest Zig in your path. You can get nightly binaries from [here](https://ziglang.org/download/). +You will need Zig 0.13.0 in your path. You can download it from [here](https://ziglang.org/download/). ``` $ zig build