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 {
718718 }
719719
720720 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
723723 it.reset();
724724
......@@ -768,11 +768,28 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
768768 else => unreachable,
769769 }
770770 };
771 log.debug("FDE at offset {x} tracks {s}", .{ offset, zld.getSymbolName(target) });
772771 if (target.getFile() != object_id) {
772 log.debug("FDE at offset {x} marked DEAD", .{offset});
773773 self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true;
774774 } 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 }
776793 }
777794 }
778795 }
......@@ -803,7 +820,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
803820
804821 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
808825 const needs_eh_frame = for (unwind_records) |record| {
809826 if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true;
......@@ -839,11 +856,28 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
839856 .code = mem.asBytes(&record),
840857 .base_offset = @as(i32, @intCast(offset)),
841858 });
842 log.debug("unwind record {d} tracks {s}", .{ record_id, zld.getSymbolName(target) });
843859 if (target.getFile() != object_id) {
860 log.debug("unwind record {d} marked DEAD", .{record_id});
844861 self.unwind_relocs_lookup[record_id].dead = true;
845862 } 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 }
847881 }
848882 }
849883}
......@@ -991,6 +1025,18 @@ pub fn getSymbolName(self: Object, index: u32) []const u8 {
9911025 return strtab[start..][0 .. len - 1 :0];
9921026}
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
9941040pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
9951041 // Find containing atom
9961042 const Predicate = struct {
......@@ -1012,11 +1058,8 @@ pub fn getSymbolByAddress(self: Object, addr: u64, sect_hint: ?u8) u32 {
10121058 if (target_sym_index > 0) {
10131059 // Hone in on the most senior alias of the target symbol.
10141060 // 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));
1061 const aliases = self.getSymbolAliases(@intCast(lookup.start + target_sym_index - 1));
1062 return aliases.start;
10201063 }
10211064 }
10221065 return self.getSectionAliasSymbolIndex(sect_id);
test/link.zig+4
......@@ -156,6 +156,10 @@ pub const cases = [_]Case{
156156 .build_root = "test/link/macho/pagezero",
157157 .import = @import("link/macho/pagezero/build.zig"),
158158 },
159 .{
160 .build_root = "test/link/macho/reexports",
161 .import = @import("link/macho/reexports/build.zig"),
162 },
159163 .{
160164 .build_root = "test/link/macho/search_strategy",
161165 .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}