Commit 51e334af44

Jakub Konka <kubkon@jakubkonka.com>
2021-07-05 20:20:07
zld: refactor section into TextBlocks conversion
1 parent 7b4063d
Changed files (2)
src
src/link/MachO/Object.zig
@@ -276,11 +276,11 @@ const NlistWithIndex = struct {
     nlist: macho.nlist_64,
     index: u32,
 
-    pub fn cmp(_: void, lhs: @This(), rhs: @This()) bool {
+    fn lessThan(_: void, lhs: @This(), rhs: @This()) bool {
         return lhs.nlist.n_value < rhs.nlist.n_value;
     }
 
-    fn filterNlistsInSection(symbols: []@This(), sect_id: u8) []@This() {
+    fn filterInSection(symbols: []@This(), sect_id: u8) []@This() {
         var start: usize = 0;
         var end: usize = symbols.len;
 
@@ -327,19 +327,111 @@ fn filterRelocs(relocs: []macho.relocation_info, start: u64, end: u64) []macho.r
     return relocs[start_id..end_id];
 }
 
-const SeniorityContext = struct {
+const TextBlockParser = struct {
+    allocator: *Allocator,
+    section: macho.section_64,
+    code: []u8,
+    object: *Object,
     zld: *Zld,
-};
-fn cmpSymBySeniority(context: SeniorityContext, lhs: u32, rhs: u32) bool {
-    const lreg = context.zld.locals.items[lhs].payload.regular;
-    const rreg = context.zld.locals.items[rhs].payload.regular;
-
-    return switch (rreg.linkage) {
-        .global => true,
-        .linkage_unit => lreg.linkage == .translation_unit,
-        else => false,
+    nlists: []NlistWithIndex,
+    index: u32 = 0,
+
+    fn peek(self: *TextBlockParser) ?NlistWithIndex {
+        return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
+    }
+
+    const SeniorityContext = struct {
+        zld: *Zld,
     };
-}
+
+    fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {
+        const lreg = context.zld.locals.items[lhs.index].payload.regular;
+        const rreg = context.zld.locals.items[rhs.index].payload.regular;
+
+        return switch (rreg.linkage) {
+            .global => true,
+            .linkage_unit => lreg.linkage == .translation_unit,
+            else => false,
+        };
+    }
+
+    pub fn next(self: *TextBlockParser) !?*TextBlock {
+        if (self.index == self.nlists.len) return null;
+
+        var aliases = std.ArrayList(NlistWithIndex).init(self.allocator);
+        defer aliases.deinit();
+
+        const next_nlist: ?NlistWithIndex = blk: while (true) {
+            const curr_nlist = self.nlists[self.index];
+            try aliases.append(curr_nlist);
+
+            if (self.peek()) |next_nlist| {
+                if (curr_nlist.nlist.n_value == next_nlist.nlist.n_value) {
+                    self.index += 1;
+                    continue;
+                }
+                break :blk next_nlist;
+            }
+            break :blk null;
+        } else null;
+
+        for (aliases.items) |*nlist_with_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});
+                log.err("  when remapping {s}", .{sym.name});
+                return error.SymbolIsNotRegular;
+            }
+            assert(sym.payload.regular.local_sym_index != 0); // This means the symbol has not been properly resolved.
+            nlist_with_index.index = sym.payload.regular.local_sym_index;
+        }
+
+        if (aliases.items.len > 1) {
+            // Bubble-up senior symbol as the main link to the text block.
+            std.sort.sort(
+                NlistWithIndex,
+                aliases.items,
+                SeniorityContext{ .zld = self.zld },
+                @This().lessThanBySeniority,
+            );
+        }
+
+        const senior_nlist = aliases.pop();
+        const senior_sym = self.zld.locals.items[senior_nlist.index];
+        assert(senior_sym.payload == .regular);
+
+        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;
+
+        const code = self.code[start_addr..end_addr];
+        const size = code.len;
+
+        const alias_only_indices = if (aliases.items.len > 0) blk: {
+            var out = std.ArrayList(u32).init(self.allocator);
+            try out.ensureTotalCapacity(aliases.items.len);
+            for (aliases.items) |alias| {
+                out.appendAssumeCapacity(alias.index);
+            }
+            break :blk out.toOwnedSlice();
+        } else null;
+
+        const block = try self.allocator.create(TextBlock);
+        errdefer self.allocator.destroy(block);
+
+        block.* = .{
+            .local_sym_index = senior_nlist.index,
+            .aliases = alias_only_indices,
+            .code = code,
+            .size = size,
+            .alignment = self.section.@"align",
+        };
+
+        self.index += 1;
+        block.print_this(self.zld);
+
+        return block;
+    }
+};
 
 pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock {
     const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
@@ -361,7 +453,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock {
         });
     }
 
