Commit c30cc4dbbf

Jakub Konka <kubkon@jakubkonka.com>
2021-07-31 10:26:21
macho: don't store allocator in Object
instead, pass it in functions that require it. Also, when parsing relocs, make Object part of the context struct where we pass in additional goodies such as `*MachO` or `*Allocator`.
1 parent 1f95c50
Changed files (4)
src/link/MachO/Archive.zig
@@ -264,14 +264,12 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
     errdefer self.allocator.destroy(object);
 
     object.* = .{
-        .allocator = self.allocator,
-        .arch = self.arch.?,
         .file = try fs.cwd().openFile(self.name.?, .{}),
         .name = name,
         .file_offset = @intCast(u32, try reader.context.getPos()),
         .mtime = try self.header.?.date(),
     };
-    try object.parse();
+    try object.parse(self.allocator, self.arch.?);
     try reader.context.seekTo(0);
 
     return object;
src/link/MachO/Object.zig
@@ -10,20 +10,22 @@ const macho = std.macho;
 const math = std.math;
 const mem = std.mem;
 const sort = std.sort;
+const commands = @import("commands.zig");
+const segmentName = commands.segmentName;
+const sectionName = commands.sectionName;
 
 const Allocator = mem.Allocator;
 const Arch = std.Target.Cpu.Arch;
+const LoadCommand = commands.LoadCommand;
 const MachO = @import("../MachO.zig");
 const TextBlock = @import("TextBlock.zig");
 
-usingnamespace @import("commands.zig");
+file: fs.File,
+name: []const u8,
 
-allocator: *Allocator,
-arch: ?Arch = null,
-header: ?macho.mach_header_64 = null,
-file: ?fs.File = null,
 file_offset: ?u32 = null,
-name: ?[]const u8 = null,
+
+header: ?macho.mach_header_64 = null,
 
 load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
 
@@ -139,15 +141,13 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u
     errdefer allocator.free(name);
 
     object.* = .{
-        .allocator = allocator,
-        .arch = arch,
         .name = name,
         .file = file,
     };
 
-    object.parse() catch |err| switch (err) {
+    object.parse(allocator, arch) catch |err| switch (err) {
         error.EndOfStream, error.NotObject => {
-            object.deinit();
+            object.deinit(allocator);
             allocator.destroy(object);
             return null;
         },
@@ -157,44 +157,35 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u
     return object;
 }
 
-pub fn deinit(self: *Object) void {
+pub fn deinit(self: *Object, allocator: *Allocator) void {
     for (self.load_commands.items) |*lc| {
-        lc.deinit(self.allocator);
+        lc.deinit(allocator);
     }
-    self.load_commands.deinit(self.allocator);
-    self.data_in_code_entries.deinit(self.allocator);
-    self.symtab.deinit(self.allocator);
-    self.strtab.deinit(self.allocator);
-    self.text_blocks.deinit(self.allocator);
-    self.sections_as_symbols.deinit(self.allocator);
-    self.symbol_mapping.deinit(self.allocator);
-    self.reverse_symbol_mapping.deinit(self.allocator);
+    self.load_commands.deinit(allocator);
+    self.data_in_code_entries.deinit(allocator);
+    self.symtab.deinit(allocator);
+    self.strtab.deinit(allocator);
+    self.text_blocks.deinit(allocator);
+    self.sections_as_symbols.deinit(allocator);
+    self.symbol_mapping.deinit(allocator);
+    self.reverse_symbol_mapping.deinit(allocator);
+    allocator.free(self.name);
 
     if (self.debug_info) |*db| {
-        db.deinit(self.allocator);
+        db.deinit(allocator);
     }
 
     if (self.tu_name) |n| {
-        self.allocator.free(n);
+        allocator.free(n);
     }
 
     if (self.tu_comp_dir) |n| {
-        self.allocator.free(n);
-    }
-
-    if (self.name) |n| {
-        self.allocator.free(n);
-    }
-}
-
-pub fn closeFile(self: Object) void {
-    if (self.file) |f| {
-        f.close();
+        allocator.free(n);
     }
 }
 
-pub fn parse(self: *Object) !void {
-    var reader = self.file.?.reader();
+pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void {
+    var reader = self.file.reader();
     if (self.file_offset) |offset| {
         try reader.context.seekTo(offset);
     }
@@ -214,26 +205,28 @@ pub fn parse(self: *Object) !void {
             return error.UnsupportedCpuArchitecture;
         },
     };
-    if (this_arch != self.arch.?) {
-        log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch });
+    if (this_arch != arch) {
+        log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch });
         return error.MismatchedCpuArchitecture;
     }
 
     self.header = header;
 
-    try self.readLoadCommands(reader);
-    try self.parseSymtab();
-    try self.parseDataInCode();
-    try self.parseDebugInfo();
+    try self.readLoadCommands(allocator, reader);
+    try self.parseSymtab(allocator);
+    try self.parseDataInCode(allocator);
+    try self.parseDebugInfo(allocator);
 }
 
-pub fn readLoadCommands(self: *Object, reader: anytype) !void {
+pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) !void {
+    const header = self.header orelse unreachable; // Unreachable here signifies a fatal unexplored condition.
     const offset = self.file_offset orelse 0;
-    try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds);
+
+    try self.load_commands.ensureCapacity(allocator, header.ncmds);
 
     var i: u16 = 0;
