diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 208ff6d600..2a200933b7 100644 --- a/src/link/MachO/Object.zig +++ b/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 diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 83c857ee5e..995269440f 100644 --- a/src/link/MachO/Zld.zig +++ b/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);