diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 3e752d4a19..06458ea3cd 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -75,11 +75,11 @@ exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{}, eh_frame_sect_id: ?u8 = null, eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{}, -eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{}, +eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{}, unwind_info_sect_id: ?u8 = null, unwind_relocs_lookup: []Record = undefined, -unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{}, +unwind_records_lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, const Entry = struct { start: u32 = 0, @@ -274,11 +274,11 @@ const SymbolAtIndex = struct { const sym = self.getSymbol(ctx); if (!sym.ext()) { const sym_name = self.getSymbolName(ctx); - if (mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L")) return 0; - return 1; + if (mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L")) return 3; + return 2; } - if (sym.weakDef() or sym.pext()) return 2; - return 3; + if (sym.weakDef() or sym.pext()) return 1; + return 0; } /// Performs lexicographic-like check. @@ -423,8 +423,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { zld, object_id, sym_index, - 0, - 0, + sym_index, + 1, sect.size, sect.@"align", out_sect_id, @@ -502,8 +502,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { zld, object_id, sym_index, - 0, - 0, + sym_index, + 1, atom_size, sect.@"align", out_sect_id, @@ -521,7 +521,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { const atom_loc = filterSymbolsByAddress(symtab[next_sym_index..], addr, addr + 1); assert(atom_loc.len > 0); const atom_sym_index = atom_loc.index + next_sym_index; - const nsyms_trailing = atom_loc.len - 1; + const nsyms_trailing = atom_loc.len; next_sym_index += atom_loc.len; const atom_size = if (next_sym_index < sect_start_index + sect_loc.len) @@ -538,7 +538,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { zld, object_id, atom_sym_index, - atom_sym_index + 1, + atom_sym_index, nsyms_trailing, atom_size, atom_align, @@ -772,8 +772,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { if (target.getFile() != object_id) { self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true; } else { - const atom_index = self.getAtomIndexForSymbol(target.sym_index).?; - self.eh_frame_records_lookup.putAssumeCapacityNoClobber(atom_index, offset); + self.eh_frame_records_lookup.putAssumeCapacityNoClobber(target, offset); } } } @@ -802,10 +801,10 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { _ = try zld.initSection("__TEXT", "__unwind_info", .{}); } - try self.unwind_records_lookup.ensureTotalCapacity(gpa, @as(u32, @intCast(self.exec_atoms.items.len))); - const unwind_records = self.getUnwindRecords(); + try self.unwind_records_lookup.ensureTotalCapacity(gpa, @as(u32, @intCast(unwind_records.len))); + const needs_eh_frame = for (unwind_records) |record| { if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true; } else false; @@ -844,8 +843,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { if (target.getFile() != object_id) { self.unwind_relocs_lookup[record_id].dead = true; } else { - const atom_index = self.getAtomIndexForSymbol(target.sym_index).?; - self.unwind_records_lookup.putAssumeCapacityNoClobber(atom_index, @as(u32, @intCast(record_id))); + self.unwind_records_lookup.putAssumeCapacityNoClobber(target, @as(u32, @intCast(record_id))); } } } @@ -1012,7 +1010,13 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 { Predicate{ .addr = @as(i64, @intCast(addr)) }, ); if (target_sym_index > 0) { - return @as(u32, @intCast(lookup.start + target_sym_index - 1)); + // Hone in on the most senior alias of the target symbol. + // See SymbolAtIndex.lessThan for more context. + var start = target_sym_index - 1; + while (start > 0 and + self.source_address_lookup[lookup.start..][start - 1] == addr) : (start -= 1) + {} + return @as(u32, @intCast(lookup.start + start)); } } return self.getSectionAliasSymbolIndex(sect_id); diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig index 84885ed182..aff1681d38 100644 --- a/src/link/MachO/UnwindInfo.zig +++ b/src/link/MachO/UnwindInfo.zig @@ -26,7 +26,7 @@ gpa: Allocator, /// List of all unwind records gathered from all objects and sorted /// by source function address. records: std.ArrayListUnmanaged(macho.compact_unwind_entry) = .{}, -records_lookup: std.AutoHashMapUnmanaged(AtomIndex, RecordIndex) = .{}, +records_lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, RecordIndex) = .{}, /// List of all personalities referenced by either unwind info entries /// or __eh_frame entries. @@ -211,23 +211,22 @@ pub fn scanRelocs(zld: *Zld) !void { for (zld.objects.items, 0..) |*object, object_id| { const unwind_records = object.getUnwindRecords(); for (object.exec_atoms.items) |atom_index| { - const record_id = object.unwind_records_lookup.get(atom_index) orelse continue; - if (object.unwind_relocs_lookup[record_id].dead) continue; - const record = unwind_records[record_id]; - if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { - if (getPersonalityFunctionReloc( - zld, - @as(u32, @intCast(object_id)), - record_id, - )) |rel| { - // Personality function; add GOT pointer. - const target = Atom.parseRelocTarget(zld, .{ - .object_id = @as(u32, @intCast(object_id)), - .rel = rel, - .code = mem.asBytes(&record), - .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), - }); - try Atom.addGotEntry(zld, target); + var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); + while (inner_syms_it.next()) |sym| { + const record_id = object.unwind_records_lookup.get(sym) orelse continue; + if (object.unwind_relocs_lookup[record_id].dead) continue; + const record = unwind_records[record_id]; + if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { + if (getPersonalityFunctionReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| { + // Personality function; add GOT pointer. + const target = Atom.parseRelocTarget(zld, .{ + .object_id = @as(u32, @intCast(object_id)), + .rel = rel, + .code = mem.asBytes(&record), + .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), + }); + try Atom.addGotEntry(zld, target); + } } } } @@ -242,8 +241,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa); defer records.deinit(); - var atom_indexes = std.ArrayList(AtomIndex).init(info.gpa); - defer atom_indexes.deinit(); + var sym_indexes = std.ArrayList(SymbolWithLoc).init(info.gpa); + defer sym_indexes.deinit(); // TODO handle dead stripping for (zld.objects.items, 0..) |*object, object_id| { @@ -253,80 +252,101 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { // Contents of unwind records does not have to cover all symbol in executable section // so we need insert them ourselves. try records.ensureUnusedCapacity(object.exec_atoms.items.len); - try atom_indexes.ensureUnusedCapacity(object.exec_atoms.items.len); + try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len); for (object.exec_atoms.items) |atom_index| { - var record = if (object.unwind_records_lookup.get(atom_index)) |record_id| blk: { - if (object.unwind_relocs_lookup[record_id].dead) continue; - var record = unwind_records[record_id]; + var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); + var prev_symbol: ?SymbolWithLoc = null; + while (inner_syms_it.next()) |symbol| { + var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: { + if (object.unwind_relocs_lookup[record_id].dead) continue; + var record = unwind_records[record_id]; - if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { - try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), atom_index, &record); - } else { - if (getPersonalityFunctionReloc( - zld, - @as(u32, @intCast(object_id)), - record_id, - )) |rel| { - const target = Atom.parseRelocTarget(zld, .{ - .object_id = @as(u32, @intCast(object_id)), - .rel = rel, - .code = mem.asBytes(&record), - .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), - }); - const personality_index = info.getPersonalityFunction(target) orelse inner: { - const personality_index = info.personalities_count; - info.personalities[personality_index] = target; - info.personalities_count += 1; - break :inner personality_index; - }; + if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { + try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record); + } else { + if (getPersonalityFunctionReloc( + zld, + @as(u32, @intCast(object_id)), + record_id, + )) |rel| { + const target = Atom.parseRelocTarget(zld, .{ + .object_id = @as(u32, @intCast(object_id)), + .rel = rel, + .code = mem.asBytes(&record), + .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), + }); + const personality_index = info.getPersonalityFunction(target) orelse inner: { + const personality_index = info.personalities_count; + info.personalities[personality_index] = target; + info.personalities_count += 1; + break :inner personality_index; + }; - record.personalityFunction = personality_index + 1; - UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1); - } - - if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| { - const target = Atom.parseRelocTarget(zld, .{ - .object_id = @as(u32, @intCast(object_id)), - .rel = rel, - .code = mem.asBytes(&record), - .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), - }); - record.lsda = @as(u64, @bitCast(target)); - } - } - break :blk record; - } else blk: { - const atom = zld.getAtom(atom_index); - const sym = zld.getSymbol(atom.getSymbolWithLoc()); - if (sym.n_desc == N_DEAD) continue; - - if (!object.hasUnwindRecords()) { - if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| { - if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue; - var record = nullRecord(); - try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), atom_index, &record); - switch (cpu_arch) { - .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF), - .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF), - else => unreachable, + record.personalityFunction = personality_index + 1; + UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1); + } + + if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| { + const target = Atom.parseRelocTarget(zld, .{ + .object_id = @as(u32, @intCast(object_id)), + .rel = rel, + .code = mem.asBytes(&record), + .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), + }); + record.lsda = @as(u64, @bitCast(target)); } - break :blk record; } - } + break :blk record; + } else blk: { + const sym = zld.getSymbol(symbol); + if (sym.n_desc == N_DEAD) continue; + if (prev_symbol) |prev_sym| { + const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value; + const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value; + if (prev_addr == curr_addr) continue; + } - break :blk nullRecord(); - }; + if (!object.hasUnwindRecords()) { + if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| { + if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue; + var record = nullRecord(); + try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record); + switch (cpu_arch) { + .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF), + .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF), + else => unreachable, + } + break :blk record; + } + } - const atom = zld.getAtom(atom_index); - const sym_loc = atom.getSymbolWithLoc(); - const sym = zld.getSymbol(sym_loc); - assert(sym.n_desc != N_DEAD); - record.rangeStart = sym.n_value; - record.rangeLength = @as(u32, @intCast(atom.size)); + break :blk nullRecord(); + }; - records.appendAssumeCapacity(record); - atom_indexes.appendAssumeCapacity(atom_index); + const atom = zld.getAtom(atom_index); + const sym = zld.getSymbol(symbol); + assert(sym.n_desc != N_DEAD); + const size = if (inner_syms_it.next()) |next_sym| blk: { + // All this trouble to account for symbol aliases. + // TODO I think that remodelling the linker so that a Symbol references an Atom + // is the way to go, kinda like we do for ELF. We might also want to perhaps tag + // symbol aliases somehow so that they are excluded from everything except relocation + // resolution. + defer inner_syms_it.pos -= 1; + const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value; + const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value; + if (next_addr > curr_addr) break :blk next_addr - curr_addr; + break :blk zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value; + } else zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value; + record.rangeStart = sym.n_value; + record.rangeLength = @as(u32, @intCast(size)); + + try records.append(record); + try sym_indexes.append(symbol); + + prev_symbol = symbol; + } } } @@ -339,7 +359,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { // Fold records try info.records.ensureTotalCapacity(info.gpa, records.items.len); - try info.records_lookup.ensureTotalCapacity(info.gpa, @as(u32, @intCast(atom_indexes.items.len))); + try info.records_lookup.ensureTotalCapacity(info.gpa, @as(u32, @intCast(sym_indexes.items.len))); var maybe_prev: ?macho.compact_unwind_entry = null; for (records.items, 0..) |record, i| { @@ -365,7 +385,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void { break :blk record_id; } }; - info.records_lookup.putAssumeCapacityNoClobber(atom_indexes.items[i], record_id); + info.records_lookup.putAssumeCapacityNoClobber(sym_indexes.items[i], record_id); } // Calculate common encodings @@ -501,12 +521,12 @@ fn collectPersonalityFromDwarf( info: *UnwindInfo, zld: *Zld, object_id: u32, - atom_index: u32, + sym_loc: SymbolWithLoc, record: *macho.compact_unwind_entry, ) !void { const object = &zld.objects.items[object_id]; var it = object.getEhFrameRecordsIterator(); - const fde_offset = object.eh_frame_records_lookup.get(atom_index).?; + const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?; it.seekTo(fde_offset); const fde = (try it.next()).?; const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset); diff --git a/src/link/MachO/ZldAtom.zig b/src/link/MachO/ZldAtom.zig index 613f0fc86c..f97bfca326 100644 --- a/src/link/MachO/ZldAtom.zig +++ b/src/link/MachO/ZldAtom.zig @@ -84,14 +84,14 @@ pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc { const InnerSymIterator = struct { sym_index: u32, - count: u32, + nsyms: u32, file: u32, + pos: u32 = 0, pub fn next(it: *@This()) ?SymbolWithLoc { - if (it.count == 0) return null; - const res = SymbolWithLoc{ .sym_index = it.sym_index, .file = it.file }; - it.sym_index += 1; - it.count -= 1; + if (it.pos == it.nsyms) return null; + const res = SymbolWithLoc{ .sym_index = it.sym_index + it.pos, .file = it.file }; + it.pos += 1; return res; } }; @@ -103,7 +103,7 @@ pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: AtomIndex) InnerSymIterato assert(atom.getFile() != null); return .{ .sym_index = atom.inner_sym_index, - .count = atom.inner_nsyms_trailing, + .nsyms = atom.inner_nsyms_trailing, .file = atom.file, }; } @@ -228,11 +228,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct { // Find containing atom log.debug(" | locating symbol by address @{x} in section {d}", .{ address_in_section, sect_id }); - const candidate = object.getSymbolByAddress(address_in_section, sect_id); - // Make sure we are not dealing with a local alias. - const atom_index = object.getAtomIndexForSymbol(candidate) orelse break :sym_index candidate; - const atom = zld.getAtom(atom_index); - break :sym_index atom.sym_index; + break :sym_index object.getSymbolByAddress(address_in_section, sect_id); } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum]; const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 }; diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index 22da7a4c99..563d68cc89 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -294,124 +294,131 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void { const unwind_records = object.getUnwindRecords(); for (object.exec_atoms.items) |atom_index| { - if (!object.hasUnwindRecords()) { - if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| { - const ptr = object.eh_frame_relocs_lookup.getPtr(fde_offset).?; - if (ptr.dead) continue; // already marked - if (!alive.contains(atom_index)) { - // Mark dead and continue. - ptr.dead = true; - } else { - // Mark references live and continue. - try markEhFrameRecord(zld, object_id, atom_index, alive); - } - continue; - } - } + var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); - const record_id = object.unwind_records_lookup.get(atom_index) orelse continue; - if (object.unwind_relocs_lookup[record_id].dead) continue; // already marked, nothing to do - if (!alive.contains(atom_index)) { - // Mark the record dead and continue. - object.unwind_relocs_lookup[record_id].dead = true; - if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| { - object.eh_frame_relocs_lookup.getPtr(fde_offset).?.dead = true; + if (!object.hasUnwindRecords()) { + if (alive.contains(atom_index)) { + // Mark references live and continue. + try markEhFrameRecords(zld, object_id, atom_index, alive); + } else { + while (inner_syms_it.next()) |sym| { + if (object.eh_frame_records_lookup.get(sym)) |fde_offset| { + // Mark dead and continue. + object.eh_frame_relocs_lookup.getPtr(fde_offset).?.dead = true; + } + } } continue; } - const record = unwind_records[record_id]; - if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { - try markEhFrameRecord(zld, object_id, atom_index, alive); - } else { - if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| { - const target = Atom.parseRelocTarget(zld, .{ - .object_id = object_id, - .rel = rel, - .code = mem.asBytes(&record), - .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), - }); - const target_sym = zld.getSymbol(target); - if (!target_sym.undf()) { + while (inner_syms_it.next()) |sym| { + const record_id = object.unwind_records_lookup.get(sym) orelse continue; + if (object.unwind_relocs_lookup[record_id].dead) continue; // already marked, nothing to do + if (!alive.contains(atom_index)) { + // Mark the record dead and continue. + object.unwind_relocs_lookup[record_id].dead = true; + if (object.eh_frame_records_lookup.get(sym)) |fde_offset| { + object.eh_frame_relocs_lookup.getPtr(fde_offset).?.dead = true; + } + continue; + } + + const record = unwind_records[record_id]; + if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { + try markEhFrameRecords(zld, object_id, atom_index, alive); + } else { + if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| { + const target = Atom.parseRelocTarget(zld, .{ + .object_id = object_id, + .rel = rel, + .code = mem.asBytes(&record), + .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), + }); + const target_sym = zld.getSymbol(target); + if (!target_sym.undf()) { + const target_object = zld.objects.items[target.getFile().?]; + const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; + markLive(zld, target_atom_index, alive); + } + } + + if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| { + const target = Atom.parseRelocTarget(zld, .{ + .object_id = object_id, + .rel = rel, + .code = mem.asBytes(&record), + .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), + }); const target_object = zld.objects.items[target.getFile().?]; const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; markLive(zld, target_atom_index, alive); } } - - if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| { - const target = Atom.parseRelocTarget(zld, .{ - .object_id = object_id, - .rel = rel, - .code = mem.asBytes(&record), - .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), - }); - const target_object = zld.objects.items[target.getFile().?]; - const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; - markLive(zld, target_atom_index, alive); - } } } } -fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *AtomTable) !void { +fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *AtomTable) !void { const cpu_arch = zld.options.target.cpu.arch; const object = &zld.objects.items[object_id]; var it = object.getEhFrameRecordsIterator(); + var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); - const fde_offset = object.eh_frame_records_lookup.get(atom_index).?; - it.seekTo(fde_offset); - const fde = (try it.next()).?; + while (inner_syms_it.next()) |sym| { + const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias + it.seekTo(fde_offset); + const fde = (try it.next()).?; - const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset); - const cie_offset = fde_offset + 4 - cie_ptr; - it.seekTo(cie_offset); - const cie = (try it.next()).?; + const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset); + const cie_offset = fde_offset + 4 - cie_ptr; + it.seekTo(cie_offset); + const cie = (try it.next()).?; - switch (cpu_arch) { - .aarch64 => { - // Mark FDE references which should include any referenced LSDA record - const relocs = eh_frame.getRelocs(zld, object_id, fde_offset); - for (relocs) |rel| { - const target = Atom.parseRelocTarget(zld, .{ - .object_id = object_id, - .rel = rel, - .code = fde.data, - .base_offset = @as(i32, @intCast(fde_offset)) + 4, + switch (cpu_arch) { + .aarch64 => { + // Mark FDE references which should include any referenced LSDA record + const relocs = eh_frame.getRelocs(zld, object_id, fde_offset); + for (relocs) |rel| { + const target = Atom.parseRelocTarget(zld, .{ + .object_id = object_id, + .rel = rel, + .code = fde.data, + .base_offset = @as(i32, @intCast(fde_offset)) + 4, + }); + const target_sym = zld.getSymbol(target); + if (!target_sym.undf()) blk: { + const target_object = zld.objects.items[target.getFile().?]; + const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse + break :blk; + markLive(zld, target_atom_index, alive); + } + } + }, + .x86_64 => { + const sect = object.getSourceSection(object.eh_frame_sect_id.?); + const lsda_ptr = try fde.getLsdaPointer(cie, .{ + .base_addr = sect.addr, + .base_offset = fde_offset, }); - const target_sym = zld.getSymbol(target); - if (!target_sym.undf()) blk: { - const target_object = zld.objects.items[target.getFile().?]; - const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse - break :blk; + if (lsda_ptr) |lsda_address| { + // Mark LSDA record as live + const sym_index = object.getSymbolByAddress(lsda_address, null); + const target_atom_index = object.getAtomIndexForSymbol(sym_index).?; markLive(zld, target_atom_index, alive); } - } - }, - .x86_64 => { - const sect = object.getSourceSection(object.eh_frame_sect_id.?); - const lsda_ptr = try fde.getLsdaPointer(cie, .{ - .base_addr = sect.addr, - .base_offset = fde_offset, - }); - if (lsda_ptr) |lsda_address| { - // Mark LSDA record as live - const sym_index = object.getSymbolByAddress(lsda_address, null); - const target_atom_index = object.getAtomIndexForSymbol(sym_index).?; + }, + else => unreachable, + } + + // Mark CIE references which should include any referenced personalities + // that are defined locally. + if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| { + const target_sym = zld.getSymbol(target); + if (!target_sym.undf()) { + const target_object = zld.objects.items[target.getFile().?]; + const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; markLive(zld, target_atom_index, alive); } - }, - else => unreachable, - } - - // Mark CIE references which should include any referenced personalities - // that are defined locally. - if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| { - const target_sym = zld.getSymbol(target); - if (!target_sym.undf()) { - const target_object = zld.objects.items[target.getFile().?]; - const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; - markLive(zld, target_atom_index, alive); } } } @@ -458,8 +465,8 @@ fn prune(zld: *Zld, alive: AtomTable) void { section.last_atom_index = prev_index; } else { assert(section.header.size == 0); - section.first_atom_index = undefined; - section.last_atom_index = undefined; + section.first_atom_index = 0; + section.last_atom_index = 0; } } diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index 36c8a79403..9a30e863b9 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -24,19 +24,22 @@ pub fn scanRelocs(zld: *Zld) !void { var it = object.getEhFrameRecordsIterator(); for (object.exec_atoms.items) |atom_index| { - const fde_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue; - if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue; - it.seekTo(fde_offset); - const fde = (try it.next()).?; + var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); + while (inner_syms_it.next()) |sym| { + const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; + if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue; + it.seekTo(fde_offset); + const fde = (try it.next()).?; - const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset); - const cie_offset = fde_offset + 4 - cie_ptr; + const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset); + const cie_offset = fde_offset + 4 - cie_ptr; - if (!cies.contains(cie_offset)) { - try cies.putNoClobber(cie_offset, {}); - it.seekTo(cie_offset); - const cie = (try it.next()).?; - try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset); + if (!cies.contains(cie_offset)) { + try cies.putNoClobber(cie_offset, {}); + it.seekTo(cie_offset); + const cie = (try it.next()).?; + try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset); + } } } } @@ -59,35 +62,38 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void { var eh_it = object.getEhFrameRecordsIterator(); for (object.exec_atoms.items) |atom_index| { - const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue; - if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue; + var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); + while (inner_syms_it.next()) |sym| { + const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue; + if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue; - const record_id = unwind_info.records_lookup.get(atom_index) orelse continue; - const record = unwind_info.records.items[record_id]; + const record_id = unwind_info.records_lookup.get(sym) orelse continue; + const record = unwind_info.records.items[record_id]; - // TODO skip this check if no __compact_unwind is present - const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch); - if (!is_dwarf) continue; + // TODO skip this check if no __compact_unwind is present + const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch); + if (!is_dwarf) continue; - eh_it.seekTo(fde_record_offset); - const source_fde_record = (try eh_it.next()).?; + eh_it.seekTo(fde_record_offset); + const source_fde_record = (try eh_it.next()).?; - const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset); - const cie_offset = fde_record_offset + 4 - cie_ptr; + const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset); + const cie_offset = fde_record_offset + 4 - cie_ptr; - const gop = try cies.getOrPut(cie_offset); - if (!gop.found_existing) { - eh_it.seekTo(cie_offset); - const source_cie_record = (try eh_it.next()).?; - gop.value_ptr.* = size; - size += source_cie_record.getSize(); + const gop = try cies.getOrPut(cie_offset); + if (!gop.found_existing) { + eh_it.seekTo(cie_offset); + const source_cie_record = (try eh_it.next()).?; + gop.value_ptr.* = size; + size += source_cie_record.getSize(); + } + + size += source_fde_record.getSize(); } - - size += source_fde_record.getSize(); } - } - sect.size = size; + sect.size = size; + } } pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { @@ -118,97 +124,99 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { var eh_it = object.getEhFrameRecordsIterator(); for (object.exec_atoms.items) |atom_index| { - const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue; - if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue; + var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index); + while (inner_syms_it.next()) |target| { + const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue; + if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue; - const record_id = unwind_info.records_lookup.get(atom_index) orelse continue; - const record = &unwind_info.records.items[record_id]; + const record_id = unwind_info.records_lookup.get(target) orelse continue; + const record = &unwind_info.records.items[record_id]; - // TODO skip this check if no __compact_unwind is present - const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch); - if (!is_dwarf) continue; + // TODO skip this check if no __compact_unwind is present + const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch); + if (!is_dwarf) continue; - eh_it.seekTo(fde_record_offset); - const source_fde_record = (try eh_it.next()).?; + eh_it.seekTo(fde_record_offset); + const source_fde_record = (try eh_it.next()).?; - const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset); - const cie_offset = fde_record_offset + 4 - cie_ptr; + const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset); + const cie_offset = fde_record_offset + 4 - cie_ptr; - const gop = try cies.getOrPut(cie_offset); - if (!gop.found_existing) { - eh_it.seekTo(cie_offset); - const source_cie_record = (try eh_it.next()).?; - var cie_record = try source_cie_record.toOwned(gpa); - try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{ - .source_offset = cie_offset, + const gop = try cies.getOrPut(cie_offset); + if (!gop.found_existing) { + eh_it.seekTo(cie_offset); + const source_cie_record = (try eh_it.next()).?; + var cie_record = try source_cie_record.toOwned(gpa); + try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{ + .source_offset = cie_offset, + .out_offset = eh_frame_offset, + .sect_addr = sect.addr, + }); + eh_records.putAssumeCapacityNoClobber(eh_frame_offset, cie_record); + gop.value_ptr.* = eh_frame_offset; + eh_frame_offset += cie_record.getSize(); + } + + var fde_record = try source_fde_record.toOwned(gpa); + try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{ + .source_offset = fde_record_offset, .out_offset = eh_frame_offset, .sect_addr = sect.addr, }); - eh_records.putAssumeCapacityNoClobber(eh_frame_offset, cie_record); - gop.value_ptr.* = eh_frame_offset; - eh_frame_offset += cie_record.getSize(); - } + fde_record.setCiePointer(eh_frame_offset + 4 - gop.value_ptr.*); - var fde_record = try source_fde_record.toOwned(gpa); - try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{ - .source_offset = fde_record_offset, - .out_offset = eh_frame_offset, - .sect_addr = sect.addr, - }); - fde_record.setCiePointer(eh_frame_offset + 4 - gop.value_ptr.*); - - switch (cpu_arch) { - .aarch64 => {}, // relocs take care of LSDA pointers - .x86_64 => { - // We need to relocate target symbol address ourselves. - const atom = zld.getAtom(atom_index); - const atom_sym = zld.getSymbol(atom.getSymbolWithLoc()); - try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{ - .base_addr = sect.addr, - .base_offset = eh_frame_offset, - }); - - // We need to parse LSDA pointer and relocate ourselves. - const cie_record = eh_records.get( - eh_frame_offset + 4 - fde_record.getCiePointer(), - ).?; - const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?); - const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{ - .base_addr = eh_frame_sect.addr, - .base_offset = fde_record_offset, - }); - if (source_lsda_ptr) |ptr| { - const sym_index = object.getSymbolByAddress(ptr, null); - const sym = object.symtab[sym_index]; - try fde_record.setLsdaPointer(cie_record, sym.n_value, .{ + switch (cpu_arch) { + .aarch64 => {}, // relocs take care of LSDA pointers + .x86_64 => { + // We need to relocate target symbol address ourselves. + const atom_sym = zld.getSymbol(target); + try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{ .base_addr = sect.addr, .base_offset = eh_frame_offset, }); - } - }, - else => unreachable, + + // We need to parse LSDA pointer and relocate ourselves. + const cie_record = eh_records.get( + eh_frame_offset + 4 - fde_record.getCiePointer(), + ).?; + const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?); + const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{ + .base_addr = eh_frame_sect.addr, + .base_offset = fde_record_offset, + }); + if (source_lsda_ptr) |ptr| { + const sym_index = object.getSymbolByAddress(ptr, null); + const sym = object.symtab[sym_index]; + try fde_record.setLsdaPointer(cie_record, sym.n_value, .{ + .base_addr = sect.addr, + .base_offset = eh_frame_offset, + }); + } + }, + else => unreachable, + } + + eh_records.putAssumeCapacityNoClobber(eh_frame_offset, fde_record); + + UnwindInfo.UnwindEncoding.setDwarfSectionOffset( + &record.compactUnwindEncoding, + cpu_arch, + @as(u24, @intCast(eh_frame_offset)), + ); + + const cie_record = eh_records.get( + eh_frame_offset + 4 - fde_record.getCiePointer(), + ).?; + const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{ + .base_addr = sect.addr, + .base_offset = eh_frame_offset, + }); + if (lsda_ptr) |ptr| { + record.lsda = ptr - seg.vmaddr; + } + + eh_frame_offset += fde_record.getSize(); } - - eh_records.putAssumeCapacityNoClobber(eh_frame_offset, fde_record); - - UnwindInfo.UnwindEncoding.setDwarfSectionOffset( - &record.compactUnwindEncoding, - cpu_arch, - @as(u24, @intCast(eh_frame_offset)), - ); - - const cie_record = eh_records.get( - eh_frame_offset + 4 - fde_record.getCiePointer(), - ).?; - const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{ - .base_addr = sect.addr, - .base_offset = eh_frame_offset, - }); - if (lsda_ptr) |ptr| { - record.lsda = ptr - seg.vmaddr; - } - - eh_frame_offset += fde_record.getSize(); } } diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index 0ba55a217c..39b64fe178 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -1492,6 +1492,42 @@ pub const Zld = struct { try thunks.createThunks(self, @as(u8, @intCast(sect_id))); } } + + // Update offsets of all symbols contained within each Atom. + // We need to do this since our unwind info synthesiser relies on + // traversing the symbols when synthesising unwind info and DWARF CFI records. + for (slice.items(.first_atom_index)) |first_atom_index| { + if (first_atom_index == 0) continue; + var atom_index = first_atom_index; + + while (true) { + const atom = self.getAtom(atom_index); + const sym = self.getSymbol(atom.getSymbolWithLoc()); + + if (atom.getFile() != null) { + // Update each symbol contained within the atom + var it = Atom.getInnerSymbolsIterator(self, atom_index); + while (it.next()) |sym_loc| { + const inner_sym = self.getSymbolPtr(sym_loc); + inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset( + self, + atom_index, + sym_loc.sym_index, + ); + } + + // If there is a section alias, update it now too + if (Atom.getSectionAlias(self, atom_index)) |sym_loc| { + const alias = self.getSymbolPtr(sym_loc); + alias.n_value = sym.n_value; + } + } + + if (atom.next_index) |next_index| { + atom_index = next_index; + } else break; + } + } } fn allocateSegments(self: *Zld) !void { diff --git a/test/link.zig b/test/link.zig index f79b4a478c..56b1cf415d 100644 --- a/test/link.zig +++ b/test/link.zig @@ -96,6 +96,10 @@ pub const cases = [_]Case{ .build_root = "test/link/macho/bugs/16308", .import = @import("link/macho/bugs/16308/build.zig"), }, + .{ + .build_root = "test/link/macho/bugs/16628", + .import = @import("link/macho/bugs/16628/build.zig"), + }, .{ .build_root = "test/link/macho/dead_strip", .import = @import("link/macho/dead_strip/build.zig"), diff --git a/test/link/macho/bugs/16628/a_arm64.s b/test/link/macho/bugs/16628/a_arm64.s new file mode 100644 index 0000000000..215deb8514 --- /dev/null +++ b/test/link/macho/bugs/16628/a_arm64.s @@ -0,0 +1,37 @@ +.globl _foo +.align 4 +_foo: + .cfi_startproc + stp x29, x30, [sp, #-32]! + .cfi_def_cfa_offset 32 + .cfi_offset w30, -24 + .cfi_offset w29, -32 + mov x29, sp + .cfi_def_cfa w29, 32 + bl _bar + ldp x29, x30, [sp], #32 + .cfi_restore w29 + .cfi_restore w30 + .cfi_def_cfa_offset 0 + ret + .cfi_endproc + +.globl _bar +.align 4 +_bar: + .cfi_startproc + sub sp, sp, #32 + .cfi_def_cfa_offset -32 + stp x29, x30, [sp, #16] + .cfi_offset w30, -24 + .cfi_offset w29, -32 + mov x29, sp + .cfi_def_cfa w29, 32 + mov w0, #4 + ldp x29, x30, [sp, #16] + .cfi_restore w29 + .cfi_restore w30 + add sp, sp, #32 + .cfi_def_cfa_offset 0 + ret + .cfi_endproc diff --git a/test/link/macho/bugs/16628/a_x64.s b/test/link/macho/bugs/16628/a_x64.s new file mode 100644 index 0000000000..cc1712585e --- /dev/null +++ b/test/link/macho/bugs/16628/a_x64.s @@ -0,0 +1,29 @@ +.globl _foo +_foo: + .cfi_startproc + push %rbp + .cfi_def_cfa_offset 8 + .cfi_offset %rbp, -8 + mov %rsp, %rbp + .cfi_def_cfa_register %rbp + call _bar + pop %rbp + .cfi_restore %rbp + .cfi_def_cfa_offset 0 + ret + .cfi_endproc + +.globl _bar +_bar: + .cfi_startproc + push %rbp + .cfi_def_cfa_offset 8 + .cfi_offset %rbp, -8 + mov %rsp, %rbp + .cfi_def_cfa_register %rbp + mov $4, %rax + pop %rbp + .cfi_restore %rbp + .cfi_def_cfa_offset 0 + ret + .cfi_endproc diff --git a/test/link/macho/bugs/16628/build.zig b/test/link/macho/bugs/16628/build.zig new file mode 100644 index 0000000000..aa396f7a3b --- /dev/null +++ b/test/link/macho/bugs/16628/build.zig @@ -0,0 +1,42 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +pub const requires_symlinks = true; +pub const requires_macos_sdk = false; + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + add(b, test_step, .Debug); + add(b, test_step, .ReleaseFast); + add(b, test_step, .ReleaseSmall); + add(b, test_step, .ReleaseSafe); +} + +fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { + const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + + const exe = b.addExecutable(.{ + .name = "test", + .optimize = optimize, + .target = target, + }); + exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} }); + switch (builtin.cpu.arch) { + .aarch64 => { + exe.addCSourceFile(.{ .file = .{ .path = "a_arm64.s" }, .flags = &[0][]const u8{} }); + }, + .x86_64 => { + exe.addCSourceFile(.{ .file = .{ .path = "a_x64.s" }, .flags = &[0][]const u8{} }); + }, + else => unreachable, + } + exe.linkLibC(); + + const run = b.addRunArtifact(exe); + run.skip_foreign_checks = true; + run.expectStdOutEqual("4\n"); + + test_step.dependOn(&run.step); +} diff --git a/test/link/macho/bugs/16628/main.c b/test/link/macho/bugs/16628/main.c new file mode 100644 index 0000000000..3ac8481dca --- /dev/null +++ b/test/link/macho/bugs/16628/main.c @@ -0,0 +1,8 @@ +#include + +int foo(); + +int main() { + printf("%d\n", foo()); + return 0; +}