Commit 496903c6a8

Jakub Konka <kubkon@jakubkonka.com>
2021-07-11 23:34:35
zld: add DICE support mainly for x86_64-macos
1 parent e3fe9a9
Changed files (2)
src
src/link/MachO/Object.zig
@@ -325,6 +325,21 @@ fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64)
     return relocs[start..end];
 }
 
+fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) []macho.data_in_code_entry {
+    const Predicate = struct {
+        addr: u64,
+
+        fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
+            return dice.offset >= self.addr;
+        }
+    };
+
+    const start = findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
+    const end = findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
+
+    return dices[start..end];
+}
+
 const TextBlockParser = struct {
     allocator: *Allocator,
     section: macho.section_64,
@@ -445,6 +460,23 @@ const TextBlockParser = struct {
             try self.object.parseRelocs(self.zld, relocs, block, start_addr);
         }
 
+        if (self.zld.has_dices) {
+            const dices = filterDice(
+                self.object.data_in_code_entries.items,
+                senior_nlist.nlist.n_value,
+                senior_nlist.nlist.n_value + size,
+            );
+            try block.dices.ensureTotalCapacity(dices.len);
+
+            for (dices) |dice| {
+                block.dices.appendAssumeCapacity(.{
+                    .offset = dice.offset - try math.cast(u32, senior_nlist.nlist.n_value),
+                    .length = dice.length,
+                    .kind = dice.kind,
+                });
+            }
+        }
+
         self.index += 1;
 
         return block;
@@ -504,6 +536,16 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
         const is_splittable = self.header.?.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
         // const is_splittable = false;
 
+        const has_dices: bool = blk: {
+            if (self.text_section_index) |index| {
+                if (index != id) break :blk false;
+                if (self.data_in_code_entries.items.len == 0) break :blk false;
+                break :blk true;
+            }
+            break :blk false;
+        };
+        zld.has_dices = has_dices;
+
         next: {
             if (is_splittable) blocks: {
                 if (filtered_nlists.len == 0) break :blocks;
@@ -593,6 +635,19 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
                 try self.parseRelocs(zld, relocs, block, 0);
             }
 
+            if (zld.has_dices) {
+                const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);
+                try block.dices.ensureTotalCapacity(dices.len);
+
+                for (dices) |dice| {
+                    block.dices.appendAssumeCapacity(.{
+                        .offset = dice.offset - try math.cast(u32, sect.addr),
+                        .length = dice.length,
+                        .kind = dice.kind,
+                    });
+                }
+            }
+
             // Since this is block gets a helper local temporary symbol that didn't exist
             // in the object file which encompasses the entire section, we need traverse
             // the filtered symbols and note which symbol is contained within so that
src/link/MachO/Zld.zig
@@ -114,6 +114,8 @@ stub_helper_stubs_start_off: ?u64 = null,
 
 blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
 
+has_dices: bool = false,
+
 pub const Output = struct {
     tag: enum { exe, dylib },
     path: []const u8,
@@ -131,6 +133,7 @@ pub const TextBlock = struct {
     size: u64,
     alignment: u32,
     rebases: std.ArrayList(u64),
+    dices: std.ArrayList(macho.data_in_code_entry),
     next: ?*TextBlock = null,
     prev: ?*TextBlock = null,
 
@@ -149,6 +152,7 @@ pub const TextBlock = struct {
             .size = undefined,
             .alignment = undefined,
             .rebases = std.ArrayList(u64).init(allocator),
+            .dices = std.ArrayList(macho.data_in_code_entry).init(allocator),
         };
     }
 
@@ -163,6 +167,7 @@ pub const TextBlock = struct {
         self.allocator.free(self.code);
         self.relocs.deinit();
         self.rebases.deinit();
+        self.dices.deinit();
     }
 
     pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
@@ -205,6 +210,9 @@ pub const TextBlock = struct {
         if (self.rebases.items.len > 0) {
             log.warn("  rebases: {any}", .{self.rebases.items});
         }
+        if (self.dices.items.len > 0) {
+            log.warn("  dices: {any}", .{self.dices.items});
+        }
         log.warn("  size = {}", .{self.size});
         log.warn("  align = {}", .{self.alignment});
     }
@@ -2071,8 +2079,7 @@ fn flush(self: *Zld) !void {
     try self.writeBindInfoTable();
     try self.writeLazyBindInfoTable();
     try self.writeExportInfo();
-    // TODO DICE for x86_64
-    // try self.writeDataInCode();
+    try self.writeDices();
 
     {
         const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
@@ -2606,7 +2613,9 @@ fn writeStringTable(self: *Zld) !void {
     }
 }
 
-fn writeDataInCode(self: *Zld) !void {
+fn writeDices(self: *Zld) !void {
+    if (!self.has_dices) return;
+
     const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
     const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData;
     const fileoff = seg.inner.fileoff + seg.inner.filesize;
@@ -2614,24 +2623,40 @@ fn writeDataInCode(self: *Zld) !void {
     var buf = std.ArrayList(u8).init(self.allocator);
     defer buf.deinit();
 
+    var block: *TextBlock = self.blocks.get(.{
+        .seg = self.text_segment_cmd_index orelse return,
+        .sect = self.text_section_index orelse return,
+    }) orelse return;
+
+    while (block.prev) |prev| {
+        block = prev;
+    }
+
     const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
     const text_sect = text_seg.sections.items[self.text_section_index.?];
-    for (self.objects.items) |object| {
-        const source_sect = object.sections.items[object.text_section_index.?];
-        const target_map = source_sect.target_map orelse continue;
 
-        try buf.ensureCapacity(
-            buf.items.len + object.data_in_code_entries.items.len * @sizeOf(macho.data_in_code_entry),
-        );
-        for (object.data_in_code_entries.items) |dice| {
-            const new_dice: macho.data_in_code_entry = .{
-                .offset = text_sect.offset + target_map.offset + dice.offset,
-                .length = dice.length,
-                .kind = dice.kind,
-            };
-            buf.appendSliceAssumeCapacity(mem.asBytes(&new_dice));
+    while (true) {
+        if (block.dices.items.len > 0) {
+            const sym = self.locals.items[block.local_sym_index];
+            const reg = sym.payload.regular;
+            const base_off = try math.cast(u32, reg.address - text_sect.addr + text_sect.offset);
+
+            try buf.ensureUnusedCapacity(block.dices.items.len * @sizeOf(macho.data_in_code_entry));
+            for (block.dices.items) |dice| {
+                const rebased_dice = macho.data_in_code_entry{
+                    .offset = base_off + dice.offset,
+                    .length = dice.length,
+                    .kind = dice.kind,
+                };
+                buf.appendSliceAssumeCapacity(mem.asBytes(&rebased_dice));
+            }
         }
+
+        if (block.next) |next| {
+            block = next;
+        } else break;
     }
+
     const datasize = @intCast(u32, buf.items.len);
 
     dice_cmd.dataoff = @intCast(u32, fileoff);