authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-31 11:38:13+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-12-31 11:38:13+01:00
log46ea7a704ce4ce0669941b324296ca6092f9f2f6
treeebe2ed9db503188e661eca7c0a31a954aad033c3
parent595397dbeb17305d50f5995871fc2bf8f0377580
parent0fd3015e558a8b4decf535e75481cdbc29540ff8
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7608 from kubkon/macho-dsym

macho: add preliminary support for DWARF debugging symbols

3 files changed, 1826 insertions(+), 138 deletions(-)

src/link/MachO.zig+240-137
......@@ -3,6 +3,7 @@ const MachO = @This();
33const std = @import("std");
44const Allocator = std.mem.Allocator;
55const assert = std.debug.assert;
6const fmt = std.fmt;
67const fs = std.fs;
78const log = std.log.scoped(.link);
89const macho = std.macho;
......@@ -12,7 +13,6 @@ const math = std.math;
1213const mem = std.mem;
1314
1415const trace = @import("../tracy.zig").trace;
15const Type = @import("../type.zig").Type;
1616const build_options = @import("build_options");
1717const Module = @import("../Module.zig");
1818const Compilation = @import("../Compilation.zig");
......@@ -21,6 +21,7 @@ const File = link.File;
2121const Cache = @import("../Cache.zig");
2222const target_util = @import("../target.zig");
2323
24const DebugSymbols = @import("MachO/DebugSymbols.zig");
2425const Trie = @import("MachO/Trie.zig");
2526const CodeSignature = @import("MachO/CodeSignature.zig");
2627
......@@ -31,6 +32,9 @@ pub const base_tag: File.Tag = File.Tag.macho;
3132
3233base: File,
3334
35/// Debug symbols bundle (or dSym).
36d_sym: ?DebugSymbols = null,
37
3438/// Page size is dependent on the target cpu architecture.
3539/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
3640page_size: u16,
......@@ -74,6 +78,8 @@ main_cmd_index: ?u16 = null,
7478version_min_cmd_index: ?u16 = null,
7579/// Source version
7680source_version_cmd_index: ?u16 = null,
81/// UUID load command
82uuid_cmd_index: ?u16 = null,
7783/// Code signature
7884code_signature_cmd_index: ?u16 = null,
7985
......@@ -155,8 +161,8 @@ pub const PieFixup = struct {
155161};
156162
157163/// `alloc_num / alloc_den` is the factor of padding when allocating.
158const alloc_num = 4;
159const alloc_den = 3;
164pub const alloc_num = 4;
165pub const alloc_den = 3;
160166
161167/// Default path to dyld
162168/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
......@@ -197,12 +203,25 @@ pub const TextBlock = struct {
197203 prev: ?*TextBlock,
198204 next: ?*TextBlock,
199205
206 /// Previous/next linked list pointers. This value is `next ^ prev`.
207 /// This is the linked list node for this Decl's corresponding .debug_info tag.
208 dbg_info_prev: ?*TextBlock,
209 dbg_info_next: ?*TextBlock,
210 /// Offset into .debug_info pointing to the tag for this Decl.
211 dbg_info_off: u32,
212 /// Size of the .debug_info tag for this Decl, not including padding.
213 dbg_info_len: u32,
214
200215 pub const empty = TextBlock{
201216 .local_sym_index = 0,
202217 .offset_table_index = undefined,
203218 .size = 0,
204219 .prev = null,
205220 .next = null,
221 .dbg_info_prev = null,
222 .dbg_info_next = null,
223 .dbg_info_off = undefined,
224 .dbg_info_len = undefined,
206225 };
207226
208227 /// Returns how much room there is to grow in virtual address space.
......@@ -238,7 +257,23 @@ pub const Export = struct {
238257};
239258
240259pub const SrcFn = struct {
241 pub const empty = SrcFn{};
260 /// Offset from the beginning of the Debug Line Program header that contains this function.
261 off: u32,
262 /// Size of the line number program component belonging to this function, not
263 /// including padding.
264 len: u32,
265
266 /// Points to the previous and next neighbors, based on the offset from .debug_line.
267 /// This can be used to find, for example, the capacity of this `SrcFn`.
268 prev: ?*SrcFn,
269 next: ?*SrcFn,
270
271 pub const empty: SrcFn = .{
272 .off = 0,
273 .len = 0,
274 .prev = null,
275 .next = null,
276 };
242277};
243278
244279pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*MachO {
......@@ -262,6 +297,20 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
262297
263298 self.base.file = file;
264299
300 // Create dSYM bundle.
301 const d_sym_path = try fmt.allocPrint(allocator, "{}.dSYM/Contents/Resources/DWARF/", .{sub_path});
302 defer allocator.free(d_sym_path);
303 var d_sym_bundle = try options.emit.?.directory.handle.makeOpenPath(d_sym_path, .{});
304 defer d_sym_bundle.close();
305 const d_sym_file = try d_sym_bundle.createFile(sub_path, .{
306 .truncate = false,
307 .read = true,
308 });
309 self.d_sym = .{
310 .base = self,
311 .file = d_sym_file,
312 };
313
265314 // Index 0 is always a null symbol.
266315 try self.local_symbols.append(allocator, .{
267316 .n_strx = 0,
......@@ -278,6 +327,12 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
278327 }
279328
280329 try self.populateMissingMetadata();
330 try self.writeLocalSymbol(0);
331
332 if (self.d_sym) |*ds| {
333 try ds.populateMissingMetadata(allocator);
334 try ds.writeLocalSymbol(0);
335 }
281336
282337 return self;
283338}
......@@ -331,6 +386,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
331386 try self.writeStringTable();
332387 try self.updateLinkeditSegmentSizes();
333388
389 if (self.d_sym) |*ds| {
390 // Flush debug symbols bundle.
391 try ds.flushModule(self.base.allocator, self.base.options);
392 }
393
334394 if (target.cpu.arch == .aarch64) {
335395 // Preallocate space for the code signature.
336396 // We need to do this at this stage so that we have the load commands with proper values
......@@ -941,6 +1001,9 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
9411001}
9421002
9431003pub fn deinit(self: *MachO) void {
1004 if (self.d_sym) |*ds| {
1005 ds.deinit(self.base.allocator);
1006 }
9441007 self.binding_info_table.deinit(self.base.allocator);
9451008 self.lazy_binding_info_table.deinit(self.base.allocator);
9461009 self.pie_fixups.deinit(self.base.allocator);
......@@ -1054,8 +1117,30 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
10541117 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
10551118 defer code_buffer.deinit();
10561119
1120 var debug_buffers = if (self.d_sym) |*ds| try ds.initDeclDebugBuffers(self.base.allocator, module, decl) else null;
1121 defer {
1122 if (debug_buffers) |*dbg| {
1123 dbg.dbg_line_buffer.deinit();
1124 dbg.dbg_info_buffer.deinit();
1125 var it = dbg.dbg_info_type_relocs.iterator();
1126 while (it.next()) |entry| {
1127 entry.value.relocs.deinit(self.base.allocator);
1128 }
1129 dbg.dbg_info_type_relocs.deinit(self.base.allocator);
1130 }
1131 }
1132
10571133 const typed_value = decl.typed_value.most_recent.typed_value;
1058 const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
1134 const res = if (debug_buffers) |*dbg|
1135 try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .{
1136 .dwarf = .{
1137 .dbg_line = &dbg.dbg_line_buffer,
1138 .dbg_info = &dbg.dbg_info_buffer,
1139 .dbg_info_type_relocs = &dbg.dbg_info_type_relocs,
1140 },
1141 })
1142 else
1143 try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none);
10591144
10601145 const code = switch (res) {
10611146 .externally_managed => |x| x,
......@@ -1093,6 +1178,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
10931178 symbol.n_desc = 0;
10941179
10951180 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1181 if (self.d_sym) |*ds|
1182 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
10961183 } else {
10971184 const decl_name = mem.spanZ(decl.name);
10981185 const name_str_index = try self.makeString(decl_name);
......@@ -1110,6 +1197,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11101197 self.offset_table.items[decl.link.macho.offset_table_index] = addr;
11111198
11121199 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1200 if (self.d_sym) |*ds|
1201 try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
11131202 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
11141203 }
11151204
......@@ -1139,12 +1228,26 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11391228 const file_offset = text_section.offset + section_offset;
11401229 try self.base.file.?.pwriteAll(code, file_offset);
11411230
1231 if (debug_buffers) |*db| {
1232 try self.d_sym.?.commitDeclDebugInfo(
1233 self.base.allocator,
1234 module,
1235 decl,
1236 db,
1237 self.base.options.target,
1238 );
1239 }
1240
11421241 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
11431242 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
11441243 try self.updateDeclExports(module, decl, decl_exports);
11451244}
11461245
1147pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
1246pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
1247 if (self.d_sym) |*ds| {
1248 try ds.updateDeclLineNumber(module, decl);
1249 }
1250}
11481251
11491252pub fn updateDeclExports(
11501253 self: *MachO,
......@@ -1358,7 +1461,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13581461 };
13591462 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
13601463 const needed_size = self.base.options.program_code_size_hint;
1361 const off = self.findFreeSpace(text_segment, needed_size, @as(u16, 1) << alignment);
1464 const off = text_segment.findFreeSpace(needed_size, @as(u16, 1) << alignment, self.header_pad);
13621465
13631466 log.debug("found __text section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
13641467
......@@ -1386,7 +1489,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13861489
13871490 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
13881491 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1389 const off = self.findFreeSpace(text_segment, needed_size, @alignOf(u64));
1492 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
13901493 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
13911494
13921495 log.debug("found __ziggot section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
......@@ -1397,7 +1500,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13971500 .addr = text_segment.inner.vmaddr + off,
13981501 .size = needed_size,
13991502 .offset = @intCast(u32, off),
1400 .@"align" = @sizeOf(u64),
1503 .@"align" = 3, // 2^@sizeOf(u64)
14011504 .reloff = 0,
14021505 .nreloc = 0,
14031506 .flags = flags,
......@@ -1437,11 +1540,10 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14371540 }
14381541 if (self.dyld_info_cmd_index == null) {
14391542 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
1440 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
14411543
14421544 // TODO Preallocate rebase, binding, and lazy binding info.
14431545 const export_size = 2;
1444 const export_off = self.findFreeSpace(&linkedit_segment, export_size, 1);
1546 const export_off = self.findFreeSpaceLinkedit(export_size, 1);
14451547
14461548 log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
14471549
......@@ -1466,16 +1568,15 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14661568 }
14671569 if (self.symtab_cmd_index == null) {
14681570 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
1469 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
14701571
14711572 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);
1472 const symtab_off = self.findFreeSpace(&linkedit_segment, symtab_size, @sizeOf(macho.nlist_64));
1573 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
14731574
14741575 log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });
14751576
14761577 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.
14771578 const strtab_size = self.string_table.items.len;
1478 const strtab_off = self.findFreeSpace(&linkedit_segment, strtab_size, 1);
1579 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1);
14791580
14801581 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });
14811582
......@@ -1489,7 +1590,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14891590 .strsize = @intCast(u32, strtab_size),
14901591 },
14911592 });
1492 try self.writeLocalSymbol(0);
14931593 self.header_dirty = true;
14941594 self.load_commands_dirty = true;
14951595 self.string_table_dirty = true;
......@@ -1611,9 +1711,20 @@ pub fn populateMissingMetadata(self: *MachO) !void {
16111711 self.header_dirty = true;
16121712 self.load_commands_dirty = true;
16131713 }
1714 if (self.uuid_cmd_index == null) {
1715 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
1716 var uuid_cmd: macho.uuid_command = .{
1717 .cmd = macho.LC_UUID,
1718 .cmdsize = @sizeOf(macho.uuid_command),
1719 .uuid = undefined,
1720 };
1721 std.crypto.random.bytes(&uuid_cmd.uuid);
1722 try self.load_commands.append(self.base.allocator, .{ .Uuid = uuid_cmd });
1723 self.header_dirty = true;
1724 self.load_commands_dirty = true;
1725 }
16141726 if (self.code_signature_cmd_index == null) {
16151727 self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
1616 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
16171728 try self.load_commands.append(self.base.allocator, .{
16181729 .LinkeditData = .{
16191730 .cmd = macho.LC_CODE_SIGNATURE,
......@@ -1710,8 +1821,14 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
17101821
17111822 self.last_text_block = text_block;
17121823 text_section.size = needed_size;
1713
17141824 self.load_commands_dirty = true; // TODO Make more granular.
1825
1826 if (self.d_sym) |*ds| {
1827 const debug_text_seg = &ds.load_commands.items[ds.text_segment_cmd_index.?].Segment;
1828 const debug_text_sect = &debug_text_seg.sections.items[ds.text_section_index.?];
1829 debug_text_sect.size = needed_size;
1830 ds.load_commands_dirty = true;
1831 }
17151832 }
17161833 text_block.size = new_block_size;
17171834
......@@ -1750,6 +1867,8 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {
17501867 self.string_table.appendSliceAssumeCapacity(bytes);
17511868 self.string_table.appendAssumeCapacity(0);
17521869 self.string_table_dirty = true;
1870 if (self.d_sym) |*ds|
1871 ds.string_table_dirty = true;
17531872 return @intCast(u32, result);
17541873}
17551874
......@@ -1790,48 +1909,41 @@ fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
17901909 };
17911910}
17921911
1793fn allocatedSize(self: *MachO, segment: *const SegmentCommand, start: u64) u64 {
1912fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 {
17941913 assert(start > 0);
17951914 var min_pos: u64 = std.math.maxInt(u64);
17961915
1797 if (parseAndCmpName(&segment.inner.segname, "__LINKEDIT")) {
1798 assert(segment.sections.items.len == 0);
1799 // __LINKEDIT is a weird segment where sections get their own load commands so we
1800 // special-case it.
1801 if (self.dyld_info_cmd_index) |idx| {
1802 const dyld_info = self.load_commands.items[idx].DyldInfoOnly;
1803 if (dyld_info.rebase_off > start and dyld_info.rebase_off < min_pos) min_pos = dyld_info.rebase_off;
1804 if (dyld_info.bind_off > start and dyld_info.bind_off < min_pos) min_pos = dyld_info.bind_off;
1805 if (dyld_info.weak_bind_off > start and dyld_info.weak_bind_off < min_pos) min_pos = dyld_info.weak_bind_off;
1806 if (dyld_info.lazy_bind_off > start and dyld_info.lazy_bind_off < min_pos) min_pos = dyld_info.lazy_bind_off;
1807 if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off;
1808 }
1916 // __LINKEDIT is a weird segment where sections get their own load commands so we
1917 // special-case it.
1918 if (self.dyld_info_cmd_index) |idx| {
1919 const dyld_info = self.load_commands.items[idx].DyldInfoOnly;
1920 if (dyld_info.rebase_off > start and dyld_info.rebase_off < min_pos) min_pos = dyld_info.rebase_off;
1921 if (dyld_info.bind_off > start and dyld_info.bind_off < min_pos) min_pos = dyld_info.bind_off;
1922 if (dyld_info.weak_bind_off > start and dyld_info.weak_bind_off < min_pos) min_pos = dyld_info.weak_bind_off;
1923 if (dyld_info.lazy_bind_off > start and dyld_info.lazy_bind_off < min_pos) min_pos = dyld_info.lazy_bind_off;
1924 if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off;
1925 }
18091926
1810 if (self.function_starts_cmd_index) |idx| {
1811 const fstart = self.load_commands.items[idx].LinkeditData;
1812 if (fstart.dataoff > start and fstart.dataoff < min_pos) min_pos = fstart.dataoff;
1813 }
1927 if (self.function_starts_cmd_index) |idx| {
1928 const fstart = self.load_commands.items[idx].LinkeditData;
1929 if (fstart.dataoff > start and fstart.dataoff < min_pos) min_pos = fstart.dataoff;
1930 }
18141931
1815 if (self.data_in_code_cmd_index) |idx| {
1816 const dic = self.load_commands.items[idx].LinkeditData;
1817 if (dic.dataoff > start and dic.dataoff < min_pos) min_pos = dic.dataoff;
1818 }
1932 if (self.data_in_code_cmd_index) |idx| {
1933 const dic = self.load_commands.items[idx].LinkeditData;
1934 if (dic.dataoff > start and dic.dataoff < min_pos) min_pos = dic.dataoff;
1935 }
18191936
1820 if (self.dysymtab_cmd_index) |idx| {
1821 const dysymtab = self.load_commands.items[idx].Dysymtab;
1822 if (dysymtab.indirectsymoff > start and dysymtab.indirectsymoff < min_pos) min_pos = dysymtab.indirectsymoff;
1823 // TODO Handle more dynamic symbol table sections.
1824 }
1937 if (self.dysymtab_cmd_index) |idx| {
1938 const dysymtab = self.load_commands.items[idx].Dysymtab;
1939 if (dysymtab.indirectsymoff > start and dysymtab.indirectsymoff < min_pos) min_pos = dysymtab.indirectsymoff;
1940 // TODO Handle more dynamic symbol table sections.
1941 }
18251942
1826 if (self.symtab_cmd_index) |idx| {
1827 const symtab = self.load_commands.items[idx].Symtab;
1828 if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;
1829 if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff;
1830 }
1831 } else {
1832 for (segment.sections.items) |section| {
1833 if (section.offset > start and section.offset < min_pos) min_pos = section.offset;
1834 }
1943 if (self.symtab_cmd_index) |idx| {
1944 const symtab = self.load_commands.items[idx].Symtab;
1945 if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;
1946 if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff;
18351947 }
18361948
18371949 return min_pos - start;
......@@ -1846,101 +1958,90 @@ inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 {
18461958 return null;
18471959}
18481960
1849fn detectAllocCollision(self: *MachO, segment: *const SegmentCommand, start: u64, size: u64) ?u64 {
1961fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 {
18501962 const end = start + satMul(size, alloc_num) / alloc_den;
18511963
1852 if (parseAndCmpName(&segment.inner.segname, "__LINKEDIT")) {
1853 assert(segment.sections.items.len == 0);
1854 // __LINKEDIT is a weird segment where sections get their own load commands so we
1855 // special-case it.
1856 if (self.dyld_info_cmd_index) |idx| outer: {
1857 if (self.load_commands.items.len == idx) break :outer;
1858 const dyld_info = self.load_commands.items[idx].DyldInfoOnly;
1859 if (checkForCollision(start, end, dyld_info.rebase_off, dyld_info.rebase_size)) |pos| {
1860 return pos;
1861 }
1862 // Binding info
1863 if (checkForCollision(start, end, dyld_info.bind_off, dyld_info.bind_size)) |pos| {
1864 return pos;
1865 }
1866 // Weak binding info
1867 if (checkForCollision(start, end, dyld_info.weak_bind_off, dyld_info.weak_bind_size)) |pos| {
1868 return pos;
1869 }
1870 // Lazy binding info
1871 if (checkForCollision(start, end, dyld_info.lazy_bind_off, dyld_info.lazy_bind_size)) |pos| {
1872 return pos;
1873 }
1874 // Export info
1875 if (checkForCollision(start, end, dyld_info.export_off, dyld_info.export_size)) |pos| {
1876 return pos;
1877 }
1964 // __LINKEDIT is a weird segment where sections get their own load commands so we
1965 // special-case it.
1966 if (self.dyld_info_cmd_index) |idx| outer: {
1967 if (self.load_commands.items.len == idx) break :outer;
1968 const dyld_info = self.load_commands.items[idx].DyldInfoOnly;
1969 if (checkForCollision(start, end, dyld_info.rebase_off, dyld_info.rebase_size)) |pos| {
1970 return pos;
1971 }
1972 // Binding info
1973 if (checkForCollision(start, end, dyld_info.bind_off, dyld_info.bind_size)) |pos| {
1974 return pos;
1975 }
1976 // Weak binding info
1977 if (checkForCollision(start, end, dyld_info.weak_bind_off, dyld_info.weak_bind_size)) |pos| {
1978 return pos;
1979 }
1980 // Lazy binding info
1981 if (checkForCollision(start, end, dyld_info.lazy_bind_off, dyld_info.lazy_bind_size)) |pos| {
1982 return pos;
18781983 }
1984 // Export info
1985 if (checkForCollision(start, end, dyld_info.export_off, dyld_info.export_size)) |pos| {
1986 return pos;
1987 }
1988 }
18791989
1880 if (self.function_starts_cmd_index) |idx| outer: {
1881 if (self.load_commands.items.len == idx) break :outer;
1882 const fstart = self.load_commands.items[idx].LinkeditData;
1883 if (checkForCollision(start, end, fstart.dataoff, fstart.datasize)) |pos| {
1884 return pos;
1885 }
1990 if (self.function_starts_cmd_index) |idx| outer: {
1991 if (self.load_commands.items.len == idx) break :outer;
1992 const fstart = self.load_commands.items[idx].LinkeditData;
1993 if (checkForCollision(start, end, fstart.dataoff, fstart.datasize)) |pos| {
1994 return pos;
18861995 }
1996 }
18871997
1888 if (self.data_in_code_cmd_index) |idx| outer: {
1889 if (self.load_commands.items.len == idx) break :outer;
1890 const dic = self.load_commands.items[idx].LinkeditData;
1891 if (checkForCollision(start, end, dic.dataoff, dic.datasize)) |pos| {
1892 return pos;
1893 }
1998 if (self.data_in_code_cmd_index) |idx| outer: {
1999 if (self.load_commands.items.len == idx) break :outer;
2000 const dic = self.load_commands.items[idx].LinkeditData;
2001 if (checkForCollision(start, end, dic.dataoff, dic.datasize)) |pos| {
2002 return pos;
18942003 }
2004 }
18952005
1896 if (self.dysymtab_cmd_index) |idx| outer: {
1897 if (self.load_commands.items.len == idx) break :outer;
1898 const dysymtab = self.load_commands.items[idx].Dysymtab;
1899 // Indirect symbol table
1900 const nindirectsize = dysymtab.nindirectsyms * @sizeOf(u32);
1901 if (checkForCollision(start, end, dysymtab.indirectsymoff, nindirectsize)) |pos| {
1902 return pos;
1903 }
1904 // TODO Handle more dynamic symbol table sections.
2006 if (self.dysymtab_cmd_index) |idx| outer: {
2007 if (self.load_commands.items.len == idx) break :outer;
2008 const dysymtab = self.load_commands.items[idx].Dysymtab;
2009 // Indirect symbol table
2010 const nindirectsize = dysymtab.nindirectsyms * @sizeOf(u32);
2011 if (checkForCollision(start, end, dysymtab.indirectsymoff, nindirectsize)) |pos| {
2012 return pos;
19052013 }
2014 // TODO Handle more dynamic symbol table sections.
2015 }
19062016
1907 if (self.symtab_cmd_index) |idx| outer: {
1908 if (self.load_commands.items.len == idx) break :outer;
1909 const symtab = self.load_commands.items[idx].Symtab;
1910 // Symbol table
1911 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
1912 if (checkForCollision(start, end, symtab.symoff, symsize)) |pos| {
1913 return pos;
1914 }
1915 // String table
1916 if (checkForCollision(start, end, symtab.stroff, symtab.strsize)) |pos| {
1917 return pos;
1918 }
2017 if (self.symtab_cmd_index) |idx| outer: {
2018 if (self.load_commands.items.len == idx) break :outer;
2019 const symtab = self.load_commands.items[idx].Symtab;
2020 // Symbol table
2021 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
2022 if (checkForCollision(start, end, symtab.symoff, symsize)) |pos| {
2023 return pos;
19192024 }
1920 } else {
1921 for (segment.sections.items) |section| {
1922 if (checkForCollision(start, end, section.offset, section.size)) |pos| {
1923 return pos;
1924 }
2025 // String table
2026 if (checkForCollision(start, end, symtab.stroff, symtab.strsize)) |pos| {
2027 return pos;
19252028 }
19262029 }
19272030
19282031 return null;
19292032}
19302033
1931fn findFreeSpace(self: *MachO, segment: *const SegmentCommand, object_size: u64, min_alignment: u16) u64 {
1932 var start: u64 = if (parseAndCmpName(&segment.inner.segname, "__TEXT"))
1933 self.header_pad
1934 else
1935 segment.inner.fileoff;
1936 while (self.detectAllocCollision(segment, start, object_size)) |item_end| {
2034fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16) u64 {
2035 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2036 var start: u64 = linkedit.inner.fileoff;
2037 while (self.detectAllocCollisionLinkedit(start, object_size)) |item_end| {
19372038 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
19382039 }
19392040 return start;
19402041}
19412042
19422043/// Saturating multiplication
1943fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
2044pub fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
19442045 const T = @TypeOf(a, b);
19452046 return std.math.mul(T, a, b) catch std.math.maxInt(T);
19462047}
......@@ -1993,9 +2094,9 @@ fn relocateSymbolTable(self: *MachO) !void {
19932094 if (symtab.nsyms < nsyms) {
19942095 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
19952096 const needed_size = nsyms * @sizeOf(macho.nlist_64);
1996 if (needed_size > self.allocatedSize(&linkedit_segment, symtab.symoff)) {
2097 if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) {
19972098 // Move the entire symbol table to a new location
1998 const new_symoff = self.findFreeSpace(&linkedit_segment, needed_size, @alignOf(macho.nlist_64));
2099 const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64));
19992100 const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64);
20002101
20012102 log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{
......@@ -2140,12 +2241,12 @@ fn writeExportTrie(self: *MachO) !void {
21402241
21412242 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
21422243 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2143 const allocated_size = self.allocatedSize(&linkedit_segment, dyld_info.export_off);
2244 const allocated_size = self.allocatedSizeLinkedit(dyld_info.export_off);
21442245 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
21452246
21462247 if (needed_size > allocated_size) {
21472248 dyld_info.export_off = 0;
2148 dyld_info.export_off = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1));
2249 dyld_info.export_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
21492250 }
21502251 dyld_info.export_size = @intCast(u32, needed_size);
21512252 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
......@@ -2170,12 +2271,12 @@ fn writeBindingInfoTable(self: *MachO) !void {
21702271
21712272 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
21722273 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2173 const allocated_size = self.allocatedSize(&linkedit_segment, dyld_info.bind_off);
2274 const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off);
21742275 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
21752276
21762277 if (needed_size > allocated_size) {
21772278 dyld_info.bind_off = 0;
2178 dyld_info.bind_off = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1));
2279 dyld_info.bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
21792280 }
21802281
21812282 dyld_info.bind_size = @intCast(u32, needed_size);
......@@ -2198,12 +2299,12 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
21982299
21992300 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
22002301 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2201 const allocated_size = self.allocatedSize(&linkedit_segment, dyld_info.lazy_bind_off);
2302 const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off);
22022303 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
22032304
22042305 if (needed_size > allocated_size) {
22052306 dyld_info.lazy_bind_off = 0;
2206 dyld_info.lazy_bind_off = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1));
2307 dyld_info.lazy_bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
22072308 }
22082309
22092310 dyld_info.lazy_bind_size = @intCast(u32, needed_size);
......@@ -2220,14 +2321,13 @@ fn writeStringTable(self: *MachO) !void {
22202321 const tracy = trace(@src());
22212322 defer tracy.end();
22222323
2223 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
22242324 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2225 const allocated_size = self.allocatedSize(&linkedit_segment, symtab.stroff);
2325 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
22262326 const needed_size = mem.alignForwardGeneric(u64, self.string_table.items.len, @alignOf(u64));
22272327
22282328 if (needed_size > allocated_size) {
22292329 symtab.strsize = 0;
2230 symtab.stroff = @intCast(u32, self.findFreeSpace(&linkedit_segment, needed_size, 1));
2330 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
22312331 }
22322332 symtab.strsize = @intCast(u32, needed_size);
22332333 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
......@@ -2280,7 +2380,7 @@ fn updateLinkeditSegmentSizes(self: *MachO) !void {
22802380 const filesize = final_offset - linkedit_segment.inner.fileoff;
22812381 linkedit_segment.inner.filesize = filesize;
22822382 linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, filesize, self.page_size);
2283 try self.base.file.?.pwriteAll(&[_]u8{ 0 }, final_offset);
2383 try self.base.file.?.pwriteAll(&[_]u8{0}, final_offset);
22842384 self.load_commands_dirty = true;
22852385}
22862386
......@@ -2301,7 +2401,7 @@ fn writeLoadCommands(self: *MachO) !void {
23012401 }
23022402
23032403 const off = @sizeOf(macho.mach_header_64);
2304 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{self.load_commands.items.len, off, off + sizeofcmds});
2404 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
23052405 try self.base.file.?.pwriteAll(buffer, off);
23062406 self.load_commands_dirty = false;
23072407}
......@@ -2368,6 +2468,9 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
23682468 macho.LC_SOURCE_VERSION => {
23692469 self.source_version_cmd_index = i;
23702470 },
2471 macho.LC_UUID => {
2472 self.uuid_cmd_index = i;
2473 },
23712474 macho.LC_MAIN => {
23722475 self.main_cmd_index = i;
23732476 },
src/link/MachO/DebugSymbols.zig created+1540
......@@ -0,0 +1,1540 @@
1const DebugSymbols = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const macho = std.macho;
8const mem = std.mem;
9const DW = std.dwarf;
10const leb = std.leb;
11const Allocator = mem.Allocator;
12
13const build_options = @import("build_options");
14const trace = @import("../../tracy.zig").trace;
15const Module = @import("../../Module.zig");
16const Type = @import("../../type.zig").Type;
17const link = @import("../../link.zig");
18const MachO = @import("../MachO.zig");
19const SrcFn = MachO.SrcFn;
20const TextBlock = MachO.TextBlock;
21const satMul = MachO.satMul;
22const alloc_num = MachO.alloc_num;
23const alloc_den = MachO.alloc_den;
24const makeStaticString = MachO.makeStaticString;
25
26usingnamespace @import("commands.zig");
27
28const page_size: u16 = 0x1000;
29
30base: *MachO,
31file: fs.File,
32
33/// Mach header
34header: ?macho.mach_header_64 = null,
35
36/// Table of all load commands
37load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
38/// __PAGEZERO segment
39pagezero_segment_cmd_index: ?u16 = null,
40/// __TEXT segment
41text_segment_cmd_index: ?u16 = null,
42/// __DATA segment
43data_segment_cmd_index: ?u16 = null,
44/// __LINKEDIT segment
45linkedit_segment_cmd_index: ?u16 = null,
46/// __DWARF segment
47dwarf_segment_cmd_index: ?u16 = null,
48/// Symbol table
49symtab_cmd_index: ?u16 = null,
50/// UUID load command
51uuid_cmd_index: ?u16 = null,
52
53/// Index into __TEXT,__text section.
54text_section_index: ?u16 = null,
55
56linkedit_off: u16 = page_size,
57linkedit_size: u16 = page_size,
58
59debug_info_section_index: ?u16 = null,
60debug_abbrev_section_index: ?u16 = null,
61debug_str_section_index: ?u16 = null,
62debug_aranges_section_index: ?u16 = null,
63debug_line_section_index: ?u16 = null,
64
65debug_abbrev_table_offset: ?u64 = null,
66
67/// A list of `SrcFn` whose Line Number Programs have surplus capacity.
68/// This is the same concept as `text_block_free_list`; see those doc comments.
69dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
70dbg_line_fn_first: ?*SrcFn = null,
71dbg_line_fn_last: ?*SrcFn = null,
72
73/// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity.
74/// This is the same concept as `text_block_free_list`; see those doc comments.
75dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
76dbg_info_decl_first: ?*TextBlock = null,
77dbg_info_decl_last: ?*TextBlock = null,
78
79/// Table of debug symbol names aka the debug string table.
80debug_string_table: std.ArrayListUnmanaged(u8) = .{},
81
82header_dirty: bool = false,
83load_commands_dirty: bool = false,
84string_table_dirty: bool = false,
85debug_string_table_dirty: bool = false,
86debug_abbrev_section_dirty: bool = false,
87debug_aranges_section_dirty: bool = false,
88debug_info_header_dirty: bool = false,
89debug_line_header_dirty: bool = false,
90
91const abbrev_compile_unit = 1;
92const abbrev_subprogram = 2;
93const abbrev_subprogram_retvoid = 3;
94const abbrev_base_type = 4;
95const abbrev_pad1 = 5;
96const abbrev_parameter = 6;
97
98/// The reloc offset for the virtual address of a function in its Line Number Program.
99/// Size is a virtual address integer.
100const dbg_line_vaddr_reloc_index = 3;
101/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
102/// Size is a virtual address integer.
103const dbg_info_low_pc_reloc_index = 1;
104
105const min_nop_size = 2;
106
107/// You must call this function *after* `MachO.populateMissingMetadata()`
108/// has been called to get a viable debug symbols output.
109pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void {
110 if (self.header == null) {
111 const base_header = self.base.header.?;
112 var header: macho.mach_header_64 = undefined;
113 header.magic = macho.MH_MAGIC_64;
114 header.cputype = base_header.cputype;
115 header.cpusubtype = base_header.cpusubtype;
116 header.filetype = macho.MH_DSYM;
117 // These will get populated at the end of flushing the results to file.
118 header.ncmds = 0;
119 header.sizeofcmds = 0;
120 header.flags = 0;
121 header.reserved = 0;
122 self.header = header;
123 self.header_dirty = true;
124 }
125 if (self.uuid_cmd_index == null) {
126 const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?];
127 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
128 try self.load_commands.append(allocator, base_cmd);
129 self.header_dirty = true;
130 self.load_commands_dirty = true;
131 }
132 if (self.symtab_cmd_index == null) {
133 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
134 const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].Symtab;
135 const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64);
136 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
137
138 log.debug("found dSym symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });
139
140 const strtab_off = self.findFreeSpaceLinkedit(base_cmd.strsize, 1);
141
142 log.debug("found dSym string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize });
143
144 try self.load_commands.append(allocator, .{
145 .Symtab = .{
146 .cmd = macho.LC_SYMTAB,
147 .cmdsize = @sizeOf(macho.symtab_command),
148 .symoff = @intCast(u32, symtab_off),
149 .nsyms = base_cmd.nsyms,
150 .stroff = @intCast(u32, strtab_off),
151 .strsize = base_cmd.strsize,
152 },
153 });
154 self.header_dirty = true;
155 self.load_commands_dirty = true;
156 self.string_table_dirty = true;
157 }
158 if (self.pagezero_segment_cmd_index == null) {
159 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
160 const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].Segment;
161 const cmd = try self.copySegmentCommand(allocator, base_cmd);
162 try self.load_commands.append(allocator, .{ .Segment = cmd });
163 self.header_dirty = true;
164 self.load_commands_dirty = true;
165 }
166 if (self.text_segment_cmd_index == null) {
167 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
168 const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].Segment;
169 const cmd = try self.copySegmentCommand(allocator, base_cmd);
170 try self.load_commands.append(allocator, .{ .Segment = cmd });
171 self.header_dirty = true;
172 self.load_commands_dirty = true;
173 }
174 if (self.data_segment_cmd_index == null) outer: {
175 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
176 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
177 const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].Segment;
178 const cmd = try self.copySegmentCommand(allocator, base_cmd);
179 try self.load_commands.append(allocator, .{ .Segment = cmd });
180 self.header_dirty = true;
181 self.load_commands_dirty = true;
182 }
183 if (self.linkedit_segment_cmd_index == null) {
184 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
185 const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].Segment;
186 var cmd = try self.copySegmentCommand(allocator, base_cmd);
187 cmd.inner.vmsize = self.linkedit_size;
188 cmd.inner.fileoff = self.linkedit_off;
189 cmd.inner.filesize = self.linkedit_size;
190 try self.load_commands.append(allocator, .{ .Segment = cmd });
191 self.header_dirty = true;
192 self.load_commands_dirty = true;
193 }
194 if (self.dwarf_segment_cmd_index == null) {
195 self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
196
197 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
198 const ideal_size: u16 = 200 + 128 + 160 + 250;
199 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, page_size);
200 const off = linkedit.inner.fileoff + linkedit.inner.filesize;
201 const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;
202
203 log.debug("found dSym __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
204
205 try self.load_commands.append(allocator, .{
206 .Segment = SegmentCommand.empty(.{
207 .cmd = macho.LC_SEGMENT_64,
208 .cmdsize = @sizeOf(macho.segment_command_64),
209 .segname = makeStaticString("__DWARF"),
210 .vmaddr = vmaddr,
211 .vmsize = needed_size,
212 .fileoff = off,
213 .filesize = needed_size,
214 .maxprot = 0,
215 .initprot = 0,
216 .nsects = 0,
217 .flags = 0,
218 }),
219 });
220 self.header_dirty = true;
221 self.load_commands_dirty = true;
222 }
223 if (self.debug_str_section_index == null) {
224 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
225 self.debug_str_section_index = @intCast(u16, dwarf_segment.sections.items.len);
226 assert(self.debug_string_table.items.len == 0);
227
228 try dwarf_segment.addSection(allocator, .{
229 .sectname = makeStaticString("__debug_str"),
230 .segname = makeStaticString("__DWARF"),
231 .addr = dwarf_segment.inner.vmaddr,
232 .size = @intCast(u32, self.debug_string_table.items.len),
233 .offset = @intCast(u32, dwarf_segment.inner.fileoff),
234 .@"align" = 1,
235 .reloff = 0,
236 .nreloc = 0,
237 .flags = macho.S_REGULAR,
238 .reserved1 = 0,
239 .reserved2 = 0,
240 .reserved3 = 0,
241 });
242 self.header_dirty = true;
243 self.load_commands_dirty = true;
244 self.debug_string_table_dirty = true;
245 }
246 if (self.debug_info_section_index == null) {
247 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
248 self.debug_info_section_index = @intCast(u16, dwarf_segment.sections.items.len);
249
250 const file_size_hint = 200;
251 const p_align = 1;
252 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
253
254 log.debug("found dSym __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
255
256 try dwarf_segment.addSection(allocator, .{
257 .sectname = makeStaticString("__debug_info"),
258 .segname = makeStaticString("__DWARF"),
259 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
260 .size = file_size_hint,
261 .offset = @intCast(u32, off),
262 .@"align" = p_align,
263 .reloff = 0,
264 .nreloc = 0,
265 .flags = macho.S_REGULAR,
266 .reserved1 = 0,
267 .reserved2 = 0,
268 .reserved3 = 0,
269 });
270 self.header_dirty = true;
271 self.load_commands_dirty = true;
272 self.debug_info_header_dirty = true;
273 }
274 if (self.debug_abbrev_section_index == null) {
275 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
276 self.debug_abbrev_section_index = @intCast(u16, dwarf_segment.sections.items.len);
277
278 const file_size_hint = 128;
279 const p_align = 1;
280 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
281
282 log.debug("found dSym __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
283
284 try dwarf_segment.addSection(allocator, .{
285 .sectname = makeStaticString("__debug_abbrev"),
286 .segname = makeStaticString("__DWARF"),
287 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
288 .size = file_size_hint,
289 .offset = @intCast(u32, off),
290 .@"align" = p_align,
291 .reloff = 0,
292 .nreloc = 0,
293 .flags = macho.S_REGULAR,
294 .reserved1 = 0,
295 .reserved2 = 0,
296 .reserved3 = 0,
297 });
298 self.header_dirty = true;
299 self.load_commands_dirty = true;
300 self.debug_abbrev_section_dirty = true;
301 }
302 if (self.debug_aranges_section_index == null) {
303 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
304 self.debug_aranges_section_index = @intCast(u16, dwarf_segment.sections.items.len);
305
306 const file_size_hint = 160;
307 const p_align = 16;
308 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
309
310 log.debug("found dSym __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
311
312 try dwarf_segment.addSection(allocator, .{
313 .sectname = makeStaticString("__debug_aranges"),
314 .segname = makeStaticString("__DWARF"),
315 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
316 .size = file_size_hint,
317 .offset = @intCast(u32, off),
318 .@"align" = p_align,
319 .reloff = 0,
320 .nreloc = 0,
321 .flags = macho.S_REGULAR,
322 .reserved1 = 0,
323 .reserved2 = 0,
324 .reserved3 = 0,
325 });
326 self.header_dirty = true;
327 self.load_commands_dirty = true;
328 self.debug_aranges_section_dirty = true;
329 }
330 if (self.debug_line_section_index == null) {
331 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
332 self.debug_line_section_index = @intCast(u16, dwarf_segment.sections.items.len);
333
334 const file_size_hint = 250;
335 const p_align = 1;
336 const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
337
338 log.debug("found dSym __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
339
340 try dwarf_segment.addSection(allocator, .{
341 .sectname = makeStaticString("__debug_line"),
342 .segname = makeStaticString("__DWARF"),
343 .addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
344 .size = file_size_hint,
345 .offset = @intCast(u32, off),
346 .@"align" = p_align,
347 .reloff = 0,
348 .nreloc = 0,
349 .flags = macho.S_REGULAR,
350 .reserved1 = 0,
351 .reserved2 = 0,
352 .reserved3 = 0,
353 });
354 self.header_dirty = true;
355 self.load_commands_dirty = true;
356 self.debug_line_header_dirty = true;
357 }
358}
359
360pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Options) !void {
361 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
362 // Zig source code.
363 const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
364 const init_len_size: usize = 4;
365
366 if (self.debug_abbrev_section_dirty) {
367 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
368 const debug_abbrev_sect = &dwarf_segment.sections.items[self.debug_abbrev_section_index.?];
369
370 // These are LEB encoded but since the values are all less than 127
371 // we can simply append these bytes.
372 const abbrev_buf = [_]u8{
373 abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
374 DW.AT_stmt_list, DW.FORM_sec_offset, // offset
375 DW.AT_low_pc, DW.FORM_addr,
376 DW.AT_high_pc, DW.FORM_addr,
377 DW.AT_name, DW.FORM_strp,
378 DW.AT_comp_dir, DW.FORM_strp,
379 DW.AT_producer, DW.FORM_strp,
380 DW.AT_language, DW.FORM_data2,
381 0, 0, // table sentinel
382 abbrev_subprogram, DW.TAG_subprogram, DW.CHILDREN_yes, // header
383 DW.AT_low_pc, DW.FORM_addr, // start VM address
384 DW.AT_high_pc, DW.FORM_data4,
385 DW.AT_type, DW.FORM_ref4,
386 DW.AT_name, DW.FORM_string,
387 DW.AT_decl_line, DW.FORM_data4,
388 DW.AT_decl_file, DW.FORM_data1,
389 0, 0, // table sentinel
390 abbrev_subprogram_retvoid,
391 DW.TAG_subprogram, DW.CHILDREN_yes, // header
392 DW.AT_low_pc, DW.FORM_addr,
393 DW.AT_high_pc, DW.FORM_data4,
394 DW.AT_name, DW.FORM_string,
395 DW.AT_decl_line, DW.FORM_data4,
396 DW.AT_decl_file, DW.FORM_data1,
397 0, 0, // table sentinel
398 abbrev_base_type, DW.TAG_base_type, DW.CHILDREN_no, // header
399 DW.AT_encoding, DW.FORM_data1, DW.AT_byte_size,
400 DW.FORM_data1, DW.AT_name, DW.FORM_string,
401 0, 0, // table sentinel
402 abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
403 0, 0, // table sentinel
404 abbrev_parameter, DW.TAG_formal_parameter, DW.CHILDREN_no, // header
405 DW.AT_location, DW.FORM_exprloc, DW.AT_type,
406 DW.FORM_ref4, DW.AT_name, DW.FORM_string,
407 0, 0, // table sentinel
408 0, 0, 0, // section sentinel
409 };
410
411 const needed_size = abbrev_buf.len;
412 const allocated_size = dwarf_segment.allocatedSize(debug_abbrev_sect.offset);
413 if (needed_size > allocated_size) {
414 debug_abbrev_sect.size = 0; // free the space
415 const offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
416 debug_abbrev_sect.offset = @intCast(u32, offset);
417 debug_abbrev_sect.addr = dwarf_segment.inner.vmaddr + offset - dwarf_segment.inner.fileoff;
418 }
419 debug_abbrev_sect.size = needed_size;
420 log.debug("__debug_abbrev start=0x{x} end=0x{x}", .{
421 debug_abbrev_sect.offset,
422 debug_abbrev_sect.offset + needed_size,
423 });
424
425 const abbrev_offset = 0;
426 self.debug_abbrev_table_offset = abbrev_offset;
427 try self.file.pwriteAll(&abbrev_buf, debug_abbrev_sect.offset + abbrev_offset);
428 self.load_commands_dirty = true;
429 self.debug_abbrev_section_dirty = false;
430 }
431
432 if (self.debug_info_header_dirty) debug_info: {
433 // If this value is null it means there is an error in the module;
434 // leave debug_info_header_dirty=true.
435 const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
436 const last_dbg_info_decl = self.dbg_info_decl_last.?;
437 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
438 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
439
440 var di_buf = std.ArrayList(u8).init(allocator);
441 defer di_buf.deinit();
442
443 // We have a function to compute the upper bound size, because it's needed
444 // for determining where to put the offset of the first `LinkBlock`.
445 try di_buf.ensureCapacity(self.dbgInfoNeededHeaderBytes());
446
447 // initial length - length of the .debug_info contribution for this compilation unit,
448 // not including the initial length itself.
449 // We have to come back and write it later after we know the size.
450 const after_init_len = di_buf.items.len + init_len_size;
451 // +1 for the final 0 that ends the compilation unit children.
452 const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
453 const init_len = dbg_info_end - after_init_len;
454 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len));
455 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4); // DWARF version
456 const abbrev_offset = self.debug_abbrev_table_offset.?;
457 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset));
458 di_buf.appendAssumeCapacity(8); // address size
459 // Write the form for the compile unit, which must match the abbrev table above.
460 const name_strp = try self.makeDebugString(allocator, module.root_pkg.root_src_path);
461 const comp_dir_strp = try self.makeDebugString(allocator, module.root_pkg.root_src_directory.path orelse ".");
462 const producer_strp = try self.makeDebugString(allocator, link.producer_string);
463 // Currently only one compilation unit is supported, so the address range is simply
464 // identical to the main program header virtual address and memory size.
465 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
466 const text_section = text_segment.sections.items[self.text_section_index.?];
467 const low_pc = text_section.addr;
468 const high_pc = text_section.addr + text_section.size;
469
470 di_buf.appendAssumeCapacity(abbrev_compile_unit);
471 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT_stmt_list, DW.FORM_sec_offset
472 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc);
473 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), high_pc);
474 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, name_strp));
475 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, comp_dir_strp));
476 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, producer_strp));
477 // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
478 // http://dwarfstd.org/ShowIssue.php?issue=171115.1
479 // Until then we say it is C99.
480 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99);
481
482 if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
483 // Move the first N decls to the end to make more padding for the header.
484 @panic("TODO: handle __debug_info header exceeding its padding");
485 }
486 const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
487 try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.offset);
488 self.debug_info_header_dirty = false;
489 }
490
491 if (self.debug_aranges_section_dirty) {
492 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
493 const debug_aranges_sect = &dwarf_segment.sections.items[self.debug_aranges_section_index.?];
494 const debug_info_sect = dwarf_segment.sections.items[self.debug_info_section_index.?];
495
496 var di_buf = std.ArrayList(u8).init(allocator);
497 defer di_buf.deinit();
498
499 // Enough for all the data without resizing. When support for more compilation units
500 // is added, the size of this section will become more variable.
501 try di_buf.ensureCapacity(100);
502
503 // initial length - length of the .debug_aranges contribution for this compilation unit,
504 // not including the initial length itself.
505 // We have to come back and write it later after we know the size.
506 const init_len_index = di_buf.items.len;
507 di_buf.items.len += init_len_size;
508 const after_init_len = di_buf.items.len;
509 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2); // version
510 // When more than one compilation unit is supported, this will be the offset to it.
511 // For now it is always at offset 0 in .debug_info.
512 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // __debug_info offset
513 di_buf.appendAssumeCapacity(@sizeOf(u64)); // address_size
514 di_buf.appendAssumeCapacity(0); // segment_selector_size
515
516 const end_header_offset = di_buf.items.len;
517 const begin_entries_offset = mem.alignForward(end_header_offset, @sizeOf(u64) * 2);
518 di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
519
520 // Currently only one compilation unit is supported, so the address range is simply
521 // identical to the main program header virtual address and memory size.
522 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
523 const text_section = text_segment.sections.items[self.text_section_index.?];
524 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.addr);
525 mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.size);
526
527 // Sentinel.
528 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0);
529 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0);
530
531 // Go back and populate the initial length.
532 const init_len = di_buf.items.len - after_init_len;
533 // initial length - length of the .debug_aranges contribution for this compilation unit,
534 // not including the initial length itself.
535 mem.writeIntLittle(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len));
536
537 const needed_size = di_buf.items.len;
538 const allocated_size = dwarf_segment.allocatedSize(debug_aranges_sect.offset);
539 if (needed_size > allocated_size) {
540 debug_aranges_sect.size = 0; // free the space
541 const new_offset = dwarf_segment.findFreeSpace(needed_size, 16, null);
542 debug_aranges_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
543 debug_aranges_sect.offset = @intCast(u32, new_offset);
544 }
545 debug_aranges_sect.size = needed_size;
546 log.debug("__debug_aranges start=0x{x} end=0x{x}", .{
547 debug_aranges_sect.offset,
548 debug_aranges_sect.offset + needed_size,
549 });
550
551 try self.file.pwriteAll(di_buf.items, debug_aranges_sect.offset);
552 self.load_commands_dirty = true;
553 self.debug_aranges_section_dirty = false;
554 }
555 if (self.debug_line_header_dirty) debug_line: {
556 if (self.dbg_line_fn_first == null) {
557 break :debug_line; // Error in module; leave debug_line_header_dirty=true.
558 }
559 const dbg_line_prg_off = self.getDebugLineProgramOff();
560 const dbg_line_prg_end = self.getDebugLineProgramEnd();
561 assert(dbg_line_prg_end != 0);
562
563 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
564 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
565
566 var di_buf = std.ArrayList(u8).init(allocator);
567 defer di_buf.deinit();
568
569 // The size of this header is variable, depending on the number of directories,
570 // files, and padding. We have a function to compute the upper bound size, however,
571 // because it's needed for determining where to put the offset of the first `SrcFn`.
572 try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes(module));
573
574 // initial length - length of the .debug_line contribution for this compilation unit,
575 // not including the initial length itself.
576 const after_init_len = di_buf.items.len + init_len_size;
577 const init_len = dbg_line_prg_end - after_init_len;
578 mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len));
579 mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4); // version
580
581 // Empirically, debug info consumers do not respect this field, or otherwise
582 // consider it to be an error when it does not point exactly to the end of the header.
583 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
584 // padding rather than this field.
585 const before_header_len = di_buf.items.len;
586 di_buf.items.len += @sizeOf(u32); // We will come back and write this.
587 const after_header_len = di_buf.items.len;
588
589 const opcode_base = DW.LNS_set_isa + 1;
590 di_buf.appendSliceAssumeCapacity(&[_]u8{
591 1, // minimum_instruction_length
592 1, // maximum_operations_per_instruction
593 1, // default_is_stmt
594 1, // line_base (signed)
595 1, // line_range
596 opcode_base,
597
598 // Standard opcode lengths. The number of items here is based on `opcode_base`.
599 // The value is the number of LEB128 operands the instruction takes.
600 0, // `DW.LNS_copy`
601 1, // `DW.LNS_advance_pc`
602 1, // `DW.LNS_advance_line`
603 1, // `DW.LNS_set_file`
604 1, // `DW.LNS_set_column`
605 0, // `DW.LNS_negate_stmt`
606 0, // `DW.LNS_set_basic_block`
607 0, // `DW.LNS_const_add_pc`
608 1, // `DW.LNS_fixed_advance_pc`
609 0, // `DW.LNS_set_prologue_end`
610 0, // `DW.LNS_set_epilogue_begin`
611 1, // `DW.LNS_set_isa`
612 0, // include_directories (none except the compilation unit cwd)
613 });
614 // file_names[0]
615 di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name
616 di_buf.appendSliceAssumeCapacity(&[_]u8{
617 0, // null byte for the relative path name
618 0, // directory_index
619 0, // mtime (TODO supply this)
620 0, // file size bytes (TODO supply this)
621 0, // file_names sentinel
622 });
623
624 const header_len = di_buf.items.len - after_header_len;
625 mem.writeIntLittle(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len));
626
627 // We use NOPs because consumers empirically do not respect the header length field.
628 if (di_buf.items.len > dbg_line_prg_off) {
629 // Move the first N files to the end to make more padding for the header.
630 @panic("TODO: handle __debug_line header exceeding its padding");
631 }
632 const jmp_amt = dbg_line_prg_off - di_buf.items.len;
633 try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.offset);
634 self.debug_line_header_dirty = false;
635 }
636 {
637 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
638 const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];
639 if (self.debug_string_table_dirty or self.debug_string_table.items.len != debug_strtab_sect.size) {
640 const allocated_size = dwarf_segment.allocatedSize(debug_strtab_sect.offset);
641 const needed_size = self.debug_string_table.items.len;
642
643 if (needed_size > allocated_size) {
644 debug_strtab_sect.size = 0; // free the space
645 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
646 debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
647 debug_strtab_sect.offset = @intCast(u32, new_offset);
648 }
649 debug_strtab_sect.size = @intCast(u32, needed_size);
650
651 log.debug("__debug_strtab start=0x{x} end=0x{x}", .{
652 debug_strtab_sect.offset,
653 debug_strtab_sect.offset + needed_size,
654 });
655
656 try self.file.pwriteAll(self.debug_string_table.items, debug_strtab_sect.offset);
657 self.load_commands_dirty = true;
658 self.debug_string_table_dirty = false;
659 }
660 }
661
662 try self.writeStringTable();
663 self.updateDwarfSegment();
664 try self.writeLoadCommands(allocator);
665 try self.writeHeader();
666
667 assert(!self.header_dirty);
668 assert(!self.load_commands_dirty);
669 assert(!self.string_table_dirty);
670 assert(!self.debug_abbrev_section_dirty);
671 assert(!self.debug_aranges_section_dirty);
672 assert(!self.debug_string_table_dirty);
673}
674
675pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
676 self.dbg_info_decl_free_list.deinit(allocator);
677 self.dbg_line_fn_free_list.deinit(allocator);
678 self.debug_string_table.deinit(allocator);
679 for (self.load_commands.items) |*lc| {
680 lc.deinit(allocator);
681 }
682 self.load_commands.deinit(allocator);
683 self.file.close();
684}
685
686fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand {
687 var cmd = SegmentCommand.empty(.{
688 .cmd = macho.LC_SEGMENT_64,
689 .cmdsize = base_cmd.inner.cmdsize,
690 .segname = undefined,
691 .vmaddr = base_cmd.inner.vmaddr,
692 .vmsize = base_cmd.inner.vmsize,
693 .fileoff = 0,
694 .filesize = 0,
695 .maxprot = base_cmd.inner.maxprot,
696 .initprot = base_cmd.inner.initprot,
697 .nsects = base_cmd.inner.nsects,
698 .flags = base_cmd.inner.flags,
699 });
700 mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);
701
702 try cmd.sections.ensureCapacity(allocator, cmd.inner.nsects);
703 for (base_cmd.sections.items) |base_sect, i| {
704 var sect = macho.section_64{
705 .sectname = undefined,
706 .segname = undefined,
707 .addr = base_sect.addr,
708 .size = base_sect.size,
709 .offset = 0,
710 .@"align" = base_sect.@"align",
711 .reloff = 0,
712 .nreloc = 0,
713 .flags = base_sect.flags,
714 .reserved1 = base_sect.reserved1,
715 .reserved2 = base_sect.reserved2,
716 .reserved3 = base_sect.reserved3,
717 };
718 mem.copy(u8, &sect.sectname, &base_sect.sectname);
719 mem.copy(u8, &sect.segname, &base_sect.segname);
720
721 if (self.base.text_section_index.? == i) {
722 self.text_section_index = @intCast(u16, i);
723 }
724
725 cmd.sections.appendAssumeCapacity(sect);
726 }
727
728 return cmd;
729}
730
731fn updateDwarfSegment(self: *DebugSymbols) void {
732 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
733 var file_size: u64 = 0;
734 for (dwarf_segment.sections.items) |sect| {
735 file_size += sect.size;
736 }
737 if (file_size != dwarf_segment.inner.filesize) {
738 dwarf_segment.inner.filesize = file_size;
739 if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) {
740 dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, page_size);
741 }
742 self.load_commands_dirty = true;
743 }
744}
745
746/// Writes all load commands and section headers.
747fn writeLoadCommands(self: *DebugSymbols, allocator: *Allocator) !void {
748 if (!self.load_commands_dirty) return;
749
750 var sizeofcmds: usize = 0;
751 for (self.load_commands.items) |lc| {
752 sizeofcmds += lc.cmdsize();
753 }
754
755 var buffer = try allocator.alloc(u8, sizeofcmds);
756 defer allocator.free(buffer);
757 var writer = std.io.fixedBufferStream(buffer).writer();
758 for (self.load_commands.items) |lc| {
759 try lc.write(writer);
760 }
761
762 const off = @sizeOf(macho.mach_header_64);
763 log.debug("writing {} dSym load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
764 try self.file.pwriteAll(buffer, off);
765 self.load_commands_dirty = false;
766}
767
768fn writeHeader(self: *DebugSymbols) !void {
769 if (!self.header_dirty) return;
770
771 self.header.?.ncmds = @intCast(u32, self.load_commands.items.len);
772 var sizeofcmds: u32 = 0;
773 for (self.load_commands.items) |cmd| {
774 sizeofcmds += cmd.cmdsize();
775 }
776 self.header.?.sizeofcmds = sizeofcmds;
777 log.debug("writing Mach-O dSym header {}", .{self.header.?});
778 try self.file.pwriteAll(mem.asBytes(&self.header.?), 0);
779 self.header_dirty = false;
780}
781
782fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
783 assert(start > 0);
784 var min_pos: u64 = std.math.maxInt(u64);
785
786 if (self.symtab_cmd_index) |idx| {
787 const symtab = self.load_commands.items[idx].Symtab;
788 if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff;
789 if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff;
790 }
791
792 return min_pos - start;
793}
794
795fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 {
796 const end = start + satMul(size, alloc_num) / alloc_den;
797
798 if (self.symtab_cmd_index) |idx| outer: {
799 if (self.load_commands.items.len == idx) break :outer;
800 const symtab = self.load_commands.items[idx].Symtab;
801 {
802 // Symbol table
803 const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
804 const increased_size = satMul(symsize, alloc_num) / alloc_den;
805 const test_end = symtab.symoff + increased_size;
806 if (end > symtab.symoff and start < test_end) {
807 return test_end;
808 }
809 }
810 {
811 // String table
812 const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den;
813 const test_end = symtab.stroff + increased_size;
814 if (end > symtab.stroff and start < test_end) {
815 return test_end;
816 }
817 }
818 }
819
820 return null;
821}
822
823fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u16) u64 {
824 var start: u64 = self.linkedit_off;
825 while (self.detectAllocCollisionLinkedit(start, object_size)) |item_end| {
826 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
827 }
828 return start;
829}
830
831fn relocateSymbolTable(self: *DebugSymbols) !void {
832 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
833 const nlocals = self.base.local_symbols.items.len;
834 const nglobals = self.base.global_symbols.items.len;
835 const nsyms = nlocals + nglobals;
836
837 if (symtab.nsyms < nsyms) {
838 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
839 const needed_size = nsyms * @sizeOf(macho.nlist_64);
840 if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) {
841 // Move the entire symbol table to a new location
842 const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64));
843 const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64);
844
845 assert(new_symoff + existing_size <= self.linkedit_off + self.linkedit_size); // TODO expand LINKEDIT segment.
846 log.debug("relocating dSym symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{
847 symtab.symoff,
848 symtab.symoff + existing_size,
849 new_symoff,
850 new_symoff + existing_size,
851 });
852
853 const amt = try self.file.copyRangeAll(symtab.symoff, self.file, new_symoff, existing_size);
854 if (amt != existing_size) return error.InputOutput;
855 symtab.symoff = @intCast(u32, new_symoff);
856 }
857 symtab.nsyms = @intCast(u32, nsyms);
858 self.load_commands_dirty = true;
859 }
860}
861
862pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
863 const tracy = trace(@src());
864 defer tracy.end();
865 try self.relocateSymbolTable();
866 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
867 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
868 log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off });
869 try self.file.pwriteAll(mem.asBytes(&self.base.local_symbols.items[index]), off);
870}
871
872fn writeStringTable(self: *DebugSymbols) !void {
873 if (!self.string_table_dirty) return;
874
875 const tracy = trace(@src());
876 defer tracy.end();
877
878 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
879 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
880 const needed_size = mem.alignForwardGeneric(u64, self.base.string_table.items.len, @alignOf(u64));
881
882 if (needed_size > allocated_size) {
883 symtab.strsize = 0;
884 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
885 }
886 symtab.strsize = @intCast(u32, needed_size);
887 log.debug("writing dSym string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
888
889 try self.file.pwriteAll(self.base.string_table.items, symtab.stroff);
890 self.load_commands_dirty = true;
891 self.string_table_dirty = false;
892}
893
894pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const Module.Decl) !void {
895 const tracy = trace(@src());
896 defer tracy.end();
897
898 const container_scope = decl.scope.cast(Module.Scope.Container).?;
899 const tree = container_scope.file_scope.contents.tree;
900 const file_ast_decls = tree.root_node.decls();
901 // TODO Look into improving the performance here by adding a token-index-to-line
902 // lookup table. Currently this involves scanning over the source code for newlines.
903 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
904 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
905 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
906 const casted_line_off = @intCast(u28, line_delta);
907
908 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
909 const shdr = &dwarf_segment.sections.items[self.debug_line_section_index.?];
910 const file_pos = shdr.offset + decl.fn_link.macho.off + getRelocDbgLineOff();
911 var data: [4]u8 = undefined;
912 leb.writeUnsignedFixed(4, &data, casted_line_off);
913 try self.file.pwriteAll(&data, file_pos);
914}
915
916pub const DeclDebugBuffers = struct {
917 dbg_line_buffer: std.ArrayList(u8),
918 dbg_info_buffer: std.ArrayList(u8),
919 dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable,
920};
921
922/// Caller owns the returned memory.
923pub fn initDeclDebugBuffers(
924 self: *DebugSymbols,
925 allocator: *Allocator,
926 module: *Module,
927 decl: *Module.Decl,
928) !DeclDebugBuffers {
929 const tracy = trace(@src());
930 defer tracy.end();
931
932 var dbg_line_buffer = std.ArrayList(u8).init(allocator);
933 var dbg_info_buffer = std.ArrayList(u8).init(allocator);
934 var dbg_info_type_relocs: link.File.DbgInfoTypeRelocsTable = .{};
935
936 const typed_value = decl.typed_value.most_recent.typed_value;
937 switch (typed_value.ty.zigTypeTag()) {
938 .Fn => {
939 const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps;
940 if (zir_dumps.len != 0) {
941 for (zir_dumps) |fn_name| {
942 if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) {
943 std.debug.print("\n{}\n", .{decl.name});
944 typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
945 }
946 }
947 }
948
949 // For functions we need to add a prologue to the debug line program.
950 try dbg_line_buffer.ensureCapacity(26);
951
952 const line_off: u28 = blk: {
953 if (decl.scope.cast(Module.Scope.Container)) |container_scope| {
954 const tree = container_scope.file_scope.contents.tree;
955 const file_ast_decls = tree.root_node.decls();
956 // TODO Look into improving the performance here by adding a token-index-to-line
957 // lookup table. Currently this involves scanning over the source code for newlines.
958 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
959 const block = fn_proto.getBodyNode().?.castTag(.Block).?;
960 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
961 break :blk @intCast(u28, line_delta);
962 } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
963 const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
964 const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
965 break :blk @intCast(u28, line_delta);
966 } else {
967 unreachable;
968 }
969 };
970
971 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
972 DW.LNS_extended_op,
973 @sizeOf(u64) + 1,
974 DW.LNE_set_address,
975 });
976 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
977 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
978 dbg_line_buffer.items.len += @sizeOf(u64);
979
980 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
981 // This is the "relocatable" relative line offset from the previous function's end curly
982 // to this function's begin curly.
983 assert(getRelocDbgLineOff() == dbg_line_buffer.items.len);
984 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
985 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
986
987 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
988 assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len);
989 // Once we support more than one source file, this will have the ability to be more
990 // than one possible value.
991 const file_index = 1;
992 leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
993
994 // Emit a line for the begin curly with prologue_end=false. The codegen will
995 // do the work of setting prologue_end=true and epilogue_begin=true.
996 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
997
998 // .debug_info subprogram
999 const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
1000 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 27 + decl_name_with_null.len);
1001
1002 const fn_ret_type = typed_value.ty.fnReturnType();
1003 const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
1004 if (fn_ret_has_bits) {
1005 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
1006 } else {
1007 dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
1008 }
1009 // These get overwritten after generating the machine code. These values are
1010 // "relocations" and have to be in this fixed place so that functions can be
1011 // moved in virtual address space.
1012 assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
1013 dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT_low_pc, DW.FORM_addr
1014 assert(getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
1015 dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
1016 if (fn_ret_has_bits) {
1017 const gop = try dbg_info_type_relocs.getOrPut(allocator, fn_ret_type);
1018 if (!gop.found_existing) {
1019 gop.entry.value = .{
1020 .off = undefined,
1021 .relocs = .{},
1022 };
1023 }
1024 try gop.entry.value.relocs.append(allocator, @intCast(u32, dbg_info_buffer.items.len));
1025 dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
1026 }
1027 dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
1028 mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT_decl_line, DW.FORM_data4
1029 dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT_decl_file, DW.FORM_data1
1030 },
1031 else => {
1032 // TODO implement .debug_info for global variables
1033 },
1034 }
1035
1036 return DeclDebugBuffers{
1037 .dbg_info_buffer = dbg_info_buffer,
1038 .dbg_line_buffer = dbg_line_buffer,
1039 .dbg_info_type_relocs = dbg_info_type_relocs,
1040 };
1041}
1042
1043pub fn commitDeclDebugInfo(
1044 self: *DebugSymbols,
1045 allocator: *Allocator,
1046 module: *Module,
1047 decl: *Module.Decl,
1048 debug_buffers: *DeclDebugBuffers,
1049 target: std.Target,
1050) !void {
1051 const tracy = trace(@src());
1052 defer tracy.end();
1053
1054 var dbg_line_buffer = &debug_buffers.dbg_line_buffer;
1055 var dbg_info_buffer = &debug_buffers.dbg_info_buffer;
1056 var dbg_info_type_relocs = &debug_buffers.dbg_info_type_relocs;
1057
1058 const symbol = self.base.local_symbols.items[decl.link.macho.local_sym_index];
1059 const text_block = &decl.link.macho;
1060 // If the Decl is a function, we need to update the __debug_line program.
1061 const typed_value = decl.typed_value.most_recent.typed_value;
1062 switch (typed_value.ty.zigTypeTag()) {
1063 .Fn => {
1064 // Perform the relocations based on vaddr.
1065 {
1066 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
1067 mem.writeIntLittle(u64, ptr, symbol.n_value);
1068 }
1069 {
1070 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
1071 mem.writeIntLittle(u64, ptr, symbol.n_value);
1072 }
1073 {
1074 const ptr = dbg_info_buffer.items[getRelocDbgInfoSubprogramHighPC()..][0..4];
1075 mem.writeIntLittle(u32, ptr, @intCast(u32, text_block.size));
1076 }
1077
1078 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
1079
1080 // Now we have the full contents and may allocate a region to store it.
1081
1082 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
1083 // `TextBlock` and the .debug_info. If you are editing this logic, you
1084 // probably need to edit that logic too.
1085
1086 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
1087 const debug_line_sect = &dwarf_segment.sections.items[self.debug_line_section_index.?];
1088 const src_fn = &decl.fn_link.macho;
1089 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1090 if (self.dbg_line_fn_last) |last| {
1091 if (src_fn.next) |next| {
1092 // Update existing function - non-last item.
1093 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
1094 // It grew too big, so we move it to a new location.
1095 if (src_fn.prev) |prev| {
1096 _ = self.dbg_line_fn_free_list.put(allocator, prev, {}) catch {};
1097 prev.next = src_fn.next;
1098 }
1099 next.prev = src_fn.prev;
1100 src_fn.next = null;
1101 // Populate where it used to be with NOPs.
1102 const file_pos = debug_line_sect.offset + src_fn.off;
1103 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
1104 // TODO Look at the free list before appending at the end.
1105 src_fn.prev = last;
1106 last.next = src_fn;
1107 self.dbg_line_fn_last = src_fn;
1108
1109 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1110 }
1111 } else if (src_fn.prev == null) {
1112 // Append new function.
1113 // TODO Look at the free list before appending at the end.
1114 src_fn.prev = last;
1115 last.next = src_fn;
1116 self.dbg_line_fn_last = src_fn;
1117
1118 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1119 }
1120 } else {
1121 // This is the first function of the Line Number Program.
1122 self.dbg_line_fn_first = src_fn;
1123 self.dbg_line_fn_last = src_fn;
1124
1125 src_fn.off = self.dbgLineNeededHeaderBytes(module) * alloc_num / alloc_den;
1126 }
1127
1128 const last_src_fn = self.dbg_line_fn_last.?;
1129 const needed_size = last_src_fn.off + last_src_fn.len;
1130 if (needed_size != debug_line_sect.size) {
1131 if (needed_size > dwarf_segment.allocatedSize(debug_line_sect.offset)) {
1132 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
1133 const existing_size = last_src_fn.off;
1134
1135 log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
1136 existing_size,
1137 debug_line_sect.offset,
1138 new_offset,
1139 });
1140
1141 const amt = try self.file.copyRangeAll(debug_line_sect.offset, self.file, new_offset, existing_size);
1142 if (amt != existing_size) return error.InputOutput;
1143 debug_line_sect.offset = @intCast(u32, new_offset);
1144 debug_line_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
1145 }
1146 debug_line_sect.size = needed_size;
1147 self.load_commands_dirty = true; // TODO look into making only the one section dirty
1148 self.debug_line_header_dirty = true;
1149 }
1150 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
1151 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
1152
1153 // We only have support for one compilation unit so far, so the offsets are directly
1154 // from the .debug_line section.
1155 const file_pos = debug_line_sect.offset + src_fn.off;
1156 try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
1157
1158 // .debug_info - End the TAG_subprogram children.
1159 try dbg_info_buffer.append(0);
1160 },
1161 else => {},
1162 }
1163
1164 // Now we emit the .debug_info types of the Decl. These will count towards the size of
1165 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
1166 // relocations yet.
1167 var it = dbg_info_type_relocs.iterator();
1168 while (it.next()) |entry| {
1169 entry.value.off = @intCast(u32, dbg_info_buffer.items.len);
1170 try self.addDbgInfoType(entry.key, dbg_info_buffer, target);
1171 }
1172
1173 try self.updateDeclDebugInfoAllocation(allocator, text_block, @intCast(u32, dbg_info_buffer.items.len));
1174
1175 // Now that we have the offset assigned we can finally perform type relocations.
1176 it = dbg_info_type_relocs.iterator();
1177 while (it.next()) |entry| {
1178 for (entry.value.relocs.items) |off| {
1179 mem.writeIntLittle(
1180 u32,
1181 dbg_info_buffer.items[off..][0..4],
1182 text_block.dbg_info_off + entry.value.off,
1183 );
1184 }
1185 }
1186
1187 try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
1188}
1189
1190/// Asserts the type has codegen bits.
1191fn addDbgInfoType(
1192 self: *DebugSymbols,
1193 ty: Type,
1194 dbg_info_buffer: *std.ArrayList(u8),
1195 target: std.Target,
1196) !void {
1197 switch (ty.zigTypeTag()) {
1198 .Void => unreachable,
1199 .NoReturn => unreachable,
1200 .Bool => {
1201 try dbg_info_buffer.appendSlice(&[_]u8{
1202 abbrev_base_type,
1203 DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
1204 1, // DW.AT_byte_size, DW.FORM_data1
1205 'b',
1206 'o',
1207 'o',
1208 'l',
1209 0, // DW.AT_name, DW.FORM_string
1210 });
1211 },
1212 .Int => {
1213 const info = ty.intInfo(target);
1214 try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
1215 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
1216 // DW.AT_encoding, DW.FORM_data1
1217 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {
1218 .signed => DW.ATE_signed,
1219 .unsigned => DW.ATE_unsigned,
1220 });
1221 // DW.AT_byte_size, DW.FORM_data1
1222 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target)));
1223 // DW.AT_name, DW.FORM_string
1224 try dbg_info_buffer.writer().print("{}\x00", .{ty});
1225 },
1226 else => {
1227 std.log.scoped(.compiler).err("TODO implement .debug_info for type '{}'", .{ty});
1228 try dbg_info_buffer.append(abbrev_pad1);
1229 },
1230 }
1231}
1232
1233fn updateDeclDebugInfoAllocation(
1234 self: *DebugSymbols,
1235 allocator: *Allocator,
1236 text_block: *TextBlock,
1237 len: u32,
1238) !void {
1239 const tracy = trace(@src());
1240 defer tracy.end();
1241
1242 // This logic is nearly identical to the logic above in `updateDecl` for
1243 // `SrcFn` and the line number programs. If you are editing this logic, you
1244 // probably need to edit that logic too.
1245
1246 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
1247 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
1248 text_block.dbg_info_len = len;
1249 if (self.dbg_info_decl_last) |last| {
1250 if (text_block.dbg_info_next) |next| {
1251 // Update existing Decl - non-last item.
1252 if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) {
1253 // It grew too big, so we move it to a new location.
1254 if (text_block.dbg_info_prev) |prev| {
1255 _ = self.dbg_info_decl_free_list.put(allocator, prev, {}) catch {};
1256 prev.dbg_info_next = text_block.dbg_info_next;
1257 }
1258 next.dbg_info_prev = text_block.dbg_info_prev;
1259 text_block.dbg_info_next = null;
1260 // Populate where it used to be with NOPs.
1261 const file_pos = debug_info_sect.offset + text_block.dbg_info_off;
1262 try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos);
1263 // TODO Look at the free list before appending at the end.
1264 text_block.dbg_info_prev = last;
1265 last.dbg_info_next = text_block;
1266 self.dbg_info_decl_last = text_block;
1267
1268 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
1269 }
1270 } else if (text_block.dbg_info_prev == null) {
1271 // Append new Decl.
1272 // TODO Look at the free list before appending at the end.
1273 text_block.dbg_info_prev = last;
1274 last.dbg_info_next = text_block;
1275 self.dbg_info_decl_last = text_block;
1276
1277 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);
1278 }
1279 } else {
1280 // This is the first Decl of the .debug_info
1281 self.dbg_info_decl_first = text_block;
1282 self.dbg_info_decl_last = text_block;
1283
1284 text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den;
1285 }
1286}
1287
1288fn writeDeclDebugInfo(self: *DebugSymbols, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
1289 const tracy = trace(@src());
1290 defer tracy.end();
1291
1292 // This logic is nearly identical to the logic above in `updateDecl` for
1293 // `SrcFn` and the line number programs. If you are editing this logic, you
1294 // probably need to edit that logic too.
1295
1296 const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
1297 const debug_info_sect = &dwarf_segment.sections.items[self.debug_info_section_index.?];
1298
1299 const last_decl = self.dbg_info_decl_last.?;
1300 // +1 for a trailing zero to end the children of the decl tag.
1301 const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
1302 if (needed_size != debug_info_sect.size) {
1303 if (needed_size > dwarf_segment.allocatedSize(debug_info_sect.offset)) {
1304 const new_offset = dwarf_segment.findFreeSpace(needed_size, 1, null);
1305 const existing_size = last_decl.dbg_info_off;
1306
1307 log.debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{
1308 existing_size,
1309 debug_info_sect.offset,
1310 new_offset,
1311 });
1312
1313 const amt = try self.file.copyRangeAll(debug_info_sect.offset, self.file, new_offset, existing_size);
1314 if (amt != existing_size) return error.InputOutput;
1315 debug_info_sect.offset = @intCast(u32, new_offset);
1316 debug_info_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
1317 }
1318 debug_info_sect.size = needed_size;
1319 self.load_commands_dirty = true; // TODO look into making only the one section dirty
1320 self.debug_info_header_dirty = true;
1321 }
1322 const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev|
1323 text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len)
1324 else
1325 0;
1326 const next_padding_size: u32 = if (text_block.dbg_info_next) |next|
1327 next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len)
1328 else
1329 0;
1330
1331 // To end the children of the decl tag.
1332 const trailing_zero = text_block.dbg_info_next == null;
1333
1334 // We only have support for one compilation unit so far, so the offsets are directly
1335 // from the .debug_info section.
1336 const file_pos = debug_info_sect.offset + text_block.dbg_info_off;
1337 try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos);
1338}
1339
1340fn getDebugLineProgramOff(self: DebugSymbols) u32 {
1341 return self.dbg_line_fn_first.?.off;
1342}
1343
1344fn getDebugLineProgramEnd(self: DebugSymbols) u32 {
1345 return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
1346}
1347
1348/// TODO Improve this to use a table.
1349fn makeDebugString(self: *DebugSymbols, allocator: *Allocator, bytes: []const u8) !u32 {
1350 try self.debug_string_table.ensureCapacity(allocator, self.debug_string_table.items.len + bytes.len + 1);
1351 const result = self.debug_string_table.items.len;
1352 self.debug_string_table.appendSliceAssumeCapacity(bytes);
1353 self.debug_string_table.appendAssumeCapacity(0);
1354 return @intCast(u32, result);
1355}
1356
1357/// The reloc offset for the line offset of a function from the previous function's line.
1358/// It's a fixed-size 4-byte ULEB128.
1359fn getRelocDbgLineOff() usize {
1360 return dbg_line_vaddr_reloc_index + @sizeOf(u64) + 1;
1361}
1362
1363fn getRelocDbgFileIndex() usize {
1364 return getRelocDbgLineOff() + 5;
1365}
1366
1367fn getRelocDbgInfoSubprogramHighPC() u32 {
1368 return dbg_info_low_pc_reloc_index + @sizeOf(u64);
1369}
1370
1371fn dbgLineNeededHeaderBytes(self: DebugSymbols, module: *Module) u32 {
1372 const directory_entry_format_count = 1;
1373 const file_name_entry_format_count = 1;
1374 const directory_count = 1;
1375 const file_name_count = 1;
1376 const root_src_dir_path_len = if (module.root_pkg.root_src_directory.path) |p| p.len else 1; // "."
1377 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
1378 directory_count * 8 + file_name_count * 8 +
1379 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
1380 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
1381 root_src_dir_path_len +
1382 module.root_pkg.root_src_path.len);
1383}
1384
1385fn dbgInfoNeededHeaderBytes(self: DebugSymbols) u32 {
1386 return 120;
1387}
1388
1389/// Writes to the file a buffer, prefixed and suffixed by the specified number of
1390/// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
1391/// are less than 126,976 bytes (if this limit is ever reached, this function can be
1392/// improved to make more than one pwritev call, or the limit can be raised by a fixed
1393/// amount by increasing the length of `vecs`).
1394fn pwriteDbgLineNops(
1395 self: *DebugSymbols,
1396 prev_padding_size: usize,
1397 buf: []const u8,
1398 next_padding_size: usize,
1399 offset: u64,
1400) !void {
1401 const tracy = trace(@src());
1402 defer tracy.end();
1403
1404 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
1405 const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };
1406 var vecs: [32]std.os.iovec_const = undefined;
1407 var vec_index: usize = 0;
1408 {
1409 var padding_left = prev_padding_size;
1410 if (padding_left % 2 != 0) {
1411 vecs[vec_index] = .{
1412 .iov_base = &three_byte_nop,
1413 .iov_len = three_byte_nop.len,
1414 };
1415 vec_index += 1;
1416 padding_left -= three_byte_nop.len;
1417 }
1418 while (padding_left > page_of_nops.len) {
1419 vecs[vec_index] = .{
1420 .iov_base = &page_of_nops,
1421 .iov_len = page_of_nops.len,
1422 };
1423 vec_index += 1;
1424 padding_left -= page_of_nops.len;
1425 }
1426 if (padding_left > 0) {
1427 vecs[vec_index] = .{
1428 .iov_base = &page_of_nops,
1429 .iov_len = padding_left,
1430 };
1431 vec_index += 1;
1432 }
1433 }
1434
1435 vecs[vec_index] = .{
1436 .iov_base = buf.ptr,
1437 .iov_len = buf.len,
1438 };
1439 vec_index += 1;
1440
1441 {
1442 var padding_left = next_padding_size;
1443 if (padding_left % 2 != 0) {
1444 vecs[vec_index] = .{
1445 .iov_base = &three_byte_nop,
1446 .iov_len = three_byte_nop.len,
1447 };
1448 vec_index += 1;
1449 padding_left -= three_byte_nop.len;
1450 }
1451 while (padding_left > page_of_nops.len) {
1452 vecs[vec_index] = .{
1453 .iov_base = &page_of_nops,
1454 .iov_len = page_of_nops.len,
1455 };
1456 vec_index += 1;
1457 padding_left -= page_of_nops.len;
1458 }
1459 if (padding_left > 0) {
1460 vecs[vec_index] = .{
1461 .iov_base = &page_of_nops,
1462 .iov_len = padding_left,
1463 };
1464 vec_index += 1;
1465 }
1466 }
1467 try self.file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
1468}
1469
1470/// Writes to the file a buffer, prefixed and suffixed by the specified number of
1471/// bytes of padding.
1472fn pwriteDbgInfoNops(
1473 self: *DebugSymbols,
1474 prev_padding_size: usize,
1475 buf: []const u8,
1476 next_padding_size: usize,
1477 trailing_zero: bool,
1478 offset: u64,
1479) !void {
1480 const tracy = trace(@src());
1481 defer tracy.end();
1482
1483 const page_of_nops = [1]u8{abbrev_pad1} ** 4096;
1484 var vecs: [32]std.os.iovec_const = undefined;
1485 var vec_index: usize = 0;
1486 {
1487 var padding_left = prev_padding_size;
1488 while (padding_left > page_of_nops.len) {
1489 vecs[vec_index] = .{
1490 .iov_base = &page_of_nops,
1491 .iov_len = page_of_nops.len,
1492 };
1493 vec_index += 1;
1494 padding_left -= page_of_nops.len;
1495 }
1496 if (padding_left > 0) {
1497 vecs[vec_index] = .{
1498 .iov_base = &page_of_nops,
1499 .iov_len = padding_left,
1500 };
1501 vec_index += 1;
1502 }
1503 }
1504
1505 vecs[vec_index] = .{
1506 .iov_base = buf.ptr,
1507 .iov_len = buf.len,
1508 };
1509 vec_index += 1;
1510
1511 {
1512 var padding_left = next_padding_size;
1513 while (padding_left > page_of_nops.len) {
1514 vecs[vec_index] = .{
1515 .iov_base = &page_of_nops,
1516 .iov_len = page_of_nops.len,
1517 };
1518 vec_index += 1;
1519 padding_left -= page_of_nops.len;
1520 }
1521 if (padding_left > 0) {
1522 vecs[vec_index] = .{
1523 .iov_base = &page_of_nops,
1524 .iov_len = padding_left,
1525 };
1526 vec_index += 1;
1527 }
1528 }
1529
1530 if (trailing_zero) {
1531 var zbuf = [1]u8{0};
1532 vecs[vec_index] = .{
1533 .iov_base = &zbuf,
1534 .iov_len = zbuf.len,
1535 };
1536 vec_index += 1;
1537 }
1538
1539 try self.file.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
1540}
src/link/MachO/commands.zig+46-1
......@@ -5,9 +5,14 @@ const mem = std.mem;
55const meta = std.meta;
66const macho = std.macho;
77const testing = std.testing;
8const assert = std.debug.assert;
89
910const Allocator = std.mem.Allocator;
10const makeStaticString = @import("../MachO.zig").makeStaticString;
11const MachO = @import("../MachO.zig");
12const makeStaticString = MachO.makeStaticString;
13const satMul = MachO.satMul;
14const alloc_num = MachO.alloc_num;
15const alloc_den = MachO.alloc_den;
1116
1217pub const LoadCommand = union(enum) {
1318 Segment: SegmentCommand,
......@@ -19,6 +24,7 @@ pub const LoadCommand = union(enum) {
1924 Main: macho.entry_point_command,
2025 VersionMin: macho.version_min_command,
2126 SourceVersion: macho.source_version_command,
27 Uuid: macho.uuid_command,
2228 LinkeditData: macho.linkedit_data_command,
2329 Unknown: GenericCommandWithData(macho.load_command),
2430
......@@ -58,6 +64,9 @@ pub const LoadCommand = union(enum) {
5864 macho.LC_SOURCE_VERSION => LoadCommand{
5965 .SourceVersion = try stream.reader().readStruct(macho.source_version_command),
6066 },
67 macho.LC_UUID => LoadCommand{
68 .Uuid = try stream.reader().readStruct(macho.uuid_command),
69 },
6170 macho.LC_FUNCTION_STARTS, macho.LC_DATA_IN_CODE, macho.LC_CODE_SIGNATURE => LoadCommand{
6271 .LinkeditData = try stream.reader().readStruct(macho.linkedit_data_command),
6372 },
......@@ -75,6 +84,7 @@ pub const LoadCommand = union(enum) {
7584 .Main => |x| writeStruct(x, writer),
7685 .VersionMin => |x| writeStruct(x, writer),
7786 .SourceVersion => |x| writeStruct(x, writer),
87 .Uuid => |x| writeStruct(x, writer),
7888 .LinkeditData => |x| writeStruct(x, writer),
7989 .Segment => |x| x.write(writer),
8090 .Dylinker => |x| x.write(writer),
......@@ -91,6 +101,7 @@ pub const LoadCommand = union(enum) {
91101 .Main => |x| x.cmd,
92102 .VersionMin => |x| x.cmd,
93103 .SourceVersion => |x| x.cmd,
104 .Uuid => |x| x.cmd,
94105 .LinkeditData => |x| x.cmd,
95106 .Segment => |x| x.inner.cmd,
96107 .Dylinker => |x| x.inner.cmd,
......@@ -108,6 +119,7 @@ pub const LoadCommand = union(enum) {
108119 .VersionMin => |x| x.cmdsize,
109120 .SourceVersion => |x| x.cmdsize,
110121 .LinkeditData => |x| x.cmdsize,
122 .Uuid => |x| x.cmdsize,
111123 .Segment => |x| x.inner.cmdsize,
112124 .Dylinker => |x| x.inner.cmdsize,
113125 .Dylib => |x| x.inner.cmdsize,
......@@ -138,6 +150,7 @@ pub const LoadCommand = union(enum) {
138150 .Main => |x| meta.eql(x, other.Main),
139151 .VersionMin => |x| meta.eql(x, other.VersionMin),
140152 .SourceVersion => |x| meta.eql(x, other.SourceVersion),
153 .Uuid => |x| meta.eql(x, other.Uuid),
141154 .LinkeditData => |x| meta.eql(x, other.LinkeditData),
142155 .Segment => |x| x.eql(other.Segment),
143156 .Dylinker => |x| x.eql(other.Dylinker),
......@@ -188,6 +201,38 @@ pub const SegmentCommand = struct {
188201 self.sections.deinit(alloc);
189202 }
190203
204 pub fn allocatedSize(self: SegmentCommand, start: u64) u64 {
205 assert(start > 0);
206 if (start == self.inner.fileoff)
207 return 0;
208 var min_pos: u64 = std.math.maxInt(u64);
209 for (self.sections.items) |section| {
210 if (section.offset <= start) continue;
211 if (section.offset < min_pos) min_pos = section.offset;
212 }
213 return min_pos - start;
214 }
215
216 fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 {
217 const end = start + satMul(size, alloc_num) / alloc_den;
218 for (self.sections.items) |section| {
219 const increased_size = satMul(section.size, alloc_num) / alloc_den;
220 const test_end = section.offset + increased_size;
221 if (end > section.offset and start < test_end) {
222 return test_end;
223 }
224 }
225 return null;
226 }
227
228 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u16, start: ?u64) u64 {
229 var st: u64 = if (start) |v| v else self.inner.fileoff;
230 while (self.detectAllocCollision(st, object_size)) |item_end| {
231 st = mem.alignForwardGeneric(u64, item_end, min_alignment);
232 }
233 return st;
234 }
235
191236 fn eql(self: SegmentCommand, other: SegmentCommand) bool {
192237 if (!meta.eql(self.inner, other.inner)) return false;
193238 const lhs = self.sections.items;