Commit b9bac32a25
Changed files (5)
src
src/link/MachO/Atom.zig
@@ -47,16 +47,6 @@ pub fn getFile(self: Atom, macho_file: *MachO) File {
return macho_file.getFile(self.file).?;
}
-pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void {
- assert(buffer.len == self.size);
- switch (self.getFile(macho_file)) {
- .internal => |x| try x.getAtomData(self, buffer),
- .object => |x| try x.getAtomData(macho_file, self, buffer),
- .zig_object => |x| try x.getAtomData(macho_file, self, buffer),
- else => unreachable,
- }
-}
-
pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation {
return switch (self.getFile(macho_file)) {
.dylib => unreachable,
@@ -88,8 +78,7 @@ pub fn getPriority(self: Atom, macho_file: *MachO) u64 {
}
pub fn getUnwindRecords(self: Atom, macho_file: *MachO) []const UnwindInfo.Record.Index {
- if (!self.flags.unwind) return &[0]UnwindInfo.Record.Index{};
- const extra = self.getExtra(macho_file).?;
+ const extra = self.getExtra(macho_file);
return switch (self.getFile(macho_file)) {
.dylib => unreachable,
.zig_object, .internal => &[0]UnwindInfo.Record.Index{},
@@ -110,44 +99,39 @@ pub fn markUnwindRecordsDead(self: Atom, macho_file: *MachO) void {
}
pub fn getThunk(self: Atom, macho_file: *MachO) *Thunk {
- assert(self.flags.thunk);
- const extra = self.getExtra(macho_file).?;
+ const extra = self.getExtra(macho_file);
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,
+ rel_out_index: ?u32 = null,
+ rel_out_count: ?u32 = null,
unwind_index: ?u32 = null,
unwind_count: ?u32 = null,
- literal_index: ?u32 = null,
+ literal_pool_index: ?u32 = null,
+ literal_symbol_index: ?u32 = null,
};
-pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) !void {
- if (atom.getExtra(macho_file) == null) {
- atom.extra = try macho_file.addAtomExtra(.{});
- }
- var extra = atom.getExtra(macho_file).?;
+pub fn addExtra(atom: *Atom, opts: AddExtraOpts, macho_file: *MachO) void {
+ const file = atom.getFile(macho_file);
+ var extra = file.getAtomExtra(atom.extra);
inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
if (@field(opts, field.name)) |x| {
@field(extra, field.name) = x;
}
}
- atom.setExtra(extra, macho_file);
+ file.setAtomExtra(atom.extra, extra);
}
-pub inline fn getExtra(atom: Atom, macho_file: *MachO) ?Extra {
- return macho_file.getAtomExtra(atom.extra);
+pub inline fn getExtra(atom: Atom, macho_file: *MachO) Extra {
+ return atom.getFile(macho_file).getAtomExtra(atom.extra);
}
pub inline fn setExtra(atom: Atom, extra: Extra, macho_file: *MachO) void {
- macho_file.setAtomExtra(atom.extra, extra);
+ atom.getFile(macho_file).setAtomExtra(atom.extra, extra);
}
pub fn initOutputSection(sect: macho.section_64, macho_file: *MachO) !u8 {
@@ -467,7 +451,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
switch (rel.type) {
.branch => {
- const symbol = rel.getTargetSymbol(macho_file);
+ const symbol = rel.getTargetSymbol(self, macho_file);
if (symbol.flags.import or (symbol.flags.@"export" and symbol.flags.weak) or symbol.flags.interposable) {
symbol.flags.stubs = true;
if (symbol.flags.weak) {
@@ -482,7 +466,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
.got_load_page,
.got_load_pageoff,
=> {
- const symbol = rel.getTargetSymbol(macho_file);
+ const symbol = rel.getTargetSymbol(self, macho_file);
if (symbol.flags.import or
(symbol.flags.@"export" and symbol.flags.weak) or
symbol.flags.interposable or
@@ -496,18 +480,18 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
},
.zig_got_load => {
- assert(rel.getTargetSymbol(macho_file).flags.has_zig_got);
+ assert(rel.getTargetSymbol(self, macho_file).flags.has_zig_got);
},
.got => {
- rel.getTargetSymbol(macho_file).flags.needs_got = true;
+ rel.getTargetSymbol(self, macho_file).flags.needs_got = true;
},
.tlv,
.tlvp_page,
.tlvp_pageoff,
=> {
- const symbol = rel.getTargetSymbol(macho_file);
+ const symbol = rel.getTargetSymbol(self, macho_file);
if (!symbol.flags.tlv) {
try macho_file.reportParseError2(
self.getFile(macho_file).getIndex(),
@@ -526,7 +510,7 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
.unsigned => {
if (rel.meta.length == 3) { // TODO this really should check if this is pointer width
if (rel.tag == .@"extern") {
- const symbol = rel.getTargetSymbol(macho_file);
+ const symbol = rel.getTargetSymbol(self, macho_file);
if (symbol.isTlvInit(macho_file)) {
macho_file.has_tlv = true;
continue;
@@ -559,14 +543,15 @@ pub fn scanRelocs(self: Atom, macho_file: *MachO) !void {
fn reportUndefSymbol(self: Atom, rel: Relocation, macho_file: *MachO) !bool {
if (rel.tag == .local) return false;
- const sym = rel.getTargetSymbol(macho_file);
- if (sym.getFile(macho_file) == null) {
+ const file = self.getFile(macho_file);
+ const ref = file.getSymbolRef(rel.target, macho_file);
+ if (ref.getFile(macho_file) == null) {
const gpa = macho_file.base.comp.gpa;
- const gop = try macho_file.undefs.getOrPut(gpa, rel.target);
+ const gop = try macho_file.undefs.getOrPut(gpa, .{ .index = rel.target, .file = self.file });
if (!gop.found_existing) {
gop.value_ptr.* = .{};
}
- try gop.value_ptr.append(gpa, self.atom_index);
+ try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file });
return true;
}
@@ -582,7 +567,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
const name = self.getName(macho_file);
const relocs = self.getRelocs(macho_file);
- relocs_log.debug("{x}: {s}", .{ self.getAddress(macho_file), name });
+ relocs_log.debug("{x}: {s}", .{ self.value, name });
var has_error = false;
var stream = std.io.fixedBufferStream(buffer);
@@ -593,7 +578,7 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
const subtractor = if (rel.meta.has_subtractor) relocs[i - 1] else null;
if (rel.tag == .@"extern") {
- if (rel.getTargetSymbol(macho_file).getFile(macho_file) == null) continue;
+ if (rel.getTargetSymbol(self, macho_file).getFile(macho_file) == null) continue;
}
try stream.seekTo(rel_offset);
@@ -601,8 +586,8 @@ pub fn resolveRelocs(self: Atom, macho_file: *MachO, buffer: []u8) !void {
switch (err) {
error.RelaxFail => {
const target = switch (rel.tag) {
- .@"extern" => rel.getTargetSymbol(macho_file).getName(macho_file),
- .local => rel.getTargetAtom(macho_file).getName(macho_file),
+ .@"extern" => rel.getTargetSymbol(self, macho_file).getName(macho_file),
+ .local => rel.getTargetAtom(self, macho_file).getName(macho_file),
};
try macho_file.reportParseError2(
file.getIndex(),
@@ -642,12 +627,12 @@ fn resolveRelocInner(
const rel_offset = math.cast(usize, rel.offset - self.off) orelse return error.Overflow;
const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));
const A = rel.addend + rel.getRelocAddend(cpu_arch);
- const S: i64 = @intCast(rel.getTargetAddress(macho_file));
- const G: i64 = @intCast(rel.getGotTargetAddress(macho_file));
+ const S: i64 = @intCast(rel.getTargetAddress(self, macho_file));
+ const G: i64 = @intCast(rel.getGotTargetAddress(self, macho_file));
const TLS = @as(i64, @intCast(macho_file.getTlsAddress()));
- const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(macho_file))) else 0;
+ const SUB = if (subtractor) |sub| @as(i64, @intCast(sub.getTargetAddress(self, macho_file))) else 0;
// Address of the __got_zig table entry if any.
- const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(macho_file)));
+ const ZIG_GOT = @as(i64, @intCast(rel.getZigGotTargetAddress(self, macho_file)));
const divExact = struct {
fn divExact(atom: Atom, r: Relocation, num: u12, den: u12, ctx: *MachO) !u12 {
@@ -668,7 +653,7 @@ fn resolveRelocInner(
rel_offset,
@tagName(rel.type),
S + A - SUB,
- rel.getTargetAtom(macho_file).atom_index,
+ rel.getTargetAtom(self, macho_file).atom_index,
}),
.@"extern" => relocs_log.debug(" {x}<+{d}>: {s}: [=> {x}] G({x}) ZG({x}) ({s})", .{
P,
@@ -677,7 +662,7 @@ fn resolveRelocInner(
S + A - SUB,
G + A,
ZIG_GOT + A,
- rel.getTargetSymbol(macho_file).getName(macho_file),
+ rel.getTargetSymbol(self, macho_file).getName(macho_file),
}),
}
@@ -688,7 +673,7 @@ fn resolveRelocInner(
assert(!rel.meta.pcrel);
if (rel.meta.length == 3) {
if (rel.tag == .@"extern") {
- const sym = rel.getTargetSymbol(macho_file);
+ const sym = rel.getTargetSymbol(self, macho_file);
if (sym.isTlvInit(macho_file)) {
try writer.writeInt(u64, @intCast(S - TLS), .little);
return;
@@ -718,7 +703,7 @@ fn resolveRelocInner(
.aarch64 => {
const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
const thunk = self.getThunk(macho_file);
- const S_: i64 = @intCast(thunk.getTargetAddress(rel.target, macho_file));
+ const S_: i64 = @intCast(thunk.getTargetAddress(rel.getTargetSymbolRef(self, macho_file), macho_file));
break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
};
aarch64.writeBranchImm(disp, code[rel_offset..][0..4]);
@@ -731,7 +716,7 @@ fn resolveRelocInner(
assert(rel.tag == .@"extern");
assert(rel.meta.length == 2);
assert(rel.meta.pcrel);
- if (rel.getTargetSymbol(macho_file).flags.has_got) {
+ if (rel.getTargetSymbol(self, macho_file).flags.has_got) {
try writer.writeInt(i32, @intCast(G + A - P), .little);
} else {
try x86_64.relaxGotLoad(self, code[rel_offset - 3 ..], rel, macho_file);
@@ -754,7 +739,7 @@ fn resolveRelocInner(
assert(rel.tag == .@"extern");
assert(rel.meta.length == 2);
assert(rel.meta.pcrel);
- const sym = rel.getTargetSymbol(macho_file);
+ const sym = rel.getTargetSymbol(self, macho_file);
if (sym.flags.tlv_ptr) {
const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
try writer.writeInt(i32, @intCast(S_ + A - P), .little);
@@ -777,7 +762,7 @@ fn resolveRelocInner(
assert(rel.tag == .@"extern");
assert(rel.meta.length == 2);
assert(rel.meta.pcrel);
- const sym = rel.getTargetSymbol(macho_file);
+ const sym = rel.getTargetSymbol(self, macho_file);
const source = math.cast(u64, P) orelse return error.Overflow;
const target = target: {
const target = switch (rel.type) {
@@ -836,7 +821,7 @@ fn resolveRelocInner(
assert(rel.meta.length == 2);
assert(!rel.meta.pcrel);
- const sym = rel.getTargetSymbol(macho_file);
+ const sym = rel.getTargetSymbol(self, macho_file);
const target = target: {
const target = if (sym.flags.tlv_ptr) blk: {
const S_: i64 = @intCast(sym.getTlvPtrAddress(macho_file));
@@ -980,48 +965,47 @@ pub fn calcNumRelocs(self: Atom, macho_file: *MachO) u32 {
}
}
-pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.ArrayList(macho.relocation_info)) !void {
+pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: []macho.relocation_info) !void {
const tracy = trace(@src());
defer tracy.end();
const cpu_arch = macho_file.getTarget().cpu.arch;
const relocs = self.getRelocs(macho_file);
- var stream = std.io.fixedBufferStream(code);
+ var i: usize = 0;
for (relocs) |rel| {
+ defer i += 1;
const rel_offset = rel.offset - self.off;
const r_address: i32 = math.cast(i32, self.value + rel_offset) orelse return error.Overflow;
const r_symbolnum = r_symbolnum: {
const r_symbolnum: u32 = switch (rel.tag) {
- .local => rel.getTargetAtom(macho_file).out_n_sect + 1,
- .@"extern" => rel.getTargetSymbol(macho_file).getOutputSymtabIndex(macho_file).?,
+ .local => rel.getTargetAtom(self, macho_file).out_n_sect + 1,
+ .@"extern" => rel.getTargetSymbol(self, macho_file).getOutputSymtabIndex(macho_file).?,
};
break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow;
};
const r_extern = rel.tag == .@"extern";
var addend = rel.addend + rel.getRelocAddend(cpu_arch);
if (rel.tag == .local) {
- const target: i64 = @intCast(rel.getTargetAddress(macho_file));
+ const target: i64 = @intCast(rel.getTargetAddress(self, macho_file));
addend += target;
}
- try stream.seekTo(rel_offset);
-
switch (cpu_arch) {
.aarch64 => {
if (rel.type == .unsigned) switch (rel.meta.length) {
0, 1 => unreachable,
- 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),
- 3 => try stream.writer().writeInt(i64, addend, .little),
+ 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
+ 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
} else if (addend > 0) {
- buffer.appendAssumeCapacity(.{
+ buffer[i] = .{
.r_address = r_address,
.r_symbolnum = @bitCast(math.cast(i24, addend) orelse return error.Overflow),
.r_pcrel = 0,
.r_length = 2,
.r_extern = 0,
.r_type = @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_ADDEND),
- });
+ };
}
const r_type: macho.reloc_type_arm64 = switch (rel.type) {
@@ -1045,14 +1029,14 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
.tlv,
=> unreachable,
};
- buffer.appendAssumeCapacity(.{
+ buffer[i] = .{
.r_address = r_address,
.r_symbolnum = r_symbolnum,
.r_pcrel = @intFromBool(rel.meta.pcrel),
.r_extern = @intFromBool(r_extern),
.r_length = rel.meta.length,
.r_type = @intFromEnum(r_type),
- });
+ };
},
.x86_64 => {
if (rel.meta.pcrel) {
@@ -1064,8 +1048,8 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
}
switch (rel.meta.length) {
0, 1 => unreachable,
- 2 => try stream.writer().writeInt(i32, @truncate(addend), .little),
- 3 => try stream.writer().writeInt(i64, addend, .little),
+ 2 => mem.writeInt(i32, code[rel_offset..][0..4], @truncate(addend), .little),
+ 3 => mem.writeInt(i64, code[rel_offset..][0..8], addend, .little),
}
const r_type: macho.reloc_type_x86_64 = switch (rel.type) {
@@ -1089,18 +1073,20 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
.tlvp_pageoff,
=> unreachable,
};
- buffer.appendAssumeCapacity(.{
+ buffer[i] = .{
.r_address = r_address,
.r_symbolnum = r_symbolnum,
.r_pcrel = @intFromBool(rel.meta.pcrel),
.r_extern = @intFromBool(r_extern),
.r_length = rel.meta.length,
.r_type = @intFromEnum(r_type),
- });
+ };
},
else => unreachable,
}
}
+
+ assert(i == buffer.len);
}
pub fn format(
@@ -1139,16 +1125,15 @@ fn format2(
const atom = ctx.atom;
const macho_file = ctx.macho_file;
const file = atom.getFile(macho_file);
- try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d})", .{
- atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
- atom.out_n_sect, atom.alignment, atom.size,
- atom.getRelocs(macho_file).len,
+ try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x}) : nreloc({d}) : thunk({d})", .{
+ atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
+ atom.out_n_sect, atom.alignment, atom.size,
+ atom.getRelocs(macho_file).len, atom.getExtra(macho_file).thunk,
});
- if (atom.flags.thunk) try writer.print(" : thunk({d})", .{atom.getExtra(macho_file).?.thunk});
if (!atom.flags.alive) try writer.writeAll(" : [*]");
- if (atom.flags.unwind) {
+ if (atom.getUnwindRecords(macho_file).len > 0) {
try writer.writeAll(" : unwind{ ");
- const extra = atom.getExtra(macho_file).?;
+ const extra = atom.getExtra(macho_file);
for (atom.getUnwindRecords(macho_file), extra.unwind_index..) |index, i| {
const rec = file.object.getUnwindRecord(index);
try writer.print("{d}", .{index});
@@ -1167,18 +1152,6 @@ pub const Flags = packed struct {
/// Specifies if this atom has been visited during garbage collection.
visited: bool = false,
-
- /// Whether this atom has a range extension thunk.
- thunk: bool = false,
-
- /// Whether this atom has any relocations.
- relocs: bool = false,
-
- /// Whether this atom has any unwind records.
- unwind: bool = false,
-
- /// Whether this atom has LiteralPool entry.
- literal_pool: bool = false,
};
pub const Extra = struct {
@@ -1191,6 +1164,12 @@ pub const Extra = struct {
/// Count of relocations belonging to this atom.
rel_count: u32 = 0,
+ /// Start index of relocations being written out to file for this atom.
+ rel_out_index: u32 = 0,
+
+ /// Count of relocations written out to file for this atom.
+ rel_out_count: u32 = 0,
+
/// Start index of relocations belonging to this atom.
unwind_index: u32 = 0,
@@ -1198,7 +1177,10 @@ pub const Extra = struct {
unwind_count: u32 = 0,
/// Index into LiteralPool entry for this atom.
- literal_index: u32 = 0,
+ literal_pool_index: u32 = 0,
+
+ /// Index into the File's symbol table for local symbol representing this literal atom.
+ literal_symbol_index: u32 = 0,
};
pub const Alignment = @import("../../InternPool.zig").Alignment;
src/link/MachO/InternalObject.zig
@@ -639,6 +639,12 @@ pub fn asFile(self: *InternalObject) File {
return .{ .internal = self };
}
+pub fn getAtomRelocs(self: *const InternalObject, atom: Atom, macho_file: *MachO) []const Relocation {
+ const extra = atom.getExtra(macho_file).?;
+ const relocs = self.sections.items(.relocs)[atom.n_sect];
+ return relocs.items[extra.rel_index..][0..extra.rel_count];
+}
+
fn addAtom(self: *InternalObject, allocator: Allocator) !Atom.Index {
const atom_index: Atom.Index = @intCast(self.atoms.items.len);
const atom = try self.atoms.addOne(allocator);
src/link/MachO/Object.zig
@@ -1860,7 +1860,7 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
const name = sym.getName(macho_file);
if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
}
- const sect = macho_file.sections.items(.header)[sym.out_n_sect];
+ const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
if (sect.isCode()) {
self.output_symtab_ctx.nstabs += 4; // N_BNSYM, N_FUN, N_FUN, N_ENSYM
} else if (sym.visibility == .global) {
@@ -2198,13 +2198,13 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void {
const name = sym.getName(macho_file);
if (name.len > 0 and (name[0] == 'L' or name[0] == 'l')) continue;
}
- const sect = macho_file.sections.items(.header)[sym.out_n_sect];
+ const sect = macho_file.sections.items(.header)[sym.getOutputSectionIndex(macho_file)];
const sym_n_strx = n_strx: {
const symtab_index = sym.getOutputSymtabIndex(macho_file).?;
const osym = macho_file.symtab.items[symtab_index];
break :n_strx osym.n_strx;
};
- const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
+ const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
const sym_n_value = sym.getAddress(.{}, macho_file);
const sym_size = sym.getSize(macho_file);
if (sect.isCode()) {
@@ -2299,7 +2299,7 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void {
const osym = macho_file.symtab.items[symtab_index];
break :n_strx osym.n_strx;
};
- const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.out_n_sect + 1) else 0;
+ const sym_n_sect: u8 = if (!sym.flags.abs) @intCast(sym.getOutputSectionIndex(macho_file) + 1) else 0;
const sym_n_value = sym.getAddress(.{}, macho_file);
const sym_size = sym.getSize(macho_file);
if (stab.is_func) {
@@ -2340,6 +2340,12 @@ pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void {
}
}
+pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation {
+ const extra = atom.getExtra(macho_file).?;
+ const relocs = self.sections.items(.relocs)[atom.n_sect];
+ return relocs.items[extra.rel_index..][0..extra.rel_count];
+}
+
fn addString(self: *Object, 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);
src/link/MachO/Symbol.zig
@@ -9,17 +9,16 @@ name: u32 = 0,
/// File where this symbol is defined.
file: File.Index = 0,
-/// Atom containing this symbol if any.
-/// Index of 0 means there is no associated atom with this symbol.
+/// Reference to Atom containing this symbol if any.
/// Use `getAtom` to get the pointer to the atom.
-atom: Atom.Index = 0,
+atom_ref: MachO.Ref = .{ .index = 0, .file = 0 },
/// Assigned output section index for this symbol.
out_n_sect: u8 = 0,
/// Index of the source nlist this symbol references.
/// Use `getNlist` to pull the nlist from the relevant file.
-nlist_idx: Index = 0,
+nlist_idx: u32 = 0,
/// Misc flags for the symbol packaged as packed struct for compression.
flags: Flags = .{},
@@ -55,16 +54,19 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool {
}
pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 {
- if (symbol.flags.global) return macho_file.strings.getAssumeExists(symbol.name);
return switch (symbol.getFile(macho_file).?) {
- .dylib => unreachable, // There are no local symbols for dylibs
.zig_object => |x| x.strtab.getAssumeExists(symbol.name),
inline else => |x| x.getString(symbol.name),
};
}
pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom {
- return macho_file.getAtom(symbol.atom);
+ return symbol.atom_ref.getAtom(macho_file);
+}
+
+pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
+ if (symbol.getAtom(macho_file)) |atom| return atom.out_n_sect;
+ return symbol.out_n_sect;
}
pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
@@ -75,8 +77,10 @@ pub fn getFile(symbol: Symbol, macho_file: *MachO) ?File {
pub fn getNlist(symbol: Symbol, macho_file: *MachO) macho.nlist_64 {
const file = symbol.getFile(macho_file).?;
return switch (file) {
+ .dylib => unreachable,
+ .zig_object => unreachable,
.object => |x| x.symtab.items(.nlist)[symbol.nlist_idx],
- else => unreachable,
+ .internal => |x| x.symtab.items[symbol.nlist_idx],
};
}
@@ -124,33 +128,35 @@ pub fn getAddress(symbol: Symbol, opts: struct {
pub fn getGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.has_got) return 0;
- const extra = symbol.getExtra(macho_file).?;
+ const extra = symbol.getExtra(macho_file);
return macho_file.got.getAddress(extra.got, macho_file);
}
pub fn getStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.stubs) return 0;
- const extra = symbol.getExtra(macho_file).?;
+ const extra = symbol.getExtra(macho_file);
return macho_file.stubs.getAddress(extra.stubs, macho_file);
}
pub fn getObjcStubsAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.objc_stubs) return 0;
- const extra = symbol.getExtra(macho_file).?;
+ const extra = symbol.getExtra(macho_file);
return macho_file.objc_stubs.getAddress(extra.objc_stubs, macho_file);
}
pub fn getObjcSelrefsAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.objc_stubs) return 0;
- const extra = symbol.getExtra(macho_file).?;
- const atom = macho_file.getAtom(extra.objc_selrefs).?;
- assert(atom.flags.alive);
- return atom.getAddress(macho_file);
+ const extra = symbol.getExtra(macho_file);
+ const file = symbol.getFile(macho_file).?;
+ return switch (file) {
+ .dylib, .zig_object => unreachable,
+ .object, .internal => |x| x.symbols.items[extra.objc_selrefs].getAddress(.{}, macho_file),
+ };
}
pub fn getTlvPtrAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.tlv_ptr) return 0;
- const extra = symbol.getExtra(macho_file).?;
+ const extra = symbol.getExtra(macho_file);
return macho_file.tlv_ptr.getAddress(extra.tlv_ptr, macho_file);
}
@@ -162,14 +168,14 @@ const GetOrCreateZigGotEntryResult = struct {
pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, macho_file: *MachO) !GetOrCreateZigGotEntryResult {
assert(!macho_file.base.isRelocatable());
assert(symbol.flags.needs_zig_got);
- if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).?.zig_got };
+ if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.getExtra(macho_file).zig_got };
const index = try macho_file.zig_got.addSymbol(symbol_index, macho_file);
return .{ .found_existing = false, .index = index };
}
pub fn getZigGotAddress(symbol: Symbol, macho_file: *MachO) u64 {
if (!symbol.flags.has_zig_got) return 0;
- const extras = symbol.getExtra(macho_file).?;
+ const extras = symbol.getExtra(macho_file);
return macho_file.zig_got.entryAddress(extras.zig_got, macho_file);
}
@@ -202,11 +208,8 @@ const AddExtraOpts = struct {
symtab: ?u32 = null,
};
-pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {
- if (symbol.getExtra(macho_file) == null) {
- symbol.extra = try macho_file.addSymbolExtra(.{});
- }
- var extra = symbol.getExtra(macho_file).?;
+pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) void {
+ var extra = symbol.getExtra(macho_file);
inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
if (@field(opts, field.name)) |x| {
@field(extra, field.name) = x;
@@ -215,18 +218,22 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, macho_file: *MachO) !void {
symbol.setExtra(extra, macho_file);
}
-pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) ?Extra {
- return macho_file.getSymbolExtra(symbol.extra);
+pub inline fn getExtra(symbol: Symbol, macho_file: *MachO) Extra {
+ return switch (symbol.getFile(macho_file).?) {
+ inline else => |x| x.getSymbolExtra(symbol.extra),
+ };
}
pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void {
- macho_file.setSymbolExtra(symbol.extra, extra);
+ return switch (symbol.getFile(macho_file).?) {
+ inline else => |x| x.setSymbolExtra(symbol.extra, extra),
+ };
}
pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {
if (symbol.isLocal()) {
out.n_type = if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
- out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1);
+ out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
out.n_desc = 0;
out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
@@ -238,7 +245,7 @@ pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) vo
assert(symbol.visibility == .global);
out.n_type = macho.N_EXT;
out.n_type |= if (symbol.flags.abs) macho.N_ABS else macho.N_SECT;
- out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.out_n_sect + 1);
+ out.n_sect = if (symbol.flags.abs) 0 else @intCast(symbol.getOutputSectionIndex(macho_file) + 1);
out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
out.n_desc = 0;
@@ -318,8 +325,8 @@ fn format2(
symbol.getAddress(.{}, ctx.macho_file),
});
if (symbol.getFile(ctx.macho_file)) |file| {
- if (symbol.out_n_sect != 0) {
- try writer.print(" : sect({d})", .{symbol.out_n_sect});
+ if (symbol.getOutputSectionIndex(ctx.macho_file) != 0) {
+ try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(ctx.macho_file)});
}
if (symbol.getAtom(ctx.macho_file)) |atom| {
try writer.print(" : atom({d})", .{atom.atom_index});
@@ -346,11 +353,6 @@ pub const Flags = packed struct {
/// Whether the symbol is exported at runtime.
@"export": bool = false,
- /// Whether the symbol is effectively an extern and takes part in global
- /// symbol resolution. Then, its name will be saved in global string interning
- /// table.
- global: bool = false,
-
/// Whether this symbol is weak.
weak: bool = false,
src/link/MachO.zig
@@ -22,16 +22,10 @@ dylibs: std.ArrayListUnmanaged(File.Index) = .{},
segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
sections: std.MultiArrayList(Section) = .{},
-symbols: std.ArrayListUnmanaged(Symbol) = .{},
-symbols_extra: std.ArrayListUnmanaged(u32) = .{},
-symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
-globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
+resolver: SymbolResolver = .{},
/// This table will be populated after `scanRelocs` has run.
/// Key is symbol index.
-undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{},
-/// Global symbols we need to resolve for the link to succeed.
-undefined_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
-boundary_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+undefs: std.AutoHashMapUnmanaged(Ref, std.ArrayListUnmanaged(Ref)) = .{},
dyld_info_cmd: macho.dyld_info_command = .{},
symtab_cmd: macho.symtab_command = .{},
@@ -55,19 +49,8 @@ eh_frame_sect_index: ?u8 = null,
unwind_info_sect_index: ?u8 = null,
objc_stubs_sect_index: ?u8 = null,
-mh_execute_header_index: ?Symbol.Index = null,
-mh_dylib_header_index: ?Symbol.Index = null,
-dyld_private_index: ?Symbol.Index = null,
-dyld_stub_binder_index: ?Symbol.Index = null,
-dso_handle_index: ?Symbol.Index = null,
-objc_msg_send_index: ?Symbol.Index = null,
-entry_index: ?Symbol.Index = null,
-
thunks: std.ArrayListUnmanaged(Thunk) = .{},
-/// String interning table
-strings: StringTable = .{},
-
/// Output synthetic sections
symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
@@ -4196,7 +4179,7 @@ const Section = struct {
pub const LiteralPool = struct {
table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
keys: std.ArrayListUnmanaged(Key) = .{},
- values: std.ArrayListUnmanaged(Atom.Index) = .{},
+ values: std.ArrayListUnmanaged(MachO.Ref) = .{},
data: std.ArrayListUnmanaged(u8) = .{},
pub fn deinit(lp: *LiteralPool, allocator: Allocator) void {
@@ -4206,17 +4189,21 @@ pub const LiteralPool = struct {
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,
+ ref: *MachO.Ref,
};
+ pub fn getSymbolRef(lp: LiteralPool, index: Index) MachO.Ref {
+ assert(index < lp.values.items.len);
+ return lp.values.items[index];
+ }
+
+ pub fn getSymbol(lp: LiteralPool, index: Index, macho_file: *MachO) *Symbol {
+ return lp.getSymbolRef(index).getSymbol(macho_file).?;
+ }
+
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);
@@ -4278,12 +4265,6 @@ const HotUpdateState = struct {
mach_task: ?std.c.MachTask = null,
};
-pub const DynamicRelocs = struct {
- rebase_relocs: u32 = 0,
- bind_relocs: u32 = 0,
- weak_bind_relocs: u32 = 0,
-};
-
pub const SymtabCtx = struct {
ilocal: u32 = 0,
istab: u32 = 0,
@@ -4293,6 +4274,7 @@ pub const SymtabCtx = struct {
nstabs: u32 = 0,
nexports: u32 = 0,
nimports: u32 = 0,
+ stroff: u32 = 0,
strsize: u32 = 0,
};
@@ -4579,6 +4561,131 @@ const UndefinedTreatment = enum {
dynamic_lookup,
};
+/// A reference to atom or symbol in an input file.
+/// If file == 0, symbol is an undefined global.
+pub const Ref = struct {
+ index: u32,
+ file: File.Index,
+
+ pub fn eql(ref: Ref, other: Ref) bool {
+ return ref.index == other.index and ref.file == other.file;
+ }
+
+ pub fn getFile(ref: Ref, macho_file: *MachO) ?File {
+ return macho_file.getFile(ref.file);
+ }
+
+ pub fn getAtom(ref: Ref, macho_file: *MachO) ?*Atom {
+ const file = ref.getFile(macho_file) orelse return null;
+ return file.getAtom(ref.index);
+ }
+
+ pub fn getSymbol(ref: Ref, macho_file: *MachO) ?*Symbol {
+ const file = ref.getFile(macho_file) orelse return null;
+ return switch (file) {
+ inline else => |x| &x.symbols.items[ref.index],
+ };
+ }
+
+ pub fn format(
+ ref: Ref,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = unused_fmt_string;
+ _ = options;
+ try writer.print("%{d} in file({d})", .{ ref.index, ref.file });
+ }
+};
+
+pub const SymbolResolver = struct {
+ keys: std.ArrayListUnmanaged(Key) = .{},
+ values: std.ArrayListUnmanaged(Ref) = .{},
+ table: std.AutoArrayHashMapUnmanaged(void, void) = .{},
+
+ const Result = struct {
+ found_existing: bool,
+ index: Index,
+ ref: *Ref,
+ };
+
+ pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void {
+ resolver.keys.deinit(allocator);
+ resolver.values.deinit(allocator);
+ resolver.table.deinit(allocator);
+ }
+
+ pub fn getOrPut(
+ resolver: *SymbolResolver,
+ allocator: Allocator,
+ ref: Ref,
+ macho_file: *MachO,
+ ) !Result {
+ const adapter = Adapter{ .keys = resolver.keys.items, .macho_file = macho_file };
+ const key = Key{ .index = ref.index, .file = ref.file };
+ const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter);
+ if (!gop.found_existing) {
+ try resolver.keys.append(allocator, key);
+ _ = try resolver.values.addOne(allocator);
+ }
+ return .{
+ .found_existing = gop.found_existing,
+ .index = @intCast(gop.index + 1),
+ .ref = &resolver.values.items[gop.index],
+ };
+ }
+
+ pub fn get(resolver: SymbolResolver, index: Index) ?Ref {
+ if (index == 0) return null;
+ return resolver.values.items[index - 1];
+ }
+
+ pub fn reset(resolver: *SymbolResolver) void {
+ resolver.keys.clearRetainingCapacity();
+ resolver.values.clearRetainingCapacity();
+ resolver.table.clearRetainingCapacity();
+ }
+
+ const Key = struct {
+ index: Symbol.Index,
+ file: File.Index,
+
+ fn getName(key: Key, macho_file: *MachO) [:0]const u8 {
+ const ref = Ref{ .index = key.index, .file = key.file };
+ return ref.getSymbol(macho_file).?.getName(macho_file);
+ }
+
+ fn eql(key: Key, other: Key, macho_file: *MachO) bool {
+ const key_name = key.getName(macho_file);
+ const other_name = other.getName(macho_file);
+ return mem.eql(u8, key_name, other_name);
+ }
+
+ fn hash(key: Key, macho_file: *MachO) u32 {
+ const name = key.getName(macho_file);
+ return @truncate(Hash.hash(0, name));
+ }
+ };
+
+ const Adapter = struct {
+ keys: []const Key,
+ macho_file: *MachO,
+
+ pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool {
+ _ = b_void;
+ const other = ctx.keys[b_map_index];
+ return key.eql(other, ctx.macho_file);
+ }
+
+ pub fn hash(ctx: @This(), key: Key) u32 {
+ return key.hash(ctx.macho_file);
+ }
+ };
+
+ pub const Index = u32;
+};
+
const MachO = @This();
const std = @import("std");