authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-08-16 15:21:46+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-16 15:21:46+02:00
logfd830b146dc733657aeb689e1321556395560bfd
tree25ca14669cf6badc2771c1aad3674806d9b26a8e
parented744c4f59b6aafc0c343eb975efae3e51658080
parent573bb77ab6f4f00753bb257314d8115cbaac17ae
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16852 from ziglang/issue-16751

macho: tie FDEs and unwind records to all symbol aliases, not just the first global

5 files changed, 108 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);
test/link.zig+4
...@@ -156,6 +156,10 @@ pub const cases = [_]Case{...@@ -156,6 +156,10 @@ pub const cases = [_]Case{
156 .build_root = "test/link/macho/pagezero",156 .build_root = "test/link/macho/pagezero",
157 .import = @import("link/macho/pagezero/build.zig"),157 .import = @import("link/macho/pagezero/build.zig"),
158 },158 },
159 .{
160 .build_root = "test/link/macho/reexports",
161 .import = @import("link/macho/reexports/build.zig"),
162 },
159 .{163 .{
160 .build_root = "test/link/macho/search_strategy",164 .build_root = "test/link/macho/search_strategy",
161 .import = @import("link/macho/search_strategy/build.zig"),165 .import = @import("link/macho/search_strategy/build.zig"),
test/link/macho/reexports/a.zig created+7
...@@ -0,0 +1,7 @@
1const x: i32 = 42;
2export fn foo() i32 {
3 return x;
4}
5comptime {
6 @export(foo, .{ .name = "bar", .linkage = .Strong });
7}
test/link/macho/reexports/build.zig created+38
...@@ -0,0 +1,38 @@
1const std = @import("std");
2
3pub const requires_symlinks = true;
4
5pub fn build(b: *std.Build) void {
6 const test_step = b.step("test", "Test it");
7 b.default_step = test_step;
8
9 add(b, test_step, .Debug);
10 add(b, test_step, .ReleaseFast);
11 add(b, test_step, .ReleaseSmall);
12 add(b, test_step, .ReleaseSafe);
13}
14
15fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
16 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
17
18 const lib = b.addStaticLibrary(.{
19 .name = "a",
20 .root_source_file = .{ .path = "a.zig" },
21 .optimize = optimize,
22 .target = target,
23 });
24
25 const exe = b.addExecutable(.{
26 .name = "test",
27 .optimize = optimize,
28 .target = target,
29 });
30 exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} });
31 exe.linkLibrary(lib);
32 exe.linkLibC();
33
34 const run = b.addRunArtifact(exe);
35 run.skip_foreign_checks = true;
36 run.expectExitCode(0);
37 test_step.dependOn(&run.step);
38}
test/link/macho/reexports/main.c created+5
...@@ -0,0 +1,5 @@
1extern int foo();
2extern int bar();
3int main() {
4 return bar() - foo();
5}