Commit f519e781c6

Jakub Konka <kubkon@jakubkonka.com>
2021-07-15 18:48:41
zld: move TextBlock into standalone file
which should make managing the logic of parsing and resolving relocs that much simpler to parse.
1 parent c47ce31
src/link/MachO/Object.zig
@@ -16,7 +16,7 @@ const Allocator = mem.Allocator;
 const Arch = std.Target.Cpu.Arch;
 const Relocation = reloc.Relocation;
 const Symbol = @import("Symbol.zig");
-const TextBlock = Zld.TextBlock;
+const TextBlock = @import("TextBlock.zig");
 const Zld = @import("Zld.zig");
 
 usingnamespace @import("commands.zig");
src/link/MachO/reloc.zig
@@ -12,7 +12,7 @@ const Allocator = mem.Allocator;
 const Arch = std.Target.Cpu.Arch;
 const Object = @import("Object.zig");
 const Symbol = @import("Symbol.zig");
-const TextBlock = Zld.TextBlock;
+const TextBlock = @import("TextBlock.zig");
 const Zld = @import("Zld.zig");
 
 pub const Relocation = struct {
src/link/MachO/TextBlock.zig
@@ -0,0 +1,284 @@
+const TextBlock = @This();
+
+const std = @import("std");
+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 Zld = @import("Zld.zig");
+
+allocator: *Allocator,
+local_sym_index: u32,
+stab: ?Stab = null,
+aliases: std.ArrayList(u32),
+references: std.AutoArrayHashMap(u32, void),
+contained: ?[]SymbolAtOffset = null,
+code: []u8,
+relocs: std.ArrayList(Relocation),
+size: u64,
+alignment: u32,
+rebases: std.ArrayList(u64),
+bindings: std.ArrayList(SymbolAtOffset),
+dices: std.ArrayList(macho.data_in_code_entry),
+next: ?*TextBlock = null,
+prev: ?*TextBlock = null,
+
+pub const SymbolAtOffset = struct {
+    local_sym_index: u32,
+    offset: u64,
+    stab: ?Stab = null,
+};
+
+pub const Stab = union(enum) {
+    function: u64,
+    static,
+    global,
+
+    pub fn asNlists(stab: Stab, local_sym_index: u32, zld: *Zld) ![]macho.nlist_64 {
+        var nlists = std.ArrayList(macho.nlist_64).init(zld.allocator);
+        defer nlists.deinit();
+
+        const sym = zld.locals.items[local_sym_index];
+        const reg = sym.payload.regular;
+
+        switch (stab) {
+            .function => |size| {
+                try nlists.ensureUnusedCapacity(4);
+                const section_id = reg.sectionId(zld);
+                nlists.appendAssumeCapacity(.{
+                    .n_strx = 0,
+                    .n_type = macho.N_BNSYM,
+                    .n_sect = section_id,
+                    .n_desc = 0,
+                    .n_value = reg.address,
+                });
+                nlists.appendAssumeCapacity(.{
+                    .n_strx = sym.strx,
+                    .n_type = macho.N_FUN,
+                    .n_sect = section_id,
+                    .n_desc = 0,
+                    .n_value = reg.address,
+                });
+                nlists.appendAssumeCapacity(.{
+                    .n_strx = 0,
+                    .n_type = macho.N_FUN,
+                    .n_sect = 0,
+                    .n_desc = 0,
+                    .n_value = size,
+                });
+                nlists.appendAssumeCapacity(.{
+                    .n_strx = 0,
+                    .n_type = macho.N_ENSYM,
+                    .n_sect = section_id,
+                    .n_desc = 0,
+                    .n_value = size,
+                });
+            },
+            .global => {
+                try nlists.append(.{
+                    .n_strx = sym.strx,
+                    .n_type = macho.N_GSYM,
+                    .n_sect = 0,
+                    .n_desc = 0,
+                    .n_value = 0,
+                });
+            },
+            .static => {
+                try nlists.append(.{
+                    .n_strx = sym.strx,
+                    .n_type = macho.N_STSYM,
+                    .n_sect = reg.sectionId(zld),
+                    .n_desc = 0,
+                    .n_value = reg.address,
+                });
+            },
+        }
+
+        return nlists.toOwnedSlice();
+    }
+};
+
+pub fn init(allocator: *Allocator) TextBlock {
+    return .{
+        .allocator = allocator,
+        .local_sym_index = undefined,
+        .aliases = std.ArrayList(u32).init(allocator),
+        .references = std.AutoArrayHashMap(u32, void).init(allocator),
+        .code = undefined,
+        .relocs = std.ArrayList(Relocation).init(allocator),
+        .size = undefined,
+        .alignment = undefined,
+        .rebases = std.ArrayList(u64).init(allocator),
+        .bindings = std.ArrayList(SymbolAtOffset).init(allocator),
+        .dices = std.ArrayList(macho.data_in_code_entry).init(allocator),
+    };
+}
+
+pub fn deinit(self: *TextBlock) void {
+    self.aliases.deinit();
+    self.references.deinit();
+    if (self.contained) |contained| {
+        self.allocator.free(contained);
+    }
+    self.allocator.free(self.code);
+    self.relocs.deinit();
+    self.rebases.deinit();
+    self.bindings.deinit();
+    self.dices.deinit();
+}
+
+pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
+    for (self.relocs.items) |rel| {
+        log.debug("relocating {}", .{rel});
+
+        const source_addr = blk: {
+            const sym = zld.locals.items[self.local_sym_index];
+            break :blk sym.payload.regular.address + 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) {
+                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 {
+                    log.err("expected GOT entry for symbol '{s}'", .{zld.getString(rel.target.strx)});
+                    log.err("  this is an internal linker error", .{});
+                    return error.FailedToResolveRelocationTarget;
+                };
+                break :blk got.addr + got_index * @sizeOf(u64);
+            }
+
+            switch (rel.target.payload) {
+                .regular => |reg| {
+                    const is_tlv = is_tlv: {
+                        const sym = zld.locals.items[self.local_sym_index];
+                        const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
+                        const sect = seg.sections.items[sym.payload.regular.section_id];
+                        break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
+                    };
+                    if (is_tlv) {
+                        // For TLV relocations, the value specified as a relocation is the displacement from the
+                        // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
+                        // defined TLV template init section in the following order:
+                        // * wrt to __thread_data if defined, then
+                        // * wrt to __thread_bss
+                        const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
+                        const base_address = inner: {
+                            if (zld.tlv_data_section_index) |i| {
+                                break :inner seg.sections.items[i].addr;
+                            } else if (zld.tlv_bss_section_index) |i| {
+                                break :inner seg.sections.items[i].addr;
+                            } else {
+                                log.err("threadlocal variables present but no initializer sections found", .{});
+                                log.err("  __thread_data not found", .{});
+                                log.err("  __thread_bss not found", .{});
+                                return error.FailedToResolveRelocationTarget;
+                            }
+                        };
+                        break :blk reg.address - base_address;
+                    }
+
+                    break :blk reg.address;
+                },
+                .proxy => {
+                    if (mem.eql(u8, zld.getString(rel.target.strx), "__tlv_bootstrap")) {
+                        break :blk 0; // Dynamically bound by dyld.
+                    }
+
+                    const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
+                    const stubs = segment.sections.items[zld.stubs_section_index.?];
+                    const stubs_index = rel.target.stubs_index orelse {
+                        // TODO verify in TextBlock that the symbol is indeed dynamically bound.
+                        break :blk 0; // Dynamically bound by dyld.
+                    };
+                    break :blk stubs.addr + stubs_index * stubs.reserved2;
+                },
+                else => {
+                    log.err("failed to resolve symbol '{s}' as a relocation target", .{
+                        zld.getString(rel.target.strx),
+                    });
+                    log.err("  this is an internal linker error", .{});
+                    return error.FailedToResolveRelocationTarget;
+                },
+            }
+        };
+
+        log.debug("  | source_addr = 0x{x}", .{source_addr});
+        log.debug("  | target_addr = 0x{x}", .{target_addr});
+
+        try rel.resolve(self, source_addr, target_addr);
+    }
+}
+
+pub fn print_this(self: *const TextBlock, zld: *Zld) void {
+    log.warn("TextBlock", .{});
+    log.warn("  {}: {}", .{ self.local_sym_index, zld.locals.items[self.local_sym_index] });
+    if (self.stab) |stab| {
+        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] });
+        }
+    }
+    if (self.references.count() > 0) {
+        log.warn("  references:", .{});
+        for (self.references.keys()) |index| {
+            log.warn("    {}: {}", .{ index, zld.locals.items[index] });
+        }
+    }
+    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,
+                });
+            } else {
+                log.warn("    {}: {}\n", .{
+                    sym_at_off.offset,
+                    zld.locals.items[sym_at_off.local_sym_index],
+                });
+            }
+        }
+    }
+    log.warn("  code.len = {}", .{self.code.len});
+    if (self.relocs.items.len > 0) {
+        log.warn("  relocations:", .{});
+        for (self.relocs.items) |rel| {
+            log.warn("    {}", .{rel});
+        }
+    }
+    if (self.rebases.items.len > 0) {
+        log.warn("  rebases: {any}", .{self.rebases.items});
+    }
+    if (self.bindings.items.len > 0) {
+        log.warn("  bindings: {any}", .{self.bindings.items});
+    }
+    if (self.dices.items.len > 0) {
+        log.warn("  dices: {any}", .{self.dices.items});
+    }
+    log.warn("  size = {}", .{self.size});
+    log.warn("  align = {}", .{self.alignment});
+}
+
+pub fn print(self: *const TextBlock, zld: *Zld) void {
+    if (self.prev) |prev| {
+        prev.print(zld);
+    }
+    self.print_this(zld);
+}
src/link/MachO/Zld.zig
@@ -10,15 +10,14 @@ const macho = std.macho;
 const math = std.math;
 const log = std.log.scoped(.zld);
 const aarch64 = @import("../../codegen/aarch64.zig");
