authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-12-05 13:48:12+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-05 13:48:12+01:00
log3575048c0ae8b5bae2cd5c6fd3dbc9cb569c4b1f
treeffcda7340f8a562238f0023efe13d9c98bb9468e
parentb1c422776316344489eabf1befd47f7769806376
parent899d7a771e6653af04d273efabd671955c949cac
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13763 from ziglang/macho-dsym-incr

macho+dsym: refactor and reorganize file layout to support incremental DWARF updates

3 files changed, 164 insertions(+), 238 deletions(-)

src/link/Dwarf.zig+13-32
......@@ -1163,32 +1163,28 @@ pub fn commitDeclState(
11631163 next_padding_size,
11641164 );
11651165 },
1166
11661167 .macho => {
11671168 const macho_file = file.cast(File.MachO).?;
11681169 const d_sym = &macho_file.d_sym.?;
1169 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
11701170 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
11711171 if (needed_size != debug_line_sect.size) {
11721172 if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {
11731173 const new_offset = d_sym.findFreeSpace(needed_size, 1);
11741174 const existing_size = last_src_fn.off;
1175
1176 log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
1175 std.log.scoped(.dsym).debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
11771176 existing_size,
11781177 debug_line_sect.offset,
11791178 new_offset,
11801179 });
1181
1182 try copyRangeAllOverlappingAlloc(
1183 gpa,
1184 d_sym.file,
1180 const amt = try d_sym.file.copyRangeAll(
11851181 debug_line_sect.offset,
1182 d_sym.file,
11861183 new_offset,
11871184 existing_size,
11881185 );
1189
1186 if (amt != existing_size) return error.InputOutput;
11901187 debug_line_sect.offset = @intCast(u32, new_offset);
1191 debug_line_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
11921188 }
11931189 debug_line_sect.size = needed_size;
11941190 d_sym.debug_line_header_dirty = true;
......@@ -1202,6 +1198,7 @@ pub fn commitDeclState(
12021198 next_padding_size,
12031199 );
12041200 },
1201
12051202 .wasm => {
12061203 const wasm_file = file.cast(File.Wasm).?;
12071204 const atom = wasm_file.debug_line_atom.?;
......@@ -1318,7 +1315,7 @@ pub fn commitDeclState(
13181315 .macho => {
13191316 const macho_file = file.cast(File.MachO).?;
13201317 const d_sym = &macho_file.d_sym.?;
1321 try d_sym.relocs.append(d_sym.base.base.allocator, .{
1318 try d_sym.relocs.append(d_sym.allocator, .{
13221319 .type = switch (reloc.type) {
13231320 .direct_load => .direct_load,
13241321 .got_load => .got_load,
......@@ -1462,32 +1459,28 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
14621459 trailing_zero,
14631460 );
14641461 },
1462
14651463 .macho => {
14661464 const macho_file = file.cast(File.MachO).?;
14671465 const d_sym = &macho_file.d_sym.?;
1468 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
14691466 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
14701467 if (needed_size != debug_info_sect.size) {
14711468 if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {
14721469 const new_offset = d_sym.findFreeSpace(needed_size, 1);
14731470 const existing_size = last_decl.off;
1474
1475 log.debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{
1471 std.log.scoped(.dsym).debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{
14761472 existing_size,
14771473 debug_info_sect.offset,
14781474 new_offset,
14791475 });
1480
1481 try copyRangeAllOverlappingAlloc(
1482 gpa,
1483 d_sym.file,
1476 const amt = try d_sym.file.copyRangeAll(
14841477 debug_info_sect.offset,
1478 d_sym.file,
14851479 new_offset,
14861480 existing_size,
14871481 );
1488
1482 if (amt != existing_size) return error.InputOutput;
14891483 debug_info_sect.offset = @intCast(u32, new_offset);
1490 debug_info_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
14911484 }
14921485 debug_info_sect.size = needed_size;
14931486 d_sym.debug_info_header_dirty = true;
......@@ -1502,6 +1495,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
15021495 trailing_zero,
15031496 );
15041497 },
1498
15051499 .wasm => {
15061500 const wasm_file = file.cast(File.Wasm).?;
15071501 const info_atom = wasm_file.debug_info_atom.?;
......@@ -2569,16 +2563,3 @@ fn addDbgInfoErrorSet(
25692563 // DW.AT.enumeration_type delimit children
25702564 try dbg_info_buffer.append(0);
25712565}
2572
2573fn copyRangeAllOverlappingAlloc(
2574 allocator: Allocator,
2575 file: std.fs.File,
2576 in_offset: u64,
2577 out_offset: u64,
2578 len: usize,
2579) !void {
2580 const buf = try allocator.alloc(u8, len);
2581 defer allocator.free(buf);
2582 const amt = try file.preadAll(buf, in_offset);
2583 try file.pwriteAll(buf[0..amt], out_offset);
2584}
src/link/MachO.zig+5-4
......@@ -347,9 +347,10 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
347347 });
348348
349349 self.d_sym = .{
350 .base = self,
350 .allocator = allocator,
351351 .dwarf = link.File.Dwarf.init(allocator, .macho, options.target),
352352 .file = d_sym_file,
353 .page_size = self.page_size,
353354 };
354355 }
355356
......@@ -366,7 +367,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
366367 try self.populateMissingMetadata();
367368
368369 if (self.d_sym) |*d_sym| {
369 try d_sym.populateMissingMetadata(allocator);
370 try d_sym.populateMissingMetadata();
370371 }
371372
372373 return self;
......@@ -629,7 +630,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
629630
630631 if (self.d_sym) |*d_sym| {
631632 // Flush debug symbols bundle.
632 try d_sym.flushModule(self.base.allocator, self.base.options);
633 try d_sym.flushModule(self);
633634 }
634635
635636 // if (build_options.enable_link_snapshots) {
......@@ -1900,7 +1901,7 @@ pub fn deinit(self: *MachO) void {
19001901 }
19011902
19021903 if (self.d_sym) |*d_sym| {
1903 d_sym.deinit(gpa);
1904 d_sym.deinit();
19041905 }
19051906
19061907 self.got_entries.deinit(gpa);
src/link/MachO/DebugSymbols.zig+146-202
......@@ -20,15 +20,16 @@ const Module = @import("../../Module.zig");
2020const StringTable = @import("../strtab.zig").StringTable;
2121const Type = @import("../../type.zig").Type;
2222
23base: *MachO,
23allocator: Allocator,
2424dwarf: Dwarf,
2525file: fs.File,
26page_size: u16,
2627
2728segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
2829sections: std.ArrayListUnmanaged(macho.section_64) = .{},
2930
30linkedit_segment_cmd_index: ?u8 = null,
3131dwarf_segment_cmd_index: ?u8 = null,
32linkedit_segment_cmd_index: ?u8 = null,
3233
3334debug_info_section_index: ?u8 = null,
3435debug_abbrev_section_index: ?u8 = null,
......@@ -43,7 +44,6 @@ debug_info_header_dirty: bool = false,
4344debug_line_header_dirty: bool = false,
4445
4546strtab: StringTable(.strtab) = .{},
46
4747relocs: std.ArrayListUnmanaged(Reloc) = .{},
4848
4949pub const Reloc = struct {
......@@ -59,41 +59,20 @@ pub const Reloc = struct {
5959
6060/// You must call this function *after* `MachO.populateMissingMetadata()`
6161/// has been called to get a viable debug symbols output.
62pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {
63 if (self.linkedit_segment_cmd_index == null) {
64 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
65 const fileoff = @intCast(u64, self.base.page_size);
66 const needed_size = @intCast(u64, self.base.page_size) * 2;
67 log.debug("found __LINKEDIT segment free space 0x{x} to 0x{x}", .{ fileoff, needed_size });
68 // TODO this needs reworking
69 try self.segments.append(allocator, .{
70 .segname = makeStaticString("__LINKEDIT"),
71 .vmaddr = fileoff,
72 .vmsize = needed_size,
73 .fileoff = fileoff,
74 .filesize = needed_size,
75 .maxprot = macho.PROT.READ,
76 .initprot = macho.PROT.READ,
77 .cmdsize = @sizeOf(macho.segment_command_64),
78 });
79 }
80
62pub fn populateMissingMetadata(self: *DebugSymbols) !void {
8163 if (self.dwarf_segment_cmd_index == null) {
8264 self.dwarf_segment_cmd_index = @intCast(u8, self.segments.items.len);
8365
84 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
66 const off = @intCast(u64, self.page_size);
8567 const ideal_size: u16 = 200 + 128 + 160 + 250;
86 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);
87 const fileoff = linkedit.fileoff + linkedit.filesize;
88 const vmaddr = linkedit.vmaddr + linkedit.vmsize;
68 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
8969
90 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
70 log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
9171
92 try self.segments.append(allocator, .{
72 try self.segments.append(self.allocator, .{
9373 .segname = makeStaticString("__DWARF"),
94 .vmaddr = vmaddr,
9574 .vmsize = needed_size,
96 .fileoff = fileoff,
75 .fileoff = off,
9776 .filesize = needed_size,
9877 .cmdsize = @sizeOf(macho.segment_command_64),
9978 });
......@@ -128,10 +107,20 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
128107 self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0);
129108 self.debug_line_header_dirty = true;
130109 }
110
111 if (self.linkedit_segment_cmd_index == null) {
112 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
113 try self.segments.append(self.allocator, .{
114 .segname = makeStaticString("__LINKEDIT"),
115 .maxprot = macho.PROT.READ,
116 .initprot = macho.PROT.READ,
117 .cmdsize = @sizeOf(macho.segment_command_64),
118 });
119 }
131120}
132121
133122fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 {
134 const segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
123 const segment = self.getDwarfSegmentPtr();
135124 var sect = macho.section_64{
136125 .sectname = makeStaticString(sectname),
137126 .segname = segment.segname,
......@@ -141,8 +130,6 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
141130 const alignment_pow_2 = try math.powi(u32, 2, alignment);
142131 const off = self.findFreeSpace(size, alignment_pow_2);
143132
144 assert(off + size <= segment.fileoff + segment.filesize); // TODO expand
145
146133 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
147134 sect.segName(),
148135 sect.sectName(),
......@@ -154,7 +141,7 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
154141 sect.offset = @intCast(u32, off);
155142
156143 const index = @intCast(u8, self.sections.items.len);
157 try self.sections.append(self.base.base.allocator, sect);
144 try self.sections.append(self.allocator, sect);
158145 segment.cmdsize += @sizeOf(macho.section_64);
159146 segment.nsects += 1;
160147
......@@ -174,7 +161,7 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
174161}
175162
176163pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {
177 const segment = self.segments.items[self.dwarf_segment_cmd_index.?];
164 const segment = self.getDwarfSegmentPtr();
178165 var offset: u64 = segment.fileoff;
179166 while (self.detectAllocCollision(offset, object_size)) |item_end| {
180167 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
......@@ -182,34 +169,35 @@ pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64)
182169 return offset;
183170}
184171
185pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Options) !void {
186 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
187 // Zig source code.
172pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
173 // TODO This linker code currently assumes there is only 1 compilation unit
174 // and it corresponds to the Zig source code.
175 const options = macho_file.base.options;
188176 const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
189177
190178 for (self.relocs.items) |*reloc| {
191179 const sym = switch (reloc.type) {
192 .direct_load => self.base.getSymbol(.{ .sym_index = reloc.target, .file = null }),
180 .direct_load => macho_file.getSymbol(.{ .sym_index = reloc.target, .file = null }),
193181 .got_load => blk: {
194 const got_index = self.base.got_entries_table.get(.{
182 const got_index = macho_file.got_entries_table.get(.{
195183 .sym_index = reloc.target,
196184 .file = null,
197185 }).?;
198 const got_entry = self.base.got_entries.items[got_index];
199 break :blk got_entry.getSymbol(self.base);
186 const got_entry = macho_file.got_entries.items[got_index];
187 break :blk got_entry.getSymbol(macho_file);
200188 },
201189 };
202190 if (sym.n_value == reloc.prev_vaddr) continue;
203191
204192 const sym_name = switch (reloc.type) {
205 .direct_load => self.base.getSymbolName(.{ .sym_index = reloc.target, .file = null }),
193 .direct_load => macho_file.getSymbolName(.{ .sym_index = reloc.target, .file = null }),
206194 .got_load => blk: {
207 const got_index = self.base.got_entries_table.get(.{
195 const got_index = macho_file.got_entries_table.get(.{
208196 .sym_index = reloc.target,
209197 .file = null,
210198 }).?;
211 const got_entry = self.base.got_entries.items[got_index];
212 break :blk got_entry.getName(self.base);
199 const got_entry = macho_file.got_entries.items[got_index];
200 break :blk got_entry.getName(macho_file);
213201 },
214202 };
215203 const sect = &self.sections.items[self.debug_info_section_index.?];
......@@ -225,35 +213,35 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
225213 }
226214
227215 if (self.debug_abbrev_section_dirty) {
228 try self.dwarf.writeDbgAbbrev(&self.base.base);
216 try self.dwarf.writeDbgAbbrev(&macho_file.base);
229217 self.debug_abbrev_section_dirty = false;
230218 }
231219
232220 if (self.debug_info_header_dirty) {
233221 // Currently only one compilation unit is supported, so the address range is simply
234222 // identical to the main program header virtual address and memory size.
235 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
223 const text_section = macho_file.sections.items(.header)[macho_file.text_section_index.?];
236224 const low_pc = text_section.addr;
237225 const high_pc = text_section.addr + text_section.size;
238 try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc);
226 try self.dwarf.writeDbgInfoHeader(&macho_file.base, module, low_pc, high_pc);
239227 self.debug_info_header_dirty = false;
240228 }
241229
242230 if (self.debug_aranges_section_dirty) {
243231 // Currently only one compilation unit is supported, so the address range is simply
244232 // identical to the main program header virtual address and memory size.
245 const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
246 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);
233 const text_section = macho_file.sections.items(.header)[macho_file.text_section_index.?];
234 try self.dwarf.writeDbgAranges(&macho_file.base, text_section.addr, text_section.size);
247235 self.debug_aranges_section_dirty = false;
248236 }
249237
250238 if (self.debug_line_header_dirty) {
251 try self.dwarf.writeDbgLineHeader(&self.base.base, module);
239 try self.dwarf.writeDbgLineHeader(&macho_file.base, module);
252240 self.debug_line_header_dirty = false;
253241 }
254242
255243 {
256 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
244 const dwarf_segment = self.getDwarfSegmentPtr();
257245 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
258246 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
259247 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
......@@ -277,41 +265,45 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
277265 }
278266 }
279267
280 var lc_buffer = std.ArrayList(u8).init(allocator);
268 var lc_buffer = std.ArrayList(u8).init(self.allocator);
281269 defer lc_buffer.deinit();
282270 const lc_writer = lc_buffer.writer();
283271 var ncmds: u32 = 0;
284272
285 self.updateDwarfSegment();
286 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
287 self.updateDwarfSegment();
273 self.finalizeDwarfSegment(macho_file);
274 try self.writeLinkeditSegmentData(macho_file, &ncmds, lc_writer);
288275
289276 {
290 try lc_writer.writeStruct(self.base.uuid);
277 try lc_writer.writeStruct(macho_file.uuid);
291278 ncmds += 1;
292279 }
293280
294 var headers_buf = std.ArrayList(u8).init(allocator);
281 var headers_buf = std.ArrayList(u8).init(self.allocator);
295282 defer headers_buf.deinit();
296 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
283 try self.writeSegmentHeaders(macho_file, &ncmds, headers_buf.writer());
297284
298285 try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
299286 try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
300287
301 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
288 try self.writeHeader(
289 macho_file,
290 ncmds,
291 @intCast(u32, lc_buffer.items.len + headers_buf.items.len),
292 );
302293
303294 assert(!self.debug_abbrev_section_dirty);
304295 assert(!self.debug_aranges_section_dirty);
305296 assert(!self.debug_string_table_dirty);
306297}
307298
308pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
299pub fn deinit(self: *DebugSymbols) void {
300 const gpa = self.allocator;
309301 self.file.close();
310 self.segments.deinit(allocator);
311 self.sections.deinit(allocator);
302 self.segments.deinit(gpa);
303 self.sections.deinit(gpa);
312304 self.dwarf.deinit();
313 self.strtab.deinit(allocator);
314 self.relocs.deinit(allocator);
305 self.strtab.deinit(gpa);
306 self.relocs.deinit(gpa);
315307}
316308
317309pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
......@@ -327,47 +319,48 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
327319 }
328320}
329321
330fn updateDwarfSegment(self: *DebugSymbols) void {
331 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
332 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
333
334 const new_start_aligned = linkedit.vmaddr + linkedit.vmsize;
335 const old_start_aligned = dwarf_segment.vmaddr;
336 const diff = new_start_aligned - old_start_aligned;
337 if (diff > 0) {
338 dwarf_segment.vmaddr = new_start_aligned;
339 }
322fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
323 const base_vmaddr = blk: {
324 // Note that we purposely take the last VM address of the MachO binary including
325 // the binary's LINKEDIT segment. This is in contrast to how dsymutil does it
326 // which overwrites the the address space taken by the original MachO binary,
327 // however at the cost of having LINKEDIT preceed DWARF in dSYM binary which we
328 // do not want as we want to be able to incrementally move DWARF sections in the
329 // file as we please.
330 const last_seg = macho_file.getLinkeditSegmentPtr();
331 break :blk last_seg.vmaddr + last_seg.vmsize;
332 };
333 const dwarf_segment = self.getDwarfSegmentPtr();
340334
341 var max_offset: u64 = 0;
342 for (self.sections.items) |*sect| {
343 sect.addr += diff;
344 log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{
345 sect.segName(),
346 sect.sectName(),
347 sect.offset,
348 sect.offset + sect.size,
349 sect.addr,
350 sect.addr + sect.size,
351 });
352 if (sect.offset + sect.size > max_offset) {
353 max_offset = sect.offset + sect.size;
354 }
335 var file_size: u64 = 0;
336 for (self.sections.items) |header| {
337 file_size = @max(file_size, header.offset + header.size);
355338 }
356339
357 const file_size = max_offset - dwarf_segment.fileoff;
358 log.debug("__DWARF size 0x{x}", .{file_size});
359
360 if (file_size != dwarf_segment.filesize) {
361 dwarf_segment.filesize = file_size;
362 dwarf_segment.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.filesize, self.base.page_size);
363 }
340 const aligned_size = mem.alignForwardGeneric(u64, file_size, self.page_size);
341 dwarf_segment.vmaddr = base_vmaddr;
342 dwarf_segment.filesize = aligned_size;
343 dwarf_segment.vmsize = aligned_size;
344
345 const linkedit = self.getLinkeditSegmentPtr();
346 linkedit.vmaddr = mem.alignForwardGeneric(
347 u64,
348 dwarf_segment.vmaddr + aligned_size,
349 self.page_size,
350 );
351 linkedit.fileoff = mem.alignForwardGeneric(
352 u64,
353 dwarf_segment.fileoff + aligned_size,
354 self.page_size,
355 );
356 log.debug("found __LINKEDIT segment free space at 0x{x}", .{linkedit.fileoff});
364357}
365358
366fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void {
359fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, writer: anytype) !void {
367360 // Write segment/section headers from the binary file first.
368 const end = self.base.linkedit_segment_cmd_index.?;
369 for (self.base.segments.items[0..end]) |seg, i| {
370 const indexes = self.base.getSectionIndexes(@intCast(u8, i));
361 const end = macho_file.linkedit_segment_cmd_index.?;
362 for (macho_file.segments.items[0..end]) |seg, i| {
363 const indexes = macho_file.getSectionIndexes(@intCast(u8, i));
371364 var out_seg = seg;
372365 out_seg.fileoff = 0;
373366 out_seg.filesize = 0;
......@@ -376,7 +369,7 @@ fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void
376369
377370 // Update section headers count; any section with size of 0 is excluded
378371 // since it doesn't have any data in the final binary file.
379 for (self.base.sections.items(.header)[indexes.start..indexes.end]) |header| {
372 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
380373 if (header.size == 0) continue;
381374 out_seg.cmdsize += @sizeOf(macho.section_64);
382375 out_seg.nsects += 1;
......@@ -387,7 +380,7 @@ fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void
387380 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
388381
389382 try writer.writeStruct(out_seg);
390 for (self.base.sections.items(.header)[indexes.start..indexes.end]) |header| {
383 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
391384 if (header.size == 0) continue;
392385 var out_header = header;
393386 out_header.offset = 0;
......@@ -397,20 +390,21 @@ fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void
397390 ncmds.* += 1;
398391 }
399392 // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.
400 for (self.segments.items) |seg| {
393 for (self.segments.items) |seg, i| {
394 const indexes = self.getSectionIndexes(@intCast(u8, i));
401395 try writer.writeStruct(seg);
396 for (self.sections.items[indexes.start..indexes.end]) |header| {
397 try writer.writeStruct(header);
398 }
402399 ncmds.* += 1;
403400 }
404 for (self.sections.items) |header| {
405 try writer.writeStruct(header);
406 }
407401}
408402
409fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
403fn writeHeader(self: *DebugSymbols, macho_file: *MachO, ncmds: u32, sizeofcmds: u32) !void {
410404 var header: macho.mach_header_64 = .{};
411405 header.filetype = macho.MH_DSYM;
412406
413 switch (self.base.base.options.target.cpu.arch) {
407 switch (macho_file.base.options.target.cpu.arch) {
414408 .aarch64 => {
415409 header.cputype = macho.CPU_TYPE_ARM64;
416410 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
......@@ -431,7 +425,7 @@ fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
431425}
432426
433427pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
434 const seg = self.segments.items[self.dwarf_segment_cmd_index.?];
428 const seg = self.getDwarfSegmentPtr();
435429 assert(start >= seg.fileoff);
436430 var min_pos: u64 = std.math.maxInt(u64);
437431 for (self.sections.items) |section| {
......@@ -441,14 +435,15 @@ pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
441435 return min_pos - start;
442436}
443437
444fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype) !void {
438fn writeLinkeditSegmentData(
439 self: *DebugSymbols,
440 macho_file: *MachO,
441 ncmds: *u32,
442 lc_writer: anytype,
443) !void {
445444 const tracy = trace(@src());
446445 defer tracy.end();
447446
448 const source_vmaddr = self.base.segments.items[self.base.linkedit_segment_cmd_index.?].vmaddr;
449 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
450 seg.vmaddr = source_vmaddr;
451
452447 var symtab_cmd = macho.symtab_command{
453448 .cmdsize = @sizeOf(macho.symtab_command),
454449 .symoff = 0,
......@@ -456,43 +451,43 @@ fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype
456451 .stroff = 0,
457452 .strsize = 0,
458453 };
459 try self.writeSymtab(&symtab_cmd);
454 try self.writeSymtab(macho_file, &symtab_cmd);
460455 try self.writeStrtab(&symtab_cmd);
461456 try lc_writer.writeStruct(symtab_cmd);
462457 ncmds.* += 1;
463458
464 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.base.page_size);
465 seg.filesize = aligned_size;
459 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
460 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
466461 seg.vmsize = aligned_size;
467462}
468463
469fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
464fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_command) !void {
470465 const tracy = trace(@src());
471466 defer tracy.end();
472467
473 const gpa = self.base.base.allocator;
468 const gpa = self.allocator;
474469
475470 var locals = std.ArrayList(macho.nlist_64).init(gpa);
476471 defer locals.deinit();
477472
478 for (self.base.locals.items) |sym, sym_id| {
473 for (macho_file.locals.items) |sym, sym_id| {
479474 if (sym.n_strx == 0) continue; // no name, skip
480475 const sym_loc = MachO.SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
481 if (self.base.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
482 if (self.base.getGlobal(self.base.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
476 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
477 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
483478 var out_sym = sym;
484 out_sym.n_strx = try self.strtab.insert(gpa, self.base.getSymbolName(sym_loc));
479 out_sym.n_strx = try self.strtab.insert(gpa, macho_file.getSymbolName(sym_loc));
485480 try locals.append(out_sym);
486481 }
487482
488483 var exports = std.ArrayList(macho.nlist_64).init(gpa);
489484 defer exports.deinit();
490485
491 for (self.base.globals.items) |global| {
492 const sym = self.base.getSymbol(global);
486 for (macho_file.globals.items) |global| {
487 const sym = macho_file.getSymbol(global);
493488 if (sym.undf()) continue; // import, skip
494489 var out_sym = sym;
495 out_sym.n_strx = try self.strtab.insert(gpa, self.base.getSymbolName(global));
490 out_sym.n_strx = try self.strtab.insert(gpa, macho_file.getSymbolName(global));
496491 try exports.append(out_sym);
497492 }
498493
......@@ -503,38 +498,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
503498 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
504499 const offset = mem.alignForwardGeneric(u64, seg.fileoff, @alignOf(macho.nlist_64));
505500 const needed_size = nsyms * @sizeOf(macho.nlist_64);
506
507 if (needed_size > seg.filesize) {
508 const aligned_size = mem.alignForwardGeneric(u64, needed_size, self.base.page_size);
509 const diff = @intCast(u32, aligned_size - seg.filesize);
510 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
511 seg.filesize = aligned_size;
512
513 try copyRangeAllOverlappingAlloc(
514 self.base.base.allocator,
515 self.file,
516 dwarf_seg.fileoff,
517 dwarf_seg.fileoff + diff,
518 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
519 );
520
521 const old_seg_fileoff = dwarf_seg.fileoff;
522 dwarf_seg.fileoff += diff;
523
524 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
525
526 for (self.sections.items) |*sect| {
527 const old_offset = sect.offset;
528 sect.offset += diff;
529
530 log.debug(" (moving {s},{s} from 0x{x} to 0x{x})", .{
531 sect.segName(),
532 sect.sectName(),
533 old_offset,
534 sect.offset,
535 });
536 }
537 }
501 seg.filesize = offset + needed_size - seg.fileoff;
538502
539503 lc.symoff = @intCast(u32, offset);
540504 lc.nsyms = @intCast(u32, nsyms);
......@@ -558,57 +522,37 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
558522 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
559523 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
560524 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
561 lc.stroff = @intCast(u32, offset);
562
563525 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
564 lc.strsize = @intCast(u32, needed_size);
565
566 if (symtab_size + needed_size > seg.filesize) {
567 const aligned_size = mem.alignForwardGeneric(u64, offset + needed_size, self.base.page_size);
568 const diff = @intCast(u32, aligned_size - seg.filesize);
569 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
570 seg.filesize = aligned_size;
571
572 try copyRangeAllOverlappingAlloc(
573 self.base.base.allocator,
574 self.file,
575 dwarf_seg.fileoff,
576 dwarf_seg.fileoff + diff,
577 math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
578 );
579526
580 const old_seg_fileoff = dwarf_seg.fileoff;
581 dwarf_seg.fileoff += diff;
527 seg.filesize = offset + needed_size - seg.fileoff;
528 lc.stroff = @intCast(u32, offset);
529 lc.strsize = @intCast(u32, needed_size);
582530
583 log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
531 log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
584532
585 for (self.sections.items) |*sect| {
586 const old_offset = sect.offset;
587 sect.offset += diff;
533 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
588534
589 log.debug(" (moving {s},{s} from 0x{x} to 0x{x})", .{
590 sect.segName(),
591 sect.sectName(),
592 old_offset,
593 sect.offset,
594 });
595 }
535 if (self.strtab.buffer.items.len < needed_size) {
536 // Ensure we are always padded to the actual length of the file.
537 try self.file.pwriteAll(&[_]u8{0}, lc.stroff + lc.strsize);
596538 }
539}
597540
598 log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
541pub fn getSectionIndexes(self: *DebugSymbols, segment_index: u8) struct { start: u8, end: u8 } {
542 var start: u8 = 0;
543 const nsects = for (self.segments.items) |seg, i| {
544 if (i == segment_index) break @intCast(u8, seg.nsects);
545 start += @intCast(u8, seg.nsects);
546 } else 0;
547 return .{ .start = start, .end = start + nsects };
548}
599549
600 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
550fn getDwarfSegmentPtr(self: *DebugSymbols) *macho.segment_command_64 {
551 const index = self.dwarf_segment_cmd_index.?;
552 return &self.segments.items[index];
601553}
602554
603fn copyRangeAllOverlappingAlloc(
604 allocator: Allocator,
605 file: std.fs.File,
606 in_offset: u64,
607 out_offset: u64,
608 len: usize,
609) !void {
610 const buf = try allocator.alloc(u8, len);
611 defer allocator.free(buf);
612 const amt = try file.preadAll(buf, in_offset);
613 try file.pwriteAll(buf[0..amt], out_offset);
555fn getLinkeditSegmentPtr(self: *DebugSymbols) *macho.segment_command_64 {
556 const index = self.linkedit_segment_cmd_index.?;
557 return &self.segments.items[index];
614558}