| ... | ... | @@ -1298,10 +1298,18 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target. |
| 1298 | 1298 | superposition.getPtr(addr).?.cu = rec_index; |
| 1299 | 1299 | } |
| 1300 | 1300 | |
| 1301 | const FdeRange = struct { start: u64, end: u64 }; |
| 1302 | var fde_ranges = try std.ArrayList(FdeRange).initCapacity(allocator, self.fdes.items.len); |
| 1303 | defer fde_ranges.deinit(allocator); |
| 1304 | |
| 1301 | 1305 | for (self.fdes.items, 0..) |fde, fde_index| { |
| 1302 | 1306 | const atom = fde.getAtom(macho_file); |
| 1303 | 1307 | const addr = atom.getInputAddress(macho_file) + fde.atom_offset; |
| 1304 | 1308 | superposition.getPtr(addr).?.fde = @intCast(fde_index); |
| 1309 | |
| 1310 | // Build FDE range for coverage check |
| 1311 | const pc_range = fde.pc_range; |
| 1312 | fde_ranges.appendAssumeCapacity(.{ .start = addr, .end = addr + pc_range }); |
| 1305 | 1313 | } |
| 1306 | 1314 | |
| 1307 | 1315 | for (superposition.keys(), superposition.values()) |addr, meta| { |
| ... | ... | @@ -1333,15 +1341,43 @@ fn parseUnwindRecords(self: *Object, allocator: Allocator, cpu_arch: std.Target. |
| 1333 | 1341 | } |
| 1334 | 1342 | } |
| 1335 | 1343 | } else if (meta.cu == null and meta.fde == null) { |
| 1336 | | // Create a null record |
| 1337 | | const rec_index = try self.addUnwindRecord(allocator); |
| 1338 | | const rec = self.getUnwindRecord(rec_index); |
| 1339 | | const atom = self.getAtom(meta.atom).?; |
| 1340 | | try self.unwind_records_indexes.append(allocator, rec_index); |
| 1341 | | rec.length = @intCast(meta.size); |
| 1342 | | rec.atom = meta.atom; |
| 1343 | | rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file)); |
| 1344 | | rec.file = self.index; |
| 1344 | // Check if this address is covered by an existing FDE. |
| 1345 | // If so, don't create a null record - let the unwinder fall back to DWARF. |
| 1346 | // This is important for local labels within a function that has DWARF unwind info. |
| 1347 | const is_covered_by_fde = blk: { |
| 1348 | if (fde_ranges.items.len == 0) break :blk false; |
| 1349 | |
| 1350 | // Binary search: find the last FDE where start <= addr |
| 1351 | var left: usize = 0; |
| 1352 | var right: usize = fde_ranges.items.len; |
| 1353 | while (left < right) { |
| 1354 | const mid = left + (right - left) / 2; |
| 1355 | if (fde_ranges.items[mid].start <= addr) { |
| 1356 | left = mid + 1; |
| 1357 | } else { |
| 1358 | right = mid; |
| 1359 | } |
| 1360 | } |
| 1361 | |
| 1362 | // Check if the FDE before insertion point covers this address |
| 1363 | if (left > 0) { |
| 1364 | const range = fde_ranges.items[left - 1]; |
| 1365 | break :blk addr < range.end; |
| 1366 | } |
| 1367 | break :blk false; |
| 1368 | }; |
| 1369 | |
| 1370 | if (!is_covered_by_fde) { |
| 1371 | // Create a null record only if not covered by DWARF |
| 1372 | const rec_index = try self.addUnwindRecord(allocator); |
| 1373 | const rec = self.getUnwindRecord(rec_index); |
| 1374 | const atom = self.getAtom(meta.atom).?; |
| 1375 | try self.unwind_records_indexes.append(allocator, rec_index); |
| 1376 | rec.length = @intCast(meta.size); |
| 1377 | rec.atom = meta.atom; |
| 1378 | rec.atom_offset = @intCast(addr - atom.getInputAddress(macho_file)); |
| 1379 | rec.file = self.index; |
| 1380 | } |
| 1345 | 1381 | } |
| 1346 | 1382 | } |
| 1347 | 1383 | |