Commit 0736365fa4

Jakub Konka <kubkon@jakubkonka.com>
2021-06-22 10:29:58
zld: fix finding pointers for rebasing
1 parent d1fcb99
Changed files (1)
src
link
MachO
src/link/MachO/Zld.zig
@@ -1948,29 +1948,52 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
                                 args.source_target_sect_addr = source_sect.inner.addr;
                             }
 
-                            rebases: {
-                                var hit: bool = false;
-                                if (target_map.segment_id == self.data_segment_cmd_index.?) {
-                                    if (self.data_section_index) |index| {
-                                        if (index == target_map.section_id) hit = true;
+                            const flags = @truncate(u8, target_sect.flags & 0xff);
+                            const should_rebase = rebase: {
+                                if (!unsigned.is_64bit) break :rebase false;
+
+                                // TODO actually, a check similar to what dyld is doing, that is, verifying
+                                // that the segment is writable should be enough here.
+                                const is_right_segment = blk: {
+                                    if (self.data_segment_cmd_index) |idx| {
+                                        if (target_map.segment_id == idx) {
+                                            break :blk true;
+                                        }
                                     }
+                                    if (self.data_const_segment_cmd_index) |idx| {
+                                        if (target_map.segment_id == idx) {
+                                            break :blk true;
+                                        }
+                                    }
+                                    break :blk false;
+                                };
+
+                                if (!is_right_segment) break :rebase false;
+                                if (flags != macho.S_LITERAL_POINTERS and
+                                    flags != macho.S_REGULAR)
+                                {
+                                    break :rebase false;
                                 }
-                                if (target_map.segment_id == self.data_const_segment_cmd_index.?) {
-                                    if (self.data_const_section_index) |index| {
-                                        if (index == target_map.section_id) hit = true;
+                                if (rel.target == .symbol) {
+                                    const final = rel.target.symbol.getTopmostAlias();
+                                    if (final.cast(Symbol.Proxy)) |_| {
+                                        break :rebase false;
                                     }
                                 }
 
-                                if (!hit) break :rebases;
+                                break :rebase true;
+                            };
 
+                            if (should_rebase) {
                                 try self.local_rebases.append(self.allocator, .{
                                     .offset = source_addr - target_seg.inner.vmaddr,
                                     .segment_id = target_map.segment_id,
                                 });
                             }
+
                             // TLV is handled via a separate offset mechanism.
                             // Calculate the offset to the initializer.
-                            if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
+                            if (flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
                                 // TODO we don't want to save offset to tlv_bootstrap
                                 if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv;