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(...@@ -1163,32 +1163,28 @@ pub fn commitDeclState(
1163 next_padding_size,1163 next_padding_size,
1164 );1164 );
1165 },1165 },
1166
1166 .macho => {1167 .macho => {
1167 const macho_file = file.cast(File.MachO).?;1168 const macho_file = file.cast(File.MachO).?;
1168 const d_sym = &macho_file.d_sym.?;1169 const d_sym = &macho_file.d_sym.?;
1169 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1170 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];1170 const debug_line_sect = &d_sym.sections.items[d_sym.debug_line_section_index.?];
1171 if (needed_size != debug_line_sect.size) {1171 if (needed_size != debug_line_sect.size) {
1172 if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {1172 if (needed_size > d_sym.allocatedSize(debug_line_sect.offset)) {
1173 const new_offset = d_sym.findFreeSpace(needed_size, 1);1173 const new_offset = d_sym.findFreeSpace(needed_size, 1);
1174 const existing_size = last_src_fn.off;1174 const existing_size = last_src_fn.off;
11751175 std.log.scoped(.dsym).debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
1176 log.debug("moving __debug_line section: {} bytes from 0x{x} to 0x{x}", .{
1177 existing_size,1176 existing_size,
1178 debug_line_sect.offset,1177 debug_line_sect.offset,
1179 new_offset,1178 new_offset,
1180 });1179 });
11811180 const amt = try d_sym.file.copyRangeAll(
1182 try copyRangeAllOverlappingAlloc(
1183 gpa,
1184 d_sym.file,
1185 debug_line_sect.offset,1181 debug_line_sect.offset,
1182 d_sym.file,
1186 new_offset,1183 new_offset,
1187 existing_size,1184 existing_size,
1188 );1185 );
11891186 if (amt != existing_size) return error.InputOutput;
1190 debug_line_sect.offset = @intCast(u32, new_offset);1187 debug_line_sect.offset = @intCast(u32, new_offset);
1191 debug_line_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
1192 }1188 }
1193 debug_line_sect.size = needed_size;1189 debug_line_sect.size = needed_size;
1194 d_sym.debug_line_header_dirty = true;1190 d_sym.debug_line_header_dirty = true;
...@@ -1202,6 +1198,7 @@ pub fn commitDeclState(...@@ -1202,6 +1198,7 @@ pub fn commitDeclState(
1202 next_padding_size,1198 next_padding_size,
1203 );1199 );
1204 },1200 },
1201
1205 .wasm => {1202 .wasm => {
1206 const wasm_file = file.cast(File.Wasm).?;1203 const wasm_file = file.cast(File.Wasm).?;
1207 const atom = wasm_file.debug_line_atom.?;1204 const atom = wasm_file.debug_line_atom.?;
...@@ -1318,7 +1315,7 @@ pub fn commitDeclState(...@@ -1318,7 +1315,7 @@ pub fn commitDeclState(
1318 .macho => {1315 .macho => {
1319 const macho_file = file.cast(File.MachO).?;1316 const macho_file = file.cast(File.MachO).?;
1320 const d_sym = &macho_file.d_sym.?;1317 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, .{
1322 .type = switch (reloc.type) {1319 .type = switch (reloc.type) {
1323 .direct_load => .direct_load,1320 .direct_load => .direct_load,
1324 .got_load => .got_load,1321 .got_load => .got_load,
...@@ -1462,32 +1459,28 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co...@@ -1462,32 +1459,28 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1462 trailing_zero,1459 trailing_zero,
1463 );1460 );
1464 },1461 },
1462
1465 .macho => {1463 .macho => {
1466 const macho_file = file.cast(File.MachO).?;1464 const macho_file = file.cast(File.MachO).?;
1467 const d_sym = &macho_file.d_sym.?;1465 const d_sym = &macho_file.d_sym.?;
1468 const dwarf_segment = d_sym.segments.items[d_sym.dwarf_segment_cmd_index.?];
1469 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];1466 const debug_info_sect = &d_sym.sections.items[d_sym.debug_info_section_index.?];
1470 if (needed_size != debug_info_sect.size) {1467 if (needed_size != debug_info_sect.size) {
1471 if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {1468 if (needed_size > d_sym.allocatedSize(debug_info_sect.offset)) {
1472 const new_offset = d_sym.findFreeSpace(needed_size, 1);1469 const new_offset = d_sym.findFreeSpace(needed_size, 1);
1473 const existing_size = last_decl.off;1470 const existing_size = last_decl.off;
14741471 std.log.scoped(.dsym).debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{
1475 log.debug("moving __debug_info section: {} bytes from 0x{x} to 0x{x}", .{
1476 existing_size,1472 existing_size,
1477 debug_info_sect.offset,1473 debug_info_sect.offset,
1478 new_offset,1474 new_offset,
1479 });1475 });
14801476 const amt = try d_sym.file.copyRangeAll(
1481 try copyRangeAllOverlappingAlloc(
1482 gpa,
1483 d_sym.file,
1484 debug_info_sect.offset,1477 debug_info_sect.offset,
1478 d_sym.file,
1485 new_offset,1479 new_offset,
1486 existing_size,1480 existing_size,
1487 );1481 );
14881482 if (amt != existing_size) return error.InputOutput;
1489 debug_info_sect.offset = @intCast(u32, new_offset);1483 debug_info_sect.offset = @intCast(u32, new_offset);
1490 debug_info_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
1491 }1484 }
1492 debug_info_sect.size = needed_size;1485 debug_info_sect.size = needed_size;
1493 d_sym.debug_info_header_dirty = true;1486 d_sym.debug_info_header_dirty = true;
...@@ -1502,6 +1495,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co...@@ -1502,6 +1495,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1502 trailing_zero,1495 trailing_zero,
1503 );1496 );
1504 },1497 },
1498
1505 .wasm => {1499 .wasm => {
1506 const wasm_file = file.cast(File.Wasm).?;1500 const wasm_file = file.cast(File.Wasm).?;
1507 const info_atom = wasm_file.debug_info_atom.?;1501 const info_atom = wasm_file.debug_info_atom.?;
...@@ -2569,16 +2563,3 @@ fn addDbgInfoErrorSet(...@@ -2569,16 +2563,3 @@ fn addDbgInfoErrorSet(
2569 // DW.AT.enumeration_type delimit children2563 // DW.AT.enumeration_type delimit children
2570 try dbg_info_buffer.append(0);2564 try dbg_info_buffer.append(0);
2571}2565}
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 {...@@ -347,9 +347,10 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
347 });347 });
348348
349 self.d_sym = .{349 self.d_sym = .{
350 .base = self,350 .allocator = allocator,
351 .dwarf = link.File.Dwarf.init(allocator, .macho, options.target),351 .dwarf = link.File.Dwarf.init(allocator, .macho, options.target),
352 .file = d_sym_file,352 .file = d_sym_file,
353 .page_size = self.page_size,
353 };354 };
354 }355 }
355356
...@@ -366,7 +367,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -366,7 +367,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
366 try self.populateMissingMetadata();367 try self.populateMissingMetadata();
367368
368 if (self.d_sym) |*d_sym| {369 if (self.d_sym) |*d_sym| {
369 try d_sym.populateMissingMetadata(allocator);370 try d_sym.populateMissingMetadata();
370 }371 }
371372
372 return self;373 return self;
...@@ -629,7 +630,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -629,7 +630,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
629630
630 if (self.d_sym) |*d_sym| {631 if (self.d_sym) |*d_sym| {
631 // Flush debug symbols bundle.632 // Flush debug symbols bundle.
632 try d_sym.flushModule(self.base.allocator, self.base.options);633 try d_sym.flushModule(self);
633 }634 }
634635
635 // if (build_options.enable_link_snapshots) {636 // if (build_options.enable_link_snapshots) {
...@@ -1900,7 +1901,7 @@ pub fn deinit(self: *MachO) void {...@@ -1900,7 +1901,7 @@ pub fn deinit(self: *MachO) void {
1900 }1901 }
19011902
1902 if (self.d_sym) |*d_sym| {1903 if (self.d_sym) |*d_sym| {
1903 d_sym.deinit(gpa);1904 d_sym.deinit();
1904 }1905 }
19051906
1906 self.got_entries.deinit(gpa);1907 self.got_entries.deinit(gpa);
src/link/MachO/DebugSymbols.zig+146-202
...@@ -20,15 +20,16 @@ const Module = @import("../../Module.zig");...@@ -20,15 +20,16 @@ const Module = @import("../../Module.zig");
20const StringTable = @import("../strtab.zig").StringTable;20const StringTable = @import("../strtab.zig").StringTable;
21const Type = @import("../../type.zig").Type;21const Type = @import("../../type.zig").Type;
2222
23base: *MachO,23allocator: Allocator,
24dwarf: Dwarf,24dwarf: Dwarf,
25file: fs.File,25file: fs.File,
26page_size: u16,
2627
27segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},28segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
28sections: std.ArrayListUnmanaged(macho.section_64) = .{},29sections: std.ArrayListUnmanaged(macho.section_64) = .{},
2930
30linkedit_segment_cmd_index: ?u8 = null,
31dwarf_segment_cmd_index: ?u8 = null,31dwarf_segment_cmd_index: ?u8 = null,
32linkedit_segment_cmd_index: ?u8 = null,
3233
33debug_info_section_index: ?u8 = null,34debug_info_section_index: ?u8 = null,
34debug_abbrev_section_index: ?u8 = null,35debug_abbrev_section_index: ?u8 = null,
...@@ -43,7 +44,6 @@ debug_info_header_dirty: bool = false,...@@ -43,7 +44,6 @@ debug_info_header_dirty: bool = false,
43debug_line_header_dirty: bool = false,44debug_line_header_dirty: bool = false,
4445
45strtab: StringTable(.strtab) = .{},46strtab: StringTable(.strtab) = .{},
46
47relocs: std.ArrayListUnmanaged(Reloc) = .{},47relocs: std.ArrayListUnmanaged(Reloc) = .{},
4848
49pub const Reloc = struct {49pub const Reloc = struct {
...@@ -59,41 +59,20 @@ pub const Reloc = struct {...@@ -59,41 +59,20 @@ pub const Reloc = struct {
5959
60/// You must call this function *after* `MachO.populateMissingMetadata()`60/// You must call this function *after* `MachO.populateMissingMetadata()`
61/// has been called to get a viable debug symbols output.61/// has been called to get a viable debug symbols output.
62pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {62pub fn populateMissingMetadata(self: *DebugSymbols) !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
81 if (self.dwarf_segment_cmd_index == null) {63 if (self.dwarf_segment_cmd_index == null) {
82 self.dwarf_segment_cmd_index = @intCast(u8, self.segments.items.len);64 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);
85 const ideal_size: u16 = 200 + 128 + 160 + 250;67 const ideal_size: u16 = 200 + 128 + 160 + 250;
86 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);68 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
87 const fileoff = linkedit.fileoff + linkedit.filesize;
88 const vmaddr = linkedit.vmaddr + linkedit.vmsize;
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, .{
93 .segname = makeStaticString("__DWARF"),73 .segname = makeStaticString("__DWARF"),
94 .vmaddr = vmaddr,
95 .vmsize = needed_size,74 .vmsize = needed_size,
96 .fileoff = fileoff,75 .fileoff = off,
97 .filesize = needed_size,76 .filesize = needed_size,
98 .cmdsize = @sizeOf(macho.segment_command_64),77 .cmdsize = @sizeOf(macho.segment_command_64),
99 });78 });
...@@ -128,10 +107,20 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -128,10 +107,20 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
128 self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0);107 self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0);
129 self.debug_line_header_dirty = true;108 self.debug_line_header_dirty = true;
130 }109 }
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 }
131}120}
132121
133fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 {122fn 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();
135 var sect = macho.section_64{124 var sect = macho.section_64{
136 .sectname = makeStaticString(sectname),125 .sectname = makeStaticString(sectname),
137 .segname = segment.segname,126 .segname = segment.segname,
...@@ -141,8 +130,6 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme...@@ -141,8 +130,6 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
141 const alignment_pow_2 = try math.powi(u32, 2, alignment);130 const alignment_pow_2 = try math.powi(u32, 2, alignment);
142 const off = self.findFreeSpace(size, alignment_pow_2);131 const off = self.findFreeSpace(size, alignment_pow_2);
143132
144 assert(off + size <= segment.fileoff + segment.filesize); // TODO expand
145
146 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{133 log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
147 sect.segName(),134 sect.segName(),
148 sect.sectName(),135 sect.sectName(),
...@@ -154,7 +141,7 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme...@@ -154,7 +141,7 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
154 sect.offset = @intCast(u32, off);141 sect.offset = @intCast(u32, off);
155142
156 const index = @intCast(u8, self.sections.items.len);143 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);
158 segment.cmdsize += @sizeOf(macho.section_64);145 segment.cmdsize += @sizeOf(macho.section_64);
159 segment.nsects += 1;146 segment.nsects += 1;
160147
...@@ -174,7 +161,7 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {...@@ -174,7 +161,7 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
174}161}
175162
176pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {163pub 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();
178 var offset: u64 = segment.fileoff;165 var offset: u64 = segment.fileoff;
179 while (self.detectAllocCollision(offset, object_size)) |item_end| {166 while (self.detectAllocCollision(offset, object_size)) |item_end| {
180 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);167 offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
...@@ -182,34 +169,35 @@ pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64)...@@ -182,34 +169,35 @@ pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64)
182 return offset;169 return offset;
183}170}
184171
185pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Options) !void {172pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
186 // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the173 // TODO This linker code currently assumes there is only 1 compilation unit
187 // Zig source code.174 // and it corresponds to the Zig source code.
175 const options = macho_file.base.options;
188 const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;176 const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
189177
190 for (self.relocs.items) |*reloc| {178 for (self.relocs.items) |*reloc| {
191 const sym = switch (reloc.type) {179 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 }),
193 .got_load => blk: {181 .got_load => blk: {
194 const got_index = self.base.got_entries_table.get(.{182 const got_index = macho_file.got_entries_table.get(.{
195 .sym_index = reloc.target,183 .sym_index = reloc.target,
196 .file = null,184 .file = null,
197 }).?;185 }).?;
198 const got_entry = self.base.got_entries.items[got_index];186 const got_entry = macho_file.got_entries.items[got_index];
199 break :blk got_entry.getSymbol(self.base);187 break :blk got_entry.getSymbol(macho_file);
200 },188 },
201 };189 };
202 if (sym.n_value == reloc.prev_vaddr) continue;190 if (sym.n_value == reloc.prev_vaddr) continue;
203191
204 const sym_name = switch (reloc.type) {192 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 }),
206 .got_load => blk: {194 .got_load => blk: {
207 const got_index = self.base.got_entries_table.get(.{195 const got_index = macho_file.got_entries_table.get(.{
208 .sym_index = reloc.target,196 .sym_index = reloc.target,
209 .file = null,197 .file = null,
210 }).?;198 }).?;
211 const got_entry = self.base.got_entries.items[got_index];199 const got_entry = macho_file.got_entries.items[got_index];
212 break :blk got_entry.getName(self.base);200 break :blk got_entry.getName(macho_file);
213 },201 },
214 };202 };
215 const sect = &self.sections.items[self.debug_info_section_index.?];203 const sect = &self.sections.items[self.debug_info_section_index.?];
...@@ -225,35 +213,35 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -225,35 +213,35 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
225 }213 }
226214
227 if (self.debug_abbrev_section_dirty) {215 if (self.debug_abbrev_section_dirty) {
228 try self.dwarf.writeDbgAbbrev(&self.base.base);216 try self.dwarf.writeDbgAbbrev(&macho_file.base);
229 self.debug_abbrev_section_dirty = false;217 self.debug_abbrev_section_dirty = false;
230 }218 }
231219
232 if (self.debug_info_header_dirty) {220 if (self.debug_info_header_dirty) {
233 // Currently only one compilation unit is supported, so the address range is simply221 // Currently only one compilation unit is supported, so the address range is simply
234 // identical to the main program header virtual address and memory size.222 // 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.?];
236 const low_pc = text_section.addr;224 const low_pc = text_section.addr;
237 const high_pc = text_section.addr + text_section.size;225 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);
239 self.debug_info_header_dirty = false;227 self.debug_info_header_dirty = false;
240 }228 }
241229
242 if (self.debug_aranges_section_dirty) {230 if (self.debug_aranges_section_dirty) {
243 // Currently only one compilation unit is supported, so the address range is simply231 // Currently only one compilation unit is supported, so the address range is simply
244 // identical to the main program header virtual address and memory size.232 // 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.?];233 const text_section = macho_file.sections.items(.header)[macho_file.text_section_index.?];
246 try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);234 try self.dwarf.writeDbgAranges(&macho_file.base, text_section.addr, text_section.size);
247 self.debug_aranges_section_dirty = false;235 self.debug_aranges_section_dirty = false;
248 }236 }
249237
250 if (self.debug_line_header_dirty) {238 if (self.debug_line_header_dirty) {
251 try self.dwarf.writeDbgLineHeader(&self.base.base, module);239 try self.dwarf.writeDbgLineHeader(&macho_file.base, module);
252 self.debug_line_header_dirty = false;240 self.debug_line_header_dirty = false;
253 }241 }
254242
255 {243 {
256 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];244 const dwarf_segment = self.getDwarfSegmentPtr();
257 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];245 const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
258 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {246 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
259 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);247 const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
...@@ -277,41 +265,45 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -277,41 +265,45 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
277 }265 }
278 }266 }
279267
280 var lc_buffer = std.ArrayList(u8).init(allocator);268 var lc_buffer = std.ArrayList(u8).init(self.allocator);
281 defer lc_buffer.deinit();269 defer lc_buffer.deinit();
282 const lc_writer = lc_buffer.writer();270 const lc_writer = lc_buffer.writer();
283 var ncmds: u32 = 0;271 var ncmds: u32 = 0;
284272
285 self.updateDwarfSegment();273 self.finalizeDwarfSegment(macho_file);
286 try self.writeLinkeditSegmentData(&ncmds, lc_writer);274 try self.writeLinkeditSegmentData(macho_file, &ncmds, lc_writer);
287 self.updateDwarfSegment();
288275
289 {276 {
290 try lc_writer.writeStruct(self.base.uuid);277 try lc_writer.writeStruct(macho_file.uuid);
291 ncmds += 1;278 ncmds += 1;
292 }279 }
293280
294 var headers_buf = std.ArrayList(u8).init(allocator);281 var headers_buf = std.ArrayList(u8).init(self.allocator);
295 defer headers_buf.deinit();282 defer headers_buf.deinit();
296 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());283 try self.writeSegmentHeaders(macho_file, &ncmds, headers_buf.writer());
297284
298 try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));285 try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
299 try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);286 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
303 assert(!self.debug_abbrev_section_dirty);294 assert(!self.debug_abbrev_section_dirty);
304 assert(!self.debug_aranges_section_dirty);295 assert(!self.debug_aranges_section_dirty);
305 assert(!self.debug_string_table_dirty);296 assert(!self.debug_string_table_dirty);
306}297}
307298
308pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {299pub fn deinit(self: *DebugSymbols) void {
300 const gpa = self.allocator;
309 self.file.close();301 self.file.close();
310 self.segments.deinit(allocator);302 self.segments.deinit(gpa);
311 self.sections.deinit(allocator);303 self.sections.deinit(gpa);
312 self.dwarf.deinit();304 self.dwarf.deinit();
313 self.strtab.deinit(allocator);305 self.strtab.deinit(gpa);
314 self.relocs.deinit(allocator);306 self.relocs.deinit(gpa);
315}307}
316308
317pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {309pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
...@@ -327,47 +319,48 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {...@@ -327,47 +319,48 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
327 }319 }
328}320}
329321
330fn updateDwarfSegment(self: *DebugSymbols) void {322fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void {
331 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];323 const base_vmaddr = blk: {
332 const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];324 // Note that we purposely take the last VM address of the MachO binary including
333325 // the binary's LINKEDIT segment. This is in contrast to how dsymutil does it
334 const new_start_aligned = linkedit.vmaddr + linkedit.vmsize;326 // which overwrites the the address space taken by the original MachO binary,
335 const old_start_aligned = dwarf_segment.vmaddr;327 // however at the cost of having LINKEDIT preceed DWARF in dSYM binary which we
336 const diff = new_start_aligned - old_start_aligned;328 // do not want as we want to be able to incrementally move DWARF sections in the
337 if (diff > 0) {329 // file as we please.
338 dwarf_segment.vmaddr = new_start_aligned;330 const last_seg = macho_file.getLinkeditSegmentPtr();
339 }331 break :blk last_seg.vmaddr + last_seg.vmsize;
332 };
333 const dwarf_segment = self.getDwarfSegmentPtr();
340334
341 var max_offset: u64 = 0;335 var file_size: u64 = 0;
342 for (self.sections.items) |*sect| {336 for (self.sections.items) |header| {
343 sect.addr += diff;337 file_size = @max(file_size, header.offset + header.size);
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 }
355 }338 }
356339
357 const file_size = max_offset - dwarf_segment.fileoff;340 const aligned_size = mem.alignForwardGeneric(u64, file_size, self.page_size);
358 log.debug("__DWARF size 0x{x}", .{file_size});341 dwarf_segment.vmaddr = base_vmaddr;
359342 dwarf_segment.filesize = aligned_size;
360 if (file_size != dwarf_segment.filesize) {343 dwarf_segment.vmsize = aligned_size;
361 dwarf_segment.filesize = file_size;344
362 dwarf_segment.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.filesize, self.base.page_size);345 const linkedit = self.getLinkeditSegmentPtr();
363 }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});
364}357}
365358
366fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void {359fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, ncmds: *u32, writer: anytype) !void {
367 // Write segment/section headers from the binary file first.360 // Write segment/section headers from the binary file first.
368 const end = self.base.linkedit_segment_cmd_index.?;361 const end = macho_file.linkedit_segment_cmd_index.?;
369 for (self.base.segments.items[0..end]) |seg, i| {362 for (macho_file.segments.items[0..end]) |seg, i| {
370 const indexes = self.base.getSectionIndexes(@intCast(u8, i));363 const indexes = macho_file.getSectionIndexes(@intCast(u8, i));
371 var out_seg = seg;364 var out_seg = seg;
372 out_seg.fileoff = 0;365 out_seg.fileoff = 0;
373 out_seg.filesize = 0;366 out_seg.filesize = 0;
...@@ -376,7 +369,7 @@ fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void...@@ -376,7 +369,7 @@ fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void
376369
377 // Update section headers count; any section with size of 0 is excluded370 // Update section headers count; any section with size of 0 is excluded
378 // since it doesn't have any data in the final binary file.371 // 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| {
380 if (header.size == 0) continue;373 if (header.size == 0) continue;
381 out_seg.cmdsize += @sizeOf(macho.section_64);374 out_seg.cmdsize += @sizeOf(macho.section_64);
382 out_seg.nsects += 1;375 out_seg.nsects += 1;
...@@ -387,7 +380,7 @@ fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void...@@ -387,7 +380,7 @@ fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void
387 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;380 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
388381
389 try writer.writeStruct(out_seg);382 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| {
391 if (header.size == 0) continue;384 if (header.size == 0) continue;
392 var out_header = header;385 var out_header = header;
393 out_header.offset = 0;386 out_header.offset = 0;
...@@ -397,20 +390,21 @@ fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void...@@ -397,20 +390,21 @@ fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void
397 ncmds.* += 1;390 ncmds.* += 1;
398 }391 }
399 // Next, commit DSYM's __LINKEDIT and __DWARF segments headers.392 // 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));
401 try writer.writeStruct(seg);395 try writer.writeStruct(seg);
396 for (self.sections.items[indexes.start..indexes.end]) |header| {
397 try writer.writeStruct(header);
398 }
402 ncmds.* += 1;399 ncmds.* += 1;
403 }400 }
404 for (self.sections.items) |header| {
405 try writer.writeStruct(header);
406 }
407}401}
408402
409fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {403fn writeHeader(self: *DebugSymbols, macho_file: *MachO, ncmds: u32, sizeofcmds: u32) !void {
410 var header: macho.mach_header_64 = .{};404 var header: macho.mach_header_64 = .{};
411 header.filetype = macho.MH_DSYM;405 header.filetype = macho.MH_DSYM;
412406
413 switch (self.base.base.options.target.cpu.arch) {407 switch (macho_file.base.options.target.cpu.arch) {
414 .aarch64 => {408 .aarch64 => {
415 header.cputype = macho.CPU_TYPE_ARM64;409 header.cputype = macho.CPU_TYPE_ARM64;
416 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;410 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
...@@ -431,7 +425,7 @@ fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {...@@ -431,7 +425,7 @@ fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
431}425}
432426
433pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {427pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
434 const seg = self.segments.items[self.dwarf_segment_cmd_index.?];428 const seg = self.getDwarfSegmentPtr();
435 assert(start >= seg.fileoff);429 assert(start >= seg.fileoff);
436 var min_pos: u64 = std.math.maxInt(u64);430 var min_pos: u64 = std.math.maxInt(u64);
437 for (self.sections.items) |section| {431 for (self.sections.items) |section| {
...@@ -441,14 +435,15 @@ pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {...@@ -441,14 +435,15 @@ pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
441 return min_pos - start;435 return min_pos - start;
442}436}
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 {
445 const tracy = trace(@src());444 const tracy = trace(@src());
446 defer tracy.end();445 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
452 var symtab_cmd = macho.symtab_command{447 var symtab_cmd = macho.symtab_command{
453 .cmdsize = @sizeOf(macho.symtab_command),448 .cmdsize = @sizeOf(macho.symtab_command),
454 .symoff = 0,449 .symoff = 0,
...@@ -456,43 +451,43 @@ fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype...@@ -456,43 +451,43 @@ fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype
456 .stroff = 0,451 .stroff = 0,
457 .strsize = 0,452 .strsize = 0,
458 };453 };
459 try self.writeSymtab(&symtab_cmd);454 try self.writeSymtab(macho_file, &symtab_cmd);
460 try self.writeStrtab(&symtab_cmd);455 try self.writeStrtab(&symtab_cmd);
461 try lc_writer.writeStruct(symtab_cmd);456 try lc_writer.writeStruct(symtab_cmd);
462 ncmds.* += 1;457 ncmds.* += 1;
463458
464 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.base.page_size);459 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
465 seg.filesize = aligned_size;460 const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
466 seg.vmsize = aligned_size;461 seg.vmsize = aligned_size;
467}462}
468463
469fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {464fn writeSymtab(self: *DebugSymbols, macho_file: *MachO, lc: *macho.symtab_command) !void {
470 const tracy = trace(@src());465 const tracy = trace(@src());
471 defer tracy.end();466 defer tracy.end();
472467
473 const gpa = self.base.base.allocator;468 const gpa = self.allocator;
474469
475 var locals = std.ArrayList(macho.nlist_64).init(gpa);470 var locals = std.ArrayList(macho.nlist_64).init(gpa);
476 defer locals.deinit();471 defer locals.deinit();
477472
478 for (self.base.locals.items) |sym, sym_id| {473 for (macho_file.locals.items) |sym, sym_id| {
479 if (sym.n_strx == 0) continue; // no name, skip474 if (sym.n_strx == 0) continue; // no name, skip
480 const sym_loc = MachO.SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };475 const sym_loc = MachO.SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
481 if (self.base.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip476 if (macho_file.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, skip477 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
483 var out_sym = sym;478 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));
485 try locals.append(out_sym);480 try locals.append(out_sym);
486 }481 }
487482
488 var exports = std.ArrayList(macho.nlist_64).init(gpa);483 var exports = std.ArrayList(macho.nlist_64).init(gpa);
489 defer exports.deinit();484 defer exports.deinit();
490485
491 for (self.base.globals.items) |global| {486 for (macho_file.globals.items) |global| {
492 const sym = self.base.getSymbol(global);487 const sym = macho_file.getSymbol(global);
493 if (sym.undf()) continue; // import, skip488 if (sym.undf()) continue; // import, skip
494 var out_sym = sym;489 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));
496 try exports.append(out_sym);491 try exports.append(out_sym);
497 }492 }
498493
...@@ -503,38 +498,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {...@@ -503,38 +498,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
503 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];498 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
504 const offset = mem.alignForwardGeneric(u64, seg.fileoff, @alignOf(macho.nlist_64));499 const offset = mem.alignForwardGeneric(u64, seg.fileoff, @alignOf(macho.nlist_64));
505 const needed_size = nsyms * @sizeOf(macho.nlist_64);500 const needed_size = nsyms * @sizeOf(macho.nlist_64);
506501 seg.filesize = offset + needed_size - seg.fileoff;
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 }
538502
539 lc.symoff = @intCast(u32, offset);503 lc.symoff = @intCast(u32, offset);
540 lc.nsyms = @intCast(u32, nsyms);504 lc.nsyms = @intCast(u32, nsyms);
...@@ -558,57 +522,37 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {...@@ -558,57 +522,37 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
558 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];522 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
559 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));523 const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
560 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));524 const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
561 lc.stroff = @intCast(u32, offset);
562
563 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));525 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;527 seg.filesize = offset + needed_size - seg.fileoff;
581 dwarf_seg.fileoff += diff;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| {533 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
586 const old_offset = sect.offset;
587 sect.offset += diff;
588534
589 log.debug(" (moving {s},{s} from 0x{x} to 0x{x})", .{535 if (self.strtab.buffer.items.len < needed_size) {
590 sect.segName(),536 // Ensure we are always padded to the actual length of the file.
591 sect.sectName(),537 try self.file.pwriteAll(&[_]u8{0}, lc.stroff + lc.strsize);
592 old_offset,
593 sect.offset,
594 });
595 }
596 }538 }
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];
601}553}
602554
603fn copyRangeAllOverlappingAlloc(555fn getLinkeditSegmentPtr(self: *DebugSymbols) *macho.segment_command_64 {
604 allocator: Allocator,556 const index = self.linkedit_segment_cmd_index.?;
605 file: std.fs.File,557 return &self.segments.items[index];
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);
614}558}