Commit 54a403d4ff

Jakub Konka <kubkon@jakubkonka.com>
2021-07-16 17:18:53
zld: replace parsed reloc with a simple wrapper around macho.relocation_info
1 parent 5a2bea2
src/link/MachO/Dylib.zig
@@ -12,8 +12,8 @@ const fat = @import("fat.zig");
 
 const Allocator = mem.Allocator;
 const Arch = std.Target.Cpu.Arch;
-const Symbol = @import("Symbol.zig");
 const LibStub = @import("../tapi.zig").LibStub;
+const Zld = @import("Zld.zig");
 
 usingnamespace @import("commands.zig");
 
@@ -324,7 +324,7 @@ fn parseSymbols(self: *Dylib) !void {
     _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
 
     for (slice) |sym| {
-        const add_to_symtab = Symbol.isExt(sym) and (Symbol.isSect(sym) or Symbol.isIndr(sym));
+        const add_to_symtab = Zld.symbolIsExt(sym) and (Zld.symbolIsSect(sym) or Zld.symbolIsIndr(sym));
 
         if (!add_to_symtab) continue;
 
src/link/MachO/Object.zig
@@ -9,13 +9,10 @@ const log = std.log.scoped(.object);
 const macho = std.macho;
 const math = std.math;
 const mem = std.mem;
-const reloc = @import("reloc.zig");
 const sort = std.sort;
 
 const Allocator = mem.Allocator;
 const Arch = std.Target.Cpu.Arch;
-const Relocation = reloc.Relocation;
-const Symbol = @import("Symbol.zig");
 const TextBlock = @import("TextBlock.zig");
 const Zld = @import("Zld.zig");
 
@@ -57,6 +54,8 @@ tu_comp_dir: ?[]const u8 = null,
 mtime: ?u64 = null,
 
 text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
+sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
+symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
 
 const DebugInfo = struct {
     inner: dwarf.DwarfInfo,
@@ -163,6 +162,8 @@ pub fn deinit(self: *Object) void {
     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);
 
     if (self.debug_info) |*db| {
         db.deinit(self.allocator);
@@ -372,20 +373,17 @@ const TextBlockParser = struct {
     }
 
     const SeniorityContext = struct {
-        zld: *Zld,
+        object: *Object,
     };
 
     fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {
-        const lsym = context.zld.locals.items[lhs.index];
-        const rsym = context.zld.locals.items[rhs.index];
-        const lreg = lsym.payload.regular;
-        const rreg = rsym.payload.regular;
-
-        return switch (rreg.linkage) {
-            .global => true,
-            .linkage_unit => lreg.linkage == .translation_unit,
-            else => lsym.isTemp(context.zld),
-        };
+        if (!Zld.symbolIsExt(rhs.nlist)) {
+            return Zld.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx));
+        } else if (Zld.symbolIsPext(rhs.nlist) or Zld.symbolIsWeakDef(rhs.nlist)) {
+            return !Zld.symbolIsExt(lhs.nlist);
+        } else {
+            return true;
+        }
     }
 
     pub fn next(self: *TextBlockParser) !?*TextBlock {
@@ -409,6 +407,7 @@ const TextBlockParser = struct {
         } else null;
 
         for (aliases.items) |*nlist_with_index| {
+            nlist_with_index.index = self.symbol_mapping.get(nlist_with_index.index);
             const sym = self.object.symbols.items[nlist_with_index.index];
             if (sym.payload != .regular) {
                 log.err("expected a regular symbol, found {s}", .{sym.payload});
@@ -424,7 +423,7 @@ const TextBlockParser = struct {
             sort.sort(
                 NlistWithIndex,
                 aliases.items,
-                SeniorityContext{ .zld = self.zld },
+                SeniorityContext{ .object = self.object },
                 @This().lessThanBySeniority,
             );
         }
@@ -515,7 +514,7 @@ const TextBlockParser = struct {
 pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
     const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
 
-    log.debug("analysing {s}", .{self.name.?});
+    log.warn("analysing {s}", .{self.name.?});
 
     const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
     // We only care about defined symbols, so filter every other out.
@@ -536,14 +535,14 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
 
     for (seg.sections.items) |sect, id| {
         const sect_id = @intCast(u8, id);
-        log.debug("putting section '{s},{s}' as a TextBlock", .{
+        log.warn("putting section '{s},{s}' as a TextBlock", .{
             segmentName(sect),
             sectionName(sect),
         });
 
         // Get matching segment/section in the final artifact.
         const match = (try zld.getMatchingSection(sect)) orelse {
-            log.debug("unhandled section", .{});
+            log.warn("unhandled section", .{});
             continue;
         };
 
@@ -577,200 +576,249 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
         };
         zld.has_stabs = zld.has_stabs or self.debug_info != null;
 
-        next: {
-            if (is_splittable) blocks: {
-                if (filtered_nlists.len == 0) break :blocks;
-
-                // If the first nlist does not match the start of the section,
-                // then we need encapsulate the memory range [section start, first symbol)
-                // as a temporary symbol and insert the matching TextBlock.
-                const first_nlist = filtered_nlists[0].nlist;
-                if (first_nlist.n_value > sect.addr) {
-                    const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: {
-                        const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
-                            self.name.?,
-                            segmentName(sect),
-                            sectionName(sect),
-                        });
-                        defer self.allocator.free(name);
-                        const symbol = try zld.allocator.create(Symbol);
-                        symbol.* = .{
-                            .strx = try zld.makeString(name),
-                            .payload = .{ .undef = .{} },
-                        };
-                        try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
-                        break :symbol symbol;
-                    };
-
-                    const local_sym_index = @intCast(u32, zld.locals.items.len);
-                    symbol.payload = .{
-                        .regular = .{
-                            .linkage = .translation_unit,
-                            .address = sect.addr,
-                            .segment_id = match.seg,
-                            .section_id = match.sect,
-                            .file = self,
-                            .local_sym_index = local_sym_index,
-                        },
-                    };
-                    try zld.locals.append(zld.allocator, symbol);
-
-                    const block_code = code[0 .. first_nlist.n_value - sect.addr];
-                    const block_size = block_code.len;
-
-                    const block = try self.allocator.create(TextBlock);
-                    errdefer self.allocator.destroy(block);
-
-                    block.* = TextBlock.init(self.allocator);
-                    block.local_sym_index = local_sym_index;
-                    block.code = try self.allocator.dupe(u8, block_code);
-                    block.size = block_size;
-                    block.alignment = sect.@"align";
-
-                    const block_relocs = filterRelocs(relocs, 0, block_size);
-                    if (block_relocs.len > 0) {
-                        try self.parseRelocs(zld, block_relocs, block, 0);
-                    }
-
-                    if (zld.has_dices) {
-                        const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size);
-                        try block.dices.ensureTotalCapacity(dices.len);
-
-                        for (dices) |dice| {
-                            block.dices.appendAssumeCapacity(.{
-                                .offset = dice.offset - try math.cast(u32, sect.addr),
-                                .length = dice.length,
-                                .kind = dice.kind,
-                            });
-                        }
-                    }
-
-                    // Update target section's metadata
-                    // TODO should we update segment's size here too?
-                    // How does it tie with incremental space allocs?
-                    const tseg = &zld.load_commands.items[match.seg].Segment;
-                    const tsect = &tseg.sections.items[match.sect];
-                    const new_alignment = math.max(tsect.@"align", block.alignment);
-                    const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
-                    const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
-                    tsect.size = new_size;
-                    tsect.@"align" = new_alignment;
-
-                    if (zld.blocks.getPtr(match)) |last| {
-                        last.*.next = block;
-                        block.prev = last.*;
-                        last.* = block;
-                    } else {
-                        try zld.blocks.putNoClobber(zld.allocator, match, block);
-                    }
-
-                    try self.text_blocks.append(self.allocator, block);
-                }
-
-                var parser = TextBlockParser{
-                    .allocator = self.allocator,
-                    .section = sect,
-                    .code = code,
-                    .relocs = relocs,
-                    .object = self,
-                    .zld = zld,
-                    .nlists = filtered_nlists,
-                    .match = match,
-                };
-
-                while (try parser.next()) |block| {
-                    const sym = zld.locals.items[block.local_sym_index];
-                    const reg = &sym.payload.regular;
-                    if (reg.file) |file| {
-                        if (file != self) {
-                            log.debug("deduping definition of {s} in {s}", .{ zld.getString(sym.strx), self.name.? });
-                            block.deinit();
-                            self.allocator.destroy(block);
-                            continue;
-                        }
-                    }
-
-                    if (reg.address == sect.addr) {
-                        if (self.sections_as_symbols.get(sect_id)) |alias| {
-                            // Add alias.
-                            const local_sym_index = @intCast(u32, zld.locals.items.len);
-                            const reg_alias = &alias.payload.regular;
-                            reg_alias.segment_id = match.seg;
-                            reg_alias.section_id = match.sect;
-                            reg_alias.local_sym_index = local_sym_index;
-                            try block.aliases.append(local_sym_index);
-                            try zld.locals.append(zld.allocator, alias);
-                        }
-                    }
-
-                    // Update target section's metadata
-                    // TODO should we update segment's size here too?
-                    // How does it tie with incremental space allocs?
-                    const tseg = &zld.load_commands.items[match.seg].Segment;
-                    const tsect = &tseg.sections.items[match.sect];
-                    const new_alignment = math.max(tsect.@"align", block.alignment);
-                    const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
-                    const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
-                    tsect.size = new_size;
-                    tsect.@"align" = new_alignment;
-
-                    if (zld.blocks.getPtr(match)) |last| {
-                        last.*.next = block;
-                        block.prev = last.*;
-                        last.* = block;
-                    } else {
-                        try zld.blocks.putNoClobber(zld.allocator, match, block);
-                    }
-
-                    try self.text_blocks.append(self.allocator, block);
-                }
-
-                break :next;
-            }
+        {
+            // next: {
+            // if (is_splittable) blocks: {
+            //     if (filtered_nlists.len == 0) break :blocks;
+
+            //     // If the first nlist does not match the start of the section,
+            //     // then we need encapsulate the memory range [section start, first symbol)
+            //     // as a temporary symbol and insert the matching TextBlock.
+            //     const first_nlist = filtered_nlists[0].nlist;
+            //     if (first_nlist.n_value > sect.addr) {
+            //         const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: {
+            //             const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
+            //                 self.name.?,
+            //                 segmentName(sect),
+            //                 sectionName(sect),
+            //             });
+            //             defer self.allocator.free(name);
+            //             const symbol = try zld.allocator.create(Symbol);
+            //             symbol.* = .{
+            //                 .strx = try zld.makeString(name),
+            //                 .payload = .{ .undef = .{} },
+            //             };
+            //             try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
+            //             break :symbol symbol;
+            //         };
+
+            //         const local_sym_index = @intCast(u32, zld.locals.items.len);
+            //         symbol.payload = .{
+            //             .regular = .{
+            //                 .linkage = .translation_unit,
+            //                 .address = sect.addr,
+            //                 .segment_id = match.seg,
+            //                 .section_id = match.sect,
+            //                 .file = self,
+            //                 .local_sym_index = local_sym_index,
+            //             },
+            //         };
+            //         try zld.locals.append(zld.allocator, symbol);
+
+            //         const block_code = code[0 .. first_nlist.n_value - sect.addr];
+            //         const block_size = block_code.len;
+
+            //         const block = try self.allocator.create(TextBlock);
+            //         errdefer self.allocator.destroy(block);
+
+            //         block.* = TextBlock.init(self.allocator);
+            //         block.local_sym_index = local_sym_index;
+            //         block.code = try self.allocator.dupe(u8, block_code);
+            //         block.size = block_size;
+            //         block.alignment = sect.@"align";
+
+            //         const block_relocs = filterRelocs(relocs, 0, block_size);
+            //         if (block_relocs.len > 0) {
+            //             try self.parseRelocs(zld, block_relocs, block, 0);
+            //         }
+
+            //         if (zld.has_dices) {
+            //             const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size);
+            //             try block.dices.ensureTotalCapacity(dices.len);
+
+            //             for (dices) |dice| {
+            //                 block.dices.appendAssumeCapacity(.{
+            //                     .offset = dice.offset - try math.cast(u32, sect.addr),
+            //                     .length = dice.length,
+            //                     .kind = dice.kind,
+            //                 });
+            //             }
+            //         }
+
+            //         // Update target section's metadata
+            //         // TODO should we update segment's size here too?
+            //         // How does it tie with incremental space allocs?
+            //         const tseg = &zld.load_commands.items[match.seg].Segment;
+            //         const tsect = &tseg.sections.items[match.sect];
+            //         const new_alignment = math.max(tsect.@"align", block.alignment);
+            //         const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
+            //         const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
+            //         tsect.size = new_size;
+            //         tsect.@"align" = new_alignment;
+
+            //         if (zld.blocks.getPtr(match)) |last| {
+            //             last.*.next = block;
+            //             block.prev = last.*;
+            //             last.* = block;
+            //         } else {
+            //             try zld.blocks.putNoClobber(zld.allocator, match, block);
+            //         }
+
+            //         try self.text_blocks.append(self.allocator, block);
+            //     }
+
+            //     var parser = TextBlockParser{
+            //         .allocator = self.allocator,
+            //         .section = sect,
+            //         .code = code,
+            //         .relocs = relocs,
+            //         .object = self,
+            //         .zld = zld,
+            //         .nlists = filtered_nlists,
+            //         .match = match,
+            //     };
+
+            //     while (try parser.next()) |block| {
+            //         const sym = zld.locals.items[block.local_sym_index];
+            //         const reg = &sym.payload.regular;
+            //         if (reg.file) |file| {
+            //             if (file != self) {
+            //                 log.debug("deduping definition of {s} in {s}", .{ zld.getString(sym.strx), self.name.? });
+            //                 block.deinit();
+            //                 self.allocator.destroy(block);
+            //                 continue;
+            //             }
+            //         }
+
+            //         if (reg.address == sect.addr) {
+            //             if (self.sections_as_symbols.get(sect_id)) |alias| {
+            //                 // Add alias.
+            //                 const local_sym_index = @intCast(u32, zld.locals.items.len);
+            //                 const reg_alias = &alias.payload.regular;
+            //                 reg_alias.segment_id = match.seg;
+            //                 reg_alias.section_id = match.sect;
+            //                 reg_alias.local_sym_index = local_sym_index;
+            //                 try block.aliases.append(local_sym_index);
+            //                 try zld.locals.append(zld.allocator, alias);
+            //             }
+            //         }
+
+            //         // Update target section's metadata
+            //         // TODO should we update segment's size here too?
+            //         // How does it tie with incremental space allocs?
+            //         const tseg = &zld.load_commands.items[match.seg].Segment;
+            //         const tsect = &tseg.sections.items[match.sect];
+            //         const new_alignment = math.max(tsect.@"align", block.alignment);
+            //         const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
+            //         const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
+            //         tsect.size = new_size;
+            //         tsect.@"align" = new_alignment;
+
+            //         if (zld.blocks.getPtr(match)) |last| {
+            //             last.*.next = block;
+            //             block.prev = last.*;
+            //             last.* = block;
+            //         } else {
+            //             try zld.blocks.putNoClobber(zld.allocator, match, block);
+            //         }
+
+            //         try self.text_blocks.append(self.allocator, block);
+            //     }
+
+            //     break :next;
+            // }
 
             // 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 symbol = self.sections_as_symbols.get(sect_id) orelse symbol: {
-                const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
-                    self.name.?,
-                    segmentName(sect),
-                    sectionName(sect),
-                });
-                defer self.allocator.free(name);
-                const symbol = try zld.allocator.create(Symbol);
-                symbol.* = .{
-                    .strx = try zld.makeString(name),
-                    .payload = .{ .undef = .{} },
-                };
-                try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
-                break :symbol symbol;
-            };
-
-            const local_sym_index = @intCast(u32, zld.locals.items.len);
-            symbol.payload = .{
-                .regular = .{
-                    .linkage = .translation_unit,
-                    .address = sect.addr,
-                    .segment_id = match.seg,
-                    .section_id = match.sect,
-                    .file = self,
-                    .local_sym_index = local_sym_index,
-                },
-            };
-            try zld.locals.append(zld.allocator, symbol);
+            const block_local_sym_index = @intCast(u32, zld.locals.items.len);
+            const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
+                self.name.?,
+                segmentName(sect),
+                sectionName(sect),
+            });
+            defer self.allocator.free(sym_name);
+            try zld.locals.append(zld.allocator, .{
+                .n_strx = try zld.makeString(sym_name),
+                .n_type = macho.N_SECT,
+                .n_sect = zld.sectionId(match),
+                .n_desc = 0,
+                .n_value = sect.addr,
+            });
+            const block_local = &zld.locals.items[block_local_sym_index];
+            block_local.n_sect = zld.sectionId(match);
 
             const block = try self.allocator.create(TextBlock);
             errdefer self.allocator.destroy(block);
 
             block.* = TextBlock.init(self.allocator);
-            block.local_sym_index = local_sym_index;
+            block.local_sym_index = block_local_sym_index;
             block.code = try self.allocator.dupe(u8, code);
             block.size = sect.size;
             block.alignment = sect.@"align";
 
-            if (relocs.len > 0) {
-                try self.parseRelocs(zld, relocs, block, 0);
+            try block.relocs.ensureTotalCapacity(relocs.len);
+            for (relocs) |rel| {
+                const out_rel: TextBlock.Relocation = outer: {
+                    if (rel.r_extern == 0) {
+                        const rel_sect_id = @intCast(u16, rel.r_symbolnum - 1);
+                        const sect_sym_index = self.sections_as_symbols.get(rel_sect_id) orelse blk: {
+                            const sect_sym_index = @intCast(u32, zld.locals.items.len);
+                            const sect_sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
+                                self.name.?,
+                                segmentName(sect),
+                                sectionName(sect),
+                            });
+                            defer self.allocator.free(sect_sym_name);
+                            try zld.locals.append(zld.allocator, .{
+                                .n_strx = try zld.makeString(sect_sym_name),
+                                .n_type = macho.N_SECT,
+                                .n_sect = 0,
+                                .n_desc = 0,
+                                .n_value = 0,
+                            });
+                            try self.sections_as_symbols.putNoClobber(self.allocator, rel_sect_id, sect_sym_index);
+                            break :blk sect_sym_index;
+                        };
+                        break :outer .{
+                            .inner = rel,
+                            .where = .local,
+                            .where_index = sect_sym_index,
+                        };
+                    }
+
+                    const rel_sym = self.symtab.items[rel.r_symbolnum];
+                    const rel_sym_name = self.getString(rel_sym.n_strx);
+
+                    if (Zld.symbolIsSect(rel_sym) and !Zld.symbolIsExt(rel_sym)) {
+                        const where_index = self.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
+                        break :outer .{
+                            .inner = rel,
+                            .where = .local,
+                            .where_index = where_index,
+                        };
+                    }
+
+                    const resolv = zld.symbol_resolver.get(rel_sym_name) orelse unreachable;
+                    switch (resolv.where) {
+                        .global => {
+                            break :outer .{
+                                .inner = rel,
+                                .where = .local,
+                                .where_index = resolv.local_sym_index,
+                            };
+                        },
+                        .import => {
+                            break :outer .{
+                                .inner = rel,
+                                .where = .import,
+                                .where_index = resolv.where_index,
+                            };
+                        },
+                        else => unreachable,
+                    }
+                };
+                block.relocs.appendAssumeCapacity(out_rel);
             }
 
             if (zld.has_dices) {
@@ -791,44 +839,41 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !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.
-            if (filtered_nlists.len > 0) {
-                var contained = std.ArrayList(TextBlock.SymbolAtOffset).init(self.allocator);
-                defer contained.deinit();
-                try contained.ensureTotalCapacity(filtered_nlists.len);
-
-                for (filtered_nlists) |nlist_with_index| {
-                    const sym = self.symbols.items[nlist_with_index.index];
-                    assert(sym.payload == .regular);
-                    const reg = &sym.payload.regular;
-
-                    reg.segment_id = match.seg;
-                    reg.section_id = match.sect;
-
-                    const stab: ?TextBlock.Stab = if (self.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| {
-                                if (reg.address >= range.start and reg.address < range.end) {
-                                    break :blk TextBlock.Stab{
-                                        .function = range.end - range.start,
-                                    };
-                                }
+            var contained = std.ArrayList(TextBlock.SymbolAtOffset).init(self.allocator);
+            defer contained.deinit();
+            try contained.ensureTotalCapacity(filtered_nlists.len);
+
+            for (filtered_nlists) |nlist_with_index| {
+                const nlist = nlist_with_index.nlist;
+                const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
+                const local = &zld.locals.items[local_sym_index];
+                local.n_sect = zld.sectionId(match);
+
+                const stab: ?TextBlock.Stab = if (self.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| {
+                            if (nlist.n_value >= range.start and nlist.n_value < range.end) {
+                                break :blk TextBlock.Stab{
+                                    .function = range.end - range.start,
+                                };
                             }
                         }
-                        if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
-                        break :blk .static;
-                    } else null;
-
-                    contained.appendAssumeCapacity(.{
-                        .local_sym_index = reg.local_sym_index,
-                        .offset = nlist_with_index.nlist.n_value - sect.addr,
-                        .stab = stab,
-                    });
-                }
+                    }
+                    // TODO
+                    // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
+                    break :blk .static;
+                } else null;
 
-                block.contained = contained.toOwnedSlice();
+                contained.appendAssumeCapacity(.{
+                    .local_sym_index = local_sym_index,
+                    .offset = nlist.n_value - sect.addr,
+                    .stab = stab,
+                });
             }
 
+            block.contained = contained.toOwnedSlice();
+
             // Update target section's metadata
             // TODO should we update segment's size here too?
             // How does it tie with incremental space allocs?
@@ -853,26 +898,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
     }
 }
 
-fn parseRelocs(
-    self: *Object,
-    zld: *Zld,
-    relocs: []const macho.relocation_info,
-    block: *TextBlock,
-    base_addr: u64,
-) !void {
-    var it = reloc.RelocIterator{
-        .buffer = relocs,
-    };
-    var parser = reloc.Parser{
-        .object = self,
-        .zld = zld,
-        .it = &it,
-        .block = block,
-        .base_addr = base_addr,
-    };
-    try parser.parse();
-}
-
 pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*Symbol {
     const symbol = blk: {
         if (rel.r_extern == 1) {
src/link/MachO/Symbol.zig
@@ -1,285 +0,0 @@
-const Symbol = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const commands = @import("commands.zig");
-const macho = std.macho;
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const Dylib = @import("Dylib.zig");
-const Object = @import("Object.zig");
-const Zld = @import("Zld.zig");
-
-/// Offset into the string table.
-strx: u32,
-
-/// Index in GOT table for indirection.
-got_index: ?u32 = null,
-
-/// Index in stubs table for late binding.
-stubs_index: ?u32 = null,
-
-payload: union(enum) {
-    regular: Regular,
-    tentative: Tentative,
-    proxy: Proxy,
-    undef: Undefined,
-
-    pub fn format(self: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
-        return switch (self) {
-            .regular => |p| p.format(fmt, options, writer),
-            .tentative => |p| p.format(fmt, options, writer),
-            .proxy => |p| p.format(fmt, options, writer),
-            .undef => |p| p.format(fmt, options, writer),
-        };
-    }
-},
-
-pub const Regular = struct {
-    /// Linkage type.
-    linkage: Linkage,
-
-    /// Symbol address.
-    address: u64 = 0,
-
-    /// Segment ID
-    segment_id: u16 = 0,
-
-    /// Section ID
-    section_id: u16 = 0,
-
-    /// Whether the symbol is a weak ref.
-    weak_ref: bool = false,
-
-    /// Object file where to locate this symbol.
-    /// null means self-reference.
-    file: ?*Object = null,
-
-    local_sym_index: u32 = 0,
-
-    pub const Linkage = enum {
-        translation_unit,
-        linkage_unit,
-        global,
-    };
-
-    pub fn format(self: Regular, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
-        _ = fmt;
-        _ = options;
-        try std.fmt.format(writer, "Regular {{ ", .{});
-        try std.fmt.format(writer, ".linkage = {s},  ", .{self.linkage});
-        try std.fmt.format(writer, ".address = 0x{x}, ", .{self.address});
-        try std.fmt.format(writer, ".segment_id = {}, ", .{self.segment_id});
-        try std.fmt.format(writer, ".section_id = {}, ", .{self.section_id});
-        if (self.weak_ref) {
-            try std.fmt.format(writer, ".weak_ref, ", .{});
-        }
-        if (self.file) |file| {
-            try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
-        }
-        try std.fmt.format(writer, ".local_sym_index = {}, ", .{self.local_sym_index});
-        try std.fmt.format(writer, "}}", .{});
-    }
-
-    pub fn sectionId(self: Regular, zld: *Zld) u8 {
-        // TODO there might be a more generic way of doing this.
-        var section: u8 = 0;
-        for (zld.load_commands.items) |cmd, cmd_id| {
-            if (cmd != .Segment) break;
-            if (cmd_id == self.segment_id) {
-                section += @intCast(u8, self.section_id) + 1;
-                break;
-            }
-            section += @intCast(u8, cmd.Segment.sections.items.len);
-        }
-        return section;
-    }
-};
-
-pub const Tentative = struct {
-    /// Symbol size.
-    size: u64,
-
-    /// Symbol alignment as power of two.
-    alignment: u16,
-
-    /// File where this symbol was referenced.
-    file: ?*Object = null,
-
-    pub fn format(self: Tentative, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
-        _ = fmt;
-        _ = options;
-        try std.fmt.format(writer, "Tentative {{ ", .{});
-        try std.fmt.format(writer, ".size = 0x{x},  ", .{self.size});
-        try std.fmt.format(writer, ".alignment = 0x{x}, ", .{self.alignment});
-        if (self.file) |file| {
-            try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
-        }
-        try std.fmt.format(writer, "}}", .{});
-    }
-};
-
-pub const Proxy = struct {
-    /// Dylib where to locate this symbol.
-    /// null means self-reference.
-    file: ?*Dylib = null,
-
-    local_sym_index: u32 = 0,
-
-    pub fn dylibOrdinal(proxy: Proxy) u16 {
-        const dylib = proxy.file orelse return 0;
-        return dylib.ordinal.?;
-    }
-
-    pub fn format(self: Proxy, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
-        _ = fmt;
-        _ = options;
-        try std.fmt.format(writer, "Proxy {{ ", .{});
-        if (self.file) |file| {
-            try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
-        }
-        try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index});
-        try std.fmt.format(writer, "}}", .{});
-    }
-};
-
-pub const Undefined = struct {
-    /// File where this symbol was referenced.
-    /// null means synthetic, e.g., dyld_stub_binder.
-    file: ?*Object = null,
-
-    pub fn format(self: Undefined, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
-        _ = fmt;
-        _ = options;
-        try std.fmt.format(writer, "Undefined {{ ", .{});
-        if (self.file) |file| {
-            try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
-        }
-        try std.fmt.format(writer, "}}", .{});
-    }
-};
-
-pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
-    _ = fmt;
-    _ = options;
-    try std.fmt.format(writer, "Symbol {{", .{});
-    try std.fmt.format(writer, ".strx = {d}, ", .{self.strx});
-    if (self.got_index) |got_index| {
-        try std.fmt.format(writer, ".got_index = {}, ", .{got_index});
-    }
-    if (self.stubs_index) |stubs_index| {
-        try std.fmt.format(writer, ".stubs_index = {}, ", .{stubs_index});
-    }
-    try std.fmt.format(writer, "{}, ", .{self.payload});
-    try std.fmt.format(writer, "}}", .{});
-}
-
-pub fn isTemp(symbol: Symbol, zld: *Zld) bool {
-    const sym_name = zld.getString(symbol.strx);
-    switch (symbol.payload) {
-        .regular => |regular| {
-            if (regular.linkage == .translation_unit) {
-                return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
-            }
-        },
-        else => {},
-    }
-    return false;
-}
-
-pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
-    const nlist = nlist: {
-        switch (symbol.payload) {
-            .regular => |regular| {
-                var nlist = macho.nlist_64{
-                    .n_strx = symbol.strx,
-                    .n_type = macho.N_SECT,
-                    .n_sect = regular.sectionId(zld),
-                    .n_desc = 0,
-                    .n_value = regular.address,
-                };
-
-                if (regular.linkage != .translation_unit) {
-                    nlist.n_type |= macho.N_EXT;
-                }
-                if (regular.linkage == .linkage_unit) {
-                    nlist.n_type |= macho.N_PEXT;
-                    nlist.n_desc |= macho.N_WEAK_DEF;
-                }
-
-                break :nlist nlist;
-            },
-            .tentative => {
-                // TODO
-                break :nlist macho.nlist_64{
-                    .n_strx = symbol.strx,
-                    .n_type = macho.N_UNDF,
-                    .n_sect = 0,
-                    .n_desc = 0,
-                    .n_value = 0,
-                };
-            },
-            .proxy => |proxy| {
-                break :nlist macho.nlist_64{
-                    .n_strx = symbol.strx,
-                    .n_type = macho.N_UNDF | macho.N_EXT,
-                    .n_sect = 0,
-                    .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
-                    .n_value = 0,
-                };
-            },
-            .undef => {
-                // TODO
-                break :nlist macho.nlist_64{
-                    .n_strx = symbol.strx,
-                    .n_type = macho.N_UNDF,
-                    .n_sect = 0,
-                    .n_desc = 0,
-                    .n_value = 0,
-                };
-            },
-        }
-    };
-    return nlist;
-}
-
-pub fn isStab(sym: macho.nlist_64) bool {
-    return (macho.N_STAB & sym.n_type) != 0;
-}
-
-pub fn isPext(sym: macho.nlist_64) bool {
-    return (macho.N_PEXT & sym.n_type) != 0;
-}
-
-pub fn isExt(sym: macho.nlist_64) bool {
-    return (macho.N_EXT & sym.n_type) != 0;
-}
-
-pub fn isSect(sym: macho.nlist_64) bool {
-    const type_ = macho.N_TYPE & sym.n_type;
-    return type_ == macho.N_SECT;
-}
-
-pub fn isUndf(sym: macho.nlist_64) bool {
-    const type_ = macho.N_TYPE & sym.n_type;
-    return type_ == macho.N_UNDF;
-}
-
-pub fn isIndr(sym: macho.nlist_64) bool {
-    const type_ = macho.N_TYPE & sym.n_type;
-    return type_ == macho.N_INDR;
-}
-
-pub fn isAbs(sym: macho.nlist_64) bool {
-    const type_ = macho.N_TYPE & sym.n_type;
-    return type_ == macho.N_ABS;
-}
-
-pub fn isWeakDef(sym: macho.nlist_64) bool {
-    return (sym.n_desc & macho.N_WEAK_DEF) != 0;
-}
-
-pub fn isWeakRef(sym: macho.nlist_64) bool {
-    return (sym.n_desc & macho.N_WEAK_REF) != 0;
-}
src/link/MachO/TextBlock.zig
@@ -5,10 +5,9 @@ const commands = @import("commands.zig");
 const log = std.log.scoped(.text_block);
 const macho = std.macho;
 const mem = std.mem;
