aboutsummaryrefslogtreecommitdiff
path: root/src/link/MachO/InternalObject.zig
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2024-05-19 22:42:35 +0200
committerJakub Konka <kubkon@jakubkonka.com>2024-05-23 12:04:17 +0200
commit434e69482ed29de26ceea16dbc5679f32281c502 (patch)
treedf307c783b90dd598ae3a5afe24506b6fb59905d /src/link/MachO/InternalObject.zig
parent9be8a9000faead40b1aec4877506ff10b066659c (diff)
downloadzig-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.zig129
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 {