| ... | ... | @@ -3,7 +3,8 @@ const DebugSymbols = @This(); |
| 3 | 3 | const std = @import("std"); |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const fs = std.fs; |
| 6 | | const log = std.log.scoped(.dsym); |
| 6 | const log = std.log.scoped(.link); |
| 7 | const leb128 = std.leb; |
| 7 | 8 | const macho = std.macho; |
| 8 | 9 | const math = std.math; |
| 9 | 10 | const mem = std.mem; |
| ... | ... | @@ -22,8 +23,6 @@ const SrcFn = MachO.SrcFn; |
| 22 | 23 | const makeStaticString = MachO.makeStaticString; |
| 23 | 24 | const padToIdeal = MachO.padToIdeal; |
| 24 | 25 | |
| 25 | | const page_size: u16 = 0x1000; |
| 26 | | |
| 27 | 26 | base: *MachO, |
| 28 | 27 | file: fs.File, |
| 29 | 28 | |
| ... | ... | @@ -49,9 +48,6 @@ uuid_cmd_index: ?u16 = null, |
| 49 | 48 | /// Index into __TEXT,__text section. |
| 50 | 49 | text_section_index: ?u16 = null, |
| 51 | 50 | |
| 52 | | linkedit_off: u16 = page_size, |
| 53 | | linkedit_size: u16 = page_size, |
| 54 | | |
| 55 | 51 | debug_info_section_index: ?u16 = null, |
| 56 | 52 | debug_abbrev_section_index: ?u16 = null, |
| 57 | 53 | debug_str_section_index: ?u16 = null, |
| ... | ... | @@ -76,7 +72,6 @@ dbg_info_decl_last: ?*TextBlock = null, |
| 76 | 72 | debug_string_table: std.ArrayListUnmanaged(u8) = .{}, |
| 77 | 73 | |
| 78 | 74 | load_commands_dirty: bool = false, |
| 79 | | strtab_dirty: bool = false, |
| 80 | 75 | debug_string_table_dirty: bool = false, |
| 81 | 76 | debug_abbrev_section_dirty: bool = false, |
| 82 | 77 | debug_aranges_section_dirty: bool = false, |
| ... | ... | @@ -87,8 +82,12 @@ const abbrev_compile_unit = 1; |
| 87 | 82 | const abbrev_subprogram = 2; |
| 88 | 83 | const abbrev_subprogram_retvoid = 3; |
| 89 | 84 | const abbrev_base_type = 4; |
| 90 | | const abbrev_pad1 = 5; |
| 91 | | const abbrev_parameter = 6; |
| 85 | const abbrev_ptr_type = 5; |
| 86 | const abbrev_struct_type = 6; |
| 87 | const abbrev_anon_struct_type = 7; |
| 88 | const abbrev_struct_member = 8; |
| 89 | const abbrev_pad1 = 9; |
| 90 | const abbrev_parameter = 10; |
| 92 | 91 | |
| 93 | 92 | /// The reloc offset for the virtual address of a function in its Line Number Program. |
| 94 | 93 | /// Size is a virtual address integer. |
| ... | ... | @@ -108,30 +107,21 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void |
| 108 | 107 | try self.load_commands.append(allocator, base_cmd); |
| 109 | 108 | self.load_commands_dirty = true; |
| 110 | 109 | } |
| 110 | |
| 111 | 111 | if (self.symtab_cmd_index == null) { |
| 112 | 112 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 113 | | const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].symtab; |
| 114 | | const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64); |
| 115 | | const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64)); |
| 116 | | |
| 117 | | log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); |
| 118 | | |
| 119 | | const strtab_off = self.findFreeSpaceLinkedit(base_cmd.strsize, 1); |
| 120 | | |
| 121 | | log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize }); |
| 122 | | |
| 123 | | try self.load_commands.append(allocator, .{ |
| 113 | try self.load_commands.append(self.base.base.allocator, .{ |
| 124 | 114 | .symtab = .{ |
| 125 | 115 | .cmdsize = @sizeOf(macho.symtab_command), |
| 126 | | .symoff = @intCast(u32, symtab_off), |
| 127 | | .nsyms = base_cmd.nsyms, |
| 128 | | .stroff = @intCast(u32, strtab_off), |
| 129 | | .strsize = base_cmd.strsize, |
| 116 | .symoff = 0, |
| 117 | .nsyms = 0, |
| 118 | .stroff = 0, |
| 119 | .strsize = 0, |
| 130 | 120 | }, |
| 131 | 121 | }); |
| 132 | 122 | self.load_commands_dirty = true; |
| 133 | | self.strtab_dirty = true; |
| 134 | 123 | } |
| 124 | |
| 135 | 125 | if (self.pagezero_segment_cmd_index == null) { |
| 136 | 126 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 137 | 127 | const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].segment; |
| ... | ... | @@ -139,6 +129,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void |
| 139 | 129 | try self.load_commands.append(allocator, .{ .segment = cmd }); |
| 140 | 130 | self.load_commands_dirty = true; |
| 141 | 131 | } |
| 132 | |
| 142 | 133 | if (self.text_segment_cmd_index == null) { |
| 143 | 134 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 144 | 135 | const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].segment; |
| ... | ... | @@ -146,6 +137,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void |
| 146 | 137 | try self.load_commands.append(allocator, .{ .segment = cmd }); |
| 147 | 138 | self.load_commands_dirty = true; |
| 148 | 139 | } |
| 140 | |
| 149 | 141 | if (self.data_const_segment_cmd_index == null) outer: { |
| 150 | 142 | if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional |
| 151 | 143 | self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | ... | @@ -154,6 +146,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void |
| 154 | 146 | try self.load_commands.append(allocator, .{ .segment = cmd }); |
| 155 | 147 | self.load_commands_dirty = true; |
| 156 | 148 | } |
| 149 | |
| 157 | 150 | if (self.data_segment_cmd_index == null) outer: { |
| 158 | 151 | if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional |
| 159 | 152 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| ... | ... | @@ -162,26 +155,29 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void |
| 162 | 155 | try self.load_commands.append(allocator, .{ .segment = cmd }); |
| 163 | 156 | self.load_commands_dirty = true; |
| 164 | 157 | } |
| 158 | |
| 165 | 159 | if (self.linkedit_segment_cmd_index == null) { |
| 166 | 160 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 167 | 161 | const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment; |
| 168 | 162 | var cmd = try self.copySegmentCommand(allocator, base_cmd); |
| 169 | | cmd.inner.vmsize = self.linkedit_size; |
| 170 | | cmd.inner.fileoff = self.linkedit_off; |
| 171 | | cmd.inner.filesize = self.linkedit_size; |
| 163 | // TODO this needs reworking |
| 164 | cmd.inner.vmsize = self.base.page_size; |
| 165 | cmd.inner.fileoff = self.base.page_size; |
| 166 | cmd.inner.filesize = self.base.page_size; |
| 172 | 167 | try self.load_commands.append(allocator, .{ .segment = cmd }); |
| 173 | 168 | self.load_commands_dirty = true; |
| 174 | 169 | } |
| 170 | |
| 175 | 171 | if (self.dwarf_segment_cmd_index == null) { |
| 176 | 172 | self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 177 | 173 | |
| 178 | 174 | const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 179 | 175 | const ideal_size: u16 = 200 + 128 + 160 + 250; |
| 180 | | const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), page_size); |
| 181 | | const off = linkedit.inner.fileoff + linkedit.inner.filesize; |
| 176 | const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size); |
| 177 | const fileoff = linkedit.inner.fileoff + linkedit.inner.filesize; |
| 182 | 178 | const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize; |
| 183 | 179 | |
| 184 | | log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size }); |
| 180 | log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size }); |
| 185 | 181 | |
| 186 | 182 | try self.load_commands.append(allocator, .{ |
| 187 | 183 | .segment = .{ |
| ... | ... | @@ -189,13 +185,14 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void |
| 189 | 185 | .segname = makeStaticString("__DWARF"), |
| 190 | 186 | .vmaddr = vmaddr, |
| 191 | 187 | .vmsize = needed_size, |
| 192 | | .fileoff = off, |
| 188 | .fileoff = fileoff, |
| 193 | 189 | .filesize = needed_size, |
| 194 | 190 | }, |
| 195 | 191 | }, |
| 196 | 192 | }); |
| 197 | 193 | self.load_commands_dirty = true; |
| 198 | 194 | } |
| 195 | |
| 199 | 196 | if (self.debug_str_section_index == null) { |
| 200 | 197 | assert(self.debug_string_table.items.len == 0); |
| 201 | 198 | self.debug_str_section_index = try self.allocateSection( |
| ... | ... | @@ -205,18 +202,22 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void |
| 205 | 202 | ); |
| 206 | 203 | self.debug_string_table_dirty = true; |
| 207 | 204 | } |
| 205 | |
| 208 | 206 | if (self.debug_info_section_index == null) { |
| 209 | 207 | self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0); |
| 210 | 208 | self.debug_info_header_dirty = true; |
| 211 | 209 | } |
| 210 | |
| 212 | 211 | if (self.debug_abbrev_section_index == null) { |
| 213 | 212 | self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0); |
| 214 | 213 | self.debug_abbrev_section_dirty = true; |
| 215 | 214 | } |
| 215 | |
| 216 | 216 | if (self.debug_aranges_section_index == null) { |
| 217 | 217 | self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4); |
| 218 | 218 | self.debug_aranges_section_dirty = true; |
| 219 | 219 | } |
| 220 | |
| 220 | 221 | if (self.debug_line_section_index == null) { |
| 221 | 222 | self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0); |
| 222 | 223 | self.debug_line_header_dirty = true; |
| ... | ... | @@ -300,41 +301,91 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 300 | 301 | // we can simply append these bytes. |
| 301 | 302 | const abbrev_buf = [_]u8{ |
| 302 | 303 | abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header |
| 303 | | DW.AT.stmt_list, DW.FORM.sec_offset, // offset |
| 304 | | DW.AT.low_pc, DW.FORM.addr, |
| 305 | | DW.AT.high_pc, DW.FORM.addr, |
| 306 | | DW.AT.name, DW.FORM.strp, |
| 307 | | DW.AT.comp_dir, DW.FORM.strp, |
| 308 | | DW.AT.producer, DW.FORM.strp, |
| 309 | | DW.AT.language, DW.FORM.data2, |
| 310 | | 0, 0, // table sentinel |
| 311 | | abbrev_subprogram, DW.TAG.subprogram, DW.CHILDREN.yes, // header |
| 312 | | DW.AT.low_pc, DW.FORM.addr, // start VM address |
| 313 | | DW.AT.high_pc, DW.FORM.data4, |
| 314 | | DW.AT.type, DW.FORM.ref4, |
| 315 | | DW.AT.name, DW.FORM.string, |
| 316 | | DW.AT.decl_line, DW.FORM.data4, |
| 317 | | DW.AT.decl_file, DW.FORM.data1, |
| 304 | DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, |
| 305 | DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, |
| 306 | DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, |
| 307 | DW.FORM.strp, DW.AT.producer, DW.FORM.strp, |
| 308 | DW.AT.language, DW.FORM.data2, 0, |
| 309 | 0, // table sentinel |
| 310 | abbrev_subprogram, |
| 311 | DW.TAG.subprogram, |
| 312 | DW.CHILDREN.yes, // header |
| 313 | DW.AT.low_pc, |
| 314 | DW.FORM.addr, |
| 315 | DW.AT.high_pc, |
| 316 | DW.FORM.data4, |
| 317 | DW.AT.type, |
| 318 | DW.FORM.ref4, |
| 319 | DW.AT.name, |
| 320 | DW.FORM.string, |
| 318 | 321 | 0, 0, // table sentinel |
| 319 | 322 | abbrev_subprogram_retvoid, |
| 320 | 323 | DW.TAG.subprogram, DW.CHILDREN.yes, // header |
| 321 | 324 | DW.AT.low_pc, DW.FORM.addr, |
| 322 | 325 | DW.AT.high_pc, DW.FORM.data4, |
| 323 | 326 | DW.AT.name, DW.FORM.string, |
| 324 | | DW.AT.decl_line, DW.FORM.data4, |
| 325 | | DW.AT.decl_file, DW.FORM.data1, |
| 326 | | 0, 0, // table sentinel |
| 327 | | abbrev_base_type, DW.TAG.base_type, DW.CHILDREN.no, // header |
| 328 | | DW.AT.encoding, DW.FORM.data1, DW.AT.byte_size, |
| 329 | | DW.FORM.data1, DW.AT.name, DW.FORM.string, |
| 330 | | 0, 0, // table sentinel |
| 331 | | abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header |
| 332 | | 0, 0, // table sentinel |
| 333 | | abbrev_parameter, DW.TAG.formal_parameter, DW.CHILDREN.no, // header |
| 334 | | DW.AT.location, DW.FORM.exprloc, DW.AT.type, |
| 335 | | DW.FORM.ref4, DW.AT.name, DW.FORM.string, |
| 336 | | 0, 0, // table sentinel |
| 337 | | 0, 0, 0, // section sentinel |
| 327 | 0, |
| 328 | 0, // table sentinel |
| 329 | abbrev_base_type, |
| 330 | DW.TAG.base_type, |
| 331 | DW.CHILDREN.no, // header |
| 332 | DW.AT.encoding, |
| 333 | DW.FORM.data1, |
| 334 | DW.AT.byte_size, |
| 335 | DW.FORM.data1, |
| 336 | DW.AT.name, |
| 337 | DW.FORM.string, |
| 338 | 0, |
| 339 | 0, // table sentinel |
| 340 | abbrev_ptr_type, |
| 341 | DW.TAG.pointer_type, |
| 342 | DW.CHILDREN.no, // header |
| 343 | DW.AT.type, |
| 344 | DW.FORM.ref4, |
| 345 | 0, |
| 346 | 0, // table sentinel |
| 347 | abbrev_struct_type, |
| 348 | DW.TAG.structure_type, |
| 349 | DW.CHILDREN.yes, // header |
| 350 | DW.AT.byte_size, |
| 351 | DW.FORM.sdata, |
| 352 | DW.AT.name, |
| 353 | DW.FORM.string, |
| 354 | 0, |
| 355 | 0, // table sentinel |
| 356 | abbrev_anon_struct_type, |
| 357 | DW.TAG.structure_type, |
| 358 | DW.CHILDREN.yes, // header |
| 359 | DW.AT.byte_size, |
| 360 | DW.FORM.sdata, |
| 361 | 0, |
| 362 | 0, // table sentinel |
| 363 | abbrev_struct_member, |
| 364 | DW.TAG.member, |
| 365 | DW.CHILDREN.no, // header |
| 366 | DW.AT.name, |
| 367 | DW.FORM.string, |
| 368 | DW.AT.type, |
| 369 | DW.FORM.ref4, |
| 370 | DW.AT.data_member_location, |
| 371 | DW.FORM.sdata, |
| 372 | 0, |
| 373 | 0, // table sentinel |
| 374 | abbrev_pad1, |
| 375 | DW.TAG.unspecified_type, |
| 376 | DW.CHILDREN.no, // header |
| 377 | 0, |
| 378 | 0, // table sentinel |
| 379 | abbrev_parameter, |
| 380 | DW.TAG.formal_parameter, DW.CHILDREN.no, // header |
| 381 | DW.AT.location, DW.FORM.exprloc, |
| 382 | DW.AT.type, DW.FORM.ref4, |
| 383 | DW.AT.name, DW.FORM.string, |
| 384 | 0, |
| 385 | 0, // table sentinel |
| 386 | 0, |
| 387 | 0, |
| 388 | 0, // section sentinel |
| 338 | 389 | }; |
| 339 | 390 | |
| 340 | 391 | const needed_size = abbrev_buf.len; |
| ... | ... | @@ -583,13 +634,12 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti |
| 583 | 634 | } |
| 584 | 635 | } |
| 585 | 636 | |
| 586 | | try self.writeStringTable(); |
| 637 | try self.writeLinkeditSegment(); |
| 587 | 638 | self.updateDwarfSegment(); |
| 588 | 639 | try self.writeLoadCommands(allocator); |
| 589 | 640 | try self.writeHeader(); |
| 590 | 641 | |
| 591 | 642 | assert(!self.load_commands_dirty); |
| 592 | | assert(!self.strtab_dirty); |
| 593 | 643 | assert(!self.debug_abbrev_section_dirty); |
| 594 | 644 | assert(!self.debug_aranges_section_dirty); |
| 595 | 645 | assert(!self.debug_string_table_dirty); |
| ... | ... | @@ -663,7 +713,7 @@ fn updateDwarfSegment(self: *DebugSymbols) void { |
| 663 | 713 | if (file_size != dwarf_segment.inner.filesize) { |
| 664 | 714 | dwarf_segment.inner.filesize = file_size; |
| 665 | 715 | if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) { |
| 666 | | dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, page_size); |
| 716 | dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, self.base.page_size); |
| 667 | 717 | } |
| 668 | 718 | self.load_commands_dirty = true; |
| 669 | 719 | } |
| ... | ... | @@ -719,23 +769,10 @@ fn writeHeader(self: *DebugSymbols) !void { |
| 719 | 769 | try self.file.pwriteAll(mem.asBytes(&header), 0); |
| 720 | 770 | } |
| 721 | 771 | |
| 722 | | fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 { |
| 723 | | assert(start > 0); |
| 724 | | var min_pos: u64 = std.math.maxInt(u64); |
| 725 | | |
| 726 | | if (self.symtab_cmd_index) |idx| { |
| 727 | | const symtab = self.load_commands.items[idx].symtab; |
| 728 | | if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff; |
| 729 | | if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff; |
| 730 | | } |
| 731 | | |
| 732 | | return min_pos - start; |
| 733 | | } |
| 734 | | |
| 735 | 772 | fn allocatedSize(self: *DebugSymbols, start: u64) u64 { |
| 736 | 773 | const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; |
| 737 | 774 | assert(start >= seg.inner.fileoff); |
| 738 | | var min_pos: u64 = seg.inner.fileoff + seg.inner.filesize; |
| 775 | var min_pos: u64 = std.math.maxInt(u64); |
| 739 | 776 | for (seg.sections.items) |section| { |
| 740 | 777 | if (section.offset <= start) continue; |
| 741 | 778 | if (section.offset < min_pos) min_pos = section.offset; |
| ... | ... | @@ -743,102 +780,72 @@ fn allocatedSize(self: *DebugSymbols, start: u64) u64 { |
| 743 | 780 | return min_pos - start; |
| 744 | 781 | } |
| 745 | 782 | |
| 746 | | fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 { |
| 747 | | const end = start + padToIdeal(size); |
| 783 | fn writeLinkeditSegment(self: *DebugSymbols) !void { |
| 784 | const tracy = trace(@src()); |
| 785 | defer tracy.end(); |
| 748 | 786 | |
| 749 | | if (self.symtab_cmd_index) |idx| outer: { |
| 750 | | if (self.load_commands.items.len == idx) break :outer; |
| 751 | | const symtab = self.load_commands.items[idx].symtab; |
| 752 | | { |
| 753 | | // Symbol table |
| 754 | | const symsize = symtab.nsyms * @sizeOf(macho.nlist_64); |
| 755 | | const increased_size = padToIdeal(symsize); |
| 756 | | const test_end = symtab.symoff + increased_size; |
| 757 | | if (end > symtab.symoff and start < test_end) { |
| 758 | | return test_end; |
| 759 | | } |
| 760 | | } |
| 761 | | { |
| 762 | | // String table |
| 763 | | const increased_size = padToIdeal(symtab.strsize); |
| 764 | | const test_end = symtab.stroff + increased_size; |
| 765 | | if (end > symtab.stroff and start < test_end) { |
| 766 | | return test_end; |
| 767 | | } |
| 768 | | } |
| 769 | | } |
| 787 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 788 | seg.inner.filesize = 0; |
| 770 | 789 | |
| 771 | | return null; |
| 790 | try self.writeSymbolTable(); |
| 791 | try self.writeStringTable(); |
| 772 | 792 | } |
| 773 | 793 | |
| 774 | | fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u16) u64 { |
| 775 | | var start: u64 = self.linkedit_off; |
| 776 | | while (self.detectAllocCollisionLinkedit(start, object_size)) |item_end| { |
| 777 | | start = mem.alignForwardGeneric(u64, item_end, min_alignment); |
| 778 | | } |
| 779 | | return start; |
| 780 | | } |
| 794 | fn writeSymbolTable(self: *DebugSymbols) !void { |
| 795 | const tracy = trace(@src()); |
| 796 | defer tracy.end(); |
| 781 | 797 | |
| 782 | | fn relocateSymbolTable(self: *DebugSymbols) !void { |
| 798 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 783 | 799 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; |
| 784 | | const nlocals = self.base.locals.items.len; |
| 785 | | const nglobals = self.base.globals.items.len; |
| 786 | | const nsyms = nlocals + nglobals; |
| 787 | | |
| 788 | | if (symtab.nsyms < nsyms) { |
| 789 | | const needed_size = nsyms * @sizeOf(macho.nlist_64); |
| 790 | | if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) { |
| 791 | | // Move the entire symbol table to a new location |
| 792 | | const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64)); |
| 793 | | const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64); |
| 794 | | |
| 795 | | assert(new_symoff + existing_size <= self.linkedit_off + self.linkedit_size); // TODO expand LINKEDIT segment. |
| 796 | | log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{ |
| 797 | | symtab.symoff, |
| 798 | | symtab.symoff + existing_size, |
| 799 | | new_symoff, |
| 800 | | new_symoff + existing_size, |
| 801 | | }); |
| 800 | symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); |
| 802 | 801 | |
| 803 | | const amt = try self.file.copyRangeAll(symtab.symoff, self.file, new_symoff, existing_size); |
| 804 | | if (amt != existing_size) return error.InputOutput; |
| 805 | | symtab.symoff = @intCast(u32, new_symoff); |
| 806 | | } |
| 807 | | symtab.nsyms = @intCast(u32, nsyms); |
| 808 | | self.load_commands_dirty = true; |
| 802 | var locals = std.ArrayList(macho.nlist_64).init(self.base.base.allocator); |
| 803 | defer locals.deinit(); |
| 804 | |
| 805 | for (self.base.locals.items) |sym| { |
| 806 | if (sym.n_strx == 0) continue; |
| 807 | if (self.base.symbol_resolver.get(sym.n_strx)) |_| continue; |
| 808 | try locals.append(sym); |
| 809 | 809 | } |
| 810 | | } |
| 811 | 810 | |
| 812 | | pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void { |
| 813 | | const tracy = trace(@src()); |
| 814 | | defer tracy.end(); |
| 815 | | try self.relocateSymbolTable(); |
| 816 | | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; |
| 817 | | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| 818 | | log.debug("writing local symbol {} at 0x{x}", .{ index, off }); |
| 819 | | try self.file.pwriteAll(mem.asBytes(&self.base.locals.items[index]), off); |
| 811 | const nlocals = locals.items.len; |
| 812 | const nexports = self.base.globals.items.len; |
| 813 | |
| 814 | const locals_off = symtab.symoff; |
| 815 | const locals_size = nlocals * @sizeOf(macho.nlist_64); |
| 816 | log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off }); |
| 817 | try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off); |
| 818 | |
| 819 | const exports_off = locals_off + locals_size; |
| 820 | const exports_size = nexports * @sizeOf(macho.nlist_64); |
| 821 | log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off }); |
| 822 | try self.file.pwriteAll(mem.sliceAsBytes(self.base.globals.items), exports_off); |
| 823 | |
| 824 | symtab.nsyms = @intCast(u32, nlocals + nexports); |
| 825 | seg.inner.filesize += locals_size + exports_size; |
| 826 | |
| 827 | self.load_commands_dirty = true; |
| 820 | 828 | } |
| 821 | 829 | |
| 822 | 830 | fn writeStringTable(self: *DebugSymbols) !void { |
| 823 | | if (!self.strtab_dirty) return; |
| 824 | | |
| 825 | 831 | const tracy = trace(@src()); |
| 826 | 832 | defer tracy.end(); |
| 827 | 833 | |
| 834 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 828 | 835 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; |
| 829 | | const allocated_size = self.allocatedSizeLinkedit(symtab.stroff); |
| 830 | | const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64)); |
| 836 | symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); |
| 837 | symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64))); |
| 838 | seg.inner.filesize += symtab.strsize; |
| 831 | 839 | |
| 832 | | if (needed_size > allocated_size) { |
| 833 | | symtab.strsize = 0; |
| 834 | | symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1)); |
| 835 | | } |
| 836 | | symtab.strsize = @intCast(u32, needed_size); |
| 837 | 840 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); |
| 838 | 841 | |
| 839 | 842 | try self.file.pwriteAll(self.base.strtab.items, symtab.stroff); |
| 843 | |
| 844 | if (symtab.strsize > self.base.strtab.items.len) { |
| 845 | // This is potentially the last section, so we need to pad it out. |
| 846 | try self.file.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); |
| 847 | } |
| 840 | 848 | self.load_commands_dirty = true; |
| 841 | | self.strtab_dirty = false; |
| 842 | 849 | } |
| 843 | 850 | |
| 844 | 851 | pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void { |
| ... | ... | @@ -846,14 +853,21 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M |
| 846 | 853 | const tracy = trace(@src()); |
| 847 | 854 | defer tracy.end(); |
| 848 | 855 | |
| 856 | log.debug("updateDeclLineNumber {s}{*}", .{ decl.name, decl }); |
| 857 | |
| 849 | 858 | const func = decl.val.castTag(.function).?.data; |
| 850 | | const line_off = @intCast(u28, decl.src_line + func.lbrace_line); |
| 859 | log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ |
| 860 | decl.src_line, |
| 861 | func.lbrace_line, |
| 862 | func.rbrace_line, |
| 863 | }); |
| 864 | const line = @intCast(u28, decl.src_line + func.lbrace_line); |
| 851 | 865 | |
| 852 | 866 | const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; |
| 853 | 867 | const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?]; |
| 854 | 868 | const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff(); |
| 855 | 869 | var data: [4]u8 = undefined; |
| 856 | | leb.writeUnsignedFixed(4, &data, line_off); |
| 870 | leb.writeUnsignedFixed(4, &data, line); |
| 857 | 871 | try self.file.pwriteAll(&data, file_pos); |
| 858 | 872 | } |
| 859 | 873 | |
| ... | ... | @@ -886,7 +900,13 @@ pub fn initDeclDebugBuffers( |
| 886 | 900 | try dbg_line_buffer.ensureTotalCapacity(26); |
| 887 | 901 | |
| 888 | 902 | const func = decl.val.castTag(.function).?.data; |
| 889 | | const line_off = @intCast(u28, decl.src_line + func.lbrace_line); |
| 903 | log.debug("updateFunc {s}{*}", .{ decl.name, func.owner_decl }); |
| 904 | log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{ |
| 905 | decl.src_line, |
| 906 | func.lbrace_line, |
| 907 | func.rbrace_line, |
| 908 | }); |
| 909 | const line = @intCast(u28, decl.src_line + func.lbrace_line); |
| 890 | 910 | |
| 891 | 911 | dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{ |
| 892 | 912 | DW.LNS.extended_op, |
| ... | ... | @@ -902,7 +922,7 @@ pub fn initDeclDebugBuffers( |
| 902 | 922 | // to this function's begin curly. |
| 903 | 923 | assert(getRelocDbgLineOff() == dbg_line_buffer.items.len); |
| 904 | 924 | // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later. |
| 905 | | leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off); |
| 925 | leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line); |
| 906 | 926 | |
| 907 | 927 | dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file); |
| 908 | 928 | assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len); |
| ... | ... | @@ -917,7 +937,7 @@ pub fn initDeclDebugBuffers( |
| 917 | 937 | |
| 918 | 938 | // .debug_info subprogram |
| 919 | 939 | const decl_name_with_null = decl.name[0 .. mem.sliceTo(decl.name, 0).len + 1]; |
| 920 | | try dbg_info_buffer.ensureUnusedCapacity(27 + decl_name_with_null.len); |
| 940 | try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len); |
| 921 | 941 | |
| 922 | 942 | const fn_ret_type = decl.ty.fnReturnType(); |
| 923 | 943 | const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(); |
| ... | ... | @@ -945,8 +965,6 @@ pub fn initDeclDebugBuffers( |
| 945 | 965 | dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 |
| 946 | 966 | } |
| 947 | 967 | dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string |
| 948 | | mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT.decl_line, DW.FORM.data4 |
| 949 | | dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT.decl_file, DW.FORM.data1 |
| 950 | 968 | }, |
| 951 | 969 | else => { |
| 952 | 970 | // TODO implement .debug_info for global variables |
| ... | ... | @@ -966,7 +984,6 @@ pub fn commitDeclDebugInfo( |
| 966 | 984 | module: *Module, |
| 967 | 985 | decl: *Module.Decl, |
| 968 | 986 | debug_buffers: *DeclDebugBuffers, |
| 969 | | target: std.Target, |
| 970 | 987 | ) !void { |
| 971 | 988 | const tracy = trace(@src()); |
| 972 | 989 | defer tracy.end(); |
| ... | ... | @@ -1097,14 +1114,26 @@ pub fn commitDeclDebugInfo( |
| 1097 | 1114 | if (dbg_info_buffer.items.len == 0) |
| 1098 | 1115 | return; |
| 1099 | 1116 | |
| 1117 | // We need this for the duration of this function only so that for composite |
| 1118 | // types such as []const u32, if the type *u32 is non-existent, we create |
| 1119 | // it synthetically and store the backing bytes in this arena. After we are |
| 1120 | // done with the relocations, we can safely deinit the entire memory slab. |
| 1121 | // TODO currently, we do not store the relocations for future use, however, |
| 1122 | // if that is the case, we should move memory management to a higher scope, |
| 1123 | // such as linker scope, or whatnot. |
| 1124 | var dbg_type_arena = std.heap.ArenaAllocator.init(allocator); |
| 1125 | defer dbg_type_arena.deinit(); |
| 1126 | |
| 1100 | 1127 | { |
| 1101 | 1128 | // Now we emit the .debug_info types of the Decl. These will count towards the size of |
| 1102 | 1129 | // the buffer, so we have to do it before computing the offset, and we can't perform the actual |
| 1103 | 1130 | // relocations yet. |
| 1104 | | var it = dbg_info_type_relocs.iterator(); |
| 1105 | | while (it.next()) |entry| { |
| 1106 | | entry.value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); |
| 1107 | | try self.addDbgInfoType(entry.key_ptr.*, dbg_info_buffer, target); |
| 1131 | var it: usize = 0; |
| 1132 | while (it < dbg_info_type_relocs.count()) : (it += 1) { |
| 1133 | const ty = dbg_info_type_relocs.keys()[it]; |
| 1134 | const value_ptr = dbg_info_type_relocs.getPtr(ty).?; |
| 1135 | value_ptr.off = @intCast(u32, dbg_info_buffer.items.len); |
| 1136 | try self.addDbgInfoType(dbg_type_arena.allocator(), ty, dbg_info_buffer, dbg_info_type_relocs); |
| 1108 | 1137 | } |
| 1109 | 1138 | } |
| 1110 | 1139 | |
| ... | ... | @@ -1129,24 +1158,25 @@ pub fn commitDeclDebugInfo( |
| 1129 | 1158 | /// Asserts the type has codegen bits. |
| 1130 | 1159 | fn addDbgInfoType( |
| 1131 | 1160 | self: *DebugSymbols, |
| 1161 | arena: Allocator, |
| 1132 | 1162 | ty: Type, |
| 1133 | 1163 | dbg_info_buffer: *std.ArrayList(u8), |
| 1134 | | target: std.Target, |
| 1164 | dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable, |
| 1135 | 1165 | ) !void { |
| 1136 | | _ = self; |
| 1166 | const target = self.base.base.options.target; |
| 1167 | var relocs = std.ArrayList(struct { ty: Type, reloc: u32 }).init(arena); |
| 1168 | |
| 1137 | 1169 | switch (ty.zigTypeTag()) { |
| 1138 | | .Void => unreachable, |
| 1139 | 1170 | .NoReturn => unreachable, |
| 1171 | .Void => { |
| 1172 | try dbg_info_buffer.append(abbrev_pad1); |
| 1173 | }, |
| 1140 | 1174 | .Bool => { |
| 1141 | 1175 | try dbg_info_buffer.appendSlice(&[_]u8{ |
| 1142 | 1176 | abbrev_base_type, |
| 1143 | 1177 | DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 |
| 1144 | 1178 | 1, // DW.AT.byte_size, DW.FORM.data1 |
| 1145 | | 'b', |
| 1146 | | 'o', |
| 1147 | | 'o', |
| 1148 | | 'l', |
| 1149 | | 0, // DW.AT.name, DW.FORM.string |
| 1179 | 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string |
| 1150 | 1180 | }); |
| 1151 | 1181 | }, |
| 1152 | 1182 | .Int => { |
| ... | ... | @@ -1163,11 +1193,120 @@ fn addDbgInfoType( |
| 1163 | 1193 | // DW.AT.name, DW.FORM.string |
| 1164 | 1194 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); |
| 1165 | 1195 | }, |
| 1196 | .Optional => { |
| 1197 | if (ty.isPtrLikeOptional()) { |
| 1198 | try dbg_info_buffer.ensureUnusedCapacity(12); |
| 1199 | dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); |
| 1200 | // DW.AT.encoding, DW.FORM.data1 |
| 1201 | dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); |
| 1202 | // DW.AT.byte_size, DW.FORM.data1 |
| 1203 | dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target))); |
| 1204 | // DW.AT.name, DW.FORM.string |
| 1205 | try dbg_info_buffer.writer().print("{}\x00", .{ty}); |
| 1206 | } else { |
| 1207 | log.debug("TODO implement .debug_info for type '{}'", .{ty}); |
| 1208 | try dbg_info_buffer.append(abbrev_pad1); |
| 1209 | } |
| 1210 | }, |
| 1211 | .Pointer => { |
| 1212 | if (ty.isSlice()) { |
| 1213 | // Slices are anonymous structs: struct { .ptr = *, .len = N } |
| 1214 | try dbg_info_buffer.ensureUnusedCapacity(23); |
| 1215 | // DW.AT.structure_type |
| 1216 | dbg_info_buffer.appendAssumeCapacity(abbrev_anon_struct_type); |
| 1217 | // DW.AT.byte_size, DW.FORM.sdata |
| 1218 | dbg_info_buffer.appendAssumeCapacity(16); |
| 1219 | // DW.AT.member |
| 1220 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); |
| 1221 | // DW.AT.name, DW.FORM.string |
| 1222 | dbg_info_buffer.appendSliceAssumeCapacity("ptr"); |
| 1223 | dbg_info_buffer.appendAssumeCapacity(0); |
| 1224 | // DW.AT.type, DW.FORM.ref4 |
| 1225 | var index = dbg_info_buffer.items.len; |
| 1226 | try dbg_info_buffer.resize(index + 4); |
| 1227 | var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); |
| 1228 | const ptr_ty = ty.slicePtrFieldType(buf); |
| 1229 | try relocs.append(.{ .ty = ptr_ty, .reloc = @intCast(u32, index) }); |
| 1230 | // DW.AT.data_member_location, DW.FORM.sdata |
| 1231 | dbg_info_buffer.appendAssumeCapacity(0); |
| 1232 | // DW.AT.member |
| 1233 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); |
| 1234 | // DW.AT.name, DW.FORM.string |
| 1235 | dbg_info_buffer.appendSliceAssumeCapacity("len"); |
| 1236 | dbg_info_buffer.appendAssumeCapacity(0); |
| 1237 | // DW.AT.type, DW.FORM.ref4 |
| 1238 | index = dbg_info_buffer.items.len; |
| 1239 | try dbg_info_buffer.resize(index + 4); |
| 1240 | try relocs.append(.{ .ty = Type.initTag(.usize), .reloc = @intCast(u32, index) }); |
| 1241 | // DW.AT.data_member_location, DW.FORM.sdata |
| 1242 | dbg_info_buffer.appendAssumeCapacity(8); |
| 1243 | // DW.AT.structure_type delimit children |
| 1244 | dbg_info_buffer.appendAssumeCapacity(0); |
| 1245 | } else { |
| 1246 | try dbg_info_buffer.ensureUnusedCapacity(5); |
| 1247 | dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); |
| 1248 | // DW.AT.type, DW.FORM.ref4 |
| 1249 | const index = dbg_info_buffer.items.len; |
| 1250 | try dbg_info_buffer.resize(index + 4); |
| 1251 | try relocs.append(.{ .ty = ty.childType(), .reloc = @intCast(u32, index) }); |
| 1252 | } |
| 1253 | }, |
| 1254 | .Struct => blk: { |
| 1255 | // try dbg_info_buffer.ensureUnusedCapacity(23); |
| 1256 | // DW.AT.structure_type |
| 1257 | try dbg_info_buffer.append(abbrev_struct_type); |
| 1258 | // DW.AT.byte_size, DW.FORM.sdata |
| 1259 | const abi_size = ty.abiSize(target); |
| 1260 | try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); |
| 1261 | // DW.AT.name, DW.FORM.string |
| 1262 | const struct_name = try ty.nameAlloc(arena); |
| 1263 | try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1); |
| 1264 | dbg_info_buffer.appendSliceAssumeCapacity(struct_name); |
| 1265 | dbg_info_buffer.appendAssumeCapacity(0); |
| 1266 | |
| 1267 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 1268 | if (struct_obj.layout == .Packed) { |
| 1269 | log.debug("TODO implement .debug_info for packed structs", .{}); |
| 1270 | break :blk; |
| 1271 | } |
| 1272 | |
| 1273 | const fields = ty.structFields(); |
| 1274 | for (fields.keys()) |field_name, field_index| { |
| 1275 | const field = fields.get(field_name).?; |
| 1276 | // DW.AT.member |
| 1277 | try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); |
| 1278 | dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); |
| 1279 | // DW.AT.name, DW.FORM.string |
| 1280 | dbg_info_buffer.appendSliceAssumeCapacity(field_name); |
| 1281 | dbg_info_buffer.appendAssumeCapacity(0); |
| 1282 | // DW.AT.type, DW.FORM.ref4 |
| 1283 | var index = dbg_info_buffer.items.len; |
| 1284 | try dbg_info_buffer.resize(index + 4); |
| 1285 | try relocs.append(.{ .ty = field.ty, .reloc = @intCast(u32, index) }); |
| 1286 | // DW.AT.data_member_location, DW.FORM.sdata |
| 1287 | const field_off = ty.structFieldOffset(field_index, target); |
| 1288 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); |
| 1289 | } |
| 1290 | |
| 1291 | // DW.AT.structure_type delimit children |
| 1292 | try dbg_info_buffer.append(0); |
| 1293 | }, |
| 1166 | 1294 | else => { |
| 1167 | | std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty}); |
| 1295 | log.debug("TODO implement .debug_info for type '{}'", .{ty}); |
| 1168 | 1296 | try dbg_info_buffer.append(abbrev_pad1); |
| 1169 | 1297 | }, |
| 1170 | 1298 | } |
| 1299 | |
| 1300 | for (relocs.items) |rel| { |
| 1301 | const gop = try dbg_info_type_relocs.getOrPut(self.base.base.allocator, rel.ty); |
| 1302 | if (!gop.found_existing) { |
| 1303 | gop.value_ptr.* = .{ |
| 1304 | .off = undefined, |
| 1305 | .relocs = .{}, |
| 1306 | }; |
| 1307 | } |
| 1308 | try gop.value_ptr.relocs.append(self.base.base.allocator, rel.reloc); |
| 1309 | } |
| 1171 | 1310 | } |
| 1172 | 1311 | |
| 1173 | 1312 | fn updateDeclDebugInfoAllocation( |