authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-27 20:25:16+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-31 10:19:04+01:00
log3174508903ed0cfdd06a395dd19b6fc2cbc395ed
treed71bc3faa5c8014322372620f35f85e324924a01
parentd9ce7a021bfeca7ba4b4e478617b4da590264a99

macho: write symbol and string tables to dSym


2 files changed, 185 insertions(+), 13 deletions(-)

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