| ... | @@ -10,7 +10,11 @@ const DW = std.dwarf; | ... | @@ -10,7 +10,11 @@ const DW = std.dwarf; |
| 10 | const leb = std.leb; | 10 | const leb = std.leb; |
| 11 | const Allocator = mem.Allocator; | 11 | const Allocator = mem.Allocator; |
| 12 | | 12 | |
| | 13 | const trace = @import("../../tracy.zig").trace; |
| 13 | const MachO = @import("../MachO.zig"); | 14 | const MachO = @import("../MachO.zig"); |
| | 15 | const satMul = MachO.satMul; |
| | 16 | const alloc_num = MachO.alloc_num; |
| | 17 | const alloc_den = MachO.alloc_den; |
| 14 | | 18 | |
| 15 | usingnamespace @import("commands.zig"); | 19 | usingnamespace @import("commands.zig"); |
| 16 | | 20 | |
| ... | @@ -40,8 +44,12 @@ uuid_cmd_index: ?u16 = null, | ... | @@ -40,8 +44,12 @@ uuid_cmd_index: ?u16 = null, |
| 40 | /// Index into __TEXT,__text section. | 44 | /// Index into __TEXT,__text section. |
| 41 | text_section_index: ?u16 = null, | 45 | text_section_index: ?u16 = null, |
| 42 | | 46 | |
| | 47 | linkedit_off: u16 = 0x1000, |
| | 48 | linkedit_size: u16 = 0x1000, |
| | 49 | |
| 43 | header_dirty: bool = false, | 50 | header_dirty: bool = false, |
| 44 | load_commands_dirty: bool = false, | 51 | load_commands_dirty: bool = false, |
| | 52 | string_table_dirty: bool = false, |
| 45 | | 53 | |
| 46 | /// You must call this function *after* `MachO.populateMissingMetadata()` | 54 | /// You must call this function *after* `MachO.populateMissingMetadata()` |
| 47 | /// has been called to get a viable debug symbols output. | 55 | /// has been called to get a viable debug symbols output. |
| ... | @@ -61,36 +69,86 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void | ... | @@ -61,36 +69,86 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void |
| 61 | self.header = header; | 69 | self.header = header; |
| 62 | self.header_dirty = true; | 70 | self.header_dirty = true; |
| 63 | } | 71 | } |
| | 72 | if (self.uuid_cmd_index == null) { |
| | 73 | const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?]; |
| | 74 | self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); |
| | 75 | try self.load_commands.append(allocator, base_cmd); |
| | 76 | self.header_dirty = true; |
| | 77 | self.load_commands_dirty = true; |
| | 78 | } |
| | 79 | if (self.symtab_cmd_index == null) { |
| | 80 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| | 81 | const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].Symtab; |
| | 82 | const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64); |
| | 83 | const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64)); |
| | 84 | |
| | 85 | log.debug("found dSym symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); |
| | 86 | |
| | 87 | const strtab_off = self.findFreeSpaceLinkedit(base_cmd.strsize, 1); |
| | 88 | |
| | 89 | log.debug("found dSym string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize }); |
| | 90 | |
| | 91 | try self.load_commands.append(allocator, .{ |
| | 92 | .Symtab = .{ |
| | 93 | .cmd = macho.LC_SYMTAB, |
| | 94 | .cmdsize = @sizeOf(macho.symtab_command), |
| | 95 | .symoff = @intCast(u32, symtab_off), |
| | 96 | .nsyms = base_cmd.nsyms, |
| | 97 | .stroff = @intCast(u32, strtab_off), |
| | 98 | .strsize = base_cmd.strsize, |
| | 99 | }, |
| | 100 | }); |
| | 101 | try self.writeLocalSymbol(0); |
| | 102 | self.header_dirty = true; |
| | 103 | self.load_commands_dirty = true; |
| | 104 | self.string_table_dirty = true; |
| | 105 | } |
| 64 | if (self.pagezero_segment_cmd_index == null) { | 106 | if (self.pagezero_segment_cmd_index == null) { |
| 65 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 107 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 66 | const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].Segment; | 108 | const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].Segment; |
| 67 | try self.copySegmentCommand(allocator, base_cmd); | 109 | const cmd = try self.copySegmentCommand(allocator, base_cmd); |
| | 110 | try self.load_commands.append(allocator, .{ .Segment = cmd }); |
| | 111 | self.header_dirty = true; |
| | 112 | self.load_commands_dirty = true; |
| 68 | } | 113 | } |
| 69 | if (self.text_segment_cmd_index == null) { | 114 | if (self.text_segment_cmd_index == null) { |
| 70 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 115 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 71 | const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].Segment; | 116 | const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].Segment; |
| 72 | try self.copySegmentCommand(allocator, base_cmd); | 117 | const cmd = try self.copySegmentCommand(allocator, base_cmd); |
| | 118 | try self.load_commands.append(allocator, .{ .Segment = cmd }); |
| | 119 | self.header_dirty = true; |
| | 120 | self.load_commands_dirty = true; |
| 73 | } | 121 | } |
| 74 | if (self.data_segment_cmd_index == null) outer: { | 122 | if (self.data_segment_cmd_index == null) outer: { |
| 75 | if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional | 123 | if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional |
| 76 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 124 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 77 | const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].Segment; | 125 | const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].Segment; |
| 78 | try self.copySegmentCommand(allocator, base_cmd); | 126 | const cmd = try self.copySegmentCommand(allocator, base_cmd); |
| | 127 | try self.load_commands.append(allocator, .{ .Segment = cmd }); |
| | 128 | self.header_dirty = true; |
| | 129 | self.load_commands_dirty = true; |
| 79 | } | 130 | } |
| 80 | if (self.uuid_cmd_index == null) { | 131 | if (self.linkedit_segment_cmd_index == null) { |
| 81 | const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?]; | 132 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 82 | self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len); | 133 | const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].Segment; |
| 83 | try self.load_commands.append(allocator, base_cmd); | 134 | var cmd = try self.copySegmentCommand(allocator, base_cmd); |
| | 135 | cmd.inner.vmsize = self.linkedit_size; |
| | 136 | cmd.inner.fileoff = self.linkedit_off; |
| | 137 | cmd.inner.filesize = self.linkedit_size; |
| | 138 | try self.load_commands.append(allocator, .{ .Segment = cmd }); |
| 84 | self.header_dirty = true; | 139 | self.header_dirty = true; |
| 85 | self.load_commands_dirty = true; | 140 | self.load_commands_dirty = true; |
| 86 | } | 141 | } |
| 87 | } | 142 | } |
| 88 | | 143 | |
| 89 | pub fn flush(self: *DebugSymbols, allocator: *Allocator) !void { | 144 | pub fn flush(self: *DebugSymbols, allocator: *Allocator) !void { |
| | 145 | try self.writeStringTable(); |
| 90 | try self.writeLoadCommands(allocator); | 146 | try self.writeLoadCommands(allocator); |
| 91 | try self.writeHeader(); | 147 | try self.writeHeader(); |
| | 148 | |
| 92 | assert(!self.header_dirty); | 149 | assert(!self.header_dirty); |
| 93 | assert(!self.load_commands_dirty); | 150 | assert(!self.load_commands_dirty); |
| | 151 | assert(!self.string_table_dirty); |
| 94 | } | 152 | } |
| 95 | | 153 | |
| 96 | pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void { | 154 | pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void { |
| ... | @@ -100,7 +158,7 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void { | ... | @@ -100,7 +158,7 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void { |
| 100 | self.file.close(); | 158 | self.file.close(); |
| 101 | } | 159 | } |
| 102 | | 160 | |
| 103 | fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !void { | 161 | fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand { |
| 104 | var cmd = SegmentCommand.empty(.{ | 162 | var cmd = SegmentCommand.empty(.{ |
| 105 | .cmd = macho.LC_SEGMENT_64, | 163 | .cmd = macho.LC_SEGMENT_64, |
| 106 | .cmdsize = base_cmd.inner.cmdsize, | 164 | .cmdsize = base_cmd.inner.cmdsize, |
| ... | @@ -142,9 +200,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: Segm | ... | @@ -142,9 +200,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: Segm |
| 142 | cmd.sections.appendAssumeCapacity(sect); | 200 | cmd.sections.appendAssumeCapacity(sect); |
| 143 | } | 201 | } |
| 144 | | 202 | |
| 145 | try self.load_commands.append(allocator, .{ .Segment = cmd }); | 203 | return cmd; |
| 146 | self.header_dirty = true; | | |
| 147 | self.load_commands_dirty = true; | | |
| 148 | } | 204 | } |
| 149 | | 205 | |
| 150 | /// Writes all load commands and section headers. | 206 | /// Writes all load commands and section headers. |
| ... | @@ -182,3 +238,115 @@ fn writeHeader(self: *DebugSymbols) !void { | ... | @@ -182,3 +238,115 @@ fn writeHeader(self: *DebugSymbols) !void { |
| 182 | try self.file.pwriteAll(mem.asBytes(&self.header.?), 0); | 238 | try self.file.pwriteAll(mem.asBytes(&self.header.?), 0); |
| 183 | self.header_dirty = false; | 239 | self.header_dirty = false; |
| 184 | } | 240 | } |
| | 241 | |
| | 242 | fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 { |
| | 243 | assert(start > 0); |
| | 244 | var min_pos: u64 = std.math.maxInt(u64); |
| | 245 | |
| | 246 | if (self.symtab_cmd_index) |idx| { |
| | 247 | const symtab = self.load_commands.items[idx].Symtab; |
| | 248 | if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff; |
| | 249 | if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff; |
| | 250 | } |
| | 251 | |
| | 252 | return min_pos - start; |
| | 253 | } |
| | 254 | |
| | 255 | fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 { |
| | 256 | const end = start + satMul(size, alloc_num) / alloc_den; |
| | 257 | |
| | 258 | if (self.symtab_cmd_index) |idx| outer: { |
| | 259 | if (self.load_commands.items.len == idx) break :outer; |
| | 260 | const symtab = self.load_commands.items[idx].Symtab; |
| | 261 | { |
| | 262 | // Symbol table |
| | 263 | const symsize = symtab.nsyms * @sizeOf(macho.nlist_64); |
| | 264 | const increased_size = satMul(symsize, alloc_num) / alloc_den; |
| | 265 | const test_end = symtab.symoff + increased_size; |
| | 266 | if (end > symtab.symoff and start < test_end) { |
| | 267 | return test_end; |
| | 268 | } |
| | 269 | } |
| | 270 | { |
| | 271 | // String table |
| | 272 | const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den; |
| | 273 | const test_end = symtab.stroff + increased_size; |
| | 274 | if (end > symtab.stroff and start < test_end) { |
| | 275 | return test_end; |
| | 276 | } |
| | 277 | } |
| | 278 | } |
| | 279 | |
| | 280 | return null; |
| | 281 | } |
| | 282 | |
| | 283 | fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u16) u64 { |
| | 284 | var start: u64 = self.linkedit_off; |
| | 285 | while (self.detectAllocCollisionLinkedit(start, object_size)) |item_end| { |
| | 286 | start = mem.alignForwardGeneric(u64, item_end, min_alignment); |
| | 287 | } |
| | 288 | return start; |
| | 289 | } |
| | 290 | |
| | 291 | fn relocateSymbolTable(self: *DebugSymbols) !void { |
| | 292 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| | 293 | const nlocals = self.base.local_symbols.items.len; |
| | 294 | const nglobals = self.base.global_symbols.items.len; |
| | 295 | const nsyms = nlocals + nglobals; |
| | 296 | |
| | 297 | if (symtab.nsyms < nsyms) { |
| | 298 | const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; |
| | 299 | const needed_size = nsyms * @sizeOf(macho.nlist_64); |
| | 300 | if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) { |
| | 301 | // Move the entire symbol table to a new location |
| | 302 | const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64)); |
| | 303 | const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64); |
| | 304 | |
| | 305 | assert(new_symoff + existing_size <= self.linkedit_off + self.linkedit_size); |
| | 306 | log.debug("relocating dSym symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{ |
| | 307 | symtab.symoff, |
| | 308 | symtab.symoff + existing_size, |
| | 309 | new_symoff, |
| | 310 | new_symoff + existing_size, |
| | 311 | }); |
| | 312 | |
| | 313 | const amt = try self.file.copyRangeAll(symtab.symoff, self.file, new_symoff, existing_size); |
| | 314 | if (amt != existing_size) return error.InputOutput; |
| | 315 | symtab.symoff = @intCast(u32, new_symoff); |
| | 316 | } |
| | 317 | symtab.nsyms = @intCast(u32, nsyms); |
| | 318 | self.load_commands_dirty = true; |
| | 319 | } |
| | 320 | } |
| | 321 | |
| | 322 | pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void { |
| | 323 | const tracy = trace(@src()); |
| | 324 | defer tracy.end(); |
| | 325 | try self.relocateSymbolTable(); |
| | 326 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| | 327 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| | 328 | log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off }); |
| | 329 | try self.file.pwriteAll(mem.asBytes(&self.base.local_symbols.items[index]), off); |
| | 330 | } |
| | 331 | |
| | 332 | pub fn writeStringTable(self: *DebugSymbols) !void { |
| | 333 | if (!self.string_table_dirty) return; |
| | 334 | |
| | 335 | const tracy = trace(@src()); |
| | 336 | defer tracy.end(); |
| | 337 | |
| | 338 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| | 339 | const allocated_size = self.allocatedSizeLinkedit(symtab.stroff); |
| | 340 | const needed_size = mem.alignForwardGeneric(u64, self.base.string_table.items.len, @alignOf(u64)); |
| | 341 | |
| | 342 | if (needed_size > allocated_size) { |
| | 343 | symtab.strsize = 0; |
| | 344 | symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1)); |
| | 345 | } |
| | 346 | symtab.strsize = @intCast(u32, needed_size); |
| | 347 | log.debug("writing dSym string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); |
| | 348 | |
| | 349 | try self.file.pwriteAll(self.base.string_table.items, symtab.stroff); |
| | 350 | self.load_commands_dirty = true; |
| | 351 | self.string_table_dirty = false; |
| | 352 | } |