-    std.sort.sort(NlistWithIndex, sorted_nlists.items, {}, NlistWithIndex.cmp);
+    std.sort.sort(NlistWithIndex, sorted_nlists.items, {}, NlistWithIndex.lessThan);
 
     var last_block: ?*TextBlock = null;
 
@@ -385,53 +477,26 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock {
         // Is there any padding between symbols within the section?
         const is_padded = self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
 
-        // Section alignment will be the assumed alignment per symbol.
-        const alignment = sect.@"align";
-
         next: {
             if (is_padded) blocks: {
-                const filtered_nlists = NlistWithIndex.filterNlistsInSection(
+                const filtered_nlists = NlistWithIndex.filterInSection(
                     sorted_nlists.items,
                     @intCast(u8, sect_id + 1),
                 );
 
                 if (filtered_nlists.len == 0) break :blocks;
 
-                var nlist_indices = std.ArrayList(u32).init(self.allocator);
-                defer nlist_indices.deinit();
-
-                var i: u32 = 0;
-                while (i < filtered_nlists.len) : (i += 1) {
-                    const curr = filtered_nlists[i];
-                    try nlist_indices.append(curr.index);
-
-                    const next: ?NlistWithIndex = if (i + 1 < filtered_nlists.len)
-                        filtered_nlists[i + 1]
-                    else
-                        null;
-
-                    if (next) |n| {
-                        if (curr.nlist.n_value == n.nlist.n_value) {
-                            continue;
-                        }
-                    }
-
-                    // Bubble-up senior symbol as the main link to the text block.
-                    for (nlist_indices.items) |*index| {
-                        const sym = self.symbols.items[index.*];
-                        if (sym.payload != .regular) {
-                            log.err("expected a regular symbol, found {s}", .{sym.payload});
-                            log.err("  when remapping {s}", .{sym.name});
-                            return error.SymbolIsNotRegular;
-                        }
-                        assert(sym.payload.regular.local_sym_index != 0); // This means the symbol has not been properly resolved.
-                        index.* = sym.payload.regular.local_sym_index;
-                    }
-
-                    std.sort.sort(u32, nlist_indices.items, SeniorityContext{ .zld = zld }, cmpSymBySeniority);
-
-                    const local_sym_index = nlist_indices.pop();
-                    const sym = zld.locals.items[local_sym_index];
+                var parser = TextBlockParser{
+                    .allocator = self.allocator,
+                    .section = sect,
+                    .code = code,
+                    .object = self,
+                    .zld = zld,
+                    .nlists = filtered_nlists,
+                };
+
+                while (try parser.next()) |block| {
+                    const sym = zld.locals.items[block.local_sym_index];
                     if (sym.payload.regular.file) |file| {
                         if (file != self) {
                             log.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? });
@@ -439,27 +504,8 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock {
                         }
                     }
 
-                    const start_addr = curr.nlist.n_value - sect.addr;
-                    const end_addr = if (next) |n| n.nlist.n_value - sect.addr else sect.size;
-
-                    const tb_code = code[start_addr..end_addr];
-                    const size = tb_code.len;
-
-                    const block = try self.allocator.create(TextBlock);
-                    errdefer self.allocator.destroy(block);
-
-                    block.* = .{
-                        .local_sym_index = local_sym_index,
-                        .aliases = std.ArrayList(u32).init(self.allocator),
-                        .references = std.ArrayList(u32).init(self.allocator),
-                        .code = tb_code,
-                        .relocs = std.ArrayList(*Relocation).init(self.allocator),
-                        .size = size,
-                        .alignment = alignment,
-                        .segment_id = match.seg,
-                        .section_id = match.sect,
-                    };
-                    try block.aliases.appendSlice(nlist_indices.items);
+                    block.segment_id = match.seg;
+                    block.section_id = match.sect;
 
                     // TODO parse relocs
 
@@ -468,8 +514,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock {
                         block.prev = last;
                     }
                     last_block = block;
-
-                    nlist_indices.clearRetainingCapacity();
                 }
 
                 break :next;
@@ -498,12 +542,9 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock {
 
             block.* = .{
                 .local_sym_index = local_sym_index,
-                .aliases = std.ArrayList(u32).init(self.allocator),
-                .references = std.ArrayList(u32).init(self.allocator),
                 .code = code,
-                .relocs = std.ArrayList(*Relocation).init(self.allocator),
                 .size = sect.size,
-                .alignment = alignment,
+                .alignment = sect.@"align",
                 .segment_id = match.seg,
                 .section_id = match.sect,
             };
src/link/MachO/Zld.zig
@@ -139,47 +139,60 @@ const TlvOffset = struct {
 
 pub const TextBlock = struct {
     local_sym_index: u32,
-    aliases: std.ArrayList(u32),
-    references: std.ArrayList(u32),
+    aliases: ?[]u32 = null,
+    references: ?[]u32 = null,
     code: []u8,
-    relocs: std.ArrayList(*Relocation),
+    relocs: ?[]*Relocation = null,
     size: u64,
     alignment: u32,
-    segment_id: u16,
-    section_id: u16,
+    segment_id: u16 = 0,
+    section_id: u16 = 0,
     next: ?*TextBlock = null,
     prev: ?*TextBlock = null,
 
     pub fn deinit(block: *TextBlock, allocator: *Allocator) void {
-        block.aliases.deinit();
-        block.references.deinit();
+        if (block.aliases) |aliases| {
+            allocator.free(aliases);
+        }
+        if (block.references) |references| {
+            allocator.free(references);
+        }
         for (block.relocs.items) |reloc| {
             allocator.destroy(reloc);
         }
-        block.relocs.deinit();
+        if (block.relocs) |relocs| {
+            allocator.free(relocs);
+        }
         allocator.free(code);
     }
 
-    fn print(self: *const TextBlock, zld: *Zld) void {
-        if (self.prev) |prev| {
-            prev.print(zld);
-        }
-
+    pub fn print_this(self: *const TextBlock, zld: *Zld) void {
         log.warn("TextBlock", .{});
         log.warn("  | {}: '{s}'", .{ self.local_sym_index, zld.locals.items[self.local_sym_index].name });
-        log.warn("  | Aliases:", .{});
-        for (self.aliases.items) |index| {
-            log.warn("    | {}: '{s}'", .{ index, zld.locals.items[index].name });
+        if (self.aliases) |aliases| {
+            log.warn("  | Aliases:", .{});
+            for (aliases) |index| {
+                log.warn("    | {}: '{s}'", .{ index, zld.locals.items[index].name });
+            }
         }
-        log.warn("  | References:", .{});
-        for (self.references.items) |index| {
-            log.warn("    | {}: '{s}'", .{ index, zld.locals.items[index].name });
+        if (self.references) |references| {
+            log.warn("  | References:", .{});
+            for (references) |index| {
+                log.warn("    | {}: '{s}'", .{ index, zld.locals.items[index].name });
+            }
         }
         log.warn("  | size = {}", .{self.size});
         log.warn("  | align = {}", .{self.alignment});
         log.warn("  | segment_id = {}", .{self.segment_id});
         log.warn("  | section_id = {}", .{self.section_id});
     }
+
+    pub fn print(self: *const TextBlock, zld: *Zld) void {
+        if (self.prev) |prev| {
+            prev.print(zld);
+        }
+        self.print_this(zld);
+    }
 };
 
 /// Default path to dyld