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
301301 try self.populateMissingMetadata();
302302 try self.d_sym.?.populateMissingMetadata(allocator);
303303
304 try self.writeLocalSymbol(0);
305 try self.d_sym.?.writeLocalSymbol(0);
306
304307 return self;
305308}
306309
......@@ -1123,6 +1126,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11231126 symbol.n_desc = 0;
11241127
11251128 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1129 try self.d_sym.?.writeLocalSymbol(decl.link.macho.local_sym_index);
11261130 } else {
11271131 const decl_name = mem.spanZ(decl.name);
11281132 const name_str_index = try self.makeString(decl_name);
......@@ -1140,6 +1144,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
11401144 self.offset_table.items[decl.link.macho.offset_table_index] = addr;
11411145
11421146 try self.writeLocalSymbol(decl.link.macho.local_sym_index);
1147 try self.d_sym.?.writeLocalSymbol(decl.link.macho.local_sym_index);
11431148 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
11441149 }
11451150
......@@ -1517,7 +1522,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15171522 .strsize = @intCast(u32, strtab_size),
15181523 },
15191524 });
1520 try self.writeLocalSymbol(0);
15211525 self.header_dirty = true;
15221526 self.load_commands_dirty = true;
15231527 self.string_table_dirty = true;
......@@ -1795,6 +1799,7 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {
17951799 self.string_table.appendSliceAssumeCapacity(bytes);
17961800 self.string_table.appendAssumeCapacity(0);
17971801 self.string_table_dirty = true;
1802 self.d_sym.?.string_table_dirty = true;
17981803 return @intCast(u32, result);
17991804}
18001805
......@@ -2247,7 +2252,6 @@ fn writeStringTable(self: *MachO) !void {
22472252 const tracy = trace(@src());
22482253 defer tracy.end();
22492254
2250 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
22512255 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
22522256 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
22532257 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;
1010const leb = std.leb;
1111const Allocator = mem.Allocator;
1212
13const trace = @import("../../tracy.zig").trace;
1314const MachO = @import("../MachO.zig");
15const satMul = MachO.satMul;
16const alloc_num = MachO.alloc_num;
17const alloc_den = MachO.alloc_den;
1418
1519usingnamespace @import("commands.zig");
1620
......@@ -40,8 +44,12 @@ uuid_cmd_index: ?u16 = null,
4044/// Index into __TEXT,__text section.
4145text_section_index: ?u16 = null,
4246
47linkedit_off: u16 = 0x1000,
48linkedit_size: u16 = 0x1000,
49
4350header_dirty: bool = false,
4451load_commands_dirty: bool = false,
52string_table_dirty: bool = false,
4553
4654/// You must call this function *after* `MachO.populateMissingMetadata()`
4755/// has been called to get a viable debug symbols output.
......@@ -61,36 +69,86 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
6169 self.header = header;
6270 self.header_dirty = true;
6371 }
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 }
64106 if (self.pagezero_segment_cmd_index == null) {
65107 self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
66108 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;
68113 }
69114 if (self.text_segment_cmd_index == null) {
70115 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
71116 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;
73121 }
74122 if (self.data_segment_cmd_index == null) outer: {
75123 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
76124 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
77125 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;
79130 }
80 if (self.uuid_cmd_index == null) {
81 const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?];
82 self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
83 try self.load_commands.append(allocator, base_cmd);
131 if (self.linkedit_segment_cmd_index == null) {
132 self.linkedit_segment_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;
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 });
84139 self.header_dirty = true;
85140 self.load_commands_dirty = true;
86141 }
87142}
88143
89144pub fn flush(self: *DebugSymbols, allocator: *Allocator) !void {
145 try self.writeStringTable();
90146 try self.writeLoadCommands(allocator);
91147 try self.writeHeader();
148
92149 assert(!self.header_dirty);
93150 assert(!self.load_commands_dirty);
151 assert(!self.string_table_dirty);
94152}
95153
96154pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
......@@ -100,7 +158,7 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
100158 self.file.close();
101159}
102160
103fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !void {
161fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand {
104162 var cmd = SegmentCommand.empty(.{
105163 .cmd = macho.LC_SEGMENT_64,
106164 .cmdsize = base_cmd.inner.cmdsize,
......@@ -142,9 +200,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: Segm
142200 cmd.sections.appendAssumeCapacity(sect);
143201 }
144202
145 try self.load_commands.append(allocator, .{ .Segment = cmd });
146 self.header_dirty = true;
147 self.load_commands_dirty = true;
203 return cmd;
148204}
149205
150206/// Writes all load commands and section headers.
......@@ -182,3 +238,115 @@ fn writeHeader(self: *DebugSymbols) !void {
182238 try self.file.pwriteAll(mem.asBytes(&self.header.?), 0);
183239 self.header_dirty = false;
184240}
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}