-const reloc = @import("reloc.zig");
 
 const Allocator = mem.Allocator;
-const Relocation = reloc.Relocation;
+const Arch = std.Target.Cpu.Arch;
 const Zld = @import("Zld.zig");
 
 allocator: *Allocator,
@@ -102,6 +101,15 @@ pub const Stab = union(enum) {
     }
 };
 
+pub const Relocation = struct {
+    inner: macho.relocation_info,
+    where: enum {
+        local,
+        import,
+    },
+    where_index: u32,
+};
+
 pub fn init(allocator: *Allocator) TextBlock {
     return .{
         .allocator = allocator,
@@ -137,18 +145,10 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
 
         const source_addr = blk: {
             const sym = zld.locals.items[self.local_sym_index];
-            break :blk sym.payload.regular.address + rel.offset;
+            break :blk sym.n_value + rel.offset;
         };
         const target_addr = blk: {
-            const is_via_got = switch (rel.payload) {
-                .pointer_to_got => true,
-                .page => |page| page.kind == .got,
-                .page_off => |page_off| page_off.kind == .got,
-                .load => |load| load.kind == .got,
-                else => false,
-            };
-
-            if (is_via_got) {
+            if (isGotIndirection(rel, zld.target.?.cpu.arch)) {
                 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
                 const got = dc_seg.sections.items[zld.got_section_index.?];
                 const got_index = rel.target.got_index orelse {
@@ -228,31 +228,18 @@ pub fn print_this(self: *const TextBlock, zld: *Zld) void {
         log.warn("  stab: {}", .{stab});
     }
     if (self.aliases.items.len > 0) {
-        log.warn("  aliases:", .{});
-        for (self.aliases.items) |index| {
-            log.warn("    {}: {}", .{ index, zld.locals.items[index] });
-        }
+        log.warn("  aliases: {any}", .{self.aliases.items});
     }
     if (self.references.count() > 0) {
-        log.warn("  references:", .{});
-        for (self.references.keys()) |index| {
-            log.warn("    {}: {}", .{ index, zld.locals.items[index] });
-        }
+        log.warn("  references: {any}", .{self.references.keys()});
     }
     if (self.contained) |contained| {
         log.warn("  contained symbols:", .{});
         for (contained) |sym_at_off| {
             if (sym_at_off.stab) |stab| {
-                log.warn("    {}: {}, stab: {}\n", .{
-                    sym_at_off.offset,
-                    zld.locals.items[sym_at_off.local_sym_index],
-                    stab,
-                });
+                log.warn("    {}: {}, stab: {}", .{ sym_at_off.offset, sym_at_off.local_sym_index, stab });
             } else {
-                log.warn("    {}: {}\n", .{
-                    sym_at_off.offset,
-                    zld.locals.items[sym_at_off.local_sym_index],
-                });
+                log.warn("    {}: {}", .{ sym_at_off.offset, sym_at_off.local_sym_index });
             }
         }
     }
@@ -282,3 +269,22 @@ pub fn print(self: *const TextBlock, zld: *Zld) void {
     }
     self.print_this(zld);
 }
+
+fn isGotIndirection(rel: macho.relocation_info, arch: Arch) bool {
+    return switch (arch) {
+        .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
+            .ARM64_RELOC_POINTER_TO_GOT,
+            .ARM64_RELOC_GOT_LOAD_PAGE21,
+            .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
+            => true,
+            else => false,
+        },
+        .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
+            .X86_64_RELOC_GOT,
+            .X86_64_RELOC_GOT_LOAD,
+            => true,
+            else => false,
+        },
+        else => unreachable,
+    };
+}
src/link/MachO/Zld.zig
@@ -105,7 +105,6 @@ imports: std.ArrayListUnmanaged(macho.nlist_64) = .{},
 undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
 tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{},
 symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
-object_mapping: std.AutoHashMapUnmanaged(u16, []u32) = .{},
 
 strtab: std.ArrayListUnmanaged(u8) = .{},
 
@@ -199,14 +198,6 @@ pub fn deinit(self: *Zld) void {
     }
     self.symbol_resolver.deinit(self.allocator);
 
-    {
-        var it = self.object_mapping.valueIterator();
-        while (it.next()) |value_ptr| {
-            self.allocator.free(value_ptr.*);
-        }
-    }
-    self.object_mapping.deinit(self.allocator);
-
     self.strtab.deinit(self.allocator);
 
     // TODO dealloc all blocks
@@ -251,33 +242,33 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
     try self.resolveSymbols();
 
     log.warn("locals", .{});
-    for (self.locals.items) |sym| {
-        log.warn("  | {s}: {}", .{ self.getString(sym.n_strx), sym });
+    for (self.locals.items) |sym, id| {
+        log.warn("  {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
     }
 
     log.warn("globals", .{});
-    for (self.globals.items) |sym| {
-        log.warn("  | {s}: {}", .{ self.getString(sym.n_strx), sym });
+    for (self.globals.items) |sym, id| {
+        log.warn("  {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
     }
 
     log.warn("tentatives", .{});
-    for (self.tentatives.items) |sym| {
-        log.warn("  | {s}: {}", .{ self.getString(sym.n_strx), sym });
+    for (self.tentatives.items) |sym, id| {
+        log.warn("  {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
     }
 
     log.warn("undefines", .{});
-    for (self.undefs.items) |sym| {
-        log.warn("  | {s}: {}", .{ self.getString(sym.n_strx), sym });
+    for (self.undefs.items) |sym, id| {
+        log.warn("  {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
     }
 
     log.warn("imports", .{});
-    for (self.imports.items) |sym| {
-        log.warn("  | {s}: {}", .{ self.getString(sym.n_strx), sym });
+    for (self.imports.items) |sym, id| {
+        log.warn("  {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
     }
 
     log.warn("symbol resolver", .{});
     for (self.symbol_resolver.keys()) |key| {
-        log.warn("  | {s} => {}", .{ key, self.symbol_resolver.get(key).? });
+        log.warn("  {s} => {}", .{ key, self.symbol_resolver.get(key).? });
     }
 
     log.warn("mappings", .{});
@@ -285,7 +276,7 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
         const object_id = @intCast(u16, id);
         log.warn("  in object {s}", .{object.name.?});
         for (object.symtab.items) |sym, sym_id| {
-            if (self.localSymIndex(object_id, @intCast(u32, sym_id))) |local_id| {
+            if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| {
                 log.warn("    | {d} => {d}", .{ sym_id, local_id });
             } else {
                 log.warn("    | {d} no local mapping for {s}", .{ sym_id, object.getString(sym.n_strx) });
@@ -293,8 +284,19 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
         }
     }
 
+    try self.parseTextBlocks();
+
+    var it = self.blocks.iterator();
+    while (it.next()) |entry| {
+        const seg = self.load_commands.items[entry.key_ptr.seg].Segment;
+        const sect = seg.sections.items[entry.key_ptr.sect];
+
+        log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) });
+        log.warn("  {}", .{sect});
+        entry.value_ptr.*.print(self);
+    }
+
     return error.TODO;
-    // try self.parseTextBlocks();
     // try self.sortSections();
     // try self.addRpaths(args.rpaths);
     // try self.addDataInCodeLC();
@@ -305,16 +307,6 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
     // self.allocateLinkeditSegment();
     // try self.allocateTextBlocks();
 
-    // // var it = self.blocks.iterator();
-    // // while (it.next()) |entry| {
-    // //     const seg = self.load_commands.items[entry.key_ptr.seg].Segment;
-    // //     const sect = seg.sections.items[entry.key_ptr.sect];
-
-    // //     log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) });
-    // //     log.warn("  {}", .{sect});
-    // //     entry.value_ptr.*.print(self);
-    // // }
-
     // try self.flush();
 }
 
@@ -1414,10 +1406,6 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void {
 
     log.warn("resolving symbols in '{s}'", .{object.name});
 
-    const mapping = try self.allocator.alloc(u32, object.symtab.items.len);
-    mem.set(u32, mapping, 0);
-    try self.object_mapping.putNoClobber(self.allocator, object_id, mapping);
-
     for (object.symtab.items) |sym, id| {
         const sym_id = @intCast(u32, id);
         const sym_name = object.getString(sym.n_strx);
@@ -1464,7 +1452,7 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void {
                 .n_desc = 0,
                 .n_value = sym.n_value,
             });
-            mapping[sym_id] = local_sym_index;
+            try object.symbol_mapping.putNoClobber(self.allocator, sym_id, local_sym_index);
 
             // If the symbol's scope is not local aka translation unit, then we need work out
             // if we should save the symbol as a global, or potentially flag the error.
@@ -2987,15 +2975,6 @@ pub fn getString(self: *Zld, off: u32) []const u8 {
     return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));
 }
 
-fn localSymIndex(self: Zld, object_id: u16, orig_id: u32) ?u32 {
-    const mapping = self.object_mapping.get(object_id) orelse return null;
-    const local_sym_index = mapping[orig_id];
-    if (local_sym_index == 0) {
-        return null;
-    }
-    return local_sym_index;
-}
-
 pub fn symbolIsStab(sym: macho.nlist_64) bool {
     return (macho.N_STAB & sym.n_type) != 0;
 }
@@ -3045,10 +3024,9 @@ pub fn symbolIsNull(sym: macho.nlist_64) bool {
     return sym.n_value == 0 and sym.n_desc == 0 and sym.n_type == 0 and sym.n_strx == 0 and sym.n_sect == 0;
 }
 
-pub fn symbolIsTemp(self: Zld, sym: macho.nlist_64) bool {
+pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool {
     if (!symbolIsSect(sym)) return false;
     if (symbolIsExt(sym)) return false;
-    const sym_name = self.getString(sym.n_strx);
     return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
 }
 
CMakeLists.txt
@@ -581,7 +581,6 @@ set(ZIG_STAGE2_SOURCES
     "${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
-    "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/TextBlock.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"