| ... | ... | @@ -20,9 +20,14 @@ const MachO = @import("../MachO.zig"); |
| 20 | 20 | file: fs.File, |
| 21 | 21 | name: []const u8, |
| 22 | 22 | |
| 23 | /// Data contents of the file. Includes sections, and data of load commands. |
| 24 | /// Excludes the backing memory for the header and load commands. |
| 25 | /// Initialized in `parse`. |
| 26 | contents: []const u8 = undefined, |
| 27 | |
| 23 | 28 | file_offset: ?u32 = null, |
| 24 | 29 | |
| 25 | | header: ?macho.mach_header_64 = null, |
| 30 | header: macho.mach_header_64 = undefined, |
| 26 | 31 | |
| 27 | 32 | load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{}, |
| 28 | 33 | |
| ... | ... | @@ -41,9 +46,9 @@ dwarf_debug_line_index: ?u16 = null, |
| 41 | 46 | dwarf_debug_line_str_index: ?u16 = null, |
| 42 | 47 | dwarf_debug_ranges_index: ?u16 = null, |
| 43 | 48 | |
| 44 | | symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 45 | | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 46 | | data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, |
| 49 | symtab: []const macho.nlist_64 = &.{}, |
| 50 | strtab: []const u8 = &.{}, |
| 51 | data_in_code_entries: []const macho.data_in_code_entry = &.{}, |
| 47 | 52 | |
| 48 | 53 | // Debug info |
| 49 | 54 | debug_info: ?DebugInfo = null, |
| ... | ... | @@ -65,41 +70,41 @@ analyzed: bool = false, |
| 65 | 70 | |
| 66 | 71 | const DebugInfo = struct { |
| 67 | 72 | inner: dwarf.DwarfInfo, |
| 68 | | debug_info: []u8, |
| 69 | | debug_abbrev: []u8, |
| 70 | | debug_str: []u8, |
| 71 | | debug_line: []u8, |
| 72 | | debug_line_str: []u8, |
| 73 | | debug_ranges: []u8, |
| 73 | debug_info: []const u8, |
| 74 | debug_abbrev: []const u8, |
| 75 | debug_str: []const u8, |
| 76 | debug_line: []const u8, |
| 77 | debug_line_str: []const u8, |
| 78 | debug_ranges: []const u8, |
| 74 | 79 | |
| 75 | 80 | pub fn parseFromObject(allocator: Allocator, object: *const Object) !?DebugInfo { |
| 76 | 81 | var debug_info = blk: { |
| 77 | 82 | const index = object.dwarf_debug_info_index orelse return null; |
| 78 | | break :blk try object.readSection(allocator, index); |
| 83 | break :blk object.getSectionContents(index); |
| 79 | 84 | }; |
| 80 | 85 | var debug_abbrev = blk: { |
| 81 | 86 | const index = object.dwarf_debug_abbrev_index orelse return null; |
| 82 | | break :blk try object.readSection(allocator, index); |
| 87 | break :blk object.getSectionContents(index); |
| 83 | 88 | }; |
| 84 | 89 | var debug_str = blk: { |
| 85 | 90 | const index = object.dwarf_debug_str_index orelse return null; |
| 86 | | break :blk try object.readSection(allocator, index); |
| 91 | break :blk object.getSectionContents(index); |
| 87 | 92 | }; |
| 88 | 93 | var debug_line = blk: { |
| 89 | 94 | const index = object.dwarf_debug_line_index orelse return null; |
| 90 | | break :blk try object.readSection(allocator, index); |
| 95 | break :blk object.getSectionContents(index); |
| 91 | 96 | }; |
| 92 | 97 | var debug_line_str = blk: { |
| 93 | 98 | if (object.dwarf_debug_line_str_index) |ind| { |
| 94 | | break :blk try object.readSection(allocator, ind); |
| 99 | break :blk object.getSectionContents(ind); |
| 95 | 100 | } |
| 96 | | break :blk try allocator.alloc(u8, 0); |
| 101 | break :blk &[0]u8{}; |
| 97 | 102 | }; |
| 98 | 103 | var debug_ranges = blk: { |
| 99 | 104 | if (object.dwarf_debug_ranges_index) |ind| { |
| 100 | | break :blk try object.readSection(allocator, ind); |
| 105 | break :blk object.getSectionContents(ind); |
| 101 | 106 | } |
| 102 | | break :blk try allocator.alloc(u8, 0); |
| 107 | break :blk &[0]u8{}; |
| 103 | 108 | }; |
| 104 | 109 | |
| 105 | 110 | var inner: dwarf.DwarfInfo = .{ |
| ... | ... | @@ -125,12 +130,6 @@ const DebugInfo = struct { |
| 125 | 130 | } |
| 126 | 131 | |
| 127 | 132 | pub fn deinit(self: *DebugInfo, allocator: Allocator) void { |
| 128 | | allocator.free(self.debug_info); |
| 129 | | allocator.free(self.debug_abbrev); |
| 130 | | allocator.free(self.debug_str); |
| 131 | | allocator.free(self.debug_line); |
| 132 | | allocator.free(self.debug_line_str); |
| 133 | | allocator.free(self.debug_ranges); |
| 134 | 133 | self.inner.deinit(allocator); |
| 135 | 134 | } |
| 136 | 135 | }; |
| ... | ... | @@ -140,9 +139,7 @@ pub fn deinit(self: *Object, allocator: Allocator) void { |
| 140 | 139 | lc.deinit(allocator); |
| 141 | 140 | } |
| 142 | 141 | self.load_commands.deinit(allocator); |
| 143 | | self.data_in_code_entries.deinit(allocator); |
| 144 | | self.symtab.deinit(allocator); |
| 145 | | self.strtab.deinit(allocator); |
| 142 | allocator.free(self.contents); |
| 146 | 143 | self.sections_as_symbols.deinit(allocator); |
| 147 | 144 | self.symbol_mapping.deinit(allocator); |
| 148 | 145 | self.reverse_symbol_mapping.deinit(allocator); |
| ... | ... | @@ -155,14 +152,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void { |
| 155 | 152 | if (self.debug_info) |*db| { |
| 156 | 153 | db.deinit(allocator); |
| 157 | 154 | } |
| 158 | | |
| 159 | | if (self.tu_name) |n| { |
| 160 | | allocator.free(n); |
| 161 | | } |
| 162 | | |
| 163 | | if (self.tu_comp_dir) |n| { |
| 164 | | allocator.free(n); |
| 165 | | } |
| 166 | 155 | } |
| 167 | 156 | |
| 168 | 157 | pub fn free(self: *Object, allocator: Allocator, macho_file: *MachO) void { |
| ... | ... | @@ -233,21 +222,28 @@ fn freeAtoms(self: *Object, macho_file: *MachO) void { |
| 233 | 222 | } |
| 234 | 223 | |
| 235 | 224 | pub fn parse(self: *Object, allocator: Allocator, target: std.Target) !void { |
| 236 | | const reader = self.file.reader(); |
| 237 | | if (self.file_offset) |offset| { |
| 238 | | try reader.context.seekTo(offset); |
| 225 | const file_stat = try self.file.stat(); |
| 226 | const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow; |
| 227 | self.contents = try self.file.readToEndAlloc(allocator, file_size); |
| 228 | |
| 229 | var stream = std.io.fixedBufferStream(self.contents); |
| 230 | const reader = stream.reader(); |
| 231 | |
| 232 | const file_offset = self.file_offset orelse 0; |
| 233 | if (file_offset > 0) { |
| 234 | try reader.context.seekTo(file_offset); |
| 239 | 235 | } |
| 240 | 236 | |
| 241 | | const header = try reader.readStruct(macho.mach_header_64); |
| 242 | | if (header.filetype != macho.MH_OBJECT) { |
| 237 | self.header = try reader.readStruct(macho.mach_header_64); |
| 238 | if (self.header.filetype != macho.MH_OBJECT) { |
| 243 | 239 | log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ |
| 244 | 240 | macho.MH_OBJECT, |
| 245 | | header.filetype, |
| 241 | self.header.filetype, |
| 246 | 242 | }); |
| 247 | 243 | return error.NotObject; |
| 248 | 244 | } |
| 249 | 245 | |
| 250 | | const this_arch: std.Target.Cpu.Arch = switch (header.cputype) { |
| 246 | const this_arch: std.Target.Cpu.Arch = switch (self.header.cputype) { |
| 251 | 247 | macho.CPU_TYPE_ARM64 => .aarch64, |
| 252 | 248 | macho.CPU_TYPE_X86_64 => .x86_64, |
| 253 | 249 | else => |value| { |
| ... | ... | @@ -260,22 +256,10 @@ pub fn parse(self: *Object, allocator: Allocator, target: std.Target) !void { |
| 260 | 256 | return error.MismatchedCpuArchitecture; |
| 261 | 257 | } |
| 262 | 258 | |
| 263 | | self.header = header; |
| 264 | | |
| 265 | | try self.readLoadCommands(allocator, reader); |
| 266 | | try self.parseSymtab(allocator); |
| 267 | | try self.parseDataInCode(allocator); |
| 268 | | try self.parseDebugInfo(allocator); |
| 269 | | } |
| 270 | | |
| 271 | | pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !void { |
| 272 | | const header = self.header orelse unreachable; // Unreachable here signifies a fatal unexplored condition. |
| 273 | | const offset = self.file_offset orelse 0; |
| 274 | | |
| 275 | | try self.load_commands.ensureUnusedCapacity(allocator, header.ncmds); |
| 259 | try self.load_commands.ensureUnusedCapacity(allocator, self.header.ncmds); |
| 276 | 260 | |
| 277 | 261 | var i: u16 = 0; |
| 278 | | while (i < header.ncmds) : (i += 1) { |
| 262 | while (i < self.header.ncmds) : (i += 1) { |
| 279 | 263 | var cmd = try macho.LoadCommand.read(allocator, reader); |
| 280 | 264 | switch (cmd.cmd()) { |
| 281 | 265 | .SEGMENT_64 => { |
| ... | ... | @@ -305,18 +289,18 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v |
| 305 | 289 | } |
| 306 | 290 | } |
| 307 | 291 | |
| 308 | | sect.offset += offset; |
| 292 | sect.offset += file_offset; |
| 309 | 293 | if (sect.reloff > 0) { |
| 310 | | sect.reloff += offset; |
| 294 | sect.reloff += file_offset; |
| 311 | 295 | } |
| 312 | 296 | } |
| 313 | 297 | |
| 314 | | seg.inner.fileoff += offset; |
| 298 | seg.inner.fileoff += file_offset; |
| 315 | 299 | }, |
| 316 | 300 | .SYMTAB => { |
| 317 | 301 | self.symtab_cmd_index = i; |
| 318 | | cmd.symtab.symoff += offset; |
| 319 | | cmd.symtab.stroff += offset; |
| 302 | cmd.symtab.symoff += file_offset; |
| 303 | cmd.symtab.stroff += file_offset; |
| 320 | 304 | }, |
| 321 | 305 | .DYSYMTAB => { |
| 322 | 306 | self.dysymtab_cmd_index = i; |
| ... | ... | @@ -326,7 +310,7 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v |
| 326 | 310 | }, |
| 327 | 311 | .DATA_IN_CODE => { |
| 328 | 312 | self.data_in_code_cmd_index = i; |
| 329 | | cmd.linkedit_data.dataoff += offset; |
| 313 | cmd.linkedit_data.dataoff += file_offset; |
| 330 | 314 | }, |
| 331 | 315 | else => { |
| 332 | 316 | log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); |
| ... | ... | @@ -334,6 +318,10 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v |
| 334 | 318 | } |
| 335 | 319 | self.load_commands.appendAssumeCapacity(cmd); |
| 336 | 320 | } |
| 321 | |
| 322 | self.parseSymtab(); |
| 323 | self.parseDataInCode(); |
| 324 | try self.parseDebugInfo(allocator); |
| 337 | 325 | } |
| 338 | 326 | |
| 339 | 327 | const NlistWithIndex = struct { |
| ... | ... | @@ -373,7 +361,11 @@ const NlistWithIndex = struct { |
| 373 | 361 | } |
| 374 | 362 | }; |
| 375 | 363 | |
| 376 | | fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) []macho.data_in_code_entry { |
| 364 | fn filterDice( |
| 365 | dices: []const macho.data_in_code_entry, |
| 366 | start_addr: u64, |
| 367 | end_addr: u64, |
| 368 | ) []const macho.data_in_code_entry { |
| 377 | 369 | const Predicate = struct { |
| 378 | 370 | addr: u64, |
| 379 | 371 | |
| ... | ... | @@ -400,10 +392,10 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! |
| 400 | 392 | // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance, |
| 401 | 393 | // the GO compiler does not necessarily respect that therefore we sort immediately by type |
| 402 | 394 | // and address within. |
| 403 | | var sorted_all_nlists = try std.ArrayList(NlistWithIndex).initCapacity(allocator, self.symtab.items.len); |
| 395 | var sorted_all_nlists = try std.ArrayList(NlistWithIndex).initCapacity(allocator, self.symtab.len); |
| 404 | 396 | defer sorted_all_nlists.deinit(); |
| 405 | 397 | |
| 406 | | for (self.symtab.items) |nlist, index| { |
| 398 | for (self.symtab) |nlist, index| { |
| 407 | 399 | sorted_all_nlists.appendAssumeCapacity(.{ |
| 408 | 400 | .nlist = nlist, |
| 409 | 401 | .index = @intCast(u32, index), |
| ... | ... | @@ -439,16 +431,20 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! |
| 439 | 431 | continue; |
| 440 | 432 | }; |
| 441 | 433 | |
| 434 | const is_zerofill = blk: { |
| 435 | const section_type = sect.type_(); |
| 436 | break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL; |
| 437 | }; |
| 438 | |
| 442 | 439 | // Read section's code |
| 443 | | var code = try allocator.alloc(u8, @intCast(usize, sect.size)); |
| 444 | | defer allocator.free(code); |
| 445 | | _ = try self.file.preadAll(code, sect.offset); |
| 440 | const code: ?[]const u8 = if (!is_zerofill) self.getSectionContents(sect_id) else null; |
| 446 | 441 | |
| 447 | 442 | // Read section's list of relocations |
| 448 | | var raw_relocs = try allocator.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); |
| 449 | | defer allocator.free(raw_relocs); |
| 450 | | _ = try self.file.preadAll(raw_relocs, sect.reloff); |
| 451 | | const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs); |
| 443 | const raw_relocs = self.contents[sect.reloff..][0 .. sect.nreloc * @sizeOf(macho.relocation_info)]; |
| 444 | const relocs = mem.bytesAsSlice( |
| 445 | macho.relocation_info, |
| 446 | @alignCast(@alignOf(macho.relocation_info), raw_relocs), |
| 447 | ); |
| 452 | 448 | |
| 453 | 449 | // Symbols within this section only. |
| 454 | 450 | const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect); |
| ... | ... | @@ -456,7 +452,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! |
| 456 | 452 | macho_file.has_dices = macho_file.has_dices or blk: { |
| 457 | 453 | if (self.text_section_index) |index| { |
| 458 | 454 | if (index != id) break :blk false; |
| 459 | | if (self.data_in_code_entries.items.len == 0) break :blk false; |
| 455 | if (self.data_in_code_entries.len == 0) break :blk false; |
| 460 | 456 | break :blk true; |
| 461 | 457 | } |
| 462 | 458 | break :blk false; |
| ... | ... | @@ -482,16 +478,12 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! |
| 482 | 478 | const aligned_size = mem.alignForwardGeneric(u64, sect.size, alignment); |
| 483 | 479 | const atom = try macho_file.createEmptyAtom(atom_local_sym_index, aligned_size, sect.@"align"); |
| 484 | 480 | |
| 485 | | const is_zerofill = blk: { |
| 486 | | const section_type = sect.type_(); |
| 487 | | break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL; |
| 488 | | }; |
| 489 | | if (!is_zerofill) { |
| 490 | | mem.copy(u8, atom.code.items, code); |
| 481 | if (code) |cc| { |
| 482 | assert(!is_zerofill); |
| 483 | mem.copy(u8, atom.code.items, cc); |
| 491 | 484 | } |
| 492 | 485 | |
| 493 | | // TODO stage2 bug: @alignCast shouldn't be needed |
| 494 | | try atom.parseRelocs(@alignCast(@alignOf(macho.relocation_info), relocs), .{ |
| 486 | try atom.parseRelocs(relocs, .{ |
| 495 | 487 | .base_addr = sect.addr, |
| 496 | 488 | .allocator = allocator, |
| 497 | 489 | .object = self, |
| ... | ... | @@ -499,7 +491,7 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! |
| 499 | 491 | }); |
| 500 | 492 | |
| 501 | 493 | if (macho_file.has_dices) { |
| 502 | | const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size); |
| 494 | const dices = filterDice(self.data_in_code_entries, sect.addr, sect.addr + sect.size); |
| 503 | 495 | try atom.dices.ensureTotalCapacity(allocator, dices.len); |
| 504 | 496 | |
| 505 | 497 | for (dices) |dice| { |
| ... | ... | @@ -562,20 +554,13 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) ! |
| 562 | 554 | } |
| 563 | 555 | } |
| 564 | 556 | |
| 565 | | fn parseSymtab(self: *Object, allocator: Allocator) !void { |
| 557 | fn parseSymtab(self: *Object) void { |
| 566 | 558 | const index = self.symtab_cmd_index orelse return; |
| 567 | | const symtab_cmd = self.load_commands.items[index].symtab; |
| 568 | | |
| 569 | | var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); |
| 570 | | defer allocator.free(symtab); |
| 571 | | _ = try self.file.preadAll(symtab, symtab_cmd.symoff); |
| 572 | | const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); |
| 573 | | try self.symtab.appendSlice(allocator, slice); |
| 574 | | |
| 575 | | var strtab = try allocator.alloc(u8, symtab_cmd.strsize); |
| 576 | | defer allocator.free(strtab); |
| 577 | | _ = try self.file.preadAll(strtab, symtab_cmd.stroff); |
| 578 | | try self.strtab.appendSlice(allocator, strtab); |
| 559 | const symtab = self.load_commands.items[index].symtab; |
| 560 | const symtab_size = @sizeOf(macho.nlist_64) * symtab.nsyms; |
| 561 | const raw_symtab = self.contents[symtab.symoff..][0..symtab_size]; |
| 562 | self.symtab = mem.bytesAsSlice(macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), raw_symtab)); |
| 563 | self.strtab = self.contents[symtab.stroff..][0..symtab.strsize]; |
| 579 | 564 | } |
| 580 | 565 | |
| 581 | 566 | pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void { |
| ... | ... | @@ -599,8 +584,8 @@ pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void { |
| 599 | 584 | const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.comp_dir); |
| 600 | 585 | |
| 601 | 586 | self.debug_info = debug_info; |
| 602 | | self.tu_name = try allocator.dupe(u8, name); |
| 603 | | self.tu_comp_dir = try allocator.dupe(u8, comp_dir); |
| 587 | self.tu_name = name; |
| 588 | self.tu_comp_dir = comp_dir; |
| 604 | 589 | |
| 605 | 590 | if (self.mtime == null) { |
| 606 | 591 | self.mtime = mtime: { |
| ... | ... | @@ -610,34 +595,29 @@ pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void { |
| 610 | 595 | } |
| 611 | 596 | } |
| 612 | 597 | |
| 613 | | pub fn parseDataInCode(self: *Object, allocator: Allocator) !void { |
| 598 | pub fn parseDataInCode(self: *Object) void { |
| 614 | 599 | const index = self.data_in_code_cmd_index orelse return; |
| 615 | 600 | const data_in_code = self.load_commands.items[index].linkedit_data; |
| 616 | | |
| 617 | | var buffer = try allocator.alloc(u8, data_in_code.datasize); |
| 618 | | defer allocator.free(buffer); |
| 619 | | |
| 620 | | _ = try self.file.preadAll(buffer, data_in_code.dataoff); |
| 621 | | |
| 622 | | var stream = io.fixedBufferStream(buffer); |
| 623 | | var reader = stream.reader(); |
| 624 | | while (true) { |
| 625 | | const dice = reader.readStruct(macho.data_in_code_entry) catch |err| switch (err) { |
| 626 | | error.EndOfStream => break, |
| 627 | | }; |
| 628 | | try self.data_in_code_entries.append(allocator, dice); |
| 629 | | } |
| 601 | const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize]; |
| 602 | self.data_in_code_entries = mem.bytesAsSlice( |
| 603 | macho.data_in_code_entry, |
| 604 | @alignCast(@alignOf(macho.data_in_code_entry), raw_dice), |
| 605 | ); |
| 630 | 606 | } |
| 631 | 607 | |
| 632 | | fn readSection(self: Object, allocator: Allocator, index: u16) ![]u8 { |
| 608 | fn getSectionContents(self: Object, sect_id: u16) []const u8 { |
| 633 | 609 | const seg = self.load_commands.items[self.segment_cmd_index.?].segment; |
| 634 | | const sect = seg.sections.items[index]; |
| 635 | | var buffer = try allocator.alloc(u8, @intCast(usize, sect.size)); |
| 636 | | _ = try self.file.preadAll(buffer, sect.offset); |
| 637 | | return buffer; |
| 610 | const sect = seg.sections.items[sect_id]; |
| 611 | log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{ |
| 612 | sect.segName(), |
| 613 | sect.sectName(), |
| 614 | sect.offset, |
| 615 | sect.offset + sect.size, |
| 616 | }); |
| 617 | return self.contents[sect.offset..][0..sect.size]; |
| 638 | 618 | } |
| 639 | 619 | |
| 640 | 620 | pub fn getString(self: Object, off: u32) []const u8 { |
| 641 | | assert(off < self.strtab.items.len); |
| 642 | | return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.items.ptr + off), 0); |
| 621 | assert(off < self.strtab.len); |
| 622 | return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0); |
| 643 | 623 | } |