| ... | @@ -20,15 +20,16 @@ const Module = @import("../../Module.zig"); | ... | @@ -20,15 +20,16 @@ const Module = @import("../../Module.zig"); |
| 20 | const StringTable = @import("../strtab.zig").StringTable; | 20 | const StringTable = @import("../strtab.zig").StringTable; |
| 21 | const Type = @import("../../type.zig").Type; | 21 | const Type = @import("../../type.zig").Type; |
| 22 | | 22 | |
| 23 | base: *MachO, | 23 | allocator: Allocator, |
| 24 | dwarf: Dwarf, | 24 | dwarf: Dwarf, |
| 25 | file: fs.File, | 25 | file: fs.File, |
| | 26 | page_size: u16, |
| 26 | | 27 | |
| 27 | segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{}, | 28 | segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{}, |
| 28 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, | 29 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, |
| 29 | | 30 | |
| 30 | linkedit_segment_cmd_index: ?u8 = null, | | |
| 31 | dwarf_segment_cmd_index: ?u8 = null, | 31 | dwarf_segment_cmd_index: ?u8 = null, |
| | 32 | linkedit_segment_cmd_index: ?u8 = null, |
| 32 | | 33 | |
| 33 | debug_info_section_index: ?u8 = null, | 34 | debug_info_section_index: ?u8 = null, |
| 34 | debug_abbrev_section_index: ?u8 = null, | 35 | debug_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, |
| 43 | debug_line_header_dirty: bool = false, | 44 | debug_line_header_dirty: bool = false, |
| 44 | | 45 | |
| 45 | strtab: StringTable(.strtab) = .{}, | 46 | strtab: StringTable(.strtab) = .{}, |
| 46 | | | |
| 47 | relocs: std.ArrayListUnmanaged(Reloc) = .{}, | 47 | relocs: std.ArrayListUnmanaged(Reloc) = .{}, |
| 48 | | 48 | |
| 49 | pub const Reloc = struct { | 49 | pub const Reloc = struct { |
| ... | @@ -59,41 +59,20 @@ pub const Reloc = struct { | ... | @@ -59,41 +59,20 @@ pub const Reloc = struct { |
| 59 | | 59 | |
| 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. |
| 62 | pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void { | 62 | pub 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); |
| 83 | | 65 | |
| 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; | | |
| 89 | | 69 | |
| 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 }); |
| 91 | | 71 | |
| 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 | } |
| 132 | | 121 | |
| 133 | fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 { | 122 | fn 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); |
| 143 | | 132 | |
| 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); |
| 155 | | 142 | |
| 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; |
| 160 | | 147 | |
| ... | @@ -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 | } |
| 175 | | 162 | |
| 176 | pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 { | 163 | pub 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 | } |
| 184 | | 171 | |
| 185 | pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Options) !void { | 172 | pub 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 the | 173 | // 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; |
| 189 | | 177 | |
| 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; |
| 203 | | 191 | |
| 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 | } |
| 226 | | 214 | |
| 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 | } |
| 231 | | 219 | |
| 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 simply | 221 | // 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 | } |
| 241 | | 229 | |
| 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 simply | 231 | // 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 | } |
| 249 | | 237 | |
| 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 | } |
| 254 | | 242 | |
| 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 | } |
| 279 | | 267 | |
| 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; |
| 284 | | 272 | |
| 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(); | | |
| 288 | | 275 | |
| 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 | } |
| 293 | | 280 | |
| 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()); |
| 297 | | 284 | |
| 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); |
| 300 | | 287 | |
| 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 | ); |
| 302 | | 293 | |
| 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 | } |
| 307 | | 298 | |
| 308 | pub fn deinit(self: *DebugSymbols, allocator: Allocator) void { | 299 | pub 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 | } |
| 316 | | 308 | |
| 317 | pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void { | 309 | pub 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 | } |
| 329 | | 321 | |
| 330 | fn updateDwarfSegment(self: *DebugSymbols) void { | 322 | fn 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 |
| 333 | | 325 | // 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(); |
| 340 | | 334 | |
| 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 | } |
| 356 | | 339 | |
| 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; |
| 359 | | 342 | 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 | } |
| 365 | | 358 | |
| 366 | fn writeSegmentHeaders(self: *DebugSymbols, ncmds: *u32, writer: anytype) !void { | 359 | fn 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 |
| 376 | | 369 | |
| 377 | // Update section headers count; any section with size of 0 is excluded | 370 | // 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; |
| 388 | | 381 | |
| 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 | } |
| 408 | | 402 | |
| 409 | fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void { | 403 | fn 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; |
| 412 | | 406 | |
| 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 | } |
| 432 | | 426 | |
| 433 | pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 { | 427 | pub 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 | } |
| 443 | | 437 | |
| 444 | fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype) !void { | 438 | fn 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(); |
| 447 | | 446 | |
| 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; |
| 463 | | 458 | |
| 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 | } |
| 468 | | 463 | |
| 469 | fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void { | 464 | fn 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(); |
| 472 | | 467 | |
| 473 | const gpa = self.base.base.allocator; | 468 | const gpa = self.allocator; |
| 474 | | 469 | |
| 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(); |
| 477 | | 472 | |
| 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, skip | 474 | 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, skip | 476 | 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, skip | 477 | 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 | } |
| 487 | | 482 | |
| 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(); |
| 490 | | 485 | |
| 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, skip | 488 | 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 | } |
| 498 | | 493 | |
| ... | @@ -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); |
| 506 | | 501 | 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 | } | | |
| 538 | | 502 | |
| 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 | ); | | |
| 579 | | 526 | |
| 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); |
| 582 | | 530 | |
| 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 }); |
| 584 | | 532 | |
| 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; | | |
| 588 | | 534 | |
| 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 | } |
| 597 | | 540 | |
| 598 | log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize }); | 541 | pub 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 | } |
| 599 | | 549 | |
| 600 | try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff); | 550 | fn getDwarfSegmentPtr(self: *DebugSymbols) *macho.segment_command_64 { |
| | 551 | const index = self.dwarf_segment_cmd_index.?; |
| | 552 | return &self.segments.items[index]; |
| 601 | } | 553 | } |
| 602 | | 554 | |
| 603 | fn copyRangeAllOverlappingAlloc( | 555 | fn 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 | } |