-    while (i < self.header.?.ncmds) : (i += 1) {
-        var cmd = try LoadCommand.read(self.allocator, reader);
+    while (i < header.ncmds) : (i += 1) {
+        var cmd = try LoadCommand.read(allocator, reader);
         switch (cmd.cmd()) {
             macho.LC_SEGMENT_64 => {
                 self.segment_cmd_index = i;
@@ -347,26 +340,25 @@ fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64)
     return dices[start..end];
 }
 
-const TextBlockParser = struct {
+const Context = struct {
     allocator: *Allocator,
+    object: *Object,
+    macho_file: *MachO,
+    match: MachO.MatchingSection,
+};
+
+const TextBlockParser = struct {
     section: macho.section_64,
     code: []u8,
     relocs: []macho.relocation_info,
-    object: *Object,
-    macho_file: *MachO,
     nlists: []NlistWithIndex,
     index: u32 = 0,
-    match: MachO.MatchingSection,
 
-    fn peek(self: *TextBlockParser) ?NlistWithIndex {
+    fn peek(self: TextBlockParser) ?NlistWithIndex {
         return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
     }
 
-    const SeniorityContext = struct {
-        object: *Object,
-    };
-
-    fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {
+    fn lessThanBySeniority(context: Context, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {
         if (!MachO.symbolIsExt(rhs.nlist)) {
             return MachO.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx));
         } else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) {
@@ -376,10 +368,10 @@ const TextBlockParser = struct {
         }
     }
 
-    pub fn next(self: *TextBlockParser) !?*TextBlock {
+    pub fn next(self: *TextBlockParser, context: Context) !?*TextBlock {
         if (self.index == self.nlists.len) return null;
 
-        var aliases = std.ArrayList(NlistWithIndex).init(self.allocator);
+        var aliases = std.ArrayList(NlistWithIndex).init(context.allocator);
         defer aliases.deinit();
 
         const next_nlist: ?NlistWithIndex = blk: while (true) {
@@ -397,7 +389,7 @@ const TextBlockParser = struct {
         } else null;
 
         for (aliases.items) |*nlist_with_index| {
-            nlist_with_index.index = self.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
+            nlist_with_index.index = context.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
         }
 
         if (aliases.items.len > 1) {
@@ -405,14 +397,14 @@ const TextBlockParser = struct {
             sort.sort(
                 NlistWithIndex,
                 aliases.items,
-                SeniorityContext{ .object = self.object },
+                context,
                 TextBlockParser.lessThanBySeniority,
             );
         }
 
         const senior_nlist = aliases.pop();
-        const senior_sym = &self.macho_file.locals.items[senior_nlist.index];
-        senior_sym.n_sect = self.macho_file.section_to_ordinal.get(self.match) orelse unreachable;
+        const senior_sym = &context.macho_file.locals.items[senior_nlist.index];
+        senior_sym.n_sect = context.macho_file.section_to_ordinal.get(context.match) orelse unreachable;
 
         const start_addr = senior_nlist.nlist.n_value - self.section.addr;
         const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size;
@@ -426,7 +418,7 @@ const TextBlockParser = struct {
         else
             max_align;
 
-        const stab: ?TextBlock.Stab = if (self.object.debug_info) |di| blk: {
+        const stab: ?TextBlock.Stab = if (context.object.debug_info) |di| blk: {
             // TODO there has to be a better to handle this.
             for (di.inner.func_list.items) |func| {
                 if (func.pc_range) |range| {
@@ -442,35 +434,37 @@ const TextBlockParser = struct {
             break :blk .static;
         } else null;
 
-        const block = try self.macho_file.base.allocator.create(TextBlock);
+        const block = try context.allocator.create(TextBlock);
         block.* = TextBlock.empty;
         block.local_sym_index = senior_nlist.index;
         block.stab = stab;
         block.size = size;
         block.alignment = actual_align;
-        try self.macho_file.managed_blocks.append(self.macho_file.base.allocator, block);
+        try context.macho_file.managed_blocks.append(context.allocator, block);
 
-        try block.code.appendSlice(self.macho_file.base.allocator, code);
+        try block.code.appendSlice(context.allocator, code);
 
-        try block.aliases.ensureTotalCapacity(self.macho_file.base.allocator, aliases.items.len);
+        try block.aliases.ensureTotalCapacity(context.allocator, aliases.items.len);
         for (aliases.items) |alias| {
             block.aliases.appendAssumeCapacity(alias.index);
-            const sym = &self.macho_file.locals.items[alias.index];
-            sym.n_sect = self.macho_file.section_to_ordinal.get(self.match) orelse unreachable;
+            const sym = &context.macho_file.locals.items[alias.index];
+            sym.n_sect = context.macho_file.section_to_ordinal.get(context.match) orelse unreachable;
         }
 
-        try block.parseRelocsFromObject(self.macho_file.base.allocator, self.relocs, self.object, .{
+        try block.parseRelocs(self.relocs, .{
             .base_addr = start_addr,
-            .macho_file = self.macho_file,
+            .allocator = context.allocator,
+            .object = context.object,
+            .macho_file = context.macho_file,
         });
 
-        if (self.macho_file.has_dices) {
+        if (context.macho_file.has_dices) {
             const dices = filterDice(
-                self.object.data_in_code_entries.items,
+                context.object.data_in_code_entries.items,
                 senior_nlist.nlist.n_value,
                 senior_nlist.nlist.n_value + size,
             );
-            try block.dices.ensureTotalCapacity(self.macho_file.base.allocator, dices.len);
+            try block.dices.ensureTotalCapacity(context.allocator, dices.len);
 
             for (dices) |dice| {
                 block.dices.appendAssumeCapacity(.{
@@ -487,16 +481,16 @@ const TextBlockParser = struct {
     }
 };
 
-pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
+pub fn parseTextBlocks(self: *Object, allocator: *Allocator, macho_file: *MachO) !void {
     const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
 
-    log.debug("analysing {s}", .{self.name.?});
+    log.debug("analysing {s}", .{self.name});
 
     // You would expect that the symbol table is at least pre-sorted based on symbol's type:
     // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
     // the GO compiler does not necessarily respect that therefore we sort immediately by type
     // and address within.
-    var sorted_all_nlists = std.ArrayList(NlistWithIndex).init(self.allocator);
+    var sorted_all_nlists = std.ArrayList(NlistWithIndex).init(allocator);
     defer sorted_all_nlists.deinit();
     try sorted_all_nlists.ensureTotalCapacity(self.symtab.items.len);
 
@@ -540,14 +534,14 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
         };
 
         // Read section's code
-        var code = try self.allocator.alloc(u8, @intCast(usize, sect.size));
-        defer self.allocator.free(code);
-        _ = try self.file.?.preadAll(code, sect.offset);
+        var code = try allocator.alloc(u8, @intCast(usize, sect.size));
+        defer allocator.free(code);
+        _ = try self.file.preadAll(code, sect.offset);
 
         // Read section's list of relocations
-        var raw_relocs = try self.allocator.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
-        defer self.allocator.free(raw_relocs);
-        _ = try self.file.?.preadAll(raw_relocs, sect.reloff);
+        var raw_relocs = try allocator.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
+        defer allocator.free(raw_relocs);
+        _ = try self.file.preadAll(raw_relocs, sect.reloff);
         const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs);
 
         // Symbols within this section only.
@@ -579,46 +573,48 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
                 // as a temporary symbol and insert the matching TextBlock.
                 const first_nlist = filtered_nlists[0].nlist;
                 if (first_nlist.n_value > sect.addr) {
-                    const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
-                        self.name.?,
+                    const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{
+                        self.name,
                         segmentName(sect),
                         sectionName(sect),
                     });
-                    defer self.allocator.free(sym_name);
+                    defer allocator.free(sym_name);
 
                     const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
                         const block_local_sym_index = @intCast(u32, macho_file.locals.items.len);
-                        try macho_file.locals.append(macho_file.base.allocator, .{
+                        try macho_file.locals.append(allocator, .{
                             .n_strx = try macho_file.makeString(sym_name),
                             .n_type = macho.N_SECT,
                             .n_sect = macho_file.section_to_ordinal.get(match) orelse unreachable,
                             .n_desc = 0,
                             .n_value = sect.addr,
                         });
-                        try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, block_local_sym_index);
+                        try self.sections_as_symbols.putNoClobber(allocator, sect_id, block_local_sym_index);
                         break :blk block_local_sym_index;
                     };
 
                     const block_code = code[0 .. first_nlist.n_value - sect.addr];
                     const block_size = block_code.len;
 
-                    const block = try macho_file.base.allocator.create(TextBlock);
+                    const block = try allocator.create(TextBlock);
                     block.* = TextBlock.empty;
                     block.local_sym_index = block_local_sym_index;
                     block.size = block_size;
                     block.alignment = sect.@"align";
-                    try macho_file.managed_blocks.append(macho_file.base.allocator, block);
+                    try macho_file.managed_blocks.append(allocator, block);
 
-                    try block.code.appendSlice(macho_file.base.allocator, block_code);
+                    try block.code.appendSlice(allocator, block_code);
 
-                    try block.parseRelocsFromObject(self.allocator, relocs, self, .{
+                    try block.parseRelocs(relocs, .{
                         .base_addr = 0,
+                        .allocator = allocator,
+                        .object = self,
                         .macho_file = macho_file,
                     });
 
                     if (macho_file.has_dices) {
                         const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size);
-                        try block.dices.ensureTotalCapacity(macho_file.base.allocator, dices.len);
+                        try block.dices.ensureTotalCapacity(allocator, dices.len);
 
                         for (dices) |dice| {
                             block.dices.appendAssumeCapacity(.{
@@ -645,24 +641,25 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
                         block.prev = last.*;
                         last.* = block;
                     } else {
-                        try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block);
+                        try macho_file.blocks.putNoClobber(allocator, match, block);
                     }
 
-                    try self.text_blocks.append(self.allocator, block);
+                    try self.text_blocks.append(allocator, block);
                 }
 
                 var parser = TextBlockParser{
-                    .allocator = self.allocator,
                     .section = sect,
                     .code = code,
                     .relocs = relocs,
-                    .object = self,
-                    .macho_file = macho_file,
                     .nlists = filtered_nlists,
-                    .match = match,
                 };
 
-                while (try parser.next()) |block| {
+                while (try parser.next(.{
+                    .allocator = allocator,
+                    .object = self,
+                    .macho_file = macho_file,
+                    .match = match,
+                })) |block| {
                     const sym = macho_file.locals.items[block.local_sym_index];
                     const is_ext = blk: {
                         const orig_sym_id = self.reverse_symbol_mapping.get(block.local_sym_index) orelse unreachable;
@@ -675,9 +672,9 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
                             if (global_object != self) {
                                 log.debug("deduping definition of {s} in {s}", .{
                                     macho_file.getString(sym.n_strx),
-                                    self.name.?,
+                                    self.name,
                                 });
-                                log.debug("  already defined in {s}", .{global_object.name.?});
+                                log.debug("  already defined in {s}", .{global_object.name});
                                 continue;
                             }
                         }
@@ -688,7 +685,7 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
                             // In x86_64 relocs, it can so happen that the compiler refers to the same
                             // atom by both the actual assigned symbol and the start of the section. In this
                             // case, we need to link the two together so add an alias.
-                            try block.aliases.append(macho_file.base.allocator, alias);
+                            try block.aliases.append(allocator, alias);
                         }
                     }
 
@@ -708,10 +705,10 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
                         block.prev = last.*;
                         last.* = block;
                     } else {
-                        try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block);
+                        try macho_file.blocks.putNoClobber(allocator, match, block);
                     }
 
-                    try self.text_blocks.append(self.allocator, block);
+                    try self.text_blocks.append(allocator, block);
                 }
 
                 break :next;
@@ -720,43 +717,45 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
             // Since there is no symbol to refer to this block, we create
             // a temp one, unless we already did that when working out the relocations
             // of other text blocks.
-            const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
-                self.name.?,
+            const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{
+                self.name,
                 segmentName(sect),
                 sectionName(sect),
             });
-            defer self.allocator.free(sym_name);
+            defer allocator.free(sym_name);
 
             const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
                 const block_local_sym_index = @intCast(u32, macho_file.locals.items.len);
-                try macho_file.locals.append(macho_file.base.allocator, .{
+                try macho_file.locals.append(allocator, .{
                     .n_strx = try macho_file.makeString(sym_name),
                     .n_type = macho.N_SECT,
                     .n_sect = macho_file.section_to_ordinal.get(match) orelse unreachable,
                     .n_desc = 0,
                     .n_value = sect.addr,
                 });
-                try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, block_local_sym_index);
+                try self.sections_as_symbols.putNoClobber(allocator, sect_id, block_local_sym_index);
                 break :blk block_local_sym_index;
             };
 
-            const block = try macho_file.base.allocator.create(TextBlock);
+            const block = try allocator.create(TextBlock);
             block.* = TextBlock.empty;
             block.local_sym_index = block_local_sym_index;
             block.size = sect.size;
             block.alignment = sect.@"align";
-            try macho_file.managed_blocks.append(macho_file.base.allocator, block);
+            try macho_file.managed_blocks.append(allocator, block);
 
-            try block.code.appendSlice(macho_file.base.allocator, code);
+            try block.code.appendSlice(allocator, code);
 
-            try block.parseRelocsFromObject(self.allocator, relocs, self, .{
+            try block.parseRelocs(relocs, .{
                 .base_addr = 0,
+                .allocator = allocator,
+                .object = self,
                 .macho_file = macho_file,
             });
 
             if (macho_file.has_dices) {
                 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);
-                try block.dices.ensureTotalCapacity(macho_file.base.allocator, dices.len);
+                try block.dices.ensureTotalCapacity(allocator, dices.len);
 
                 for (dices) |dice| {
                     block.dices.appendAssumeCapacity(.{
@@ -772,7 +771,7 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
             // the filtered symbols and note which symbol is contained within so that
             // we can properly allocate addresses down the line.
             // While we're at it, we need to update segment,section mapping of each symbol too.
-            try block.contained.ensureTotalCapacity(self.allocator, filtered_nlists.len);
+            try block.contained.ensureTotalCapacity(allocator, filtered_nlists.len);
 
             for (filtered_nlists) |nlist_with_index| {
                 const nlist = nlist_with_index.nlist;
@@ -819,35 +818,35 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
                 block.prev = last.*;
                 last.* = block;
             } else {
-                try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block);
+                try macho_file.blocks.putNoClobber(allocator, match, block);
             }
 
-            try self.text_blocks.append(self.allocator, block);
+            try self.text_blocks.append(allocator, block);
         }
     }
 }
 
-fn parseSymtab(self: *Object) !void {
+fn parseSymtab(self: *Object, allocator: *Allocator) !void {
     const index = self.symtab_cmd_index orelse return;
     const symtab_cmd = self.load_commands.items[index].Symtab;
 
-    var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
-    defer self.allocator.free(symtab);
-    _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff);
+    var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
+    defer allocator.free(symtab);
+    _ = try self.file.preadAll(symtab, symtab_cmd.symoff);
     const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));
-    try self.symtab.appendSlice(self.allocator, slice);
+    try self.symtab.appendSlice(allocator, slice);
 
-    var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize);
-    defer self.allocator.free(strtab);
-    _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff);
-    try self.strtab.appendSlice(self.allocator, strtab);
+    var strtab = try allocator.alloc(u8, symtab_cmd.strsize);
+    defer allocator.free(strtab);
+    _ = try self.file.preadAll(strtab, symtab_cmd.stroff);
+    try self.strtab.appendSlice(allocator, strtab);
 }
 
-pub fn parseDebugInfo(self: *Object) !void {
-    log.debug("parsing debug info in '{s}'", .{self.name.?});
+pub fn parseDebugInfo(self: *Object, allocator: *Allocator) !void {
+    log.debug("parsing debug info in '{s}'", .{self.name});
 
     var debug_info = blk: {
-        var di = try DebugInfo.parseFromObject(self.allocator, self);
+        var di = try DebugInfo.parseFromObject(allocator, self);
         break :blk di orelse return;
     };
 
@@ -855,7 +854,7 @@ pub fn parseDebugInfo(self: *Object) !void {
     const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
         error.MissingDebugInfo => {
             // TODO audit cases with missing debug info and audit our dwarf.zig module.
-            log.debug("invalid or missing debug info in {s}; skipping", .{self.name.?});
+            log.debug("invalid or missing debug info in {s}; skipping", .{self.name});
             return;
         },
         else => |e| return e,
@@ -864,26 +863,25 @@ pub fn parseDebugInfo(self: *Object) !void {
     const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
 
     self.debug_info = debug_info;
-    self.tu_name = try self.allocator.dupe(u8, name);
-    self.tu_comp_dir = try self.allocator.dupe(u8, comp_dir);
+    self.tu_name = try allocator.dupe(u8, name);
+    self.tu_comp_dir = try allocator.dupe(u8, comp_dir);
 
     if (self.mtime == null) {
         self.mtime = mtime: {
-            const file = self.file orelse break :mtime 0;
-            const stat = file.stat() catch break :mtime 0;
+            const stat = self.file.stat() catch break :mtime 0;
             break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
         };
     }
 }
 
-pub fn parseDataInCode(self: *Object) !void {
+pub fn parseDataInCode(self: *Object, allocator: *Allocator) !void {
     const index = self.data_in_code_cmd_index orelse return;
     const data_in_code = self.load_commands.items[index].LinkeditData;
 
-    var buffer = try self.allocator.alloc(u8, data_in_code.datasize);
-    defer self.allocator.free(buffer);
+    var buffer = try allocator.alloc(u8, data_in_code.datasize);
+    defer allocator.free(buffer);
 
-    _ = try self.file.?.preadAll(buffer, data_in_code.dataoff);
+    _ = try self.file.preadAll(buffer, data_in_code.dataoff);
 
     var stream = io.fixedBufferStream(buffer);
     var reader = stream.reader();
@@ -892,7 +890,7 @@ pub fn parseDataInCode(self: *Object) !void {
             error.EndOfStream => break,
             else => |e| return e,
         };
-        try self.data_in_code_entries.append(self.allocator, dice);
+        try self.data_in_code_entries.append(allocator, dice);
     }
 }
 
@@ -900,7 +898,7 @@ fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 {
     const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
     const sect = seg.sections.items[index];
     var buffer = try allocator.alloc(u8, @intCast(usize, sect.size));
-    _ = try self.file.?.preadAll(buffer, sect.offset);
+    _ = try self.file.preadAll(buffer, sect.offset);
     return buffer;
 }
 
src/link/MachO/TextBlock.zig
@@ -606,12 +606,14 @@ pub fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
 
 const RelocContext = struct {
     base_addr: u64 = 0,
+    allocator: *Allocator,
+    object: *Object,
     macho_file: *MachO,
 };
 
-fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocContext) !Relocation {
+fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation {
     var parsed_rel = Relocation{
-        .offset = @intCast(u32, @intCast(u64, rel.r_address) - ctx.base_addr),
+        .offset = @intCast(u32, @intCast(u64, rel.r_address) - context.base_addr),
         .where = undefined,
         .where_index = undefined,
         .payload = undefined,
@@ -620,44 +622,44 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo
     if (rel.r_extern == 0) {
         const sect_id = @intCast(u16, rel.r_symbolnum - 1);
 
-        const local_sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
-            const seg = object.load_commands.items[object.segment_cmd_index.?].Segment;
+        const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {
+            const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment;
             const sect = seg.sections.items[sect_id];
-            const match = (try ctx.macho_file.getMatchingSection(sect)) orelse unreachable;
-            const local_sym_index = @intCast(u32, ctx.macho_file.locals.items.len);
-            const sym_name = try std.fmt.allocPrint(ctx.macho_file.base.allocator, "l_{s}_{s}_{s}", .{
-                object.name.?,
+            const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable;
+            const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);
+            const sym_name = try std.fmt.allocPrint(context.allocator, "l_{s}_{s}_{s}", .{
+                context.object.name,
                 commands.segmentName(sect),
                 commands.sectionName(sect),
             });
-            defer ctx.macho_file.base.allocator.free(sym_name);
+            defer context.allocator.free(sym_name);
 
-            try ctx.macho_file.locals.append(ctx.macho_file.base.allocator, .{
-                .n_strx = try ctx.macho_file.makeString(sym_name),
+            try context.macho_file.locals.append(context.allocator, .{
+                .n_strx = try context.macho_file.makeString(sym_name),
                 .n_type = macho.N_SECT,
-                .n_sect = ctx.macho_file.section_to_ordinal.get(match) orelse unreachable,
+                .n_sect = context.macho_file.section_to_ordinal.get(match) orelse unreachable,
                 .n_desc = 0,
                 .n_value = sect.addr,
             });
-            try object.sections_as_symbols.putNoClobber(object.allocator, sect_id, local_sym_index);
+            try context.object.sections_as_symbols.putNoClobber(context.allocator, sect_id, local_sym_index);
             break :blk local_sym_index;
         };
 
         parsed_rel.where = .local;
         parsed_rel.where_index = local_sym_index;
     } else {
-        const sym = object.symtab.items[rel.r_symbolnum];
-        const sym_name = object.getString(sym.n_strx);
+        const sym = context.object.symtab.items[rel.r_symbolnum];
+        const sym_name = context.object.getString(sym.n_strx);
 
         if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
-            const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
+            const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
             parsed_rel.where = .local;
             parsed_rel.where_index = where_index;
         } else {
-            const n_strx = ctx.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{
-                .strtab = &ctx.macho_file.strtab,
+            const n_strx = context.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{
+                .strtab = &context.macho_file.strtab,
             }) orelse unreachable;
-            const resolv = ctx.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
+            const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
             switch (resolv.where) {
                 .global => {
                     parsed_rel.where = .local;
@@ -675,29 +677,24 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo
     return parsed_rel;
 }
 
-pub fn parseRelocsFromObject(
-    self: *TextBlock,
-    allocator: *Allocator,
-    relocs: []macho.relocation_info,
-    object: *Object,
-    ctx: RelocContext,
-) !void {
-    const filtered_relocs = filterRelocs(relocs, ctx.base_addr, ctx.base_addr + self.size);
+pub fn parseRelocs(self: *TextBlock, relocs: []macho.relocation_info, context: RelocContext) !void {
+    const filtered_relocs = filterRelocs(relocs, context.base_addr, context.base_addr + self.size);
     var it = RelocIterator{
         .buffer = filtered_relocs,
     };
 
     var addend: u32 = 0;
     var subtractor: ?u32 = null;
+    const arch = context.macho_file.base.options.target.cpu.arch;
 
     while (it.next()) |rel| {
-        if (isAddend(rel, object.arch.?)) {
+        if (isAddend(rel, arch)) {
             // Addend is not a relocation with effect on the TextBlock, so
             // parse it and carry on.
             assert(addend == 0); // Oh no, addend was not reset!
             addend = rel.r_symbolnum;
 
-            // Verify ADDEND is followed by a load.
+            // Verify ADDEND is followed by a PAGE21 or PAGEOFF12.
             const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
             switch (next) {
                 .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {},
@@ -709,28 +706,28 @@ pub fn parseRelocsFromObject(
             continue;
         }
 
-        if (isSubtractor(rel, object.arch.?)) {
+        if (isSubtractor(rel, arch)) {
             // Subtractor is not a relocation with effect on the TextBlock, so
             // parse it and carry on.
             assert(subtractor == null); // Oh no, subtractor was not reset!
             assert(rel.r_extern == 1);
-            const sym = object.symtab.items[rel.r_symbolnum];
-            const sym_name = object.getString(sym.n_strx);
+            const sym = context.object.symtab.items[rel.r_symbolnum];
+            const sym_name = context.object.getString(sym.n_strx);
 
             if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
-                const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
+                const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
                 subtractor = where_index;
             } else {
-                const n_strx = ctx.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{
-                    .strtab = &ctx.macho_file.strtab,
+                const n_strx = context.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{
+                    .strtab = &context.macho_file.strtab,
                 }) orelse unreachable;
-                const resolv = ctx.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
+                const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable;
                 assert(resolv.where == .global);
                 subtractor = resolv.local_sym_index;
             }
 
             // Verify SUBTRACTOR is followed by UNSIGNED.
-            switch (object.arch.?) {
+            switch (arch) {
                 .aarch64 => {
                     const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type);
                     if (next != .ARM64_RELOC_UNSIGNED) {
@@ -750,19 +747,19 @@ pub fn parseRelocsFromObject(
             continue;
         }
 
-        var parsed_rel = try initRelocFromObject(rel, object, ctx);
+        var parsed_rel = try initRelocFromObject(rel, context);
 
-        switch (object.arch.?) {
+        switch (arch) {
             .aarch64 => {
                 const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type);
                 switch (rel_type) {
                     .ARM64_RELOC_ADDEND => unreachable,
                     .ARM64_RELOC_SUBTRACTOR => unreachable,
                     .ARM64_RELOC_BRANCH26 => {
-                        self.parseBranch(rel, &parsed_rel, ctx);
+                        self.parseBranch(rel, &parsed_rel, context);
                     },
                     .ARM64_RELOC_UNSIGNED => {
-                        self.parseUnsigned(rel, &parsed_rel, subtractor, ctx);
+                        self.parseUnsigned(rel, &parsed_rel, subtractor, context);
                         subtractor = null;
                     },
                     .ARM64_RELOC_PAGE21,
@@ -790,10 +787,10 @@ pub fn parseRelocsFromObject(
                 switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
                     .X86_64_RELOC_SUBTRACTOR => unreachable,
                     .X86_64_RELOC_BRANCH => {
-                        self.parseBranch(rel, &parsed_rel, ctx);
+                        self.parseBranch(rel, &parsed_rel, context);
                     },
                     .X86_64_RELOC_UNSIGNED => {
-                        self.parseUnsigned(rel, &parsed_rel, subtractor, ctx);
+                        self.parseUnsigned(rel, &parsed_rel, subtractor, context);
                         subtractor = null;
                     },
                     .X86_64_RELOC_SIGNED,
@@ -801,7 +798,7 @@ pub fn parseRelocsFromObject(
                     .X86_64_RELOC_SIGNED_2,
                     .X86_64_RELOC_SIGNED_4,
                     => {
-                        self.parseSigned(rel, &parsed_rel, ctx);
+                        self.parseSigned(rel, &parsed_rel, context);
                     },
                     .X86_64_RELOC_GOT_LOAD,
                     .X86_64_RELOC_GOT,
@@ -814,7 +811,7 @@ pub fn parseRelocsFromObject(
             else => unreachable,
         }
 
-        try self.relocs.append(allocator, parsed_rel);
+        try self.relocs.append(context.allocator, parsed_rel);
 
         const is_via_got = switch (parsed_rel.payload) {
             .pointer_to_got => true,
@@ -832,23 +829,23 @@ pub fn parseRelocsFromObject(
                 },
                 .where_index = parsed_rel.where_index,
             };
-            if (ctx.macho_file.got_entries_map.contains(key)) break :blk;
+            if (context.macho_file.got_entries_map.contains(key)) break :blk;
 
-            const got_index = @intCast(u32, ctx.macho_file.got_entries.items.len);
-            try ctx.macho_file.got_entries.append(ctx.macho_file.base.allocator, key);
-            try ctx.macho_file.got_entries_map.putNoClobber(ctx.macho_file.base.allocator, key, got_index);
+            const got_index = @intCast(u32, context.macho_file.got_entries.items.len);
+            try context.macho_file.got_entries.append(context.allocator, key);
+            try context.macho_file.got_entries_map.putNoClobber(context.allocator, key, got_index);
         } else if (parsed_rel.payload == .unsigned) {
             switch (parsed_rel.where) {
                 .import => {
-                    try self.bindings.append(allocator, .{
+                    try self.bindings.append(context.allocator, .{
                         .local_sym_index = parsed_rel.where_index,
                         .offset = parsed_rel.offset,
                     });
                 },
                 .local => {
-                    const source_sym = ctx.macho_file.locals.items[self.local_sym_index];
-                    const match = ctx.macho_file.section_ordinals.items[source_sym.n_sect];
-                    const seg = ctx.macho_file.load_commands.items[match.seg].Segment;
+                    const source_sym = context.macho_file.locals.items[self.local_sym_index];
+                    const match = context.macho_file.section_ordinals.items[source_sym.n_sect];
+                    const seg = context.macho_file.load_commands.items[match.seg].Segment;
                     const sect = seg.sections.items[match.sect];
                     const sect_type = commands.sectionType(sect);
 
@@ -858,12 +855,12 @@ pub fn parseRelocsFromObject(
                         // TODO actually, a check similar to what dyld is doing, that is, verifying
                         // that the segment is writable should be enough here.
                         const is_right_segment = blk: {
-                            if (ctx.macho_file.data_segment_cmd_index) |idx| {
+                            if (context.macho_file.data_segment_cmd_index) |idx| {
                                 if (match.seg == idx) {
                                     break :blk true;
                                 }
                             }
-                            if (ctx.macho_file.data_const_segment_cmd_index) |idx| {
+                            if (context.macho_file.data_const_segment_cmd_index) |idx| {
                                 if (match.seg == idx) {
                                     break :blk true;
                                 }
@@ -884,17 +881,17 @@ pub fn parseRelocsFromObject(
                     };
 
                     if (should_rebase) {
-                        try self.rebases.append(allocator, parsed_rel.offset);
+                        try self.rebases.append(context.allocator, parsed_rel.offset);
                     }
                 },
             }
         } else if (parsed_rel.payload == .branch) blk: {
             if (parsed_rel.where != .import) break :blk;
-            if (ctx.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;
+            if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;
 
-            const stubs_index = @intCast(u32, ctx.macho_file.stubs.items.len);
-            try ctx.macho_file.stubs.append(ctx.macho_file.base.allocator, parsed_rel.where_index);
-            try ctx.macho_file.stubs_map.putNoClobber(ctx.macho_file.base.allocator, parsed_rel.where_index, stubs_index);
+            const stubs_index = @intCast(u32, context.macho_file.stubs.items.len);
+            try context.macho_file.stubs.append(context.allocator, parsed_rel.where_index);
+            try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stubs_index);
         }
     }
 }
@@ -917,7 +914,7 @@ fn parseUnsigned(
     rel: macho.relocation_info,
     out: *Relocation,
     subtractor: ?u32,
-    ctx: RelocContext,
+    context: RelocContext,
 ) void {
     assert(rel.r_pcrel == 0);
 
@@ -934,7 +931,7 @@ fn parseUnsigned(
 
     if (rel.r_extern == 0) {
         assert(out.where == .local);
-        const target_sym = ctx.macho_file.locals.items[out.where_index];
+        const target_sym = context.macho_file.locals.items[out.where_index];
         addend -= @intCast(i64, target_sym.n_value);
     }
 
@@ -947,14 +944,14 @@ fn parseUnsigned(
     };
 }
 
-fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void {
+fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void {
     _ = self;
     assert(rel.r_pcrel == 1);
     assert(rel.r_length == 2);
 
     out.payload = .{
         .branch = .{
-            .arch = ctx.macho_file.base.options.target.cpu.arch,
+            .arch = context.macho_file.base.options.target.cpu.arch,
         },
     };
 }
@@ -1015,7 +1012,7 @@ fn parsePointerToGot(self: TextBlock, rel: macho.relocation_info, out: *Relocati
     };
 }
 
-fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void {
+fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void {
     assert(rel.r_pcrel == 1);
     assert(rel.r_length == 2);
 
@@ -1030,10 +1027,10 @@ fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct
     var addend: i64 = mem.readIntLittle(i32, self.code.items[out.offset..][0..4]) + correction;
 
     if (rel.r_extern == 0) {
-        const source_sym = ctx.macho_file.locals.items[self.local_sym_index];
+        const source_sym = context.macho_file.locals.items[self.local_sym_index];
         const target_sym = switch (out.where) {
-            .local => ctx.macho_file.locals.items[out.where_index],
-            .import => ctx.macho_file.imports.items[out.where_index],
+            .local => context.macho_file.locals.items[out.where_index],
+            .import => context.macho_file.imports.items[out.where_index],
         };
         addend = @intCast(i64, source_sym.n_value + out.offset + 4) + addend - @intCast(i64, target_sym.n_value);
     }
src/link/MachO.zig
@@ -2004,21 +2004,21 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
         if (symbolIsStab(sym)) {
             log.err("unhandled symbol type: stab", .{});
             log.err("  symbol '{s}'", .{sym_name});
-            log.err("  first definition in '{s}'", .{object.name.?});
+            log.err("  first definition in '{s}'", .{object.name});
             return error.UnhandledSymbolType;
         }
 
         if (symbolIsIndr(sym)) {
             log.err("unhandled symbol type: indirect", .{});
             log.err("  symbol '{s}'", .{sym_name});
-            log.err("  first definition in '{s}'", .{object.name.?});
+            log.err("  first definition in '{s}'", .{object.name});
             return error.UnhandledSymbolType;
         }
 
         if (symbolIsAbs(sym)) {
             log.err("unhandled symbol type: absolute", .{});
             log.err("  symbol '{s}'", .{sym_name});
-            log.err("  first definition in '{s}'", .{object.name.?});
+            log.err("  first definition in '{s}'", .{object.name});
             return error.UnhandledSymbolType;
         }
 
@@ -2068,8 +2068,8 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
                         !(symbolIsWeakDef(global.*) or symbolIsPext(global.*)))
                     {
                         log.err("symbol '{s}' defined multiple times", .{sym_name});
-                        log.err("  first definition in '{s}'", .{self.objects.items[resolv.file].name.?});
-                        log.err("  next definition in '{s}'", .{object.name.?});
+                        log.err("  first definition in '{s}'", .{self.objects.items[resolv.file].name});
+                        log.err("  next definition in '{s}'", .{object.name});
                         return error.MultipleSymbolDefinitions;
                     }
 
@@ -2448,7 +2448,7 @@ fn resolveSymbols(self: *MachO) !void {
         const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;
 
         log.err("undefined reference to symbol '{s}'", .{sym_name});
-        log.err("  first referenced in '{s}'", .{self.objects.items[resolv.file].name.?});
+        log.err("  first referenced in '{s}'", .{self.objects.items[resolv.file].name});
         has_undefined = true;
     }
 
@@ -2457,7 +2457,7 @@ fn resolveSymbols(self: *MachO) !void {
 
 fn parseTextBlocks(self: *MachO) !void {
     for (self.objects.items) |object| {
-        try object.parseTextBlocks(self);
+        try object.parseTextBlocks(self.base.allocator, self);
     }
 }
 
@@ -3190,7 +3190,7 @@ fn writeSymbolTable(self: *MachO) !void {
                 .n_value = 0,
             });
             locals.appendAssumeCapacity(.{
-                .n_strx = try self.makeString(object.name.?),
+                .n_strx = try self.makeString(object.name),
                 .n_type = macho.N_OSO,
                 .n_sect = 0,
                 .n_desc = 1,
@@ -3334,7 +3334,7 @@ pub fn deinit(self: *MachO) void {
     self.symbol_resolver.deinit(self.base.allocator);
 
     for (self.objects.items) |object| {
-        object.deinit();
+        object.deinit(self.base.allocator);
         self.base.allocator.destroy(object);
     }
     self.objects.deinit(self.base.allocator);
@@ -3372,7 +3372,7 @@ pub fn deinit(self: *MachO) void {
 
 pub fn closeFiles(self: MachO) void {
     for (self.objects.items) |object| {
-        object.closeFile();
+        object.file.close();
     }
     for (self.archives.items) |archive| {
         archive.closeFile();
@@ -5913,7 +5913,7 @@ fn printSymtabAndTextBlock(self: *MachO) void {
 
     log.debug("mappings", .{});
     for (self.objects.items) |object| {
-        log.debug("  in object {s}", .{object.name.?});
+        log.debug("  in object {s}", .{object.name});
         for (object.symtab.items) |sym, sym_id| {
             if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| {
                 log.debug("    | {d} => {d}", .{ sym_id, local_id });