Commit 9d5a900f4b
Changed files (2)
src
link
MachO
src/link/MachO/Object.zig
@@ -12,7 +12,11 @@ symtab: std.MultiArrayList(Nlist) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+symbols_extra: std.ArrayListUnmanaged(u32) = .{},
+globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
+atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{},
+atoms_extra: std.ArrayListUnmanaged(u32) = .{},
platform: ?MachO.Platform = null,
compile_unit: ?CompileUnit = null,
@@ -30,6 +34,7 @@ data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
alive: bool = true,
hidden: bool = false,
+compact_unwind_ctx: CompactUnwindCtx = .{},
output_symtab_ctx: MachO.SymtabCtx = .{},
output_ar_state: Archive.ArState = .{},
@@ -51,7 +56,11 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
self.symtab.deinit(allocator);
self.strtab.deinit(allocator);
self.symbols.deinit(allocator);
+ self.symbols_extra.deinit(allocator);
+ self.globals.deinit(allocator);
self.atoms.deinit(allocator);
+ self.atoms_indexes.deinit(allocator);
+ self.atoms_extra.deinit(allocator);
self.cies.deinit(allocator);
self.fdes.deinit(allocator);
self.eh_frame_data.deinit(allocator);
@@ -70,6 +79,10 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa;
const handle = macho_file.getFileHandle(self.file_handle);
+ const cpu_arch = macho_file.getTarget().cpu.arch;
+
+ // Atom at index 0 is reserved as null atom
+ try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) });
var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
{
@@ -86,7 +99,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
return error.InvalidCpuArch;
},
};
- if (macho_file.getTarget().cpu.arch != this_cpu_arch) {
+ if (cpu_arch != this_cpu_arch) {
try macho_file.reportParseError2(self.index, "invalid cpu architecture: {s}", .{@tagName(this_cpu_arch)});
return error.InvalidCpuArch;
}
@@ -198,20 +211,20 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
mem.sort(NlistIdx, nlists.items, self, NlistIdx.lessThan);
if (self.hasSubsections()) {
- try self.initSubsections(nlists.items, macho_file);
+ try self.initSubsections(gpa, nlists.items);
} else {
- try self.initSections(nlists.items, macho_file);
+ try self.initSections(gpa, nlists.items);
}
- try self.initCstringLiterals(macho_file);
- try self.initFixedSizeLiterals(macho_file);
- try self.initPointerLiterals(macho_file);
+ try self.initCstringLiterals(gpa, handle, macho_file);
+ try self.initFixedSizeLiterals(gpa, macho_file);
+ try self.initPointerLiterals(gpa, macho_file);
try self.linkNlistToAtom(macho_file);
try self.sortAtoms(macho_file);
- try self.initSymbols(macho_file);
- try self.initSymbolStabs(nlists.items, macho_file);
- try self.initRelocs(macho_file);
+ try self.initSymbols(gpa, macho_file);
+ try self.initSymbolStabs(gpa, nlists.items, macho_file);
+ try self.initRelocs(handle, cpu_arch, macho_file);
// Parse DWARF __TEXT,__eh_frame section
if (self.eh_frame_sect_index) |index| {
@@ -224,13 +237,13 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
}
if (self.hasUnwindRecords() or self.hasEhFrameRecords()) {
- try self.parseUnwindRecords(gpa, macho_file.getTarget().cpu.arch, macho_file);
+ try self.parseUnwindRecords(gpa, cpu_arch, macho_file);
}
if (self.platform) |platform| {
if (!macho_file.platform.eqlTarget(platform)) {
try macho_file.reportParseError2(self.index, "invalid platform: {}", .{
- platform.fmtTarget(macho_file.getTarget().cpu.arch),
+ platform.fmtTarget(cpu_arch),
});
return error.InvalidTarget;
}
@@ -247,7 +260,7 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
}
for (self.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
+ const atom = self.getAtom(atom_index) orelse continue;
const isec = atom.getInputSection(macho_file);
if (mem.eql(u8, isec.sectName(), "__eh_frame") or
mem.eql(u8, isec.sectName(), "__compact_unwind") or
@@ -276,10 +289,9 @@ pub fn isPtrLiteral(sect: macho.section_64) bool {
return sect.type() == macho.S_LITERAL_POINTERS;
}
-fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
+fn initSubsections(self: *Object, allocator: Allocator, nlists: anytype) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
for (slice.items(.header), slice.items(.subsections), 0..) |sect, *subsections, n_sect| {
if (isCstringLiteral(sect)) continue;
@@ -294,17 +306,18 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
} else nlists.len;
if (nlist_start == nlist_end or nlists[nlist_start].nlist.n_value > sect.addr) {
- const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });
- defer gpa.free(name);
+ const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
+ defer allocator.free(name);
const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr;
- const atom_index = try self.addAtom(.{
- .name = try self.addString(gpa, name),
+ const atom_index = try self.addAtom(allocator, .{
+ .name = try self.addString(allocator, name),
.n_sect = @intCast(n_sect),
.off = 0,
.size = size,
.alignment = sect.@"align",
- }, macho_file);
- try subsections.append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try subsections.append(allocator, .{
.atom = atom_index,
.off = 0,
});
@@ -327,14 +340,15 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
@min(@ctz(nlist.nlist.n_value), sect.@"align")
else
sect.@"align";
- const atom_index = try self.addAtom(.{
+ const atom_index = try self.addAtom(allocator, .{
.name = nlist.nlist.n_strx,
.n_sect = @intCast(n_sect),
.off = nlist.nlist.n_value - sect.addr,
.size = size,
.alignment = alignment,
- }, macho_file);
- try subsections.append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try subsections.append(allocator, .{
.atom = atom_index,
.off = nlist.nlist.n_value - sect.addr,
});
@@ -346,30 +360,31 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
}
}
-fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
+fn initSections(self: *Object, allocator: Allocator, nlists: anytype) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
- try self.atoms.ensureUnusedCapacity(gpa, self.sections.items(.header).len);
+ try self.atoms.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
+ try self.atoms_indexes.ensureUnusedCapacity(allocator, self.sections.items(.header).len);
for (slice.items(.header), 0..) |sect, n_sect| {
if (isCstringLiteral(sect)) continue;
if (isFixedSizeLiteral(sect)) continue;
if (isPtrLiteral(sect)) continue;
- const name = try std.fmt.allocPrintZ(gpa, "{s}${s}", .{ sect.segName(), sect.sectName() });
- defer gpa.free(name);
+ const name = try std.fmt.allocPrintZ(allocator, "{s}${s}", .{ sect.segName(), sect.sectName() });
+ defer allocator.free(name);
- const atom_index = try self.addAtom(.{
- .name = try self.addString(gpa, name),
+ const atom_index = try self.addAtom(allocator, .{
+ .name = try self.addString(allocator, name),
.n_sect = @intCast(n_sect),
.off = 0,
.size = sect.size,
.alignment = sect.@"align",
- }, macho_file);
- try slice.items(.subsections)[n_sect].append(gpa, .{ .atom = atom_index, .off = 0 });
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try slice.items(.subsections)[n_sect].append(allocator, .{ .atom = atom_index, .off = 0 });
const nlist_start = for (nlists, 0..) |nlist, i| {
if (nlist.nlist.n_sect - 1 == n_sect) break i;
@@ -398,21 +413,25 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void {
}
}
-fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {
+fn initCstringLiterals(self: *Object, allocator: Allocator, file: File.Handle, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
for (slice.items(.header), 0..) |sect, n_sect| {
if (!isCstringLiteral(sect)) continue;
- const data = try self.getSectionData(@intCast(n_sect), macho_file);
- defer gpa.free(data);
+ const sect_size = math.cast(usize, sect.size) orelse return error.Overflow;
+ const data = try allocator.alloc(u8, sect_size);
+ defer allocator.free(data);
+ const amt = try file.preadAll(data, sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ var count: u32 = 0;
var start: u32 = 0;
while (start < data.len) {
+ defer count += 1;
var end = start;
while (end < data.len - 1 and data[end] != 0) : (end += 1) {}
if (data[end] != 0) {
@@ -425,32 +444,52 @@ fn initCstringLiterals(self: *Object, macho_file: *MachO) !void {
}
end += 1;
- const atom_index = try self.addAtom(.{
- .name = 0,
+ const name = try std.fmt.allocPrintZ(allocator, "l._str{d}", .{count});
+ defer allocator.free(name);
+ const name_str = try self.addString(allocator, name);
+
+ const atom_index = try self.addAtom(allocator, .{
+ .name = name_str,
.n_sect = @intCast(n_sect),
.off = start,
.size = end - start,
.alignment = sect.@"align",
- }, macho_file);
- try slice.items(.subsections)[n_sect].append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try slice.items(.subsections)[n_sect].append(allocator, .{
.atom = atom_index,
.off = start,
});
+ const atom = self.getAtom(atom_index).?;
+ const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
+ self.symtab.set(nlist_index, .{
+ .nlist = .{
+ .n_strx = name_str,
+ .n_type = macho.N_SECT,
+ .n_sect = @intCast(atom.n_sect + 1),
+ .n_desc = 0,
+ .n_value = atom.getInputAddress(macho_file),
+ },
+ .size = atom.size,
+ .atom = atom_index,
+ });
+ atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
+
start = end;
}
}
}
-fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {
+fn initFixedSizeLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
for (slice.items(.header), 0..) |sect, n_sect| {
if (!isFixedSizeLiteral(sect)) continue;
+
const rec_size: u8 = switch (sect.type()) {
macho.S_4BYTE_LITERALS => 4,
macho.S_8BYTE_LITERALS => 8,
@@ -465,28 +504,52 @@ fn initFixedSizeLiterals(self: *Object, macho_file: *MachO) !void {
);
return error.MalformedObject;
}
+
var pos: u32 = 0;
- while (pos < sect.size) : (pos += rec_size) {
- const atom_index = try self.addAtom(.{
- .name = 0,
+ var count: u32 = 0;
+ while (pos < sect.size) : ({
+ pos += rec_size;
+ count += 1;
+ }) {
+ const name = try std.fmt.allocPrintZ(allocator, "l._literal{d}", .{count});
+ defer allocator.free(name);
+ const name_str = try self.addString(allocator, name);
+
+ const atom_index = try self.addAtom(allocator, .{
+ .name = name_str,
.n_sect = @intCast(n_sect),
.off = pos,
.size = rec_size,
.alignment = sect.@"align",
- }, macho_file);
- try slice.items(.subsections)[n_sect].append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try slice.items(.subsections)[n_sect].append(allocator, .{
.atom = atom_index,
.off = pos,
});
+
+ const atom = self.getAtom(atom_index).?;
+ const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
+ self.symtab.set(nlist_index, .{
+ .nlist = .{
+ .n_strx = name_str,
+ .n_type = macho.N_SECT,
+ .n_sect = @intCast(atom.n_sect + 1),
+ .n_desc = 0,
+ .n_value = atom.getInputAddress(macho_file),
+ },
+ .size = atom.size,
+ .atom = atom_index,
+ });
+ atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
}
}
}
-fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
+fn initPointerLiterals(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const slice = self.sections.slice();
for (slice.items(.header), 0..) |sect, n_sect| {
@@ -505,134 +568,171 @@ fn initPointerLiterals(self: *Object, macho_file: *MachO) !void {
for (0..num_ptrs) |i| {
const pos: u32 = @as(u32, @intCast(i)) * rec_size;
- const atom_index = try self.addAtom(.{
- .name = 0,
+
+ const name = try std.fmt.allocPrintZ(allocator, "l._ptr{d}", .{i});
+ defer allocator.free(name);
+ const name_str = try self.addString(allocator, name);
+
+ const atom_index = try self.addAtom(allocator, .{
+ .name = name_str,
.n_sect = @intCast(n_sect),
.off = pos,
.size = rec_size,
.alignment = sect.@"align",
- }, macho_file);
- try slice.items(.subsections)[n_sect].append(gpa, .{
+ });
+ try self.atoms_indexes.append(allocator, atom_index);
+ try slice.items(.subsections)[n_sect].append(allocator, .{
.atom = atom_index,
.off = pos,
});
+
+ const atom = self.getAtom(atom_index).?;
+ const nlist_index: u32 = @intCast(try self.symtab.addOne(allocator));
+ self.symtab.set(nlist_index, .{
+ .nlist = .{
+ .n_strx = name_str,
+ .n_type = macho.N_SECT,
+ .n_sect = @intCast(atom.n_sect + 1),
+ .n_desc = 0,
+ .n_value = atom.getInputAddress(macho_file),
+ },
+ .size = atom.size,
+ .atom = atom_index,
+ });
+ atom.addExtra(.{ .literal_symbol_index = nlist_index }, macho_file);
}
}
}
pub fn resolveLiterals(self: Object, lp: *MachO.LiteralPool, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const gpa = macho_file.base.comp.gpa;
+ const file = macho_file.getFileHandle(self.file_handle);
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
+ var sections_data = std.AutoHashMap(u32, []const u8).init(gpa);
+ try sections_data.ensureTotalCapacity(@intCast(self.sections.items(.header).len));
+ defer {
+ var it = sections_data.iterator();
+ while (it.next()) |entry| {
+ gpa.free(entry.value_ptr.*);
+ }
+ sections_data.deinit();
+ }
+
const slice = self.sections.slice();
- for (slice.items(.header), slice.items(.subsections), 0..) |header, subs, n_sect| {
+ for (slice.items(.header), slice.items(.subsections)) |header, subs| {
if (isCstringLiteral(header) or isFixedSizeLiteral(header)) {
- const data = try self.getSectionData(@intCast(n_sect), macho_file);
+ const sect_size = math.cast(usize, header.size) orelse return error.Overflow;
+ const data = try gpa.alloc(u8, sect_size);
defer gpa.free(data);
+ const amt = try file.preadAll(data, header.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
for (subs.items) |sub| {
- const atom = macho_file.getAtom(sub.atom).?;
+ const atom = self.getAtom(sub.atom).?;
const atom_off = math.cast(usize, atom.off) orelse return error.Overflow;
const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
const atom_data = data[atom_off..][0..atom_size];
const res = try lp.insert(gpa, header.type(), atom_data);
if (!res.found_existing) {
- res.atom.* = sub.atom;
+ res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
+ } else {
+ const lp_sym = lp.getSymbol(res.index, macho_file);
+ const lp_atom = lp_sym.getAtom(macho_file).?;
+ lp_atom.alignment = @max(lp_atom.alignment, atom.alignment);
+ atom.flags.alive = false;
}
- atom.flags.literal_pool = true;
- try atom.addExtra(.{ .literal_index = res.index }, macho_file);
+ atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
}
} else if (isPtrLiteral(header)) {
for (subs.items) |sub| {
- const atom = macho_file.getAtom(sub.atom).?;
+ const atom = self.getAtom(sub.atom).?;
const relocs = atom.getRelocs(macho_file);
assert(relocs.len == 1);
const rel = relocs[0];
const target = switch (rel.tag) {
- .local => rel.target,
- .@"extern" => rel.getTargetSymbol(macho_file).atom,
+ .local => rel.getTargetAtom(atom.*, macho_file),
+ .@"extern" => rel.getTargetSymbol(atom.*, macho_file).getAtom(macho_file).?,
};
const addend = math.cast(u32, rel.addend) orelse return error.Overflow;
- const target_atom = macho_file.getAtom(target).?;
- const target_atom_size = math.cast(usize, target_atom.size) orelse return error.Overflow;
- try buffer.ensureUnusedCapacity(target_atom_size);
- buffer.resize(target_atom_size) catch unreachable;
- try target_atom.getData(macho_file, buffer.items);
+ const target_size = math.cast(usize, target.size) orelse return error.Overflow;
+ try buffer.ensureUnusedCapacity(target_size);
+ buffer.resize(target_size) catch unreachable;
+ const gop = try sections_data.getOrPut(target.n_sect);
+ if (!gop.found_existing) {
+ const target_sect = slice.items(.header)[target.n_sect];
+ const target_sect_size = math.cast(usize, target_sect.size) orelse return error.Overflow;
+ const data = try gpa.alloc(u8, target_sect_size);
+ const amt = try file.preadAll(data, target_sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ gop.value_ptr.* = data;
+ }
+ const data = gop.value_ptr.*;
+ const target_off = math.cast(usize, target.off) orelse return error.Overflow;
+ @memcpy(buffer.items, data[target_off..][0..target_size]);
const res = try lp.insert(gpa, header.type(), buffer.items[addend..]);
buffer.clearRetainingCapacity();
if (!res.found_existing) {
- res.atom.* = sub.atom;
+ res.ref.* = .{ .index = atom.getExtra(macho_file).literal_symbol_index, .file = self.index };
+ } else {
+ const lp_sym = lp.getSymbol(res.index, macho_file);
+ const lp_atom = lp_sym.getAtom(macho_file).?;
+ lp_atom.alignment = @max(lp_atom.alignment, atom.alignment);
+ atom.flags.alive = false;
}
- atom.flags.literal_pool = true;
- try atom.addExtra(.{ .literal_index = res.index }, macho_file);
+ atom.addExtra(.{ .literal_pool_index = res.index }, macho_file);
}
}
}
}
-pub fn dedupLiterals(self: Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
- for (self.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index) orelse continue;
+pub fn dedupLiterals(self: *Object, lp: MachO.LiteralPool, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.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 extra = atom.getExtra(macho_file);
const relocs = self.sections.items(.relocs)[atom.n_sect].items;
break :blk relocs[extra.rel_index..][0..extra.rel_count];
};
- for (relocs) |*rel| switch (rel.tag) {
- .local => {
- const target = macho_file.getAtom(rel.target).?;
- if (target.getLiteralPoolIndex(macho_file)) |lp_index| {
- const lp_atom = lp.getAtom(lp_index, macho_file);
- if (target.atom_index != lp_atom.atom_index) {
- lp_atom.alignment = lp_atom.alignment.max(target.alignment);
- target.flags.alive = false;
- rel.target = lp_atom.atom_index;
- }
- }
- },
- .@"extern" => {
- const target_sym = rel.getTargetSymbol(macho_file);
- if (target_sym.getAtom(macho_file)) |target_atom| {
- if (target_atom.getLiteralPoolIndex(macho_file)) |lp_index| {
- const lp_atom = lp.getAtom(lp_index, macho_file);
- if (target_atom.atom_index != lp_atom.atom_index) {
- lp_atom.alignment = lp_atom.alignment.max(target_atom.alignment);
- target_atom.flags.alive = false;
- target_sym.atom = lp_atom.atom_index;
- }
- }
- }
- },
- };
+ for (relocs) |*rel| {
+ if (rel.tag != .@"extern") continue;
+ const target_sym_ref = rel.getTargetSymbolRef(atom.*, macho_file);
+ const file = target_sym_ref.getFile(macho_file) orelse continue;
+ if (file.getIndex() != self.index) continue;
+ const target_sym = target_sym_ref.getSymbol(macho_file).?;
+ const target_atom = target_sym.getAtom(macho_file) orelse continue;
+ const isec = target_atom.getInputSection(macho_file);
+ if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
+ const lp_index = target_atom.getExtra(macho_file).literal_pool_index;
+ const lp_sym = lp.getSymbol(lp_index, macho_file);
+ const lp_atom_ref = lp_sym.atom_ref;
+ if (target_atom.atom_index != lp_atom_ref.index or target_atom.file != lp_atom_ref.file) {
+ target_sym.atom_ref = lp_atom_ref;
+ }
+ }
}
-}
-
-const AddAtomArgs = struct {
- name: u32,
- n_sect: u8,
- off: u64,
- size: u64,
- alignment: u32,
-};
-fn addAtom(self: *Object, args: AddAtomArgs, macho_file: *MachO) !Atom.Index {
- const gpa = macho_file.base.comp.gpa;
- const atom_index = try macho_file.addAtom();
- const atom = macho_file.getAtom(atom_index).?;
- atom.file = self.index;
- atom.atom_index = atom_index;
- atom.name = args.name;
- atom.n_sect = args.n_sect;
- atom.size = args.size;
- atom.alignment = Atom.Alignment.fromLog2Units(args.alignment);
- atom.off = args.off;
- try self.atoms.append(gpa, atom_index);
- return atom_index;
+ for (self.symbols.items) |*sym| {
+ const atom = sym.getAtom(macho_file) orelse continue;
+ const isec = atom.getInputSection(macho_file);
+ if (!Object.isCstringLiteral(isec) and !Object.isFixedSizeLiteral(isec) and !Object.isPtrLiteral(isec)) continue;
+ const lp_index = atom.getExtra(macho_file).literal_pool_index;
+ const lp_sym = lp.getSymbol(lp_index, macho_file);
+ const lp_atom_ref = lp_sym.atom_ref;
+ if (atom.atom_index != lp_atom_ref.index or self.index != lp_atom_ref.file) {
+ sym.atom_ref = lp_atom_ref;
+ }
+ }
}
pub fn findAtom(self: Object, addr: u64) ?Atom.Index {
@@ -704,55 +804,59 @@ fn linkNlistToAtom(self: *Object, macho_file: *MachO) !void {
}
}
-fn initSymbols(self: *Object, macho_file: *MachO) !void {
+fn initSymbols(self: *Object, allocator: Allocator, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
+
const slice = self.symtab.slice();
+ const nsyms = slice.items(.nlist).len;
- try self.symbols.ensureUnusedCapacity(gpa, slice.items(.nlist).len);
+ try self.symbols.ensureTotalCapacityPrecise(allocator, nsyms);
+ try self.symbols_extra.ensureTotalCapacityPrecise(allocator, nsyms * @sizeOf(Symbol.Extra));
+ try self.globals.ensureTotalCapacityPrecise(allocator, nsyms);
+ self.globals.resize(allocator, nsyms) catch unreachable;
+ @memset(self.globals.items, 0);
for (slice.items(.nlist), slice.items(.atom), 0..) |nlist, atom_index, i| {
- if (nlist.ext()) {
- const name = self.getString(nlist.n_strx);
- const off = try macho_file.strings.insert(gpa, name);
- const gop = try macho_file.getOrCreateGlobal(off);
- self.symbols.addOneAssumeCapacity().* = gop.index;
- if (nlist.undf() and nlist.weakRef()) {
- macho_file.getSymbol(gop.index).flags.weak_ref = true;
- }
- continue;
- }
-
- const index = try macho_file.addSymbol();
- self.symbols.appendAssumeCapacity(index);
- const symbol = macho_file.getSymbol(index);
- symbol.* = .{
- .value = nlist.n_value,
- .name = nlist.n_strx,
- .nlist_idx = @intCast(i),
- .atom = 0,
- .file = self.index,
- };
-
- if (macho_file.getAtom(atom_index)) |atom| {
+ const index = self.addSymbolAssumeCapacity();
+ const symbol = &self.symbols.items[index];
+ symbol.value = nlist.n_value;
+ symbol.name = .{ .pos = nlist.n_strx, .len = @intCast(self.getNStrx(nlist.n_strx).len + 1) };
+ symbol.nlist_idx = @intCast(i);
+ symbol.extra = self.addSymbolExtraAssumeCapacity(.{});
+
+ if (self.getAtom(atom_index)) |atom| {
assert(!nlist.abs());
symbol.value -= atom.getInputAddress(macho_file);
- symbol.atom = atom_index;
+ symbol.atom_ref = .{ .index = atom_index, .file = self.index };
}
+ symbol.flags.weak = nlist.weakDef();
symbol.flags.abs = nlist.abs();
+ symbol.flags.tentative = nlist.tentative();
symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
+ symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
+ symbol.flags.interposable = nlist.ext() and (nlist.sect() or nlist.abs()) and macho_file.options.dylib and macho_file.options.namespace == .flat and !nlist.pext();
if (nlist.sect() and
self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
{
symbol.flags.tlv = true;
}
+
+ if (nlist.ext()) {
+ if (nlist.undf()) {
+ symbol.flags.weak_ref = nlist.weakRef();
+ } else if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
+ symbol.visibility = .hidden;
+ } else {
+ symbol.visibility = .global;
+ }
+ }
}
}
-fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
+fn initSymbolStabs(self: *Object, allocator: Allocator, nlists: anytype, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -778,14 +882,13 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
if (start == end) return;
- const gpa = macho_file.base.comp.gpa;
const syms = self.symtab.items(.nlist);
const sym_lookup = SymbolLookup{ .ctx = self, .entries = nlists };
// We need to cache nlists by name so that we can properly resolve local N_GSYM stabs.
// What happens is `ld -r` will emit an N_GSYM stab for a symbol that may be either an
// external or private external.
- var addr_lookup = std.StringHashMap(u64).init(gpa);
+ var addr_lookup = std.StringHashMap(u64).init(allocator);
defer addr_lookup.deinit();
for (syms) |sym| {
if (sym.sect() and (sym.ext() or sym.pext())) {
@@ -834,29 +937,35 @@ fn initSymbolStabs(self: *Object, nlists: anytype, macho_file: *MachO) !void {
return error.MalformedObject;
},
}
- try sf.stabs.append(gpa, stab);
+ try sf.stabs.append(allocator, stab);
}
- try self.stab_files.append(gpa, sf);
+ try self.stab_files.append(allocator, sf);
}
}
fn sortAtoms(self: *Object, macho_file: *MachO) !void {
- const lessThanAtom = struct {
- fn lessThanAtom(ctx: *MachO, lhs: Atom.Index, rhs: Atom.Index) bool {
- return ctx.getAtom(lhs).?.getInputAddress(ctx) < ctx.getAtom(rhs).?.getInputAddress(ctx);
+ const Ctx = struct {
+ object: *Object,
+ mfile: *MachO,
+
+ fn lessThanAtom(ctx: @This(), lhs: Atom.Index, rhs: Atom.Index) bool {
+ return ctx.object.getAtom(lhs).?.getInputAddress(ctx.mfile) <
+ ctx.object.getAtom(rhs).?.getInputAddress(ctx.mfile);
}
- }.lessThanAtom;
- mem.sort(Atom.Index, self.atoms.items, macho_file, lessThanAtom);
+ };
+ mem.sort(Atom.Index, self.atoms_indexes.items, Ctx{
+ .object = self,
+ .mfile = macho_file,
+ }, Ctx.lessThanAtom);
}
-fn initRelocs(self: *Object, macho_file: *MachO) !void {
+fn initRelocs(self: *Object, file: File.Handle, cpu_arch: std.Target.Cpu.Arch, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const cpu_arch = macho_file.getTarget().cpu.arch;
const slice = self.sections.slice();
- for (slice.items(.header), slice.items(.relocs), 0..) |sect, *out, n_sect| {
+ for (slice.items(.header), slice.items(.relocs)) |sect, *out| {
if (sect.nreloc == 0) continue;
// We skip relocs for __DWARF since even in -r mode, the linker is expected to emit
// debug symbol stabs in the relocatable. This made me curious why that is. For now,
@@ -865,8 +974,8 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
!mem.eql(u8, sect.sectName(), "__compact_unwind")) continue;
switch (cpu_arch) {
- .x86_64 => try x86_64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),
- .aarch64 => try aarch64.parseRelocs(self, @intCast(n_sect), sect, out, macho_file),
+ .x86_64 => try x86_64.parseRelocs(self, sect, out, file, macho_file),
+ .aarch64 => try aarch64.parseRelocs(self, sect, out, file, macho_file),
else => unreachable,
}
@@ -878,7 +987,7 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
var next_reloc: u32 = 0;
for (subsections.items) |subsection| {
- const atom = macho_file.getAtom(subsection.atom).?;
+ const atom = self.getAtom(subsection.atom).?;
if (!atom.flags.alive) continue;
if (next_reloc >= relocs.items.len) break;
const end_addr = atom.off + atom.size;
@@ -887,27 +996,22 @@ fn initRelocs(self: *Object, macho_file: *MachO) !void {
while (next_reloc < relocs.items.len and relocs.items[next_reloc].offset < end_addr) : (next_reloc += 1) {}
const rel_count = next_reloc - rel_index;
- try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, macho_file);
- atom.flags.relocs = true;
+ atom.addExtra(.{ .rel_index = @intCast(rel_index), .rel_count = @intCast(rel_count) }, macho_file);
}
}
}
-fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
+fn initEhFrameRecords(self: *Object, allocator: Allocator, sect_id: u8, file: File.Handle, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- const gpa = macho_file.base.comp.gpa;
const nlists = self.symtab.items(.nlist);
const slice = self.sections.slice();
const sect = slice.items(.header)[sect_id];
const relocs = slice.items(.relocs)[sect_id];
- // TODO: read into buffer directly
- const data = try self.getSectionData(sect_id, macho_file);
- defer gpa.free(data);
-
- try self.eh_frame_data.ensureTotalCapacityPrecise(gpa, data.len);
- self.eh_frame_data.appendSliceAssumeCapacity(data);
+ try self.eh_frame_data.resize(allocator, sect.size);
+ const amt = try file.preadAll(self.eh_frame_data.items, sect.offset + self.offset);
+ if (amt != self.eh_frame_data.items.len) return error.InputOutput;
// Check for non-personality relocs in FDEs and apply them
for (relocs.items, 0..) |rel, i| {
@@ -939,12 +1043,12 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void {
var it = eh_frame.Iterator{ .data = self.eh_frame_data.items };
while (try it.next()) |rec| {
switch (rec.tag) {
- .cie => try self.cies.append(gpa, .{
+ .cie => try self.cies.append(allocator, .{
.offset = rec.offset,
.size = rec.size,
.file = self.index,
}),
- .fde => try self.fdes.append(gpa, .{
+ .fde => try self.fdes.append(allocator, .{
.offset = rec.offset,
.size = rec.size,
.cie = undefined,
@@ -1205,8 +1309,7 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target.
{}
const atom = rec.getAtom(macho_file);
- try atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
- atom.flags.unwind = true;
+ atom.addExtra(.{ .unwind_index = start, .unwind_count = next_cu - start }, macho_file);
}
}
@@ -1233,11 +1336,31 @@ pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
if (debug_info_index == null or debug_abbrev_index == null) return;
- const debug_info = try self.getSectionData(@intCast(debug_info_index.?), macho_file);
+ const slice = self.sections.slice();
+ const file = macho_file.getFileHandle(self.file_handle);
+ const debug_info = blk: {
+ const sect = slice.items(.header)[debug_info_index.?];
+ const data = try gpa.alloc(u8, sect.size);
+ const amt = try file.preadAll(data, sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ break :blk data;
+ };
defer gpa.free(debug_info);
- const debug_abbrev = try self.getSectionData(@intCast(debug_abbrev_index.?), macho_file);
+ const debug_abbrev = blk: {
+ const sect = slice.items(.header)[debug_abbrev_index.?];
+ const data = try gpa.alloc(u8, sect.size);
+ const amt = try file.preadAll(data, sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ break :blk data;
+ };
defer gpa.free(debug_abbrev);
- const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{};
+ const debug_str = if (debug_str_index) |sid| blk: {
+ const sect = slice.items(.header)[sid];
+ const data = try gpa.alloc(u8, sect.size);
+ const amt = try file.preadAll(data, sect.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ break :blk data;
+ } else &[0]u8{};
defer gpa.free(debug_str);
self.compile_unit = self.findCompileUnit(.{
@@ -1347,83 +1470,51 @@ pub fn resolveSymbols(self: *Object, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items, 0..) |index, i| {
- const nlist_idx = @as(Symbol.Index, @intCast(i));
- const nlist = self.symtab.items(.nlist)[nlist_idx];
- const atom_index = self.symtab.items(.atom)[nlist_idx];
+ const gpa = macho_file.base.allocator;
+ for (self.symtab.items(.nlist), self.symtab.items(.atom), self.globals.items, 0..) |nlist, atom_index, *global, i| {
if (!nlist.ext()) continue;
- if (nlist.undf() and !nlist.tentative()) continue;
if (nlist.sect()) {
- const atom = macho_file.getAtom(atom_index).?;
- if (!atom.flags.alive) continue;
+ const atom = self.getAtom(atom_index).?;
+ if (!atom.alive.load(.seq_cst)) continue;
+ }
+
+ const gop = try macho_file.resolver.getOrPut(gpa, .{
+ .index = @intCast(i),
+ .file = self.index,
+ }, macho_file);
+ if (!gop.found_existing) {
+ gop.ref.* = .{ .index = 0, .file = 0 };
+ }
+ global.* = gop.index;
+
+ if (nlist.undf() and !nlist.tentative()) continue;
+ if (gop.ref.getFile(macho_file) == null) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
+ continue;
}
- const symbol = macho_file.getSymbol(index);
if (self.asFile().getSymbolRank(.{
.archive = !self.alive,
.weak = nlist.weakDef(),
.tentative = nlist.tentative(),
- }) < symbol.getSymbolRank(macho_file)) {
- const value = if (nlist.sect()) blk: {
- const atom = macho_file.getAtom(atom_index).?;
- break :blk nlist.n_value - atom.getInputAddress(macho_file);
- } else nlist.n_value;
- symbol.value = value;
- symbol.atom = atom_index;
- symbol.nlist_idx = nlist_idx;
- symbol.file = self.index;
- symbol.flags.weak = nlist.weakDef();
- symbol.flags.abs = nlist.abs();
- symbol.flags.tentative = nlist.tentative();
- symbol.flags.weak_ref = false;
- symbol.flags.dyn_ref = nlist.n_desc & macho.REFERENCED_DYNAMICALLY != 0;
- symbol.flags.no_dead_strip = symbol.flags.no_dead_strip or nlist.noDeadStrip();
- // TODO: symbol.flags.interposable = macho_file.base.isDynLib() and macho_file.options.namespace == .flat and !nlist.pext();
- symbol.flags.interposable = false;
-
- if (nlist.sect() and
- self.sections.items(.header)[nlist.n_sect - 1].type() == macho.S_THREAD_LOCAL_VARIABLES)
- {
- symbol.flags.tlv = true;
- }
- }
-
- // Regardless of who the winner is, we still merge symbol visibility here.
- if (nlist.pext() or (nlist.weakDef() and nlist.weakRef()) or self.hidden) {
- if (symbol.visibility != .global) {
- symbol.visibility = .hidden;
- }
- } else {
- symbol.visibility = .global;
+ }) < gop.ref.getSymbol(macho_file).?.getSymbolRank(macho_file)) {
+ gop.ref.* = .{ .index = @intCast(i), .file = self.index };
}
}
}
-pub fn resetGlobals(self: *Object, macho_file: *MachO) void {
- for (self.symbols.items, 0..) |sym_index, nlist_idx| {
- if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue;
- const sym = macho_file.getSymbol(sym_index);
- const name = sym.name;
- const global = sym.flags.global;
- const weak_ref = sym.flags.weak_ref;
- sym.* = .{};
- sym.name = name;
- sym.flags.global = global;
- sym.flags.weak_ref = weak_ref;
- }
-}
-
pub fn markLive(self: *Object, macho_file: *MachO) void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items, 0..) |index, nlist_idx| {
- const nlist = self.symtab.items(.nlist)[nlist_idx];
+ for (0..self.symbols.items.len) |i| {
+ const nlist = self.symtab.items(.nlist)[i];
if (!nlist.ext()) continue;
- const sym = macho_file.getSymbol(index);
- const file = sym.getFile(macho_file) orelse continue;
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
+ const sym = ref.getSymbol(macho_file).?;
const should_keep = nlist.undf() or (nlist.tentative() and !sym.flags.tentative);
if (should_keep and file == .object and !file.object.alive) {
file.object.alive = true;
@@ -1432,30 +1523,47 @@ pub fn markLive(self: *Object, macho_file: *MachO) void {
}
}
-pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void {
- for (self.symbols.items, 0..) |index, nlist_idx| {
- const sym = macho_file.getSymbol(index);
- if (sym.visibility != .global) continue;
- const file = sym.getFile(macho_file) orelse continue;
- if (file.getIndex() == self.index) continue;
+pub fn mergeSymbolVisibility(self: *Object, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
- const nlist = self.symtab.items(.nlist)[nlist_idx];
- if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {
- const gop = try dupes.getOrPut(index);
- if (!gop.found_existing) {
- gop.value_ptr.* = .{};
- }
- try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const global = ref.getSymbol(macho_file) orelse continue;
+ if (global.visibility != .global) {
+ global.visibility = sym.visibility;
+ }
+ if (sym.flags.weak_ref) {
+ global.flags.weak_ref = true;
}
}
}
+// TODO
+// pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void {
+// for (self.symbols.items, 0..) |index, nlist_idx| {
+// const sym = macho_file.getSymbol(index);
+// if (sym.visibility != .global) continue;
+// const file = sym.getFile(macho_file) orelse continue;
+// if (file.getIndex() == self.index) continue;
+
+// const nlist = self.symtab.items(.nlist)[nlist_idx];
+// if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) {
+// const gop = try dupes.getOrPut(index);
+// if (!gop.found_existing) {
+// gop.value_ptr.* = .{};
+// }
+// try gop.value_ptr.append(macho_file.base.comp.gpa, self.index);
+// }
+// }
+// }
+
pub fn scanRelocs(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- for (self.atoms.items) |atom_index| {
- const atom = macho_file.getAtom(atom_index).?;
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
const sect = atom.getInputSection(macho_file);
if (sect.isZerofill()) continue;
@@ -1480,38 +1588,35 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
defer tracy.end();
const gpa = macho_file.base.comp.gpa;
- for (self.symbols.items, 0..) |index, i| {
- const sym = macho_file.getSymbol(index);
+ for (self.symbols.items, self.globals.items, 0..) |*sym, off, i| {
if (!sym.flags.tentative) continue;
- const sym_file = sym.getFile(macho_file).?;
- if (sym_file.getIndex() != self.index) continue;
+ if (macho_file.resolver.get(off).?.file != self.index) continue;
const nlist_idx = @as(Symbol.Index, @intCast(i));
const nlist = &self.symtab.items(.nlist)[nlist_idx];
const nlist_atom = &self.symtab.items(.atom)[nlist_idx];
- const atom_index = try macho_file.addAtom();
- try self.atoms.append(gpa, atom_index);
-
const name = try std.fmt.allocPrintZ(gpa, "__DATA$__common${s}", .{sym.getName(macho_file)});
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 = nlist.n_value;
- atom.alignment = Atom.Alignment.fromLog2Units((nlist.n_desc >> 8) & 0x0f);
+ const alignment = (nlist.n_desc >> 8) & 0x0f;
const n_sect = try self.addSection(gpa, "__DATA", "__common");
+ const atom_index = try self.addAtom(gpa, .{
+ .name = try self.addString(gpa, name),
+ .n_sect = n_sect,
+ .off = 0,
+ .size = nlist.n_value,
+ .alignment = alignment,
+ });
+ try self.atoms_indexes.append(gpa, atom_index);
+
const sect = &self.sections.items(.header)[n_sect];
sect.flags = macho.S_ZEROFILL;
- sect.size = atom.size;
- sect.@"align" = atom.alignment.toLog2Units();
- atom.n_sect = n_sect;
+ sect.size = nlist.n_value;
+ sect.@"align" = alignment;
sym.value = 0;
- sym.atom = atom_index;
- sym.flags.global = true;
+ sym.atom_ref = .{ .index = atom_index, .file = self.index };
sym.flags.weak = false;
sym.flags.weak_ref = false;
sym.flags.tentative = false;
@@ -1525,6 +1630,56 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void {
}
}
+pub fn claimUnresolved(self: *Object, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.symbols.items, 0..) |*sym, i| {
+ const nlist = self.symtab.items(.nlist)[i];
+ if (!nlist.ext()) continue;
+ if (!nlist.undf()) continue;
+
+ if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
+
+ const is_import = switch (macho_file.options.undefined_treatment) {
+ .@"error" => false,
+ .warn, .suppress => nlist.weakRef(),
+ .dynamic_lookup => true,
+ };
+ if (is_import) {
+ sym.value = 0;
+ sym.atom_ref = .{ .index = 0, .file = 0 };
+ sym.flags.weak = false;
+ sym.flags.weak_ref = nlist.weakRef();
+ sym.flags.import = is_import;
+ sym.visibility = .global;
+
+ const idx = self.globals.items[i];
+ macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
+ }
+ }
+}
+
+pub fn claimUnresolvedRelocatable(self: *Object, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.symbols.items, self.symtab.items(.nlist), 0..) |*sym, nlist, i| {
+ if (!nlist.ext()) continue;
+ if (!nlist.undf()) continue;
+ if (self.getSymbolRef(@intCast(i), macho_file).getFile(macho_file) != null) continue;
+
+ sym.value = 0;
+ sym.atom_ref = .{ .index = 0, .file = 0 };
+ sym.flags.weak_ref = nlist.weakRef();
+ sym.flags.import = true;
+ sym.visibility = .global;
+
+ const idx = self.globals.items[i];
+ macho_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i), .file = self.index };
+ }
+}
+
fn addSection(self: *Object, allocator: Allocator, segname: []const u8, sectname: []const u8) !u32 {
const n_sect = @as(u32, @intCast(try self.sections.addOne(allocator)));
self.sections.set(n_sect, .{
@@ -1646,19 +1801,22 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ const is_obj = macho_file.base.isObject();
+
+ for (self.symbols.items, 0..) |*sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
if (sym.getAtom(macho_file)) |atom| if (!atom.flags.alive) continue;
if (sym.isSymbolStab(macho_file)) continue;
const name = sym.getName(macho_file);
+ if (name.len == 0) continue;
// TODO in -r mode, we actually want to merge symbol names and emit only one
// work it out when emitting relocs
- if (name.len > 0 and
- (name[0] == 'L' or name[0] == 'l' or
+ if ((name[0] == 'L' or name[0] == 'l' or
mem.startsWith(u8, name, "_OBJC_SELECTOR_REFERENCES_")) and
- !macho_file.base.isObject()) continue;
+ !is_obj)
+ continue;
sym.flags.output_symtab = true;
if (sym.isLocal()) {
try sym.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, macho_file);
@@ -1693,9 +1851,9 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1));
}
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
if (!sym.flags.output_symtab) continue;
if (macho_file.base.isObject()) {
@@ -1732,28 +1890,204 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
}
}
-pub fn writeSymtab(self: Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {
+pub fn writeAtoms(self: *Object, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.comp.gpa;
+ const headers = self.sections.items(.header);
+ const sections_data = try gpa.alloc([]const u8, headers.len);
+ defer {
+ for (sections_data) |data| {
+ gpa.free(data);
+ }
+ gpa.free(sections_data);
+ }
+ @memset(sections_data, &[0]u8{});
+ const file = macho_file.getFileHandle(self.file_handle);
+
+ for (headers, 0..) |header, n_sect| {
+ if (header.isZerofill()) continue;
+ const data = try gpa.alloc(u8, header.size);
+ const amt = try file.preadAll(data, header.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ sections_data[n_sect] = data;
+ }
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const sect = atom.getInputSection(macho_file);
+ if (sect.isZerofill()) continue;
+ const off = atom.value;
+ const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
+ const data = sections_data[atom.n_sect];
+ @memcpy(buffer[off..][0..atom.size], data[atom.off..][0..atom.size]);
+ try atom.resolveRelocs(macho_file, buffer[off..][0..atom.size]);
+ }
+}
+
+pub fn writeAtomsRelocatable(self: *Object, macho_file: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = macho_file.base.allocator;
+ const headers = self.sections.items(.header);
+ const sections_data = try gpa.alloc([]const u8, headers.len);
+ defer {
+ for (sections_data) |data| {
+ gpa.free(data);
+ }
+ gpa.free(sections_data);
+ }
+ @memset(sections_data, &[0]u8{});
+ const file = macho_file.getFileHandle(self.file_handle);
+
+ for (headers, 0..) |header, n_sect| {
+ if (header.isZerofill()) continue;
+ const data = try gpa.alloc(u8, header.size);
+ const amt = try file.preadAll(data, header.offset + self.offset);
+ if (amt != data.len) return error.InputOutput;
+ sections_data[n_sect] = data;
+ }
+ for (self.getAtoms()) |atom_index| {
+ const atom = self.getAtom(atom_index) orelse continue;
+ if (!atom.flags.alive) continue;
+ const sect = atom.getInputSection(macho_file);
+ if (sect.isZerofill()) continue;
+ const off = atom.value;
+ const buffer = macho_file.sections.items(.out)[atom.out_n_sect].items;
+ const data = sections_data[atom.n_sect];
+ @memcpy(buffer[off..][0..atom.size], data[atom.off..][0..atom.size]);
+ const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
+ const extra = atom.getExtra(macho_file);
+ try atom.writeRelocs(macho_file, buffer[off..][0..atom.size], relocs[extra.rel_out_index..][0..extra.rel_out_count]);
+ }
+}
+
+pub fn calcCompactUnwindSizeRelocatable(self: *Object, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const ctx = &self.compact_unwind_ctx;
+
+ for (self.unwind_records_indexes.items) |irec| {
+ const rec = self.getUnwindRecord(irec);
+ if (!rec.alive) continue;
+
+ ctx.rec_count += 1;
+ ctx.reloc_count += 1;
+ if (rec.getPersonality(macho_file)) |_| {
+ ctx.reloc_count += 1;
+ }
+ if (rec.getLsdaAtom(macho_file)) |_| {
+ ctx.reloc_count += 1;
+ }
+ }
+}
+
+pub fn writeCompactUnwindRelocatable(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ const addReloc = struct {
+ fn addReloc(offset: u32, cpu_arch: std.Target.Cpu.Arch) !macho.relocation_info {
+ return .{
+ .r_address = math.cast(i32, offset) orelse return error.Overflow,
+ .r_symbolnum = 0,
+ .r_pcrel = 0,
+ .r_length = 3,
+ .r_extern = 0,
+ .r_type = switch (cpu_arch) {
+ .aarch64 => @intFromEnum(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
+ .x86_64 => @intFromEnum(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
+ else => unreachable,
+ },
+ };
+ }
+ }.addReloc;
+
+ const nsect = macho_file.unwind_info_sect_index.?;
+ const buffer = macho_file.sections.items(.out)[nsect].items;
+ const relocs = macho_file.sections.items(.relocs)[nsect].items;
+
+ var rec_index: u32 = self.compact_unwind_ctx.rec_index;
+ var reloc_index: u32 = self.compact_unwind_ctx.reloc_index;
+
+ for (self.unwind_records_indexes.items) |irec| {
+ const rec = self.getUnwindRecord(irec);
+ if (!rec.alive) continue;
+
+ var out: macho.compact_unwind_entry = .{
+ .rangeStart = 0,
+ .rangeLength = rec.length,
+ .compactUnwindEncoding = rec.enc.enc,
+ .personalityFunction = 0,
+ .lsda = 0,
+ };
+ defer rec_index += 1;
+
+ const offset = rec_index * @sizeOf(macho.compact_unwind_entry);
+
+ {
+ // Function address
+ const atom = rec.getAtom(macho_file);
+ const addr = rec.getAtomAddress(macho_file);
+ out.rangeStart = addr;
+ var reloc = try addReloc(offset, macho_file.options.cpu_arch.?);
+ reloc.r_symbolnum = atom.out_n_sect + 1;
+ relocs[reloc_index] = reloc;
+ reloc_index += 1;
+ }
+
+ // Personality function
+ if (rec.getPersonality(macho_file)) |sym| {
+ const r_symbolnum = math.cast(u24, sym.getOutputSymtabIndex(macho_file).?) orelse return error.Overflow;
+ var reloc = try addReloc(offset + 16, macho_file.options.cpu_arch.?);
+ reloc.r_symbolnum = r_symbolnum;
+ reloc.r_extern = 1;
+ relocs[reloc_index] = reloc;
+ reloc_index += 1;
+ }
+
+ // LSDA address
+ if (rec.getLsdaAtom(macho_file)) |atom| {
+ const addr = rec.getLsdaAddress(macho_file);
+ out.lsda = addr;
+ var reloc = try addReloc(offset + 24, macho_file.options.cpu_arch.?);
+ reloc.r_symbolnum = atom.out_n_sect + 1;
+ relocs[reloc_index] = reloc;
+ reloc_index += 1;
+ }
+
+ @memcpy(buffer[offset..][0..@sizeOf(macho.compact_unwind_entry)], mem.asBytes(&out));
+ }
+}
+
+pub fn writeSymtab(self: Object, macho_file: *MachO) void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ var n_strx = self.output_symtab_ctx.stroff;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
const idx = sym.getOutputSymtabIndex(macho_file) orelse continue;
- const n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sym.getName(macho_file));
- ctx.strtab.appendAssumeCapacity(0);
- const out_sym = &ctx.symtab.items[idx];
+ const out_sym = &macho_file.symtab.items[idx];
out_sym.n_strx = n_strx;
sym.setOutputSym(macho_file, out_sym);
+ const name = sym.getName(macho_file);
+ @memcpy(macho_file.strtab.items[n_strx..][0..name.len], name);
+ n_strx += @intCast(name.len);
+ macho_file.strtab.items[n_strx] = 0;
+ n_strx += 1;
}
if (macho_file.base.comp.config.debug_format != .strip and self.hasDebugInfo())
- try self.writeStabs(macho_file, ctx);
+ try self.writeStabs(n_strx, macho_file);
}
-pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{Overflow}!void {
+pub fn writeStabs(self: *const Object, stroff: u32, macho_file: *MachO) void {
const writeFuncStab = struct {
inline fn writeFuncStab(
n_strx: u32,
@@ -1761,7 +2095,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
n_value: u64,
size: u64,
index: u32,
- context: anytype,
+ context: *MachO,
) void {
context.symtab.items[index] = .{
.n_strx = 0,
@@ -1795,6 +2129,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
}.writeFuncStab;
var index = self.output_symtab_ctx.istab;
+ var n_strx = stroff;
if (self.compile_unit) |cu| {
const comp_dir = cu.getCompDir(self);
@@ -1802,10 +2137,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
// Open scope
// N_SO comp_dir
- var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(comp_dir);
- ctx.strtab.appendAssumeCapacity(0);
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_SO,
.n_sect = 0,
@@ -1813,11 +2145,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = 0,
};
index += 1;
+ @memcpy(macho_file.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
+ n_strx += @intCast(comp_dir.len);
+ macho_file.strtab.items[n_strx] = 0;
+ n_strx += 1;
// N_SO tu_name
- n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(tu_name);
- ctx.strtab.appendAssumeCapacity(0);
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_SO,
.n_sect = 0,
@@ -1825,19 +2158,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = 0,
};
index += 1;
+ @memcpy(macho_file.strtab.items[n_strx..][0..tu_name.len], tu_name);
+ n_strx += @intCast(tu_name.len);
+ macho_file.strtab.items[n_strx] = 0;
+ n_strx += 1;
// N_OSO path
- n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- if (self.in_archive) |ar| {
- ctx.strtab.appendSliceAssumeCapacity(ar.path);
- ctx.strtab.appendAssumeCapacity('(');
- ctx.strtab.appendSliceAssumeCapacity(self.path);
- ctx.strtab.appendAssumeCapacity(')');
- ctx.strtab.appendAssumeCapacity(0);
- } else {
- ctx.strtab.appendSliceAssumeCapacity(self.path);
- ctx.strtab.appendAssumeCapacity(0);
- }
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_OSO,
.n_sect = 0,
@@ -1845,10 +2171,27 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = self.mtime,
};
index += 1;
+ if (self.in_archive) |ar| {
+ @memcpy(macho_file.strtab.items[n_strx..][0..ar.path.len], ar.path);
+ n_strx += @intCast(ar.path.len);
+ macho_file.strtab.items[n_strx] = '(';
+ n_strx += 1;
+ @memcpy(macho_file.strtab.items[n_strx..][0..self.path.len], self.path);
+ n_strx += @intCast(self.path.len);
+ macho_file.strtab.items[n_strx] = ')';
+ n_strx += 1;
+ macho_file.strtab.items[n_strx] = 0;
+ n_strx += 1;
+ } else {
+ @memcpy(macho_file.strtab.items[n_strx..][0..self.path.len], self.path);
+ n_strx += @intCast(self.path.len);
+ macho_file.strtab.items[n_strx] = 0;
+ n_strx += 1;
+ }
- for (self.symbols.items) |sym_index| {
- const sym = macho_file.getSymbol(sym_index);
- const file = sym.getFile(macho_file) orelse continue;
+ for (self.symbols.items, 0..) |sym, i| {
+ const ref = self.getSymbolRef(@intCast(i), macho_file);
+ const file = ref.getFile(macho_file) orelse continue;
if (file.getIndex() != self.index) continue;
if (!sym.flags.output_symtab) continue;
if (macho_file.base.isObject()) {
@@ -1858,17 +2201,17 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
const sect = macho_file.sections.items(.header)[sym.out_n_sect];
const sym_n_strx = n_strx: {
const symtab_index = sym.getOutputSymtabIndex(macho_file).?;
- const osym = ctx.symtab.items[symtab_index];
+ 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_value = sym.getAddress(.{}, macho_file);
const sym_size = sym.getSize(macho_file);
if (sect.isCode()) {
- writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, ctx);
+ writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, macho_file);
index += 4;
} else if (sym.visibility == .global) {
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = sym_n_strx,
.n_type = macho.N_GSYM,
.n_sect = sym_n_sect,
@@ -1877,7 +2220,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
};
index += 1;
} else {
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = sym_n_strx,
.n_type = macho.N_STSYM,
.n_sect = sym_n_sect,
@@ -1890,7 +2233,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
// Close scope
// N_SO
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = 0,
.n_type = macho.N_SO,
.n_sect = 0,
@@ -1901,12 +2244,13 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
assert(self.hasSymbolStabs());
for (self.stab_files.items) |sf| {
+ const comp_dir = sf.getCombDir(self);
+ const tu_name = sf.getTuName(self);
+ const oso_path = sf.getOsoPath(self);
+
// Open scope
// N_SO comp_dir
- var n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sf.getCompDir(self));
- ctx.strtab.appendAssumeCapacity(0);
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_SO,
.n_sect = 0,
@@ -1914,11 +2258,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = 0,
};
index += 1;
+ @memcpy(macho_file.strtab.items[n_strx..][0..comp_dir.len], comp_dir);
+ n_strx += @intCast(comp_dir.len);
+ macho_file.strtab.items[n_strx] = 0;
+ n_strx += 1;
// N_SO tu_name
- n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sf.getTuName(self));
- ctx.strtab.appendAssumeCapacity(0);
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_SO,
.n_sect = 0,
@@ -1926,11 +2271,12 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = 0,
};
index += 1;
+ @memcpy(macho_file.strtab.items[n_strx..][0..tu_name.len], tu_name);
+ n_strx += @intCast(tu_name.len);
+ macho_file.strtab.items[n_strx] = 0;
+ n_strx += 1;
// N_OSO path
- n_strx = @as(u32, @intCast(ctx.strtab.items.len));
- ctx.strtab.appendSliceAssumeCapacity(sf.getOsoPath(self));
- ctx.strtab.appendAssumeCapacity(0);
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = n_strx,
.n_type = macho.N_OSO,
.n_sect = 0,
@@ -1938,6 +2284,10 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
.n_value = sf.getOsoModTime(self),
};
index += 1;
+ @memcpy(macho_file.strtab.items[n_strx..][0..oso_path.len], oso_path);
+ n_strx += @intCast(oso_path.len);
+ macho_file.strtab.items[n_strx] = 0;
+ n_strx += 1;
for (sf.stabs.items) |stab| {
const sym = stab.getSymbol(macho_file) orelse continue;
@@ -1946,17 +2296,17 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
if (!sym.flags.output_symtab) continue;
const sym_n_strx = n_strx: {
const symtab_index = sym.getOutputSymtabIndex(macho_file).?;
- const osym = ctx.symtab.items[symtab_index];
+ 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_value = sym.getAddress(.{}, macho_file);
const sym_size = sym.getSize(macho_file);
if (stab.is_func) {
- writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, ctx);
+ writeFuncStab(sym_n_strx, sym_n_sect, sym_n_value, sym_size, index, macho_file);
index += 4;
} else if (sym.visibility == .global) {
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = sym_n_strx,
.n_type = macho.N_GSYM,
.n_sect = sym_n_sect,
@@ -1965,7 +2315,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
};
index += 1;
} else {
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = sym_n_strx,
.n_type = macho.N_STSYM,
.n_sect = sym_n_sect,
@@ -1978,7 +2328,7 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
// Close scope
// N_SO
- ctx.symtab.items[index] = .{
+ macho_file.symtab.items[index] = .{
.n_strx = 0,
.n_type = macho.N_SO,
.n_sect = 0,
@@ -1990,36 +2340,6 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
}
}
-fn getSectionData(self: *const Object, index: u32, macho_file: *MachO) ![]u8 {
- const gpa = macho_file.base.comp.gpa;
- const slice = self.sections.slice();
- assert(index < slice.items(.header).len);
- const sect = slice.items(.header)[index];
- const handle = macho_file.getFileHandle(self.file_handle);
- const size = math.cast(usize, sect.size) orelse return error.Overflow;
- const buffer = try gpa.alloc(u8, size);
- errdefer gpa.free(buffer);
- const amt = try handle.preadAll(buffer, sect.offset + self.offset);
- if (amt != buffer.len) return error.InputOutput;
- return buffer;
-}
-
-pub fn getAtomData(self: *const Object, macho_file: *MachO, atom: Atom, buffer: []u8) !void {
- assert(buffer.len == atom.size);
- const slice = self.sections.slice();
- const handle = macho_file.getFileHandle(self.file_handle);
- const sect = slice.items(.header)[atom.n_sect];
- const amt = try handle.preadAll(buffer, sect.offset + self.offset + atom.off);
- if (amt != buffer.len) return error.InputOutput;
-}
-
-pub fn getAtomRelocs(self: *const Object, atom: Atom, macho_file: *MachO) []const Relocation {
- if (!atom.flags.relocs) return &[0]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);
@@ -2073,6 +2393,143 @@ pub fn asFile(self: *Object) File {
return .{ .object = self };
}
+const AddAtomArgs = struct {
+ name: MachO.String,
+ n_sect: u8,
+ off: u64,
+ size: u64,
+ alignment: u32,
+};
+
+fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index {
+ const atom_index: Atom.Index = @intCast(self.atoms.items.len);
+ const atom = try self.atoms.addOne(allocator);
+ atom.* = .{
+ .file = self.index,
+ .atom_index = atom_index,
+ .name = args.name,
+ .n_sect = args.n_sect,
+ .size = args.size,
+ .off = args.off,
+ .extra = try self.addAtomExtra(allocator, .{}),
+ .alignment = args.alignment,
+ };
+ return atom_index;
+}
+
+pub fn getAtom(self: *Object, atom_index: Atom.Index) ?*Atom {
+ if (atom_index == 0) return null;
+ assert(atom_index < self.atoms.items.len);
+ return &self.atoms.items[atom_index];
+}
+
+pub fn getAtoms(self: *Object) []const Atom.Index {
+ return self.atoms_indexes.items;
+}
+
+fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 {
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len);
+ return self.addAtomExtraAssumeCapacity(extra);
+}
+
+fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 {
+ const index = @as(u32, @intCast(self.atoms_extra.items.len));
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ inline for (fields) |field| {
+ self.atoms_extra.appendAssumeCapacity(switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ });
+ }
+ return index;
+}
+
+pub fn getAtomExtra(self: Object, index: u32) Atom.Extra {
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ var i: usize = index;
+ var result: Atom.Extra = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.type) {
+ u32 => self.atoms_extra.items[i],
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return result;
+}
+
+pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void {
+ assert(index > 0);
+ const fields = @typeInfo(Atom.Extra).Struct.fields;
+ inline for (fields, 0..) |field, i| {
+ self.atoms_extra.items[index + i] = switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ };
+ }
+}
+
+fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index {
+ try self.symbols.ensureUnusedCapacity(allocator, 1);
+ return self.addSymbolAssumeCapacity();
+}
+
+fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {
+ const index: Symbol.Index = @intCast(self.symbols.items.len);
+ const symbol = self.symbols.addOneAssumeCapacity();
+ symbol.* = .{ .file = self.index };
+ return index;
+}
+
+pub fn getSymbolRef(self: Object, index: Symbol.Index, macho_file: *MachO) MachO.Ref {
+ const global_index = self.globals.items[index];
+ if (macho_file.resolver.get(global_index)) |ref| return ref;
+ return .{ .index = index, .file = self.index };
+}
+
+pub fn addSymbolExtra(self: *Object, allocator: Allocator, extra: Symbol.Extra) !u32 {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
+ return self.addSymbolExtraAssumeCapacity(extra);
+}
+
+fn addSymbolExtraAssumeCapacity(self: *Object, extra: Symbol.Extra) u32 {
+ const index = @as(u32, @intCast(self.symbols_extra.items.len));
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields) |field| {
+ self.symbols_extra.appendAssumeCapacity(switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ });
+ }
+ return index;
+}
+
+pub fn getSymbolExtra(self: Object, index: u32) Symbol.Extra {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ var i: usize = index;
+ var result: Symbol.Extra = undefined;
+ inline for (fields) |field| {
+ @field(result, field.name) = switch (field.type) {
+ u32 => self.symbols_extra.items[i],
+ else => @compileError("bad field type"),
+ };
+ i += 1;
+ }
+ return result;
+}
+
+pub fn setSymbolExtra(self: *Object, index: u32, extra: Symbol.Extra) void {
+ const fields = @typeInfo(Symbol.Extra).Struct.fields;
+ inline for (fields, 0..) |field, i| {
+ self.symbols_extra.items[index + i] = switch (field.type) {
+ u32 => @field(extra, field.name),
+ else => @compileError("bad field type"),
+ };
+ }
+}
+
fn addUnwindRecord(self: *Object, allocator: Allocator) !UnwindInfo.Record.Index {
try self.unwind_records.ensureUnusedCapacity(allocator, 1);
return self.addUnwindRecordAssumeCapacity();
@@ -2124,10 +2581,11 @@ fn formatAtoms(
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
+ const macho_file = ctx.macho_file;
try writer.writeAll(" atoms\n");
- for (object.atoms.items) |atom_index| {
- const atom = ctx.macho_file.getAtom(atom_index).?;
- try writer.print(" {}\n", .{atom.fmt(ctx.macho_file)});
+ for (object.getAtoms()) |atom_index| {
+ const atom = object.getAtom(atom_index) orelse continue;
+ try writer.print(" {}\n", .{atom.fmt(macho_file)});
}
}
@@ -2214,10 +2672,26 @@ fn formatSymtab(
_ = unused_fmt_string;
_ = options;
const object = ctx.object;
+ const macho_file = ctx.macho_file;
try writer.writeAll(" symbols\n");
- for (object.symbols.items) |index| {
- const sym = ctx.macho_file.getSymbol(index);
- try writer.print(" {}\n", .{sym.fmt(ctx.macho_file)});
+ for (object.symbols.items, 0..) |sym, i| {
+ const ref = object.getSymbolRef(@intCast(i), macho_file);
+ if (ref.getFile(macho_file) == null) {
+ // TODO any better way of handling this?
+ try writer.print(" {s} : unclaimed\n", .{sym.getName(macho_file)});
+ } else {
+ try writer.print(" {}\n", .{ref.getSymbol(macho_file).?.fmt(macho_file)});
+ }
+ }
+ for (object.stab_files.items) |sf| {
+ try writer.print(" stabs({s},{s},{s})\n", .{
+ sf.getCompDir(object),
+ sf.getTuName(object),
+ sf.getOsoPath(object),
+ });
+ for (sf.stabs.items) |stab| {
+ try writer.print(" {}", .{stab.fmt(object)});
+ }
}
}
@@ -2286,8 +2760,47 @@ const StabFile = struct {
is_func: bool = true,
symbol: ?Symbol.Index = null,
- fn getSymbol(stab: Stab, macho_file: *MachO) ?*Symbol {
- return if (stab.symbol) |s| macho_file.getSymbol(s) else null;
+ fn getSymbol(stab: Stab, object: *const Object) ?*Symbol {
+ const index = stab.index orelse return null;
+ return &object.symbols.items[index];
+ }
+
+ pub fn format(
+ stab: Stab,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = stab;
+ _ = unused_fmt_string;
+ _ = options;
+ _ = writer;
+ @compileError("do not format stabs directly");
+ }
+
+ const StabFormatContext = struct { Stab, *const Object };
+
+ pub fn fmt(stab: Stab, object: *const Object) std.fmt.Formatter(format2) {
+ return .{ .data = .{ stab, object } };
+ }
+
+ fn format2(
+ ctx: StabFormatContext,
+ comptime unused_fmt_string: []const u8,
+ options: std.fmt.FormatOptions,
+ writer: anytype,
+ ) !void {
+ _ = unused_fmt_string;
+ _ = options;
+ const stab, const object = ctx;
+ const sym = stab.getSymbol(object).?;
+ if (stab.is_func) {
+ try writer.print("func({d})", .{stab.index.?});
+ } else if (sym.visibility == .global) {
+ try writer.print("gsym({d})", .{stab.index.?});
+ } else {
+ try writer.print("stsym({d})", .{stab.index.?});
+ }
}
};
};
@@ -2310,6 +2823,13 @@ const InArchive = struct {
size: u32,
};
+const CompactUnwindCtx = struct {
+ rec_index: u32 = 0,
+ rec_count: u32 = 0,
+ reloc_index: u32 = 0,
+ reloc_count: u32 = 0,
+};
+
const x86_64 = struct {
fn parseRelocs(
self: *const Object,
src/link/MachO.zig
@@ -63,9 +63,6 @@ dso_handle_index: ?Symbol.Index = null,
objc_msg_send_index: ?Symbol.Index = null,
entry_index: ?Symbol.Index = null,
-/// List of atoms that are either synthetic or map directly to the Zig source program.
-atoms: std.ArrayListUnmanaged(Atom) = .{},
-atoms_extra: std.ArrayListUnmanaged(u32) = .{},
thunks: std.ArrayListUnmanaged(Thunk) = .{},
/// String interning table
@@ -549,13 +546,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
try dead_strip.gcAtoms(self);
}
- self.checkDuplicates() catch |err| switch (err) {
- error.HasDuplicates => return error.FlushFailure,
- else => |e| {
- try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{});
- return e;
- },
- };
+ // TODO
+ // self.checkDuplicates() catch |err| switch (err) {
+ // error.HasDuplicates => return error.FlushFailure,
+ // else => |e| {
+ // try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{});
+ // return e;
+ // },
+ // };
self.markImportsAndExports();
self.deadStripDylibs();