Commit 555b66c255

Jakub Konka <kubkon@jakubkonka.com>
2021-07-07 10:36:41
zld: move should_rebase logic into Symbol
1 parent dbd2eb7
Changed files (4)
src/link/MachO/Object.zig
@@ -339,6 +339,7 @@ const TextBlockParser = struct {
     zld: *Zld,
     nlists: []NlistWithIndex,
     index: u32 = 0,
+    match: Zld.MatchingSection,
 
     fn peek(self: *TextBlockParser) ?NlistWithIndex {
         return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
@@ -405,6 +406,8 @@ const TextBlockParser = struct {
         const senior_nlist = aliases.pop();
         const senior_sym = self.zld.locals.items[senior_nlist.index];
         assert(senior_sym.payload == .regular);
+        senior_sym.payload.regular.segment_id = self.match.seg;
+        senior_sym.payload.regular.section_id = self.match.sect;
 
         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;
@@ -417,6 +420,11 @@ const TextBlockParser = struct {
             try out.ensureTotalCapacity(aliases.items.len);
             for (aliases.items) |alias| {
                 out.appendAssumeCapacity(alias.index);
+
+                const sym = self.zld.locals.items[alias.index];
+                const reg = &sym.payload.regular;
+                reg.segment_id = self.match.seg;
+                reg.section_id = self.match.sect;
             }
             break :blk out.toOwnedSlice();
         } else null;
@@ -439,6 +447,18 @@ const TextBlockParser = struct {
             try self.object.parseRelocs(self.zld, relocs, block, start_addr);
         }
 
+        const is_zerofill = blk: {
+            const tseg = self.zld.load_commands.items[self.match.seg].Segment;
+            const tsect = tseg.sections.items[self.match.sect];
+            const tsect_type = sectionType(tsect);
+            break :blk tsect_type == macho.S_ZEROFILL or
+                tsect_type == macho.S_THREAD_LOCAL_ZEROFILL or
+                tsect_type == macho.S_THREAD_LOCAL_VARIABLES;
+        };
+        if (is_zerofill) {
+            mem.set(u8, block.code, 0);
+        }
+
         self.index += 1;
 
         return block;
@@ -511,28 +531,16 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
                     .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.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? });
-                                continue;
-                            }
-                        }
-                        reg.segment_id = match.seg;
-                        reg.section_id = match.sect;
-                    }
-
-                    if (block.aliases) |aliases| {
-                        for (aliases) |alias| {
-                            const sym = zld.locals.items[alias];
-                            const reg = &sym.payload.regular;
-                            reg.segment_id = match.seg;
-                            reg.section_id = match.sect;
+                    const sym = zld.locals.items[block.local_sym_index];
+                    const reg = &sym.payload.regular;
+                    if (reg.file) |file| {
+                        if (file != self) {
+                            log.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? });
+                            continue;
                         }
                     }
 
@@ -587,6 +595,18 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
                 try self.parseRelocs(zld, relocs, block, 0);
             }
 
