Skip to content

Commit

Permalink
Merge pull request #20032 from ziglang/macho-literals
Browse files Browse the repository at this point in the history
link/macho: implement logic for merging literals
  • Loading branch information
kubkon committed May 23, 2024
2 parents 9be8a90 + d31eb74 commit fb88cfd
Show file tree
Hide file tree
Showing 9 changed files with 1,082 additions and 107 deletions.
136 changes: 122 additions & 14 deletions src/link/MachO.zig
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node

try self.convertTentativeDefinitions();
try self.createObjcSections();
try self.dedupLiterals();
try self.claimUnresolved();

if (self.base.gc_sections) {
Expand Down Expand Up @@ -1491,6 +1492,33 @@ fn createObjcSections(self: *MachO) !void {
const name = eatPrefix(sym.getName(self), "_objc_msgSend$").?;
const selrefs_index = try internal.addObjcMsgsendSections(name, self);
try sym.addExtra(.{ .objc_selrefs = selrefs_index }, self);
sym.flags.objc_stubs = true;
}
}

pub fn dedupLiterals(self: *MachO) !void {
const gpa = self.base.comp.gpa;
var lp: LiteralPool = .{};
defer lp.deinit(gpa);

if (self.getZigObject()) |zo| {
try zo.resolveLiterals(&lp, self);
}
for (self.objects.items) |index| {
try self.getFile(index).?.object.resolveLiterals(&lp, self);
}
if (self.getInternalObject()) |object| {
try object.resolveLiterals(&lp, self);
}

if (self.getZigObject()) |zo| {
zo.dedupLiterals(lp, self);
}
for (self.objects.items) |index| {
self.getFile(index).?.object.dedupLiterals(lp, self);
}
if (self.getInternalObject()) |object| {
object.dedupLiterals(lp, self);
}
}

Expand Down Expand Up @@ -1728,20 +1756,18 @@ fn initOutputSections(self: *MachO) !void {
atom.out_n_sect = try Atom.initOutputSection(atom.getInputSection(self), self);
}
}
if (self.text_sect_index == null) {
self.text_sect_index = try self.addSection("__TEXT", "__text", .{
.alignment = switch (self.getTarget().cpu.arch) {
.x86_64 => 0,
.aarch64 => 2,
else => unreachable,
},
.flags = macho.S_REGULAR |
macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
});
}
if (self.data_sect_index == null) {
self.data_sect_index = try self.addSection("__DATA", "__data", .{});
}
self.text_sect_index = self.getSectionByName("__TEXT", "__text") orelse
try self.addSection("__TEXT", "__text", .{
.alignment = switch (self.getTarget().cpu.arch) {
.x86_64 => 0,
.aarch64 => 2,
else => unreachable,
},
.flags = macho.S_REGULAR |
macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
});
self.data_sect_index = self.getSectionByName("__DATA", "__data") orelse
try self.addSection("__DATA", "__data", .{});
}

fn initSyntheticSections(self: *MachO) !void {
Expand Down Expand Up @@ -4387,6 +4413,87 @@ const Section = struct {
last_atom_index: Atom.Index = 0,
};

pub const LiteralPool = struct {
table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
keys: std.ArrayListUnmanaged(Key) = .{},
values: std.ArrayListUnmanaged(Atom.Index) = .{},
data: std.ArrayListUnmanaged(u8) = .{},

pub fn deinit(lp: *LiteralPool, allocator: Allocator) void {
lp.table.deinit(allocator);
lp.keys.deinit(allocator);
lp.values.deinit(allocator);
lp.data.deinit(allocator);
}

pub fn getAtom(lp: LiteralPool, index: Index, macho_file: *MachO) *Atom {
assert(index < lp.values.items.len);
return macho_file.getAtom(lp.values.items[index]).?;
}

const InsertResult = struct {
found_existing: bool,
index: Index,
atom: *Atom.Index,
};

pub fn insert(lp: *LiteralPool, allocator: Allocator, @"type": u8, string: []const u8) !InsertResult {
const size: u32 = @intCast(string.len);
try lp.data.ensureUnusedCapacity(allocator, size);
const off: u32 = @intCast(lp.data.items.len);
lp.data.appendSliceAssumeCapacity(string);
const adapter = Adapter{ .lp = lp };
const key = Key{ .off = off, .size = size, .seed = @"type" };
const gop = try lp.table.getOrPutAdapted(allocator, key, adapter);
if (!gop.found_existing) {
try lp.keys.append(allocator, key);
_ = try lp.values.addOne(allocator);
}
return .{
.found_existing = gop.found_existing,
.index = @intCast(gop.index),
.atom = &lp.values.items[gop.index],
};
}

const Key = struct {
off: u32,
size: u32,
seed: u8,

fn getData(key: Key, lp: *const LiteralPool) []const u8 {
return lp.data.items[key.off..][0..key.size];
}

fn eql(key: Key, other: Key, lp: *const LiteralPool) bool {
const key_data = key.getData(lp);
const other_data = other.getData(lp);
return mem.eql(u8, key_data, other_data);
}

fn hash(key: Key, lp: *const LiteralPool) u32 {
const data = key.getData(lp);
return @truncate(Hash.hash(key.seed, data));
}
};

const Adapter = struct {
lp: *const LiteralPool,

pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool {
_ = b_void;
const other = ctx.lp.keys.items[b_map_index];
return key.eql(other, ctx.lp);
}

pub fn hash(ctx: @This(), key: Key) u32 {
return key.hash(ctx.lp);
}
};

pub const Index = u32;
};

const HotUpdateState = struct {
mach_task: ?std.c.MachTask = null,
};
Expand Down Expand Up @@ -4738,6 +4845,7 @@ const Dylib = @import("MachO/Dylib.zig");
const ExportTrieSection = synthetic.ExportTrieSection;
const File = @import("MachO/file.zig").File;
const GotSection = synthetic.GotSection;
const Hash = std.hash.Wyhash;
const Indsymtab = synthetic.Indsymtab;
const InternalObject = @import("MachO/InternalObject.zig");
const ObjcStubsSection = synthetic.ObjcStubsSection;
Expand Down
58 changes: 43 additions & 15 deletions src/link/MachO/Atom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,18 @@ pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
return macho_file.getThunk(extra.thunk);
}

