authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-16 12:07:34+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-16 12:07:37+02:00
log787e755a2f36660b5bc22f2a3a5d051a8c34445f
tree29234550eacf556e327cdfdbce34f9ccd4175c60
parent340a45683ca8e0b23e95f5fb86bd9c827970e6e8

macho: tie FDEs and unwind records to all symbol aliases

This is in particular very important to the Zig language which allows exporting the same symbol under different names. For instance, it is possible to have a case such that: ``` ... 4258 T _foo 4258 T _bar ... ``` In this case we need to keep track of both symbol names when resolving FDEs and unwind records.

1 files changed, 54 insertions(+), 11 deletions(-)

src/link/MachO/Object.zig+54-11
...@@ -718,7 +718,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -718,7 +718,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
718 }718 }
719719
720 try self.eh_frame_relocs_lookup.ensureTotalCapacity(gpa, record_count);720 try self.eh_frame_relocs_lookup.ensureTotalCapacity(gpa, record_count);
721 try self.eh_frame_records_lookup.ensureTotalCapacity(gpa, record_count);721 try self.eh_frame_records_lookup.ensureUnusedCapacity(gpa, record_count);
722722
723 it.reset();723 it.reset();
724724
...@@ -768,11 +768,28 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -768,11 +768,28 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
768 else => unreachable,768 else => unreachable,
769 }769 }
770 };770 };
771 log.debug("FDE at offset {x} tracks {s}", .{ offset, zld.getSymbolName(target) });
772 if (target.getFile() != object_id) {771 if (target.getFile() != object_id) {
772 log.debug("FDE at offset {x} marked DEAD", .{offset});
773 self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true;773 self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true;
774 } else {774 } else {
775 self.eh_frame_records_lookup.putAssumeCapacityNoClobber(target, offset);775 // You would think that we are done but turns out that the compilers may use
776 // whichever symbol alias they want for a target symbol. This in particular
777 // very problematic when using Zig's @export feature to re-export symbols under
778 // additional names. For that reason, we need to ensure we record aliases here
779 // too so that we can tie them with their matching unwind records and vice versa.
780 const aliases = self.getSymbolAliases(target.sym_index);
781 var i: u32 = 0;
782 while (i < aliases.len) : (i += 1) {
783 const actual_target = SymbolWithLoc{
784 .sym_index = i + aliases.start,
785 .file = target.file,
786 };
787 log.debug("FDE at offset {x} tracks {s}", .{
788 offset,
789 zld.getSymbolName(actual_target),
790 });
791 try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset);
792 }
776 }793 }
777 }794 }
778 }795 }
...@@ -803,7 +820,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -803,7 +820,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
803820
804 const unwind_records = self.getUnwindRecords();821 const unwind_records = self.getUnwindRecords();
805822
806 try self.unwind_records_lookup.ensureTotalCapacity(gpa, @as(u32, @intCast(unwind_records.len)));823 try self.unwind_records_lookup.ensureUnusedCapacity(gpa, @as(u32, @intCast(unwind_records.len)));
807824
808 const needs_eh_frame = for (unwind_records) |record| {825 const needs_eh_frame = for (unwind_records) |record| {
809 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;826 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
...@@ -839,11 +856,28 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {...@@ -839,11 +856,28 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
839 .code = mem.asBytes(&record),856 .code = mem.asBytes(&record),
840 .base_offset = @as(i32, @intCast(offset)),857 .base_offset = @as(i32, @intCast(offset)),
841 });858 });
842 log.debug("unwind record {d} tracks {s}", .{ record_id, zld.getSymbolName(target) });
843 if (target.getFile() != object_id) {859 if (target.getFile() != object_id) {
860 log.debug("unwind record {d} marked DEAD", .{record_id});
844 self.unwind_relocs_lookup[record_id].dead = true;861 self.unwind_relocs_lookup[record_id].dead = true;
845 } else {862 } else {
846 self.unwind_records_lookup.putAssumeCapacityNoClobber(target, @as(u32, @intCast(record_id)));863 // You would think that we are done but turns out that the compilers may use
864 // whichever symbol alias they want for a target symbol. This in particular
865 // very problematic when using Zig's @export feature to re-export symbols under
866 // additional names. For that reason, we need to ensure we record aliases here
867 // too so that we can tie them with their matching unwind records and vice versa.
868 const aliases = self.getSymbolAliases(target.sym_index);
869 var i: u32 = 0;
870 while (i < aliases.len) : (i += 1) {
871 const actual_target = SymbolWithLoc{
872 .sym_index = i + aliases.start,
873 .file = target.file,
874 };
875 log.debug("unwind record {d} tracks {s}", .{
876 record_id,
877 zld.getSymbolName(actual_target),
878 });
879 try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id));
880 }
847 }881 }
848 }882 }
849}883}
...@@ -991,6 +1025,18 @@ pub fn getSymbolName(self: Object, index: u32) []const u8 {...@@ -991,6 +1025,18 @@ pub fn getSymbolName(self: Object, index: u32) []const u8 {
991 return strtab[start..][0 .. len - 1 :0];1025 return strtab[start..][0 .. len - 1 :0];
992}1026}
9931027
1028fn getSymbolAliases(self: Object, index: u32) Entry {
1029 const addr = self.source_address_lookup[index];
1030 var start = index;
1031 while (start > 0 and
1032 self.source_address_lookup[start - 1] == addr) : (start -= 1)
1033 {}
1034 const end: u32 = for (self.source_address_lookup[start..], start..) |saddr, i| {
1035 if (saddr != addr) break @as(u32, @intCast(i));
1036 } else @as(u32, @intCast(self.source_address_lookup.len));
1037 return .{ .start = start, .len = end - start };
1038}
1039
994pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {1040pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
995 // Find containing atom1041 // Find containing atom
996 const Predicate = struct {1042 const Predicate = struct {
...@@ -1012,11 +1058,8 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {...@@ -1012,11 +1058,8 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
1012 if (target_sym_index > 0) {1058 if (target_sym_index > 0) {
1013 // Hone in on the most senior alias of the target symbol.1059 // Hone in on the most senior alias of the target symbol.
1014 // See SymbolAtIndex.lessThan for more context.1060 // See SymbolAtIndex.lessThan for more context.
1015 var start = target_sym_index - 1;1061 const aliases = self.getSymbolAliases(@intCast(lookup.start + target_sym_index - 1));
1016 while (start > 0 and1062 return aliases.start;
1017 self.source_address_lookup[lookup.start..][start - 1] == addr) : (start -= 1)
1018 {}
1019 return @as(u32, @intCast(lookup.start + start));
1020 }1063 }
1021 }1064 }
1022 return self.getSectionAliasSymbolIndex(sect_id);1065 return self.getSectionAliasSymbolIndex(sect_id);