Commit 7aeedc0912

Jakub Konka <kubkon@jakubkonka.com>
2021-07-08 00:29:10
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.
1 parent e524f43
Changed files (4)
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.*;
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,
+                            });
                         }
                     },
                 }
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 {
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;
-    };
-
-    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;
-
-    log.debug("resolving symbol '{s}' at 0x{x}", .{ symbol.name, address });
-
-    // 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;
+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 seg = self.load_commands.items[match.seg].Segment;
+        const sect = seg.sections.items[match.sect];
+        var base_addr: u64 = sect.addr + sect.size;
+
+        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;
+
+            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,
                 };