From 88d40fc005894ef84eb8bf54314da293772c4bba Mon Sep 17 00:00:00 2001
From: Jakub Konka <kubkon@jakubkonka.com>
Date: Thu, 6 May 2021 17:02:39 +0200
Subject: [PATCH] zld: sort tlv offsets by source address

---
 src/link/MachO/Archive.zig |  9 ++++++---
 src/link/MachO/Object.zig  |  2 +-
 src/link/MachO/Zld.zig     | 40 ++++++++++++++++++++------------------
 3 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig
index 5a0b9609ad99..702a807a4d4e 100644
--- a/src/link/MachO/Archive.zig
+++ b/src/link/MachO/Archive.zig
@@ -16,7 +16,7 @@ allocator: *Allocator,
 arch: ?std.Target.Cpu.Arch = null,
 file: ?fs.File = null,
 header: ?ar_hdr = null,
-name: ?[]u8 = null,
+name: ?[]const u8 = null,
 
 /// Parsed table of contents.
 /// Each symbol name points to a list of all definition
@@ -195,7 +195,7 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void {
 }
 
 /// Caller owns the Object instance.
-pub fn parseObject(self: Archive, offset: u32) !Object {
+pub fn parseObject(self: Archive, offset: u32) !*Object {
     var reader = self.file.?.reader();
     try reader.context.seekTo(offset);
 
@@ -217,7 +217,10 @@ pub fn parseObject(self: Archive, offset: u32) !Object {
         break :name try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object_name });
     };
 
-    var object = Object.init(self.allocator);
+    var object = try self.allocator.create(Object);
+    errdefer self.allocator.destroy(object);
+
+    object.* = Object.init(self.allocator);
     object.arch = self.arch.?;
     object.file = try fs.cwd().openFile(self.name.?, .{});
     object.name = name;
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 3b8aa7a6cf52..4d2ade7aadb0 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -22,7 +22,7 @@ arch: ?std.Target.Cpu.Arch = null,
 header: ?macho.mach_header_64 = null,
 file: ?fs.File = null,
 file_offset: ?u32 = null,
-name: ?[]u8 = null,
+name: ?[]const u8 = null,
 
 load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
 sections: std.ArrayListUnmanaged(Section) = .{},
diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig
index 50a4f7710983..4d19da1e9790 100644
--- a/src/link/MachO/Zld.zig
+++ b/src/link/MachO/Zld.zig
@@ -82,7 +82,7 @@ unresolved: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
 strtab: std.ArrayListUnmanaged(u8) = .{},
 strtab_dir: std.StringHashMapUnmanaged(u32) = .{},
 
-threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
+threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction
 local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
 stubs: std.ArrayListUnmanaged(*Symbol) = .{},
 got_entries: std.ArrayListUnmanaged(*Symbol) = .{},
@@ -92,6 +92,15 @@ stub_helper_stubs_start_off: ?u64 = null,
 mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
 unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
 
+const TlvOffset = struct {
+    source_addr: u64,
+    offset: u64,
+
+    fn cmp(context: void, a: TlvOffset, b: TlvOffset) bool {
+        return a.source_addr < b.source_addr;
+    }
+};
+
 const MappingKey = struct {
     object_id: u16,
     source_sect_id: u16,
@@ -277,7 +286,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
 
                 object.* = Object.init(self.allocator);
                 object.arch = self.arch.?;
-                object.name = try self.allocator.dupe(u8, input.name);
+                object.name = input.name;
                 object.file = input.file;
                 try object.parse();
                 try self.objects.append(self.allocator, object);
@@ -288,7 +297,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
 
                 archive.* = Archive.init(self.allocator);
                 archive.arch = self.arch.?;
-                archive.name = try self.allocator.dupe(u8, input.name);
+                archive.name = input.name;
                 archive.file = input.file;
                 try archive.parse();
                 try self.archives.append(self.allocator, archive);
@@ -1362,10 +1371,7 @@ fn resolveSymbols(self: *Zld) !void {
             };
             assert(offsets.items.len > 0);
 
-            const object = try self.allocator.create(Object);
-            errdefer self.allocator.destroy(object);
-
-            object.* = try archive.parseObject(offsets.items[0]);
+            const object = try archive.parseObject(offsets.items[0]);
             try self.objects.append(self.allocator, object);
             try self.resolveSymbolsInObject(object);
 
@@ -1553,16 +1559,9 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
                             // TLV is handled via a separate offset mechanism.
                             // Calculate the offset to the initializer.
                             if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
-                                log.warn("HIT", .{});
-                                log.warn("    | rel {any}", .{rel.cast(reloc.Unsigned).?});
-                                log.warn("    | name {s}", .{rel.target.symbol.name});
-                                log.warn("    | target address 0x{x}", .{args.target_addr});
-
                                 // TODO we don't want to save offset to tlv_bootstrap
                                 if (mem.eql(u8, rel.target.symbol.name, "__tlv_bootstrap")) break :tlv;
 
-                                log.warn("    | object {s}", .{rel.target.symbol.cast(Symbol.Regular).?.file.name.?});
-
                                 const base_addr = blk: {
                                     if (self.tlv_data_section_index) |index| {
                                         const tlv_data = target_seg.sections.items[index];
@@ -1572,11 +1571,12 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
                                         break :blk tlv_bss.addr;
                                     }
                                 };
-                                log.warn("    | base address 0x{x}", .{base_addr});
-                                log.warn("    | offset 0x{x}", .{args.target_addr - base_addr});
                                 // Since we require TLV data to always preceed TLV bss section, we calculate
                                 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
-                                try self.threadlocal_offsets.append(self.allocator, args.target_addr - base_addr);
+                                try self.threadlocal_offsets.append(self.allocator, .{
+                                    .source_addr = args.source_addr,
+                                    .offset = args.target_addr - base_addr,
+                                });
                             }
                         },
                         .got_page, .got_page_off, .got_load, .got => {
@@ -2102,10 +2102,12 @@ fn flush(self: *Zld) !void {
         var stream = std.io.fixedBufferStream(buffer);
         var writer = stream.writer();
 
+        std.sort.sort(TlvOffset, self.threadlocal_offsets.items, {}, TlvOffset.cmp);
+
         const seek_amt = 2 * @sizeOf(u64);
-        while (self.threadlocal_offsets.popOrNull()) |offset| {
+        for (self.threadlocal_offsets.items) |tlv| {
             try writer.context.seekBy(seek_amt);
-            try writer.writeIntLittle(u64, offset);
+            try writer.writeIntLittle(u64, tlv.offset);
         }
 
         try self.file.?.pwriteAll(buffer, sect.offset);