From 7aeedc0912c8218773891ae98a729bb2b1be5231 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 8 Jul 2021 00:29:10 +0200 Subject: [PATCH] zld: allocate TextBlocks temporarily by iterating over all defined TextBlocks. However, once we merge this with MachO incremental, updates will be done at the point of creation and/or update. Also, fix mining TLV knowledge for working out TLV pointers. --- src/link/MachO/Object.zig | 21 ++++++++- src/link/MachO/Symbol.zig | 15 +++++++ src/link/MachO/Zld.zig | 95 +++++++++++++++++++-------------------- src/link/MachO/reloc.zig | 7 +-- 4 files changed, 83 insertions(+), 55 deletions(-) diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index f000119edf..867821fa6f 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -7,6 +7,7 @@ const fs = std.fs; const io = std.io; 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; @@ -436,7 +437,7 @@ const TextBlockParser = struct { .code = try self.allocator.dupe(u8, code), .relocs = std.ArrayList(Relocation).init(self.allocator), .rebases = std.ArrayList(u64).init(self.allocator), - .tlv_offsets = std.ArrayList(u64).init(self.allocator), + .tlv_offsets = std.ArrayList(TextBlock.TlvOffset).init(self.allocator), .size = size, .alignment = self.section.@"align", }; @@ -533,6 +534,14 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { } } + // 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]; + tsect.size += block.size; + tsect.@"align" = math.max(tsect.@"align", block.alignment); + if (zld.blocks.getPtr(match)) |last| { last.*.next = block; block.prev = last.*; @@ -580,7 +589,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { .code = try self.allocator.dupe(u8, code), .relocs = std.ArrayList(Relocation).init(self.allocator), .rebases = std.ArrayList(u64).init(self.allocator), - .tlv_offsets = std.ArrayList(u64).init(self.allocator), + .tlv_offsets = std.ArrayList(TextBlock.TlvOffset).init(self.allocator), .size = sect.size, .alignment = sect.@"align", }; @@ -589,6 +598,14 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { try self.parseRelocs(zld, relocs, block, 0); } + // 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]; + tsect.size += block.size; + tsect.@"align" = math.max(tsect.@"align", block.alignment); + if (zld.blocks.getPtr(match)) |last| { last.*.next = block; block.prev = last.*; diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 86624653f5..0ed122cc95 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -10,6 +10,7 @@ const Allocator = mem.Allocator; const Dylib = @import("Dylib.zig"); const Object = @import("Object.zig"); const StringTable = @import("StringTable.zig"); +const Zld = @import("Zld.zig"); /// Symbol name. Owned slice. name: []const u8, @@ -80,6 +81,20 @@ pub const Regular = struct { } 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 { diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 53bb31a718..e137e16e9b 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -129,10 +129,15 @@ pub const TextBlock = struct { size: u64, alignment: u32, rebases: std.ArrayList(u64), - tlv_offsets: std.ArrayList(u64), + tlv_offsets: std.ArrayList(TlvOffset), next: ?*TextBlock = null, prev: ?*TextBlock = null, + pub const TlvOffset = struct { + local_sym_index: u32, + offset: u64, + }; + pub fn deinit(block: *TextBlock, allocator: *Allocator) void { if (block.aliases) |aliases| { allocator.free(aliases); @@ -281,10 +286,11 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg try self.addRpaths(args.rpaths); try self.addDataInCodeLC(); try self.addCodeSignatureLC(); - // try self.allocateTextSegment(); - // try self.allocateDataConstSegment(); - // try self.allocateDataSegment(); - // self.allocateLinkeditSegment(); + try self.allocateTextSegment(); + try self.allocateDataConstSegment(); + try self.allocateDataSegment(); + self.allocateLinkeditSegment(); + try self.allocateTextBlocks(); var it = self.blocks.iterator(); while (it.next()) |entry| { @@ -292,6 +298,7 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg 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; @@ -865,14 +872,14 @@ fn sortSections(self: *Zld) !void { while (it.next()) |entry| { const old = entry.key_ptr.*; const sect = if (old.seg == self.text_segment_cmd_index.?) - text_index_mapping.get(old.sect) + text_index_mapping.get(old.sect).? else if (old.seg == self.data_const_segment_cmd_index.?) - data_const_index_mapping.get(old.sect) + data_const_index_mapping.get(old.sect).? else - data_index_mapping.get(old.sect); + data_index_mapping.get(old.sect).?; transient.putAssumeCapacityNoClobber(.{ .seg = old.seg, - .sect = old.sect, + .sect = sect, }, entry.value_ptr.*); } @@ -880,6 +887,18 @@ fn sortSections(self: *Zld) !void { self.blocks.deinit(self.allocator); self.blocks = transient; } + + for (self.locals.items) |sym, i| { + if (i == 0) continue; // skip the null symbol + assert(sym.payload == .regular); + const reg = &sym.payload.regular; + reg.section_id = if (reg.segment_id == self.text_segment_cmd_index.?) + text_index_mapping.get(reg.section_id).? + else if (reg.segment_id == self.data_const_segment_cmd_index.?) + data_const_index_mapping.get(reg.section_id).? + else + data_index_mapping.get(reg.section_id).?; + } } fn allocateTextSegment(self: *Zld) !void { @@ -991,50 +1010,26 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64) !void { seg.inner.vmsize = seg_size_aligned; } -fn allocateSymbol(self: *Zld, symbol: *Symbol) !void { - const reg = &symbol.payload.regular; - const object = reg.file orelse return; - const source_sect = &object.sections.items[reg.section]; - const target_map = source_sect.target_map orelse { - log.debug("section '{s},{s}' not mapped for symbol '{s}'", .{ - segmentName(source_sect.inner), - sectionName(source_sect.inner), - symbol.name, - }); - return; - }; +fn allocateTextBlocks(self: *Zld) !void { + var it = self.blocks.iterator(); + while (it.next()) |entry| { + const match = entry.key_ptr.*; + var block: *TextBlock = entry.value_ptr.*; - const target_seg = self.load_commands.items[target_map.segment_id].Segment; - const target_sect = target_seg.sections.items[target_map.section_id]; - const target_addr = target_sect.addr + target_map.offset; - const address = reg.address - source_sect.inner.addr + target_addr; + const seg = self.load_commands.items[match.seg].Segment; + const sect = seg.sections.items[match.sect]; + var base_addr: u64 = sect.addr + sect.size; - log.debug("resolving symbol '{s}' at 0x{x}", .{ symbol.name, address }); + while (true) { + const sym = self.locals.items[block.local_sym_index]; + assert(sym.payload == .regular); + sym.payload.regular.address = base_addr - block.size; + base_addr -= block.size; - // TODO there might be a more generic way of doing this. - var section: u8 = 0; - for (self.load_commands.items) |cmd, cmd_id| { - if (cmd != .Segment) break; - if (cmd_id == target_map.segment_id) { - section += @intCast(u8, target_map.section_id) + 1; - break; + if (block.prev) |prev| { + block = prev; + } else break; } - section += @intCast(u8, cmd.Segment.sections.items.len); - } - - reg.address = address; - reg.section = section; -} - -fn allocateSymbols(self: *Zld) !void { - for (self.locals.items) |symbol| { - if (symbol.payload != .regular) continue; - try self.allocateSymbol(symbol); - } - - for (self.globals.values()) |symbol| { - if (symbol.payload != .regular) continue; - try self.allocateSymbol(symbol); } } @@ -1487,7 +1482,7 @@ fn resolveSymbols(self: *Zld) !void { .code = code, .relocs = std.ArrayList(Relocation).init(self.allocator), .rebases = std.ArrayList(u64).init(self.allocator), - .tlv_offsets = std.ArrayList(u64).init(self.allocator), + .tlv_offsets = std.ArrayList(TextBlock.TlvOffset).init(self.allocator), .size = size, .alignment = alignment, }; diff --git a/src/link/MachO/reloc.zig b/src/link/MachO/reloc.zig index b645ec152e..07d5186a63 100644 --- a/src/link/MachO/reloc.zig +++ b/src/link/MachO/reloc.zig @@ -674,10 +674,11 @@ pub const Parser = struct { } // TLV is handled via a separate offset mechanism. - // Save the offset to the initializer. - // TODO I believe this can be simplified a lot! if (sect_type == macho.S_THREAD_LOCAL_VARIABLES) { - try self.block.tlv_offsets.append(out_rel.offset); + try self.block.tlv_offsets.append(.{ + .local_sym_index = out_rel.target.payload.regular.local_sym_index, + .offset = out_rel.offset, + }); } }, }