pub fn getLiteralPoolIndex(self: Atom, macho_file: *MachO) ?MachO.LiteralPool.Index {
if (!self.flags.literal_pool) return null;
return self.getExtra(macho_file).?.literal_index;
}

const AddExtraOpts = struct {
thunk: ?u32 = null,
rel_index: ?u32 = null,
rel_count: ?u32 = null,
unwind_index: ?u32 = null,
unwind_count: ?u32 = null,
literal_index: ?u32 = null,
};

pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void {
Expand All @@ -143,6 +149,16 @@ pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void {
}

pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
if (macho_file.base.isRelocatable()) {
const osec = macho_file.getSectionByName(sect.segName(), sect.sectName()) orelse
try macho_file.addSection(
sect.segName(),
sect.sectName(),
.{ .flags = sect.flags },
);
return osec;
}

const segname, const sectname, const flags = blk: {
if (sect.isCode()) break :blk .{
"__TEXT",
Expand Down Expand Up @@ -200,18 +216,11 @@ pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
else => break :blk .{ sect.segName(), sect.sectName(), sect.flags },
}
};
const osec = macho_file.getSectionByName(segname, sectname) orelse try macho_file.addSection(
return macho_file.getSectionByName(segname, sectname) orelse try macho_file.addSection(
segname,
sectname,
.{ .flags = flags },
);
if (mem.eql(u8, segname, "__TEXT") and mem.eql(u8, sectname, "__text")) {
macho_file.text_sect_index = osec;
}
if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__data")) {
macho_file.data_sect_index = osec;
}
return osec;
}

/// Returns how much room there is to grow in virtual address space.
Expand Down Expand Up @@ -651,6 +660,19 @@ fn resolveRelocInner(
// Address of the __got_zig table entry if any.
const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file)));

const divExact = struct {
fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 {
return math.divExact(u12, num, den) catch {
try ctx.reportParseError2(atom.getFile(ctx).getIndex(), "{s}: unexpected remainder when resolving {s} at offset 0x{x}", .{
atom.getName(ctx),
r.fmtPretty(ctx.getTarget().cpu.arch),
r.offset,
});
return error.UnexpectedRemainder;
};
}
}.divExact;

switch (rel.tag) {
.local => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] atom({d})", .{
P,
Expand Down Expand Up @@ -822,12 +844,12 @@ fn resolveRelocInner(
};
inst.load_store_register.offset = switch (inst.load_store_register.size) {
0 => if (inst.load_store_register.v == 1)
try math.divExact(u12, @truncate(target), 16)
try divExact(self, rel, @truncate(target), 16, macho_file)
else
@truncate(target),
1 => try math.divExact(u12, @truncate(target), 2),
2 => try math.divExact(u12, @truncate(target), 4),
3 => try math.divExact(u12, @truncate(target), 8),
1 => try divExact(self, rel, @truncate(target), 2, macho_file),
2 => try divExact(self, rel, @truncate(target), 4, macho_file),
3 => try divExact(self, rel, @truncate(target), 8, macho_file),
};
try writer.writeInt(u32, inst.toU32(), .little);
}
Expand All @@ -838,7 +860,7 @@ fn resolveRelocInner(
assert(rel.meta.length == 2);
assert(!rel.meta.pcrel);
const target = math.cast(u64, G + A) orelse return error.Overflow;
aarch64.writeLoadStoreRegInst(try math.divExact(u12, @truncate(target), 8), code[rel_offset..][0..4]);
aarch64.writeLoadStoreRegInst(try divExact(self, rel, @truncate(target), 8, macho_file), code[rel_offset..][0..4]);
},

.tlvp_pageoff => {
Expand Down Expand Up @@ -890,7 +912,7 @@ fn resolveRelocInner(
.load_store_register = .{
.rt = reg_info.rd,
.rn = reg_info.rn,
.offset = try math.divExact(u12, @truncate(target), 8),
.offset = try divExact(self, rel, @truncate(target), 8, macho_file),
.opc = 0b01,
.op1 = 0b01,
.v = 0,
Expand Down Expand Up @@ -1174,7 +1196,7 @@ pub const Flags = packed struct {
/// Specifies whether this atom is alive or has been garbage collected.
alive: bool = true,

/// Specifies if the atom has been visited during garbage collection.
/// Specifies if this atom has been visited during garbage collection.
visited: bool = false,

/// Whether this atom has a range extension thunk.
Expand All @@ -1185,6 +1207,9 @@ pub const Flags = packed struct {

/// Whether this atom has any unwind records.
unwind: bool = false,

/// Whether this atom has LiteralPool entry.
literal_pool: bool = false,
};

pub const Extra = struct {
Expand All @@ -1202,6 +1227,9 @@ pub const Extra = struct {

/// Count of relocations belonging to this atom.
unwind_count: u32 = 0,

/// Index into LiteralPool entry for this atom.
literal_index: u32 = 0,
};

pub const Alignment = @import("../../InternPool.zig").Alignment;
Expand Down
Loading

0 comments on commit fb88cfd

Please sign in to comment.