+            const is_zerofill = blk: {
+                const tseg = zld.load_commands.items[match.seg].Segment;
+                const tsect = tseg.sections.items[match.sect];
+                const tsect_type = sectionType(tsect);
+                break :blk tsect_type == macho.S_ZEROFILL or
+                    tsect_type == macho.S_THREAD_LOCAL_ZEROFILL or
+                    tsect_type == macho.S_THREAD_LOCAL_VARIABLES;
+            };
+            if (is_zerofill) {
+                mem.set(u8, block.code, 0);
+            }
+
             if (zld.last_text_block) |last| {
                 last.next = block;
                 block.prev = last;
src/link/MachO/reloc.zig
@@ -1,6 +1,7 @@
 const std = @import("std");
 const aarch64 = @import("../../codegen/aarch64.zig");
 const assert = std.debug.assert;
+const commands = @import("commands.zig");
 const log = std.log.scoped(.reloc);
 const macho = std.macho;
 const math = std.math;
@@ -567,14 +568,60 @@ pub const Parser = struct {
                 const index = @intCast(u32, self.zld.got_entries.items.len);
                 out_rel.target.got_index = index;
                 try self.zld.got_entries.append(self.zld.allocator, out_rel.target);
+
                 log.debug("adding GOT entry for symbol {s} at index {}", .{ out_rel.target.name, index });
-            }
+            } else if (out_rel.payload == .unsigned) {
+                const sym = out_rel.target;
+                switch (sym.payload) {
+                    .proxy => {
+                        try sym.payload.proxy.bind_info.append(self.zld.allocator, .{
+                            .local_sym_index = self.block.local_sym_index,
+                            .offset = out_rel.offset,
+                        });
+                    },
+                    else => {
+                        const source_sym = self.zld.locals.items[self.block.local_sym_index];
+                        const source_reg = &source_sym.payload.regular;
+                        const seg = self.zld.load_commands.items[source_reg.segment_id].Segment;
+                        const sect = seg.sections.items[source_reg.section_id];
+                        const sect_type = commands.sectionType(sect);
+
+                        const should_rebase = rebase: {
+                            if (!out_rel.payload.unsigned.is_64bit) break :rebase false;
+
+                            // 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 (self.zld.data_segment_cmd_index) |idx| {
+                                    if (source_reg.segment_id == idx) {
+                                        break :blk true;
+                                    }
+                                }
+                                if (self.zld.data_const_segment_cmd_index) |idx| {
+                                    if (source_reg.segment_id == idx) {
+                                        break :blk true;
+                                    }
+                                }
+                                break :blk false;
+                            };
+
+                            if (!is_right_segment) break :rebase false;
+                            if (sect_type != macho.S_LITERAL_POINTERS and
+                                sect_type != macho.S_REGULAR)
+                            {
+                                break :rebase false;
+                            }
 
-            if (out_rel.payload == .branch) {
+                            break :rebase true;
+                        };
+                        source_reg.should_rebase = should_rebase;
+                    },
+                }
+            } else if (out_rel.payload == .branch) blk: {
                 const sym = out_rel.target;
 
-                if (sym.stubs_index != null) continue;
-                if (sym.payload != .proxy) continue;
+                if (sym.stubs_index != null) break :blk;
+                if (sym.payload != .proxy) break :blk;
 
                 const index = @intCast(u32, self.zld.stubs.items.len);
                 sym.stubs_index = index;
src/link/MachO/Symbol.zig
@@ -2,6 +2,7 @@ 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;
 
@@ -57,6 +58,8 @@ pub const Regular = struct {
 
     local_sym_index: u32 = 0,
 
+    should_rebase: bool = false,
+
     pub const Linkage = enum {
         translation_unit,
         linkage_unit,
@@ -74,6 +77,9 @@ pub const Regular = struct {
         if (self.weak_ref) {
             try std.fmt.format(writer, ".weak_ref, ", .{});
         }
+        if (self.should_rebase) {
+            try std.fmt.format(writer, ".should_rebase, ", .{});
+        }
         if (self.file) |file| {
             try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
         }
@@ -108,8 +114,8 @@ pub const Proxy = struct {
     /// Dynamic binding info - spots within the final
     /// executable where this proxy is referenced from.
     bind_info: std.ArrayListUnmanaged(struct {
-        segment_id: u16,
-        address: u64,
+        local_sym_index: u32,
+        offset: u32,
     }) = .{},
 
     /// Dylib where to locate this symbol.
@@ -198,6 +204,17 @@ pub fn isTemp(symbol: Symbol) bool {
     return false;
 }
 
+pub fn needsTlvOffset(self: Symbol, zld: *Zld) bool {
+    if (self.payload != .regular) return false;
+
+    const reg = self.payload.regular;
+    const seg = zld.load_command.items[reg.segment_id].Segment;
+    const sect = seg.sections.items[reg.section_id];
+    const sect_type = commands.sectionType(sect);
+
+    return sect_type == macho.S_THREAD_LOCAL_VARIABLES;
+}
+
 pub fn asNlist(symbol: *Symbol, strtab: *StringTable) !macho.nlist_64 {
     const n_strx = try strtab.getOrPut(symbol.name);
     const nlist = nlist: {
src/link/MachO/Zld.zig
@@ -107,8 +107,6 @@ locals: std.ArrayListUnmanaged(*Symbol) = .{},
 imports: std.ArrayListUnmanaged(*Symbol) = .{},
 globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
 
-threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction
-local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
 stubs: std.ArrayListUnmanaged(*Symbol) = .{},
 got_entries: std.ArrayListUnmanaged(*Symbol) = .{},
 
@@ -197,8 +195,6 @@ pub fn init(allocator: *Allocator) !Zld {
 }
 
 pub fn deinit(self: *Zld) void {
-    self.threadlocal_offsets.deinit(self.allocator);
-    self.local_rebases.deinit(self.allocator);
     self.stubs.deinit(self.allocator);
     self.got_entries.deinit(self.allocator);
 
@@ -225,8 +221,6 @@ pub fn deinit(self: *Zld) void {
     }
     self.dylibs.deinit(self.allocator);
 
-    self.globals.deinit(self.allocator);
-
     for (self.imports.items) |sym| {
         sym.deinit(self.allocator);
         self.allocator.destroy(sym);
@@ -239,6 +233,7 @@ pub fn deinit(self: *Zld) void {
     }
     self.locals.deinit(self.allocator);
 
+    self.globals.deinit(self.allocator);
     self.strtab.deinit();
 }
 
@@ -290,7 +285,6 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
     // try self.allocateDataSegment();
     // self.allocateLinkeditSegment();
     // try self.allocateSymbols();
-    // try self.allocateProxyBindAddresses();
     // try self.flush();
 }
 
@@ -449,7 +443,7 @@ fn updateMetadata(self: *Zld) !void {
     }
 }
 
-const MatchingSection = struct {
+pub const MatchingSection = struct {
     seg: u16,
     sect: u16,
 };
@@ -1140,31 +1134,6 @@ fn allocateSymbols(self: *Zld) !void {
     }
 }
 
-fn allocateProxyBindAddresses(self: *Zld) !void {
-    for (self.objects.items) |object| {
-        for (object.sections.items) |sect| {
-            const relocs = sect.relocs orelse continue;
-
-            for (relocs) |rel| {
-                if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased
-                if (rel.target != .symbol) continue;
-
-                const sym = object.symbols.items[rel.target.symbol];
-                if (sym.payload != .proxy) continue;
-
-                const target_map = sect.target_map orelse continue;
-                const target_seg = self.load_commands.items[target_map.segment_id].Segment;
-                const target_sect = target_seg.sections.items[target_map.section_id];
-
-                try sym.payload.proxy.bind_info.append(self.allocator, .{
-                    .segment_id = target_map.segment_id,
-                    .address = target_sect.addr + target_map.offset + rel.offset,
-                });
-            }
-        }
-    }
-}
-
 fn writeStubHelperCommon(self: *Zld) !void {
     const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
     const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
@@ -1748,72 +1717,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
                                 args.source_source_sect_addr = sect.inner.addr;
                                 args.source_target_sect_addr = source_sect.inner.addr;
                             }
-
-                            const sect_type = sectionType(target_sect);
-                            const should_rebase = rebase: {
-                                if (!unsigned.is_64bit) break :rebase false;
-
-                                // 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 (self.data_segment_cmd_index) |idx| {
-                                        if (target_map.segment_id == idx) {
-                                            break :blk true;
-                                        }
-                                    }
-                                    if (self.data_const_segment_cmd_index) |idx| {
-                                        if (target_map.segment_id == idx) {
-                                            break :blk true;
-                                        }
-                                    }
-                                    break :blk false;
-                                };
-
-                                if (!is_right_segment) break :rebase false;
-                                if (sect_type != macho.S_LITERAL_POINTERS and
-                                    sect_type != macho.S_REGULAR)
-                                {
-                                    break :rebase false;
-                                }
-                                if (rel.target == .symbol) {
-                                    const sym = object.symbols.items[rel.target.symbol];
-                                    if (sym.payload == .proxy) {
-                                        break :rebase false;
-                                    }
-                                }
-
-                                break :rebase true;
-                            };
-
-                            if (should_rebase) {
-                                try self.local_rebases.append(self.allocator, .{
-                                    .offset = source_addr - target_seg.inner.vmaddr,
-                                    .segment_id = target_map.segment_id,
-                                });
-                            }
-
-                            // TLV is handled via a separate offset mechanism.
-                            // Calculate the offset to the initializer.
-                            if (sect_type == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
-                                // TODO we don't want to save offset to tlv_bootstrap
-                                if (mem.eql(u8, object.symbols.items[rel.target.symbol].name, "__tlv_bootstrap")) break :tlv;
-
-                                const base_addr = blk: {
-                                    if (self.tlv_data_section_index) |index| {
-                                        const tlv_data = target_seg.sections.items[index];
-                                        break :blk tlv_data.addr;
-                                    } else {
-                                        const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
-                                        break :blk tlv_bss.addr;
-                                    }
-                                };
-                                // Since we require TLV data to always preceed TLV bss section, we calculate
-                                // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
-                                try self.threadlocal_offsets.append(self.allocator, .{
-                                    .source_addr = args.source_addr,
-                                    .offset = args.target_addr - base_addr,
-                                });
-                            }
                         },
                         .got_page, .got_page_off, .got_load, .got, .pointer_to_got => {
                             const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
@@ -1839,34 +1742,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
                     try rel.resolve(args);
                 }
             }
-
-            log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{
-                segname,
-                sectname,
-                object.name,
-                target_sect_off,
-                target_sect_off + sect.code.len,
-            });
-
-            if (sectionType(target_sect) == macho.S_ZEROFILL or
-                sectionType(target_sect) == macho.S_THREAD_LOCAL_ZEROFILL or
-                sectionType(target_sect) == macho.S_THREAD_LOCAL_VARIABLES)
-            {
-                log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{
-                    segmentName(target_sect),
-                    sectionName(target_sect),
-                    target_sect_off,
-                    target_sect_off + sect.code.len,
-                });
-
-                // Zero-out the space
-                var zeroes = try self.allocator.alloc(u8, sect.code.len);
-                defer self.allocator.free(zeroes);
-                mem.set(u8, zeroes, 0);
-                try self.file.?.pwriteAll(zeroes, target_sect_off);
-            } else {
-                try self.file.?.pwriteAll(sect.code, target_sect_off);
-            }
         }
     }
 }