diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2024-05-19 22:42:35 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2024-05-23 12:04:17 +0200 |
| commit | 434e69482ed29de26ceea16dbc5679f32281c502 (patch) | |
| tree | df307c783b90dd598ae3a5afe24506b6fb59905d /src/link/MachO/InternalObject.zig | |
| parent | 9be8a9000faead40b1aec4877506ff10b066659c (diff) | |
| download | zig-434e69482ed29de26ceea16dbc5679f32281c502.tar.gz zig-434e69482ed29de26ceea16dbc5679f32281c502.zip | |
link/macho: dedup literals in objects and internal object file
Diffstat (limited to 'src/link/MachO/InternalObject.zig')
| -rw-r--r-- | src/link/MachO/InternalObject.zig | 129 |
1 files changed, 96 insertions, 33 deletions
diff --git a/src/link/MachO/InternalObject.zig b/src/link/MachO/InternalObject.zig index 9f42eca114..f25508f037 100644 --- a/src/link/MachO/InternalObject.zig +++ b/src/link/MachO/InternalObject.zig @@ -3,7 +3,6 @@ index: File.Index, sections: std.MultiArrayList(Section) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -strtab: std.ArrayListUnmanaged(u8) = .{}, objc_methnames: std.ArrayListUnmanaged(u8) = .{}, objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64), @@ -18,7 +17,6 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void { self.sections.deinit(allocator); self.atoms.deinit(allocator); self.symbols.deinit(allocator); - self.strtab.deinit(allocator); self.objc_methnames.deinit(allocator); } @@ -38,9 +36,9 @@ pub fn addSymbol(self: *InternalObject, name: [:0]const u8, macho_file: *MachO) } /// Creates a fake input sections __TEXT,__objc_methname and __DATA,__objc_selrefs. -pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !u32 { +pub fn addObjcMsgsendSections(self: *InternalObject, sym_name: []const u8, macho_file: *MachO) !Atom.Index { const methname_atom_index = try self.addObjcMethnameSection(sym_name, macho_file); - return try self.addObjcSelrefsSection(sym_name, methname_atom_index, macho_file); + return try self.addObjcSelrefsSection(methname_atom_index, macho_file); } fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_file: *MachO) !Atom.Index { @@ -48,11 +46,8 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil const atom_index = try macho_file.addAtom(); try self.atoms.append(gpa, atom_index); - const name = try std.fmt.allocPrintZ(gpa, "__TEXT$__objc_methname${s}", .{methname}); - defer gpa.free(name); const atom = macho_file.getAtom(atom_index).?; atom.atom_index = atom_index; - atom.name = try self.addString(gpa, name); atom.file = self.index; atom.size = methname.len + 1; atom.alignment = .@"1"; @@ -72,21 +67,13 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil return atom_index; } -fn addObjcSelrefsSection( - self: *InternalObject, - methname: []const u8, - methname_atom_index: Atom.Index, - macho_file: *MachO, -) !Atom.Index { +fn addObjcSelrefsSection(self: *InternalObject, methname_atom_index: Atom.Index, macho_file: *MachO) !Atom.Index { const gpa = macho_file.base.comp.gpa; const atom_index = try macho_file.addAtom(); try self.atoms.append(gpa, atom_index); - const name = try std.fmt.allocPrintZ(gpa, "__DATA$__objc_selrefs${s}", .{methname}); - defer gpa.free(name); const atom = macho_file.getAtom(atom_index).?; atom.atom_index = atom_index; - atom.name = try self.addString(gpa, name); atom.file = self.index; atom.size = @sizeOf(u64); atom.alignment = .@"8"; @@ -122,6 +109,83 @@ fn addObjcSelrefsSection( return atom_index; } +pub fn dedupLiterals(self: InternalObject, lp: *MachO.LiteralPool, macho_file: *MachO) !void { + const gpa = macho_file.base.comp.gpa; + + var killed_atoms = std.AutoHashMap(Atom.Index, Atom.Index).init(gpa); + defer killed_atoms.deinit(); + + var buffer = std.ArrayList(u8).init(gpa); + defer buffer.deinit(); + + const slice = self.sections.slice(); + for (slice.items(.header), self.atoms.items, 0..) |header, atom_index, n_sect| { + if (Object.isCstringLiteral(header) or Object.isFixedSizeLiteral(header)) { + const data = try self.getSectionData(@intCast(n_sect)); + const atom = macho_file.getAtom(atom_index).?; + const res = try lp.insert(gpa, header.type(), data); + if (!res.found_existing) { + res.atom.* = atom_index; + continue; + } + atom.flags.alive = false; + try killed_atoms.putNoClobber(atom_index, res.atom.*); + } else if (Object.isPtrLiteral(header)) { + const atom = macho_file.getAtom(atom_index).?; + const relocs = atom.getRelocs(macho_file); + assert(relocs.len == 1); + const rel = relocs[0]; + assert(rel.tag == .local); + const target = macho_file.getAtom(rel.target).?; + const addend = std.math.cast(u32, rel.addend) orelse return error.Overflow; + try buffer.ensureUnusedCapacity(target.size); + buffer.resize(target.size) catch unreachable; + try target.getData(macho_file, buffer.items); + const res = try lp.insert(gpa, header.type(), buffer.items[addend..]); + buffer.clearRetainingCapacity(); + if (!res.found_existing) { + res.atom.* = atom_index; + continue; + } + atom.flags.alive = false; + try killed_atoms.putNoClobber(atom_index, res.atom.*); + } + } + + for (self.atoms.items) |atom_index| { + if (killed_atoms.get(atom_index)) |_| continue; + const atom = macho_file.getAtom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + if (!atom.flags.relocs) continue; + + const relocs = blk: { + const extra = atom.getExtra(macho_file).?; + const relocs = slice.items(.relocs)[atom.n_sect].items; + break :blk relocs[extra.rel_index..][0..extra.rel_count]; + }; + for (relocs) |*rel| switch (rel.tag) { + .local => if (killed_atoms.get(rel.target)) |new_target| { + rel.target = new_target; + }, + .@"extern" => { + const target = rel.getTargetSymbol(macho_file); + if (killed_atoms.get(target.atom)) |new_atom| { + target.atom = new_atom; + } + }, + }; + } + + for (self.symbols.items) |sym_index| { + const sym = macho_file.getSymbol(sym_index); + if (!sym.flags.objc_stubs) continue; + const extra = sym.getExtra(macho_file).?; + if (killed_atoms.get(extra.objc_selrefs)) |new_atom| { + try sym.addExtra(.{ .objc_selrefs = new_atom }, macho_file); + } + } +} + pub fn calcSymtabSize(self: *InternalObject, macho_file: *MachO) !void { for (self.symbols.items) |sym_index| { const sym = macho_file.getSymbol(sym_index); @@ -167,18 +231,23 @@ fn addSection(self: *InternalObject, allocator: Allocator, segname: []const u8, return n_sect; } -pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) !void { - assert(buffer.len == atom.size); +fn getSectionData(self: *const InternalObject, index: u32) error{Overflow}![]const u8 { const slice = self.sections.slice(); - const sect = slice.items(.header)[atom.n_sect]; - const extra = slice.items(.extra)[atom.n_sect]; - const data = if (extra.is_objc_methname) blk: { + assert(index < slice.items(.header).len); + const sect = slice.items(.header)[index]; + const extra = slice.items(.extra)[index]; + if (extra.is_objc_methname) { const size = std.math.cast(usize, sect.size) orelse return error.Overflow; - break :blk self.objc_methnames.items[sect.offset..][0..size]; + return self.objc_methnames.items[sect.offset..][0..size]; } else if (extra.is_objc_selref) - &self.objc_selrefs + return &self.objc_selrefs else @panic("ref to non-existent section"); +} + +pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) error{Overflow}!void { + assert(buffer.len == atom.size); + const data = try self.getSectionData(atom.n_sect); const off = std.math.cast(usize, atom.off) orelse return error.Overflow; const size = std.math.cast(usize, atom.size) orelse return error.Overflow; @memcpy(buffer, data[off..][0..size]); @@ -191,17 +260,11 @@ pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO return relocs.items[extra.rel_index..][0..extra.rel_count]; } -fn addString(self: *InternalObject, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 { - const off: u32 = @intCast(self.strtab.items.len); - try self.strtab.ensureUnusedCapacity(allocator, name.len + 1); - self.strtab.appendSliceAssumeCapacity(name); - self.strtab.appendAssumeCapacity(0); - return off; -} - pub fn getString(self: InternalObject, off: u32) [:0]const u8 { - assert(off < self.strtab.items.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); + _ = self; + _ = off; + // We don't have any local strings for synthetic atoms. + return ""; } pub fn asFile(self: *InternalObject) File { |
