Skip to content

Commit

Permalink
macho: fix emitting objects in -r mode and merging literals
Browse files Browse the repository at this point in the history
  • Loading branch information
kubkon committed May 14, 2024
1 parent 3a47020 commit 170903f
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 28 deletions.
8 changes: 6 additions & 2 deletions src/MachO.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,7 @@ fn createObjcSections(self: *MachO) !void {
}
}

fn resolveMergeSections(self: *MachO) !void {
pub fn resolveMergeSections(self: *MachO) !void {
for (self.objects.items) |index| {
try self.getFile(index).?.object.resolveMergeSections(self);
}
Expand Down Expand Up @@ -2955,7 +2955,11 @@ pub fn getOrCreateMergeSection(
macho.S_8BYTE_LITERALS,
macho.S_16BYTE_LITERALS,
macho.S_CSTRING_LITERALS,
=> .{ .type = @"type" },
=> .{
.segname = try self.string_intern.insert(gpa, segname),
.sectname = try self.string_intern.insert(gpa, sectname),
.type = @"type",
},
else => unreachable,
};
return index;
Expand Down
32 changes: 24 additions & 8 deletions src/MachO/Atom.zig
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,22 @@ pub fn getInputSection(self: Atom, macho_file: *MachO) macho.section_64 {
};
}

pub fn getOutputSectionIndex(self: Atom, macho_file: *MachO) u8 {
if (self.flags.literal) {
const extra = self.getExtra(macho_file).?;
const msec = macho_file.getMergeSection(extra.merge_section_index);
return msec.out_n_sect;
}
return self.out_n_sect;
}

pub fn getAddress(self: Atom, macho_file: *MachO) u64 {
if (self.flags.literal) {
const extra = self.getExtra(macho_file).?;
const msec = macho_file.getMergeSection(extra.merge_section_index);
return msec.getAddress(macho_file) + self.value;
}
const header = macho_file.sections.items(.header)[self.out_n_sect];
const header = macho_file.sections.items(.header)[self.getOutputSectionIndex(macho_file)];
return header.addr + self.value;
}

Expand Down Expand Up @@ -166,6 +175,15 @@ pub fn initOutputSection(args: struct {
flags: u32,
is_code: bool = false,
}, macho_file: *MachO) !u8 {
if (macho_file.options.relocatable) {
const osec = macho_file.getSectionByName(args.segname, args.sectname) orelse try macho_file.addSection(
args.segname,
args.sectname,
.{ .flags = args.flags },
);
return osec;
}

const @"type": u8 = @truncate(args.flags & 0xff);
const segname, const sectname, const flags = blk: {
if (args.is_code) break :blk .{
Expand Down Expand Up @@ -227,9 +245,6 @@ pub fn initOutputSection(args: struct {
sectname,
.{ .flags = flags },
);
if (mem.eql(u8, segname, "__DATA") and mem.eql(u8, sectname, "__data")) {
macho_file.data_sect_index = osec;
}
return osec;
}

Expand Down Expand Up @@ -400,7 +415,7 @@ fn resolveRelocInner(
) ResolveError!void {
const cpu_arch = macho_file.options.cpu_arch.?;
const rel_offset = rel.offset - self.off;
const seg_id = macho_file.sections.items(.segment_id)[self.out_n_sect];
const seg_id = macho_file.sections.items(.segment_id)[self.getOutputSectionIndex(macho_file)];
const seg = macho_file.segments.items[seg_id];
const P = @as(i64, @intCast(self.getAddress(macho_file))) + @as(i64, @intCast(rel_offset));
const A = rel.addend + rel.getRelocAddend(cpu_arch);
Expand Down Expand Up @@ -729,7 +744,7 @@ pub fn writeRelocs(self: Atom, macho_file: *MachO, code: []u8, buffer: *std.Arra
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,
.local => rel.getTargetAtom(macho_file).getOutputSectionIndex(macho_file) + 1,
.@"extern" => rel.getTargetSymbol(macho_file).getOutputSymtabIndex(macho_file).?,
};
break :r_symbolnum math.cast(u24, r_symbolnum) orelse return error.Overflow;
Expand Down Expand Up @@ -873,8 +888,9 @@ fn format2(
const atom = ctx.atom;
const macho_file = ctx.macho_file;
try writer.print("atom({d}) : {s} : @{x} : sect({d}) : align({x}) : size({x})", .{
atom.atom_index, atom.getName(macho_file), atom.getAddress(macho_file),
atom.out_n_sect, atom.alignment, atom.size,
atom.atom_index, atom.getName(macho_file),
atom.getAddress(macho_file), atom.getOutputSectionIndex(macho_file),
atom.alignment, atom.size,
});
if (atom.flags.thunk) try writer.print(" : thunk({d})", .{atom.getExtra(macho_file).?.thunk});
if (!atom.flags.alive) try writer.writeAll(" : [*]");
Expand Down
10 changes: 6 additions & 4 deletions src/MachO/Object.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1372,7 +1372,8 @@ pub fn calcStabsSize(self: *Object, 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 out_n_sect = sym.getOutputSectionIndex(macho_file);
const sect = macho_file.sections.items(.header)[out_n_sect];
if (sect.isCode()) {
self.output_symtab_ctx.nstabs += 4; // N_BNSYM, N_FUN, N_FUN, N_ENSYM
} else if (sym.visibility == .global) {
Expand Down Expand Up @@ -1526,13 +1527,14 @@ pub fn writeStabs(self: *const Object, 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 out_n_sect = sym.getOutputSectionIndex(macho_file);
const sect = macho_file.sections.items(.header)[out_n_sect];
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(out_n_sect + 1) else 0;
const sym_n_value = sym.getAddress(.{}, macho_file);
const sym_size = sym.getSize(macho_file);
if (sect.isCode()) {
Expand Down Expand Up @@ -1620,7 +1622,7 @@ pub fn writeStabs(self: *const Object, 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) {
Expand Down
31 changes: 20 additions & 11 deletions src/MachO/Symbol.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ file: File.Index = 0,
/// Use `getAtom` to get the pointer to the atom.
atom: Atom.Index = 0,

/// Assigned output section index for this atom.
out_n_sect: u16 = 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.
Expand Down Expand Up @@ -94,6 +94,13 @@ pub fn getDylibOrdinal(symbol: Symbol, macho_file: *MachO) ?u16 {
};
}

pub fn getOutputSectionIndex(symbol: Symbol, macho_file: *MachO) u8 {
if (symbol.getAtom(macho_file)) |atom| {
return atom.getOutputSectionIndex(macho_file);
}
return symbol.out_n_sect;
}

pub fn getSymbolRank(symbol: Symbol, macho_file: *MachO) u32 {
const file = symbol.getFile(macho_file) orelse return std.math.maxInt(u32);
const in_archive = switch (file) {
Expand Down Expand Up @@ -203,9 +210,10 @@ pub inline fn setExtra(symbol: Symbol, extra: Extra, macho_file: *MachO) void {
}

pub fn setOutputSym(symbol: Symbol, macho_file: *MachO, out: *macho.nlist_64) void {
const out_n_sect = symbol.getOutputSectionIndex(macho_file);
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(out_n_sect + 1);
out.n_desc = 0;
out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);

Expand All @@ -217,7 +225,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(out_n_sect + 1);
out.n_value = symbol.getAddress(.{ .stubs = false }, macho_file);
out.n_desc = 0;

Expand Down Expand Up @@ -286,24 +294,25 @@ fn format2(
_ = options;
_ = unused_fmt_string;
const symbol = ctx.symbol;
const macho_file = ctx.macho_file;
try writer.print("%{d} : {s} : @{x}", .{
symbol.nlist_idx,
symbol.getName(ctx.macho_file),
symbol.getAddress(.{}, ctx.macho_file),
symbol.getName(macho_file),
symbol.getAddress(.{}, 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.getFile(macho_file)) |file| {
if (symbol.getOutputSectionIndex(macho_file) != 0) {
try writer.print(" : sect({d})", .{symbol.getOutputSectionIndex(macho_file)});
}
if (symbol.getAtom(ctx.macho_file)) |atom| {
if (symbol.getAtom(macho_file)) |atom| {
try writer.print(" : atom({d})", .{atom.atom_index});
}
var buf: [2]u8 = .{'_'} ** 2;
if (symbol.flags.@"export") buf[0] = 'E';
if (symbol.flags.import) buf[1] = 'I';
try writer.print(" : {s}", .{&buf});
if (symbol.flags.weak) try writer.writeAll(" : weak");
if (symbol.isSymbolStab(ctx.macho_file)) try writer.writeAll(" : stab");
if (symbol.isSymbolStab(macho_file)) try writer.writeAll(" : stab");
switch (file) {
.internal => |x| try writer.print(" : internal({d})", .{x.index}),
.object => |x| try writer.print(" : object({d})", .{x.index}),
Expand Down
5 changes: 3 additions & 2 deletions src/MachO/UnwindInfo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ pub fn generate(info: *UnwindInfo, macho_file: *MachO) !void {
const rhs = ctx.getUnwindRecord(rhs_index);
const lhsa = lhs.getAtom(ctx);
const rhsa = rhs.getAtom(ctx);
if (lhsa.out_n_sect == rhsa.out_n_sect) return lhs.getAtomAddress(ctx) < rhs.getAtomAddress(ctx);
return lhsa.out_n_sect < rhsa.out_n_sect;
if (lhsa.getOutputSectionIndex(ctx) == rhsa.getOutputSectionIndex(ctx))
return lhs.getAtomAddress(ctx) < rhs.getAtomAddress(ctx);
return lhsa.getOutputSectionIndex(ctx) < rhsa.getOutputSectionIndex(ctx);
}
}.sortFn;
mem.sort(Record.Index, info.records.items, macho_file, sortFn);
Expand Down
37 changes: 37 additions & 0 deletions src/MachO/relocatable.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pub fn flush(macho_file: *MachO) !void {
try macho_file.resolveMergeSections();
markExports(macho_file);
claimUnresolved(macho_file);
try macho_file.finalizeMergeSections();
try initOutputSections(macho_file);
try macho_file.sortSections();
try macho_file.addAtomsToSections();
Expand Down Expand Up @@ -103,6 +105,7 @@ fn initOutputSections(macho_file: *MachO) !void {
for (object.atoms.items) |atom_index| {
const atom = macho_file.getAtom(atom_index) orelse continue;
if (!atom.flags.alive) continue;
if (atom.flags.literal) continue;
const isec = atom.getInputSection(macho_file);
atom.out_n_sect = try Atom.initOutputSection(.{
.segname = isec.segName(),
Expand All @@ -113,6 +116,14 @@ fn initOutputSections(macho_file: *MachO) !void {
}
}

for (macho_file.merge_sections.items) |*msec| {
msec.out_n_sect = try Atom.initOutputSection(.{
.segname = msec.segName(macho_file),
.sectname = msec.sectName(macho_file),
.flags = msec.type,
}, macho_file);
}

const needs_unwind_info = for (macho_file.objects.items) |index| {
if (macho_file.getFile(index).?.object.hasUnwindRecords()) break true;
} else false;
Expand Down Expand Up @@ -147,6 +158,17 @@ fn calcSectionSizes(macho_file: *MachO) !void {
}
}

for (macho_file.merge_sections.items) |*msec| {
try msec.calcSize(macho_file);
const header = &macho_file.sections.items(.header)[msec.out_n_sect];
const msec_alignment = try math.powi(u32, 2, msec.alignment);
const offset = mem.alignForward(u64, header.size, msec_alignment);
const padding = offset - header.size;
msec.value = offset;
header.size += padding + msec.size;
header.@"align" = @max(header.@"align", msec.alignment);
}

if (macho_file.unwind_info_sect_index) |index| {
calcCompactUnwindSize(macho_file, index);
}
Expand Down Expand Up @@ -254,6 +276,21 @@ fn writeAtoms(macho_file: *MachO) !void {
try macho_file.base.file.pwriteAll(code, header.offset);
try macho_file.base.file.pwriteAll(mem.sliceAsBytes(relocs.items), header.reloff);
}

for (macho_file.merge_sections.items) |msec| {
const header = slice.items(.header)[msec.out_n_sect];
const offset = msec.value + header.offset;
const buffer = try gpa.alloc(u8, msec.size);
defer gpa.free(buffer);
for (msec.atoms.items) |atom_index| {
const atom = macho_file.getAtom(atom_index).?;
const data = atom.getLiteralString(macho_file).?;
const off = atom.value;
@memcpy(buffer[off..][0..atom.size], data);
// TODO relocs?
}
try macho_file.base.file.pwriteAll(buffer, offset);
}
}

fn writeCompactUnwind(macho_file: *MachO) !void {
Expand Down
2 changes: 1 addition & 1 deletion src/MachO/thunks.zig
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fn advance(sect: *macho.section_64, size: u64, pow2_align: u32) !u64 {
fn isReachable(atom: *const Atom, rel: Relocation, macho_file: *MachO) bool {
const target = rel.getTargetSymbol(macho_file);
if (target.flags.stubs or target.flags.objc_stubs) return false;
if (atom.out_n_sect != target.out_n_sect) return false;
if (atom.getOutputSectionIndex(macho_file) != target.out_n_sect) return false;
const target_atom = target.getAtom(macho_file).?;
if (target_atom.value == @as(u64, @bitCast(@as(i64, -1)))) return false;
const saddr = @as(i64, @intCast(atom.getAddress(macho_file))) + @as(i64, @intCast(rel.offset - atom.off));
Expand Down

0 comments on commit 170903f

Please sign in to comment.