-const reloc = @import("reloc.zig");
 
 const Allocator = mem.Allocator;
 const Archive = @import("Archive.zig");
 const CodeSignature = @import("CodeSignature.zig");
 const Dylib = @import("Dylib.zig");
 const Object = @import("Object.zig");
-const Relocation = reloc.Relocation;
 const Symbol = @import("Symbol.zig");
+const TextBlock = @import("TextBlock.zig");
 const Trie = @import("Trie.zig");
 
 usingnamespace @import("commands.zig");
@@ -123,280 +122,6 @@ pub const Output = struct {
     install_name: ?[]const u8 = null,
 };
 
-pub const TextBlock = struct {
-    allocator: *Allocator,
-    local_sym_index: u32,
-    stab: ?Stab = null,
-    aliases: std.ArrayList(u32),
-    references: std.AutoArrayHashMap(u32, void),
-    contained: ?[]SymbolAtOffset = null,
-    code: []u8,
-    relocs: std.ArrayList(Relocation),
-    size: u64,
-    alignment: u32,
-    rebases: std.ArrayList(u64),
-    bindings: std.ArrayList(SymbolAtOffset),
-    dices: std.ArrayList(macho.data_in_code_entry),
-    next: ?*TextBlock = null,
-    prev: ?*TextBlock = null,
-
-    pub const SymbolAtOffset = struct {
-        local_sym_index: u32,
-        offset: u64,
-        stab: ?Stab = null,
-    };
-
-    pub const Stab = union(enum) {
-        function: u64,
-        static,
-        global,
-
-        pub fn asNlists(stab: Stab, local_sym_index: u32, zld: *Zld) ![]macho.nlist_64 {
-            var nlists = std.ArrayList(macho.nlist_64).init(zld.allocator);
-            defer nlists.deinit();
-
-            const sym = zld.locals.items[local_sym_index];
-            const reg = sym.payload.regular;
-
-            switch (stab) {
-                .function => |size| {
-                    try nlists.ensureUnusedCapacity(4);
-                    const section_id = reg.sectionId(zld);
-                    nlists.appendAssumeCapacity(.{
-                        .n_strx = 0,
-                        .n_type = macho.N_BNSYM,
-                        .n_sect = section_id,
-                        .n_desc = 0,
-                        .n_value = reg.address,
-                    });
-                    nlists.appendAssumeCapacity(.{
-                        .n_strx = sym.strx,
-                        .n_type = macho.N_FUN,
-                        .n_sect = section_id,
-                        .n_desc = 0,
-                        .n_value = reg.address,
-                    });
-                    nlists.appendAssumeCapacity(.{
-                        .n_strx = 0,
-                        .n_type = macho.N_FUN,
-                        .n_sect = 0,
-                        .n_desc = 0,
-                        .n_value = size,
-                    });
-                    nlists.appendAssumeCapacity(.{
-                        .n_strx = 0,
-                        .n_type = macho.N_ENSYM,
-                        .n_sect = section_id,
-                        .n_desc = 0,
-                        .n_value = size,
-                    });
-                },
-                .global => {
-                    try nlists.append(.{
-                        .n_strx = sym.strx,
-                        .n_type = macho.N_GSYM,
-                        .n_sect = 0,
-                        .n_desc = 0,
-                        .n_value = 0,
-                    });
-                },
-                .static => {
-                    try nlists.append(.{
-                        .n_strx = sym.strx,
-                        .n_type = macho.N_STSYM,
-                        .n_sect = reg.sectionId(zld),
-                        .n_desc = 0,
-                        .n_value = reg.address,
-                    });
-                },
-            }
-
-            return nlists.toOwnedSlice();
-        }
-    };
-
-    pub fn init(allocator: *Allocator) TextBlock {
-        return .{
-            .allocator = allocator,
-            .local_sym_index = undefined,
-            .aliases = std.ArrayList(u32).init(allocator),
-            .references = std.AutoArrayHashMap(u32, void).init(allocator),
-            .code = undefined,
-            .relocs = std.ArrayList(Relocation).init(allocator),
-            .size = undefined,
-            .alignment = undefined,
-            .rebases = std.ArrayList(u64).init(allocator),
-            .bindings = std.ArrayList(SymbolAtOffset).init(allocator),
-            .dices = std.ArrayList(macho.data_in_code_entry).init(allocator),
-        };
-    }
-
-    pub fn deinit(self: *TextBlock) void {
-        self.aliases.deinit();
-        self.references.deinit();
-        if (self.contained) |contained| {
-            self.allocator.free(contained);
-        }
-        self.allocator.free(self.code);
-        self.relocs.deinit();
-        self.rebases.deinit();
-        self.bindings.deinit();
-        self.dices.deinit();
-    }
-
-    pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
-        for (self.relocs.items) |rel| {
-            log.debug("relocating {}", .{rel});
-
-            const source_addr = blk: {
-                const sym = zld.locals.items[self.local_sym_index];
-                break :blk sym.payload.regular.address + 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) {
-                    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 {
-                        log.err("expected GOT entry for symbol '{s}'", .{zld.getString(rel.target.strx)});
-                        log.err("  this is an internal linker error", .{});
-                        return error.FailedToResolveRelocationTarget;
-                    };
-                    break :blk got.addr + got_index * @sizeOf(u64);
-                }
-
-                switch (rel.target.payload) {
-                    .regular => |reg| {
-                        const is_tlv = is_tlv: {
-                            const sym = zld.locals.items[self.local_sym_index];
-                            const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
-                            const sect = seg.sections.items[sym.payload.regular.section_id];
-                            break :is_tlv sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
-                        };
-                        if (is_tlv) {
-                            // For TLV relocations, the value specified as a relocation is the displacement from the
-                            // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
-                            // defined TLV template init section in the following order:
-                            // * wrt to __thread_data if defined, then
-                            // * wrt to __thread_bss
-                            const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
-                            const base_address = inner: {
-                                if (zld.tlv_data_section_index) |i| {
-                                    break :inner seg.sections.items[i].addr;
-                                } else if (zld.tlv_bss_section_index) |i| {
-                                    break :inner seg.sections.items[i].addr;
-                                } else {
-                                    log.err("threadlocal variables present but no initializer sections found", .{});
-                                    log.err("  __thread_data not found", .{});
-                                    log.err("  __thread_bss not found", .{});
-                                    return error.FailedToResolveRelocationTarget;
-                                }
-                            };
-                            break :blk reg.address - base_address;
-                        }
-
-                        break :blk reg.address;
-                    },
-                    .proxy => {
-                        if (mem.eql(u8, zld.getString(rel.target.strx), "__tlv_bootstrap")) {
-                            break :blk 0; // Dynamically bound by dyld.
-                        }
-
-                        const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
-                        const stubs = segment.sections.items[zld.stubs_section_index.?];
-                        const stubs_index = rel.target.stubs_index orelse {
-                            // TODO verify in TextBlock that the symbol is indeed dynamically bound.
-                            break :blk 0; // Dynamically bound by dyld.
-                        };
-                        break :blk stubs.addr + stubs_index * stubs.reserved2;
-                    },
-                    else => {
-                        log.err("failed to resolve symbol '{s}' as a relocation target", .{
-                            zld.getString(rel.target.strx),
-                        });
-                        log.err("  this is an internal linker error", .{});
-                        return error.FailedToResolveRelocationTarget;
-                    },
-                }
-            };
-
-            log.debug("  | source_addr = 0x{x}", .{source_addr});
-            log.debug("  | target_addr = 0x{x}", .{target_addr});
-
-            try rel.resolve(self, source_addr, target_addr);
-        }
-    }
-
-    pub fn print_this(self: *const TextBlock, zld: *Zld) void {
-        log.warn("TextBlock", .{});
-        log.warn("  {}: {}", .{ self.local_sym_index, zld.locals.items[self.local_sym_index] });
-        if (self.stab) |stab| {
-            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] });
-            }
-        }
-        if (self.references.count() > 0) {
-            log.warn("  references:", .{});
-            for (self.references.keys()) |index| {
-                log.warn("    {}: {}", .{ index, zld.locals.items[index] });
-            }
-        }
-        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,
-                    });
-                } else {
-                    log.warn("    {}: {}\n", .{
-                        sym_at_off.offset,
-                        zld.locals.items[sym_at_off.local_sym_index],
-                    });
-                }
-            }
-        }
-        log.warn("  code.len = {}", .{self.code.len});
-        if (self.relocs.items.len > 0) {
-            log.warn("  relocations:", .{});
-            for (self.relocs.items) |rel| {
-                log.warn("    {}", .{rel});
-            }
-        }
-        if (self.rebases.items.len > 0) {
-            log.warn("  rebases: {any}", .{self.rebases.items});
-        }
-        if (self.bindings.items.len > 0) {
-            log.warn("  bindings: {any}", .{self.bindings.items});
-        }
-        if (self.dices.items.len > 0) {
-            log.warn("  dices: {any}", .{self.dices.items});
-        }
-        log.warn("  size = {}", .{self.size});
-        log.warn("  align = {}", .{self.alignment});
-    }
-
-    pub fn print(self: *const TextBlock, zld: *Zld) void {
-        if (self.prev) |prev| {
-            prev.print(zld);
-        }
-        self.print_this(zld);
-    }
-};
-
 /// Default path to dyld
 const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
 
CMakeLists.txt
@@ -582,6 +582,7 @@ set(ZIG_STAGE2_SOURCES
     "${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"
     "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"