authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-02 22:28:02-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-02 22:28:02-07:00
loge85a46cb84c80a7a97316f066b77e45649ce97fb
tree5209cbbcaa201fde6b74bc3f89eda77174be7d02
parenta61495a988c9e1a68ee595cafab5025a314f0e40
parent26adbf2cb7377730b63a0ef182a402060eb732d3
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16656 from ziglang/issue-16628

macho: track unwind/dwarf cfi records by symbol rather than atom

11 files changed, 525 insertions(+), 334 deletions(-)

src/link/MachO/Object.zig+23-19
......@@ -75,11 +75,11 @@ exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{},
7575
7676eh_frame_sect_id: ?u8 = null,
7777eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{},
78eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{},
78eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},
7979
8080unwind_info_sect_id: ?u8 = null,
8181unwind_relocs_lookup: []Record = undefined,
82unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{},
82unwind_records_lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
8383
8484const Entry = struct {
8585 start: u32 = 0,
......@@ -274,11 +274,11 @@ const SymbolAtIndex = struct {
274274 const sym = self.getSymbol(ctx);
275275 if (!sym.ext()) {
276276 const sym_name = self.getSymbolName(ctx);
277 if (mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L")) return 0;
278 return 1;
277 if (mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L")) return 3;
278 return 2;
279279 }
280 if (sym.weakDef() or sym.pext()) return 2;
281 return 3;
280 if (sym.weakDef() or sym.pext()) return 1;
281 return 0;
282282 }
283283
284284 /// Performs lexicographic-like check.
......@@ -423,8 +423,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
423423 zld,
424424 object_id,
425425 sym_index,
426 0,
427 0,
426 sym_index,
427 1,
428428 sect.size,
429429 sect.@"align",
430430 out_sect_id,
......@@ -502,8 +502,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
502502 zld,
503503 object_id,
504504 sym_index,
505 0,
506 0,
505 sym_index,
506 1,
507507 atom_size,
508508 sect.@"align",
509509 out_sect_id,
......@@ -521,7 +521,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
521521 const atom_loc = filterSymbolsByAddress(symtab[next_sym_index..], addr, addr + 1);
522522 assert(atom_loc.len > 0);
523523 const atom_sym_index = atom_loc.index + next_sym_index;
524 const nsyms_trailing = atom_loc.len - 1;
524 const nsyms_trailing = atom_loc.len;
525525 next_sym_index += atom_loc.len;
526526
527527 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 {
538538 zld,
539539 object_id,
540540 atom_sym_index,
541 atom_sym_index + 1,
541 atom_sym_index,
542542 nsyms_trailing,
543543 atom_size,
544544 atom_align,
......@@ -772,8 +772,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
772772 if (target.getFile() != object_id) {
773773 self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true;
774774 } else {
775 const atom_index = self.getAtomIndexForSymbol(target.sym_index).?;
776 self.eh_frame_records_lookup.putAssumeCapacityNoClobber(atom_index, offset);
775 self.eh_frame_records_lookup.putAssumeCapacityNoClobber(target, offset);
777776 }
778777 }
779778 }
......@@ -802,10 +801,10 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
802801 _ = try zld.initSection("__TEXT", "__unwind_info", .{});
803802 }
804803
805 try self.unwind_records_lookup.ensureTotalCapacity(gpa, @as(u32, @intCast(self.exec_atoms.items.len)));
806
807804 const unwind_records = self.getUnwindRecords();
808805
806 try self.unwind_records_lookup.ensureTotalCapacity(gpa, @as(u32, @intCast(unwind_records.len)));
807
809808 const needs_eh_frame = for (unwind_records) |record| {
810809 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
811810 } else false;
......@@ -844,8 +843,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
844843 if (target.getFile() != object_id) {
845844 self.unwind_relocs_lookup[record_id].dead = true;
846845 } else {
847 const atom_index = self.getAtomIndexForSymbol(target.sym_index).?;
848 self.unwind_records_lookup.putAssumeCapacityNoClobber(atom_index, @as(u32, @intCast(record_id)));
846 self.unwind_records_lookup.putAssumeCapacityNoClobber(target, @as(u32, @intCast(record_id)));
849847 }
850848 }
851849}
......@@ -1012,7 +1010,13 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
10121010 Predicate{ .addr = @as(i64, @intCast(addr)) },
10131011 );
10141012 if (target_sym_index > 0) {
1015 return @as(u32, @intCast(lookup.start + target_sym_index - 1));
1013 // Hone in on the most senior alias of the target symbol.
1014 // See SymbolAtIndex.lessThan for more context.
1015 var start = target_sym_index - 1;
1016 while (start > 0 and
1017 self.source_address_lookup[lookup.start..][start - 1] == addr) : (start -= 1)
1018 {}
1019 return @as(u32, @intCast(lookup.start + start));
10161020 }
10171021 }
10181022 return self.getSectionAliasSymbolIndex(sect_id);
src/link/MachO/UnwindInfo.zig+109-89
......@@ -26,7 +26,7 @@ gpa: Allocator,
2626/// List of all unwind records gathered from all objects and sorted
2727/// by source function address.
2828records: std.ArrayListUnmanaged(macho.compact_unwind_entry) = .{},
29records_lookup: std.AutoHashMapUnmanaged(AtomIndex, RecordIndex) = .{},
29records_lookup: std.AutoHashMapUnmanaged(SymbolWithLoc, RecordIndex) = .{},
3030
3131/// List of all personalities referenced by either unwind info entries
3232/// or __eh_frame entries.
......@@ -211,23 +211,22 @@ pub fn scanRelocs(zld: *Zld) !void {
211211 for (zld.objects.items, 0..) |*object, object_id| {
212212 const unwind_records = object.getUnwindRecords();
213213 for (object.exec_atoms.items) |atom_index| {
214 const record_id = object.unwind_records_lookup.get(atom_index) orelse continue;
215 if (object.unwind_relocs_lookup[record_id].dead) continue;
216 const record = unwind_records[record_id];
217 if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
218 if (getPersonalityFunctionReloc(
219 zld,
220 @as(u32, @intCast(object_id)),
221 record_id,
222 )) |rel| {
223 // Personality function; add GOT pointer.
224 const target = Atom.parseRelocTarget(zld, .{
225 .object_id = @as(u32, @intCast(object_id)),
226 .rel = rel,
227 .code = mem.asBytes(&record),
228 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
229 });
230 try Atom.addGotEntry(zld, target);
214 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
215 while (inner_syms_it.next()) |sym| {
216 const record_id = object.unwind_records_lookup.get(sym) orelse continue;
217 if (object.unwind_relocs_lookup[record_id].dead) continue;
218 const record = unwind_records[record_id];
219 if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
220 if (getPersonalityFunctionReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
221 // Personality function; add GOT pointer.
222 const target = Atom.parseRelocTarget(zld, .{
223 .object_id = @as(u32, @intCast(object_id)),
224 .rel = rel,
225 .code = mem.asBytes(&record),
226 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
227 });
228 try Atom.addGotEntry(zld, target);
229 }
231230 }
232231 }
233232 }
......@@ -242,8 +241,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
242241 var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);
243242 defer records.deinit();
244243
245 var atom_indexes = std.ArrayList(AtomIndex).init(info.gpa);
246 defer atom_indexes.deinit();
244 var sym_indexes = std.ArrayList(SymbolWithLoc).init(info.gpa);
245 defer sym_indexes.deinit();
247246
248247 // TODO handle dead stripping
249248 for (zld.objects.items, 0..) |*object, object_id| {
......@@ -253,80 +252,101 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
253252 // Contents of unwind records does not have to cover all symbol in executable section
254253 // so we need insert them ourselves.
255254 try records.ensureUnusedCapacity(object.exec_atoms.items.len);
256 try atom_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
255 try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
257256
258257 for (object.exec_atoms.items) |atom_index| {
259 var record = if (object.unwind_records_lookup.get(atom_index)) |record_id| blk: {
260 if (object.unwind_relocs_lookup[record_id].dead) continue;
261 var record = unwind_records[record_id];
258 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
259 var prev_symbol: ?SymbolWithLoc = null;
260 while (inner_syms_it.next()) |symbol| {
261 var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: {
262 if (object.unwind_relocs_lookup[record_id].dead) continue;
263 var record = unwind_records[record_id];
264
265 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
266 try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
267 } else {
268 if (getPersonalityFunctionReloc(
269 zld,
270 @as(u32, @intCast(object_id)),
271 record_id,
272 )) |rel| {
273 const target = Atom.parseRelocTarget(zld, .{
274 .object_id = @as(u32, @intCast(object_id)),
275 .rel = rel,
276 .code = mem.asBytes(&record),
277 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
278 });
279 const personality_index = info.getPersonalityFunction(target) orelse inner: {
280 const personality_index = info.personalities_count;
281 info.personalities[personality_index] = target;
282 info.personalities_count += 1;
283 break :inner personality_index;
284 };
285
286 record.personalityFunction = personality_index + 1;
287 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
288 }
262289
263 if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
264 try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), atom_index, &record);
265 } else {
266 if (getPersonalityFunctionReloc(
267 zld,
268 @as(u32, @intCast(object_id)),
269 record_id,
270 )) |rel| {
271 const target = Atom.parseRelocTarget(zld, .{
272 .object_id = @as(u32, @intCast(object_id)),
273 .rel = rel,
274 .code = mem.asBytes(&record),
275 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
276 });
277 const personality_index = info.getPersonalityFunction(target) orelse inner: {
278 const personality_index = info.personalities_count;
279 info.personalities[personality_index] = target;
280 info.personalities_count += 1;
281 break :inner personality_index;
282 };
283
284 record.personalityFunction = personality_index + 1;
285 UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
290 if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
291 const target = Atom.parseRelocTarget(zld, .{
292 .object_id = @as(u32, @intCast(object_id)),
293 .rel = rel,
294 .code = mem.asBytes(&record),
295 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
296 });
297 record.lsda = @as(u64, @bitCast(target));
298 }
286299 }
287
288 if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
289 const target = Atom.parseRelocTarget(zld, .{
290 .object_id = @as(u32, @intCast(object_id)),
291 .rel = rel,
292 .code = mem.asBytes(&record),
293 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
294 });
295 record.lsda = @as(u64, @bitCast(target));
300 break :blk record;
301 } else blk: {
302 const sym = zld.getSymbol(symbol);
303 if (sym.n_desc == N_DEAD) continue;
304 if (prev_symbol) |prev_sym| {
305 const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value;
306 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
307 if (prev_addr == curr_addr) continue;
296308 }
297 }
298 break :blk record;
299 } else blk: {
300 const atom = zld.getAtom(atom_index);
301 const sym = zld.getSymbol(atom.getSymbolWithLoc());
302 if (sym.n_desc == N_DEAD) continue;
303
304 if (!object.hasUnwindRecords()) {
305 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
306 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
307 var record = nullRecord();
308 try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), atom_index, &record);
309 switch (cpu_arch) {
310 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
311 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
312 else => unreachable,
309
310 if (!object.hasUnwindRecords()) {
311 if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| {
312 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
313 var record = nullRecord();
314 try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
315 switch (cpu_arch) {
316 .aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
317 .x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
318 else => unreachable,
319 }
320 break :blk record;
313321 }
314 break :blk record;
315322 }
316 }
317
318 break :blk nullRecord();
319 };
320323
321 const atom = zld.getAtom(atom_index);
322 const sym_loc = atom.getSymbolWithLoc();
323 const sym = zld.getSymbol(sym_loc);
324 assert(sym.n_desc != N_DEAD);
325 record.rangeStart = sym.n_value;
326 record.rangeLength = @as(u32, @intCast(atom.size));
324 break :blk nullRecord();
325 };
327326
328 records.appendAssumeCapacity(record);
329 atom_indexes.appendAssumeCapacity(atom_index);
327 const atom = zld.getAtom(atom_index);
328 const sym = zld.getSymbol(symbol);
329 assert(sym.n_desc != N_DEAD);
330 const size = if (inner_syms_it.next()) |next_sym| blk: {
331 // All this trouble to account for symbol aliases.
332 // TODO I think that remodelling the linker so that a Symbol references an Atom
333 // is the way to go, kinda like we do for ELF. We might also want to perhaps tag
334 // symbol aliases somehow so that they are excluded from everything except relocation
335 // resolution.
336 defer inner_syms_it.pos -= 1;
337 const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
338 const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value;
339 if (next_addr > curr_addr) break :blk next_addr - curr_addr;
340 break :blk zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
341 } else zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
342 record.rangeStart = sym.n_value;
343 record.rangeLength = @as(u32, @intCast(size));
344
345 try records.append(record);
346 try sym_indexes.append(symbol);
347
348 prev_symbol = symbol;
349 }
330350 }
331351 }
332352
......@@ -339,7 +359,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
339359
340360 // Fold records
341361 try info.records.ensureTotalCapacity(info.gpa, records.items.len);
342 try info.records_lookup.ensureTotalCapacity(info.gpa, @as(u32, @intCast(atom_indexes.items.len)));
362 try info.records_lookup.ensureTotalCapacity(info.gpa, @as(u32, @intCast(sym_indexes.items.len)));
343363
344364 var maybe_prev: ?macho.compact_unwind_entry = null;
345365 for (records.items, 0..) |record, i| {
......@@ -365,7 +385,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
365385 break :blk record_id;
366386 }
367387 };
368 info.records_lookup.putAssumeCapacityNoClobber(atom_indexes.items[i], record_id);
388 info.records_lookup.putAssumeCapacityNoClobber(sym_indexes.items[i], record_id);
369389 }
370390
371391 // Calculate common encodings
......@@ -501,12 +521,12 @@ fn collectPersonalityFromDwarf(
501521 info: *UnwindInfo,
502522 zld: *Zld,
503523 object_id: u32,
504 atom_index: u32,
524 sym_loc: SymbolWithLoc,
505525 record: *macho.compact_unwind_entry,
506526) !void {
507527 const object = &zld.objects.items[object_id];
508528 var it = object.getEhFrameRecordsIterator();
509 const fde_offset = object.eh_frame_records_lookup.get(atom_index).?;
529 const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?;
510530 it.seekTo(fde_offset);
511531 const fde = (try it.next()).?;
512532 const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
src/link/MachO/ZldAtom.zig+7-11
......@@ -84,14 +84,14 @@ pub inline fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
8484
8585const InnerSymIterator = struct {
8686 sym_index: u32,
87 count: u32,
87 nsyms: u32,
8888 file: u32,
89 pos: u32 = 0,
8990
9091 pub fn next(it: *@This()) ?SymbolWithLoc {
91 if (it.count == 0) return null;
92 const res = SymbolWithLoc{ .sym_index = it.sym_index, .file = it.file };
93 it.sym_index += 1;
94 it.count -= 1;
92 if (it.pos == it.nsyms) return null;
93 const res = SymbolWithLoc{ .sym_index = it.sym_index + it.pos, .file = it.file };
94 it.pos += 1;
9595 return res;
9696 }
9797};
......@@ -103,7 +103,7 @@ pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: AtomIndex) InnerSymIterato
103103 assert(atom.getFile() != null);
104104 return .{
105105 .sym_index = atom.inner_sym_index,
106 .count = atom.inner_nsyms_trailing,
106 .nsyms = atom.inner_nsyms_trailing,
107107 .file = atom.file,
108108 };
109109}
......@@ -228,11 +228,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
228228
229229 // Find containing atom
230230 log.debug(" | locating symbol by address @{x} in section {d}", .{ address_in_section, sect_id });
231 const candidate = object.getSymbolByAddress(address_in_section, sect_id);
232 // Make sure we are not dealing with a local alias.
233 const atom_index = object.getAtomIndexForSymbol(candidate) orelse break :sym_index candidate;
234 const atom = zld.getAtom(atom_index);
235 break :sym_index atom.sym_index;
231 break :sym_index object.getSymbolByAddress(address_in_section, sect_id);
236232 } else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
237233
238234 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };
src/link/MachO/dead_strip.zig+103-96
......@@ -294,124 +294,131 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
294294 const unwind_records = object.getUnwindRecords();
295295
296296 for (object.exec_atoms.items) |atom_index| {
297 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
298
297299 if (!object.hasUnwindRecords()) {
298 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
299 const ptr = object.eh_frame_relocs_lookup.getPtr(fde_offset).?;
300 if (ptr.dead) continue; // already marked
301 if (!alive.contains(atom_index)) {
302 // Mark dead and continue.
303 ptr.dead = true;
304 } else {
305 // Mark references live and continue.
306 try markEhFrameRecord(zld, object_id, atom_index, alive);
300 if (alive.contains(atom_index)) {
301 // Mark references live and continue.
302 try markEhFrameRecords(zld, object_id, atom_index, alive);
303 } else {
304 while (inner_syms_it.next()) |sym| {
305 if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {
306 // Mark dead and continue.
307 object.eh_frame_relocs_lookup.getPtr(fde_offset).?.dead = true;
308 }
307309 }
308 continue;
309310 }
311 continue;
310312 }
311313
312 const record_id = object.unwind_records_lookup.get(atom_index) orelse continue;
313 if (object.unwind_relocs_lookup[record_id].dead) continue; // already marked, nothing to do
314 if (!alive.contains(atom_index)) {
315 // Mark the record dead and continue.
316 object.unwind_relocs_lookup[record_id].dead = true;
317 if (object.eh_frame_records_lookup.get(atom_index)) |fde_offset| {
318 object.eh_frame_relocs_lookup.getPtr(fde_offset).?.dead = true;
314 while (inner_syms_it.next()) |sym| {
315 const record_id = object.unwind_records_lookup.get(sym) orelse continue;
316 if (object.unwind_relocs_lookup[record_id].dead) continue; // already marked, nothing to do
317 if (!alive.contains(atom_index)) {
318 // Mark the record dead and continue.
319 object.unwind_relocs_lookup[record_id].dead = true;
320 if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {
321 object.eh_frame_relocs_lookup.getPtr(fde_offset).?.dead = true;
322 }
323 continue;
319324 }
320 continue;
321 }
322325
323 const record = unwind_records[record_id];
324 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
325 try markEhFrameRecord(zld, object_id, atom_index, alive);
326 } else {
327 if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {
328 const target = Atom.parseRelocTarget(zld, .{
329 .object_id = object_id,
330 .rel = rel,
331 .code = mem.asBytes(&record),
332 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
333 });
334 const target_sym = zld.getSymbol(target);
335 if (!target_sym.undf()) {
326 const record = unwind_records[record_id];
327 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
328 try markEhFrameRecords(zld, object_id, atom_index, alive);
329 } else {
330 if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {
331 const target = Atom.parseRelocTarget(zld, .{
332 .object_id = object_id,
333 .rel = rel,
334 .code = mem.asBytes(&record),
335 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
336 });
337 const target_sym = zld.getSymbol(target);
338 if (!target_sym.undf()) {
339 const target_object = zld.objects.items[target.getFile().?];
340 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
341 markLive(zld, target_atom_index, alive);
342 }
343 }
344
345 if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {
346 const target = Atom.parseRelocTarget(zld, .{
347 .object_id = object_id,
348 .rel = rel,
349 .code = mem.asBytes(&record),
350 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
351 });
336352 const target_object = zld.objects.items[target.getFile().?];
337353 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
338354 markLive(zld, target_atom_index, alive);
339355 }
340356 }
341
342 if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {
343 const target = Atom.parseRelocTarget(zld, .{
344 .object_id = object_id,
345 .rel = rel,
346 .code = mem.asBytes(&record),
347 .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
348 });
349 const target_object = zld.objects.items[target.getFile().?];
350 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
351 markLive(zld, target_atom_index, alive);
352 }
353357 }
354358 }
355359}
356360
357fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *AtomTable) !void {
361fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *AtomTable) !void {
358362 const cpu_arch = zld.options.target.cpu.arch;
359363 const object = &zld.objects.items[object_id];
360364 var it = object.getEhFrameRecordsIterator();
361
362 const fde_offset = object.eh_frame_records_lookup.get(atom_index).?;
363 it.seekTo(fde_offset);
364 const fde = (try it.next()).?;
365
366 const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
367 const cie_offset = fde_offset + 4 - cie_ptr;
368 it.seekTo(cie_offset);
369 const cie = (try it.next()).?;
370
371 switch (cpu_arch) {
372 .aarch64 => {
373 // Mark FDE references which should include any referenced LSDA record
374 const relocs = eh_frame.getRelocs(zld, object_id, fde_offset);
375 for (relocs) |rel| {
376 const target = Atom.parseRelocTarget(zld, .{
377 .object_id = object_id,
378 .rel = rel,
379 .code = fde.data,
380 .base_offset = @as(i32, @intCast(fde_offset)) + 4,
365 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
366
367 while (inner_syms_it.next()) |sym| {
368 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias
369 it.seekTo(fde_offset);
370 const fde = (try it.next()).?;
371
372 const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
373 const cie_offset = fde_offset + 4 - cie_ptr;
374 it.seekTo(cie_offset);
375 const cie = (try it.next()).?;
376
377 switch (cpu_arch) {
378 .aarch64 => {
379 // Mark FDE references which should include any referenced LSDA record
380 const relocs = eh_frame.getRelocs(zld, object_id, fde_offset);
381 for (relocs) |rel| {
382 const target = Atom.parseRelocTarget(zld, .{
383 .object_id = object_id,
384 .rel = rel,
385 .code = fde.data,
386 .base_offset = @as(i32, @intCast(fde_offset)) + 4,
387 });
388 const target_sym = zld.getSymbol(target);
389 if (!target_sym.undf()) blk: {
390 const target_object = zld.objects.items[target.getFile().?];
391 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
392 break :blk;
393 markLive(zld, target_atom_index, alive);
394 }
395 }
396 },
397 .x86_64 => {
398 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
399 const lsda_ptr = try fde.getLsdaPointer(cie, .{
400 .base_addr = sect.addr,
401 .base_offset = fde_offset,
381402 });
382 const target_sym = zld.getSymbol(target);
383 if (!target_sym.undf()) blk: {
384 const target_object = zld.objects.items[target.getFile().?];
385 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
386 break :blk;
403 if (lsda_ptr) |lsda_address| {
404 // Mark LSDA record as live
405 const sym_index = object.getSymbolByAddress(lsda_address, null);
406 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
387407 markLive(zld, target_atom_index, alive);
388408 }
389 }
390 },
391 .x86_64 => {
392 const sect = object.getSourceSection(object.eh_frame_sect_id.?);
393 const lsda_ptr = try fde.getLsdaPointer(cie, .{
394 .base_addr = sect.addr,
395 .base_offset = fde_offset,
396 });
397 if (lsda_ptr) |lsda_address| {
398 // Mark LSDA record as live
399 const sym_index = object.getSymbolByAddress(lsda_address, null);
400 const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
409 },
410 else => unreachable,
411 }
412
413 // Mark CIE references which should include any referenced personalities
414 // that are defined locally.
415 if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {
416 const target_sym = zld.getSymbol(target);
417 if (!target_sym.undf()) {
418 const target_object = zld.objects.items[target.getFile().?];
419 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
401420 markLive(zld, target_atom_index, alive);
402421 }
403 },
404 else => unreachable,
405 }
406
407 // Mark CIE references which should include any referenced personalities
408 // that are defined locally.
409 if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {
410 const target_sym = zld.getSymbol(target);
411 if (!target_sym.undf()) {
412 const target_object = zld.objects.items[target.getFile().?];
413 const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
414 markLive(zld, target_atom_index, alive);
415422 }
416423 }
417424}
......@@ -458,8 +465,8 @@ fn prune(zld: *Zld, alive: AtomTable) void {
458465 section.last_atom_index = prev_index;
459466 } else {
460467 assert(section.header.size == 0);
461 section.first_atom_index = undefined;
462 section.last_atom_index = undefined;
468 section.first_atom_index = 0;
469 section.last_atom_index = 0;
463470 }
464471 }
465472
src/link/MachO/eh_frame.zig+127-119
......@@ -24,19 +24,22 @@ pub fn scanRelocs(zld: *Zld) !void {
2424 var it = object.getEhFrameRecordsIterator();
2525
2626 for (object.exec_atoms.items) |atom_index| {
27 const fde_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
28 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
29 it.seekTo(fde_offset);
30 const fde = (try it.next()).?;
31
32 const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset);
33 const cie_offset = fde_offset + 4 - cie_ptr;
34
35 if (!cies.contains(cie_offset)) {
36 try cies.putNoClobber(cie_offset, {});
37 it.seekTo(cie_offset);
38 const cie = (try it.next()).?;
39 try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset);
27 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
28 while (inner_syms_it.next()) |sym| {
29 const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
30 if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
31 it.seekTo(fde_offset);
32 const fde = (try it.next()).?;
33
34 const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset);
35 const cie_offset = fde_offset + 4 - cie_ptr;
36
37 if (!cies.contains(cie_offset)) {
38 try cies.putNoClobber(cie_offset, {});
39 it.seekTo(cie_offset);
40 const cie = (try it.next()).?;
41 try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset);
42 }
4043 }
4144 }
4245 }
......@@ -59,35 +62,38 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
5962 var eh_it = object.getEhFrameRecordsIterator();
6063
6164 for (object.exec_atoms.items) |atom_index| {
62 const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
63 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
64
65 const record_id = unwind_info.records_lookup.get(atom_index) orelse continue;
66 const record = unwind_info.records.items[record_id];
67
68 // TODO skip this check if no __compact_unwind is present
69 const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
70 if (!is_dwarf) continue;
71
72 eh_it.seekTo(fde_record_offset);
73 const source_fde_record = (try eh_it.next()).?;
74
75 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
76 const cie_offset = fde_record_offset + 4 - cie_ptr;
65 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
66 while (inner_syms_it.next()) |sym| {
67 const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
68 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
69
70 const record_id = unwind_info.records_lookup.get(sym) orelse continue;
71 const record = unwind_info.records.items[record_id];
72
73 // TODO skip this check if no __compact_unwind is present
74 const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
75 if (!is_dwarf) continue;
76
77 eh_it.seekTo(fde_record_offset);
78 const source_fde_record = (try eh_it.next()).?;
79
80 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
81 const cie_offset = fde_record_offset + 4 - cie_ptr;
82
83 const gop = try cies.getOrPut(cie_offset);
84 if (!gop.found_existing) {
85 eh_it.seekTo(cie_offset);
86 const source_cie_record = (try eh_it.next()).?;
87 gop.value_ptr.* = size;
88 size += source_cie_record.getSize();
89 }
7790
78 const gop = try cies.getOrPut(cie_offset);
79 if (!gop.found_existing) {
80 eh_it.seekTo(cie_offset);
81 const source_cie_record = (try eh_it.next()).?;
82 gop.value_ptr.* = size;
83 size += source_cie_record.getSize();
91 size += source_fde_record.getSize();
8492 }
85
86 size += source_fde_record.getSize();
8793 }
88 }
8994
90 sect.size = size;
95 sect.size = size;
96 }
9197}
9298
9399pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
......@@ -118,97 +124,99 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
118124 var eh_it = object.getEhFrameRecordsIterator();
119125
120126 for (object.exec_atoms.items) |atom_index| {
121 const fde_record_offset = object.eh_frame_records_lookup.get(atom_index) orelse continue;
122 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
123
124 const record_id = unwind_info.records_lookup.get(atom_index) orelse continue;
125 const record = &unwind_info.records.items[record_id];
126
127 // TODO skip this check if no __compact_unwind is present
128 const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
129 if (!is_dwarf) continue;
130
131 eh_it.seekTo(fde_record_offset);
132 const source_fde_record = (try eh_it.next()).?;
133
134 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
135 const cie_offset = fde_record_offset + 4 - cie_ptr;
127 var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
128 while (inner_syms_it.next()) |target| {
129 const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;
130 if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
131
132 const record_id = unwind_info.records_lookup.get(target) orelse continue;
133 const record = &unwind_info.records.items[record_id];
134
135 // TODO skip this check if no __compact_unwind is present
136 const is_dwarf = UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch);
137 if (!is_dwarf) continue;
138
139 eh_it.seekTo(fde_record_offset);
140 const source_fde_record = (try eh_it.next()).?;
141
142 const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
143 const cie_offset = fde_record_offset + 4 - cie_ptr;
144
145 const gop = try cies.getOrPut(cie_offset);
146 if (!gop.found_existing) {
147 eh_it.seekTo(cie_offset);
148 const source_cie_record = (try eh_it.next()).?;
149 var cie_record = try source_cie_record.toOwned(gpa);
150 try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{
151 .source_offset = cie_offset,
152 .out_offset = eh_frame_offset,
153 .sect_addr = sect.addr,
154 });
155 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, cie_record);
156 gop.value_ptr.* = eh_frame_offset;
157 eh_frame_offset += cie_record.getSize();
158 }
136159
137 const gop = try cies.getOrPut(cie_offset);
138 if (!gop.found_existing) {
139 eh_it.seekTo(cie_offset);
140 const source_cie_record = (try eh_it.next()).?;
141 var cie_record = try source_cie_record.toOwned(gpa);
142 try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{
143 .source_offset = cie_offset,
160 var fde_record = try source_fde_record.toOwned(gpa);
161 try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{
162 .source_offset = fde_record_offset,
144163 .out_offset = eh_frame_offset,
145164 .sect_addr = sect.addr,
146165 });
147 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, cie_record);
148 gop.value_ptr.* = eh_frame_offset;
149 eh_frame_offset += cie_record.getSize();
150 }
151
152 var fde_record = try source_fde_record.toOwned(gpa);
153 try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{
154 .source_offset = fde_record_offset,
155 .out_offset = eh_frame_offset,
156 .sect_addr = sect.addr,
157 });
158 fde_record.setCiePointer(eh_frame_offset + 4 - gop.value_ptr.*);
159
160 switch (cpu_arch) {
161 .aarch64 => {}, // relocs take care of LSDA pointers
162 .x86_64 => {
163 // We need to relocate target symbol address ourselves.
164 const atom = zld.getAtom(atom_index);
165 const atom_sym = zld.getSymbol(atom.getSymbolWithLoc());
166 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
167 .base_addr = sect.addr,
168 .base_offset = eh_frame_offset,
169 });
166 fde_record.setCiePointer(eh_frame_offset + 4 - gop.value_ptr.*);
170167
171 // We need to parse LSDA pointer and relocate ourselves.
172 const cie_record = eh_records.get(
173 eh_frame_offset + 4 - fde_record.getCiePointer(),
174 ).?;
175 const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);
176 const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
177 .base_addr = eh_frame_sect.addr,
178 .base_offset = fde_record_offset,
179 });
180 if (source_lsda_ptr) |ptr| {
181 const sym_index = object.getSymbolByAddress(ptr, null);
182 const sym = object.symtab[sym_index];
183 try fde_record.setLsdaPointer(cie_record, sym.n_value, .{
168 switch (cpu_arch) {
169 .aarch64 => {}, // relocs take care of LSDA pointers
170 .x86_64 => {
171 // We need to relocate target symbol address ourselves.
172 const atom_sym = zld.getSymbol(target);
173 try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
184174 .base_addr = sect.addr,
185175 .base_offset = eh_frame_offset,
186176 });
187 }
188 },
189 else => unreachable,
190 }
191177
192 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, fde_record);
193
194 UnwindInfo.UnwindEncoding.setDwarfSectionOffset(
195 &record.compactUnwindEncoding,
196 cpu_arch,
197 @as(u24, @intCast(eh_frame_offset)),
198 );
199
200 const cie_record = eh_records.get(
201 eh_frame_offset + 4 - fde_record.getCiePointer(),
202 ).?;
203 const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
204 .base_addr = sect.addr,
205 .base_offset = eh_frame_offset,
206 });
207 if (lsda_ptr) |ptr| {
208 record.lsda = ptr - seg.vmaddr;
209 }
178 // We need to parse LSDA pointer and relocate ourselves.
179 const cie_record = eh_records.get(
180 eh_frame_offset + 4 - fde_record.getCiePointer(),
181 ).?;
182 const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?);
183 const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
184 .base_addr = eh_frame_sect.addr,
185 .base_offset = fde_record_offset,
186 });
187 if (source_lsda_ptr) |ptr| {
188 const sym_index = object.getSymbolByAddress(ptr, null);
189 const sym = object.symtab[sym_index];
190 try fde_record.setLsdaPointer(cie_record, sym.n_value, .{
191 .base_addr = sect.addr,
192 .base_offset = eh_frame_offset,
193 });
194 }
195 },
196 else => unreachable,
197 }
198
199 eh_records.putAssumeCapacityNoClobber(eh_frame_offset, fde_record);
200
201 UnwindInfo.UnwindEncoding.setDwarfSectionOffset(
202 &record.compactUnwindEncoding,
203 cpu_arch,
204 @as(u24, @intCast(eh_frame_offset)),
205 );
210206
211 eh_frame_offset += fde_record.getSize();
207 const cie_record = eh_records.get(
208 eh_frame_offset + 4 - fde_record.getCiePointer(),
209 ).?;
210 const lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{
211 .base_addr = sect.addr,
212 .base_offset = eh_frame_offset,
213 });
214 if (lsda_ptr) |ptr| {
215 record.lsda = ptr - seg.vmaddr;
216 }
217
218 eh_frame_offset += fde_record.getSize();
219 }
212220 }
213221 }
214222
src/link/MachO/zld.zig+36
......@@ -1492,6 +1492,42 @@ pub const Zld = struct {
14921492 try thunks.createThunks(self, @as(u8, @intCast(sect_id)));
14931493 }
14941494 }
1495
1496 // Update offsets of all symbols contained within each Atom.
1497 // We need to do this since our unwind info synthesiser relies on
1498 // traversing the symbols when synthesising unwind info and DWARF CFI records.
1499 for (slice.items(.first_atom_index)) |first_atom_index| {
1500 if (first_atom_index == 0) continue;
1501 var atom_index = first_atom_index;
1502
1503 while (true) {
1504 const atom = self.getAtom(atom_index);
1505 const sym = self.getSymbol(atom.getSymbolWithLoc());
1506
1507 if (atom.getFile() != null) {
1508 // Update each symbol contained within the atom
1509 var it = Atom.getInnerSymbolsIterator(self, atom_index);
1510 while (it.next()) |sym_loc| {
1511 const inner_sym = self.getSymbolPtr(sym_loc);
1512 inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
1513 self,
1514 atom_index,
1515 sym_loc.sym_index,
1516 );
1517 }
1518
1519 // If there is a section alias, update it now too
1520 if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
1521 const alias = self.getSymbolPtr(sym_loc);
1522 alias.n_value = sym.n_value;
1523 }
1524 }
1525
1526 if (atom.next_index) |next_index| {
1527 atom_index = next_index;
1528 } else break;
1529 }
1530 }
14951531 }
14961532
14971533 fn allocateSegments(self: *Zld) !void {
test/link.zig+4
......@@ -96,6 +96,10 @@ pub const cases = [_]Case{
9696 .build_root = "test/link/macho/bugs/16308",
9797 .import = @import("link/macho/bugs/16308/build.zig"),
9898 },
99 .{
100 .build_root = "test/link/macho/bugs/16628",
101 .import = @import("link/macho/bugs/16628/build.zig"),
102 },
99103 .{
100104 .build_root = "test/link/macho/dead_strip",
101105 .import = @import("link/macho/dead_strip/build.zig"),
test/link/macho/bugs/16628/a_arm64.s created+37
......@@ -0,0 +1,37 @@
1.globl _foo
2.align 4
3_foo:
4 .cfi_startproc
5 stp x29, x30, [sp, #-32]!
6 .cfi_def_cfa_offset 32
7 .cfi_offset w30, -24
8 .cfi_offset w29, -32
9 mov x29, sp
10 .cfi_def_cfa w29, 32
11 bl _bar
12 ldp x29, x30, [sp], #32
13 .cfi_restore w29
14 .cfi_restore w30
15 .cfi_def_cfa_offset 0
16 ret
17 .cfi_endproc
18
19.globl _bar
20.align 4
21_bar:
22 .cfi_startproc
23 sub sp, sp, #32
24 .cfi_def_cfa_offset -32
25 stp x29, x30, [sp, #16]
26 .cfi_offset w30, -24
27 .cfi_offset w29, -32
28 mov x29, sp
29 .cfi_def_cfa w29, 32
30 mov w0, #4
31 ldp x29, x30, [sp, #16]
32 .cfi_restore w29
33 .cfi_restore w30
34 add sp, sp, #32
35 .cfi_def_cfa_offset 0
36 ret
37 .cfi_endproc
test/link/macho/bugs/16628/a_x64.s created+29
......@@ -0,0 +1,29 @@
1.globl _foo
2_foo:
3 .cfi_startproc
4 push %rbp
5 .cfi_def_cfa_offset 8
6 .cfi_offset %rbp, -8
7 mov %rsp, %rbp
8 .cfi_def_cfa_register %rbp
9 call _bar
10 pop %rbp
11 .cfi_restore %rbp
12 .cfi_def_cfa_offset 0
13 ret
14 .cfi_endproc
15
16.globl _bar
17_bar:
18 .cfi_startproc
19 push %rbp
20 .cfi_def_cfa_offset 8
21 .cfi_offset %rbp, -8
22 mov %rsp, %rbp
23 .cfi_def_cfa_register %rbp
24 mov $4, %rax
25 pop %rbp
26 .cfi_restore %rbp
27 .cfi_def_cfa_offset 0
28 ret
29 .cfi_endproc
test/link/macho/bugs/16628/build.zig created+42
......@@ -0,0 +1,42 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4pub const requires_symlinks = true;
5pub const requires_macos_sdk = false;
6
7pub fn build(b: *std.Build) void {
8 const test_step = b.step("test", "Test it");
9 b.default_step = test_step;
10
11 add(b, test_step, .Debug);
12 add(b, test_step, .ReleaseFast);
13 add(b, test_step, .ReleaseSmall);
14 add(b, test_step, .ReleaseSafe);
15}
16
17fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
18 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
19
20 const exe = b.addExecutable(.{
21 .name = "test",
22 .optimize = optimize,
23 .target = target,
24 });
25 exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} });
26 switch (builtin.cpu.arch) {
27 .aarch64 => {
28 exe.addCSourceFile(.{ .file = .{ .path = "a_arm64.s" }, .flags = &[0][]const u8{} });
29 },
30 .x86_64 => {
31 exe.addCSourceFile(.{ .file = .{ .path = "a_x64.s" }, .flags = &[0][]const u8{} });
32 },
33 else => unreachable,
34 }
35 exe.linkLibC();
36
37 const run = b.addRunArtifact(exe);
38 run.skip_foreign_checks = true;
39 run.expectStdOutEqual("4\n");
40
41 test_step.dependOn(&run.step);
42}
test/link/macho/bugs/16628/main.c created+8
......@@ -0,0 +1,8 @@
1#include <stdio.h>
2
3int foo();
4
5int main() {
6 printf("%d\n", foo());
7 return 0;
8}