| author | |
| committer | |
| log | abb8e7478d365088301a9390c12f86b2ad381ce9 |
| tree | 800c02a60af03be8dd4136d86d8168491e4f6673 |
| parent | 96a5f7c8edac4bb2f50bdfe31c1287207d90d29b |
| parent | 5b315f8a3a6c925bdcbbc182d5bdc14f5f726146 |
| signature |
macho: reduce heap allocations11 files changed, 405 insertions(+), 296 deletions(-)
build.zig+1-1| ... | ... | @@ -623,7 +623,7 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St |
| 623 | 623 | .root_source_file = .{ .path = "src/main.zig" }, |
| 624 | 624 | .target = options.target, |
| 625 | 625 | .optimize = options.optimize, |
| 626 | .max_rss = 8_000_000_000, | |
| 626 | .max_rss = 7_000_000_000, | |
| 627 | 627 | .strip = options.strip, |
| 628 | 628 | .sanitize_thread = options.sanitize_thread, |
| 629 | 629 | .single_threaded = options.single_threaded, |
src/link/MachO.zig+17-40| ... | ... | @@ -610,7 +610,10 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 610 | 610 | if (mem.indexOf(u8, sect.segName(), "ZIG") == null) continue; // Non-Zig sections are handled separately |
| 611 | 611 | // TODO: we will resolve and write ZigObject's TLS data twice: |
| 612 | 612 | // once here, and once in writeAtoms |
| 613 | const code = zo.getAtomDataAlloc(self, gpa, atom.*) catch |err| switch (err) { | |
| 613 | const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; | |
| 614 | const code = try gpa.alloc(u8, atom_size); | |
| 615 | defer gpa.free(code); | |
| 616 | atom.getData(self, code) catch |err| switch (err) { | |
| 614 | 617 | error.InputOutput => { |
| 615 | 618 | try self.reportUnexpectedError("fetching code for '{s}' failed", .{ |
| 616 | 619 | atom.getName(self), |
| ... | ... | @@ -625,7 +628,6 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node |
| 625 | 628 | return error.FlushFailure; |
| 626 | 629 | }, |
| 627 | 630 | }; |
| 628 | defer gpa.free(code); | |
| 629 | 631 | const file_offset = sect.offset + atom.value - sect.addr; |
| 630 | 632 | atom.resolveRelocs(self, code) catch |err| switch (err) { |
| 631 | 633 | error.ResolveFailed => has_resolve_error = true, |
| ... | ... | @@ -974,17 +976,15 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void { |
| 974 | 976 | |
| 975 | 977 | const gpa = self.base.comp.gpa; |
| 976 | 978 | const file = try std.fs.cwd().openFile(path, .{}); |
| 977 | defer file.close(); | |
| 978 | 979 | const mtime: u64 = mtime: { |
| 979 | 980 | const stat = file.stat() catch break :mtime 0; |
| 980 | 981 | break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000))); |
| 981 | 982 | }; |
| 982 | const data = try file.readToEndAlloc(gpa, std.math.maxInt(u32)); | |
| 983 | 983 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); |
| 984 | 984 | self.files.set(index, .{ .object = .{ |
| 985 | 985 | .path = try gpa.dupe(u8, path), |
| 986 | .file = file, | |
| 986 | 987 | .mtime = mtime, |
| 987 | .data = data, | |
| 988 | 988 | .index = index, |
| 989 | 989 | } }); |
| 990 | 990 | try self.objects.append(gpa, index); |
| ... | ... | @@ -1013,17 +1013,9 @@ fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Ar |
| 1013 | 1013 | const file = try std.fs.cwd().openFile(lib.path, .{}); |
| 1014 | 1014 | defer file.close(); |
| 1015 | 1015 | |
| 1016 | const data = if (fat_arch) |arch| blk: { | |
| 1017 | try file.seekTo(arch.offset); | |
| 1018 | const data = try gpa.alloc(u8, arch.size); | |
| 1019 | const nread = try file.readAll(data); | |
| 1020 | if (nread != arch.size) return error.InputOutput; | |
| 1021 | break :blk data; | |
| 1022 | } else try file.readToEndAlloc(gpa, std.math.maxInt(u32)); | |
| 1023 | ||
| 1024 | var archive = Archive{ .path = try gpa.dupe(u8, lib.path), .data = data }; | |
| 1016 | var archive = Archive{}; | |
| 1025 | 1017 | defer archive.deinit(gpa); |
| 1026 | try archive.parse(self); | |
| 1018 | try archive.parse(self, lib.path, file, fat_arch); | |
| 1027 | 1019 | |
| 1028 | 1020 | var has_parse_error = false; |
| 1029 | 1021 | for (archive.objects.items) |extracted| { |
| ... | ... | @@ -1058,18 +1050,9 @@ fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) |
| 1058 | 1050 | const file = try std.fs.cwd().openFile(lib.path, .{}); |
| 1059 | 1051 | defer file.close(); |
| 1060 | 1052 | |
| 1061 | const data = if (fat_arch) |arch| blk: { | |
| 1062 | try file.seekTo(arch.offset); | |
| 1063 | const data = try gpa.alloc(u8, arch.size); | |
| 1064 | const nread = try file.readAll(data); | |
| 1065 | if (nread != arch.size) return error.InputOutput; | |
| 1066 | break :blk data; | |
| 1067 | } else try file.readToEndAlloc(gpa, std.math.maxInt(u32)); | |
| 1068 | ||
| 1069 | 1053 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); |
| 1070 | 1054 | self.files.set(index, .{ .dylib = .{ |
| 1071 | 1055 | .path = try gpa.dupe(u8, lib.path), |
| 1072 | .data = data, | |
| 1073 | 1056 | .index = index, |
| 1074 | 1057 | .needed = lib.needed, |
| 1075 | 1058 | .weak = lib.weak, |
| ... | ... | @@ -1077,7 +1060,7 @@ fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) |
| 1077 | 1060 | .explicit = explicit, |
| 1078 | 1061 | } }); |
| 1079 | 1062 | const dylib = &self.files.items(.data)[index].dylib; |
| 1080 | try dylib.parse(self); | |
| 1063 | try dylib.parse(self, file, fat_arch); | |
| 1081 | 1064 | |
| 1082 | 1065 | try self.dylibs.append(gpa, index); |
| 1083 | 1066 | |
| ... | ... | @@ -1098,7 +1081,6 @@ fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!File.Index |
| 1098 | 1081 | const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); |
| 1099 | 1082 | self.files.set(index, .{ .dylib = .{ |
| 1100 | 1083 | .path = try gpa.dupe(u8, lib.path), |
| 1101 | .data = &[0]u8{}, | |
| 1102 | 1084 | .index = index, |
| 1103 | 1085 | .needed = lib.needed, |
| 1104 | 1086 | .weak = lib.weak, |
| ... | ... | @@ -1404,6 +1386,8 @@ pub fn resolveSymbols(self: *MachO) !void { |
| 1404 | 1386 | const index = self.objects.items[i]; |
| 1405 | 1387 | if (!self.getFile(index).?.object.alive) { |
| 1406 | 1388 | _ = self.objects.orderedRemove(i); |
| 1389 | self.files.items(.data)[index].object.deinit(self.base.comp.gpa); | |
| 1390 | self.files.set(index, .null); | |
| 1407 | 1391 | } else i += 1; |
| 1408 | 1392 | } |
| 1409 | 1393 | |
| ... | ... | @@ -1511,18 +1495,13 @@ fn createObjcSections(self: *MachO) !void { |
| 1511 | 1495 | } |
| 1512 | 1496 | |
| 1513 | 1497 | for (objc_msgsend_syms.keys()) |sym_index| { |
| 1498 | const internal = self.getInternalObject().?; | |
| 1514 | 1499 | const sym = self.getSymbol(sym_index); |
| 1515 | sym.value = 0; | |
| 1516 | sym.atom = 0; | |
| 1517 | sym.nlist_idx = 0; | |
| 1518 | sym.file = self.internal_object.?; | |
| 1519 | sym.flags = .{}; | |
| 1500 | _ = try internal.addSymbol(sym.getName(self), self); | |
| 1520 | 1501 | sym.visibility = .hidden; |
| 1521 | const object = self.getInternalObject().?; | |
| 1522 | 1502 | const name = eatPrefix(sym.getName(self), "_objc_msgSend$").?; |
| 1523 | const selrefs_index = try object.addObjcMsgsendSections(name, self); | |
| 1503 | const selrefs_index = try internal.addObjcMsgsendSections(name, self); | |
| 1524 | 1504 | try sym.addExtra(.{ .objc_selrefs = selrefs_index }, self); |
| 1525 | try object.symbols.append(gpa, sym_index); | |
| 1526 | 1505 | } |
| 1527 | 1506 | } |
| 1528 | 1507 | |
| ... | ... | @@ -1659,6 +1638,8 @@ fn deadStripDylibs(self: *MachO) void { |
| 1659 | 1638 | const index = self.dylibs.items[i]; |
| 1660 | 1639 | if (!self.getFile(index).?.dylib.isAlive(self)) { |
| 1661 | 1640 | _ = self.dylibs.orderedRemove(i); |
| 1641 | self.files.items(.data)[index].dylib.deinit(self.base.comp.gpa); | |
| 1642 | self.files.set(index, .null); | |
| 1662 | 1643 | } else i += 1; |
| 1663 | 1644 | } |
| 1664 | 1645 | } |
| ... | ... | @@ -2609,13 +2590,8 @@ fn writeAtoms(self: *MachO) !void { |
| 2609 | 2590 | const atom = self.getAtom(atom_index).?; |
| 2610 | 2591 | assert(atom.flags.alive); |
| 2611 | 2592 | const off = math.cast(usize, atom.value - header.addr) orelse return error.Overflow; |
| 2612 | const data = switch (atom.getFile(self)) { | |
| 2613 | .object => |x| try x.getAtomData(atom.*), | |
| 2614 | .zig_object => |x| try x.getAtomDataAlloc(self, arena.allocator(), atom.*), | |
| 2615 | else => unreachable, | |
| 2616 | }; | |
| 2617 | 2593 | const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; |
| 2618 | @memcpy(buffer[off..][0..atom_size], data); | |
| 2594 | try atom.getData(self, buffer[off..][0..atom_size]); | |
| 2619 | 2595 | atom.resolveRelocs(self, buffer[off..][0..atom_size]) catch |err| switch (err) { |
| 2620 | 2596 | error.ResolveFailed => has_resolve_error = true, |
| 2621 | 2597 | else => |e| return e, |
| ... | ... | @@ -3734,6 +3710,7 @@ pub fn getOrCreateGlobal(self: *MachO, off: u32) !GetOrCreateGlobalResult { |
| 3734 | 3710 | const index = try self.addSymbol(); |
| 3735 | 3711 | const global = self.getSymbol(index); |
| 3736 | 3712 | global.name = off; |
| 3713 | global.flags.global = true; | |
| 3737 | 3714 | gop.value_ptr.* = index; |
| 3738 | 3715 | } |
| 3739 | 3716 | return .{ |
src/link/MachO/Archive.zig+26-18| ... | ... | @@ -1,6 +1,3 @@ |
| 1 | path: []const u8, | |
| 2 | data: []const u8, | |
| 3 | ||
| 4 | 1 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 5 | 2 | |
| 6 | 3 | // Archive files start with the ARMAG identifying string. Then follows a |
| ... | ... | @@ -73,62 +70,73 @@ pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool { |
| 73 | 70 | } |
| 74 | 71 | |
| 75 | 72 | pub fn deinit(self: *Archive, allocator: Allocator) void { |
| 76 | allocator.free(self.data); | |
| 77 | allocator.free(self.path); | |
| 78 | 73 | self.objects.deinit(allocator); |
| 79 | 74 | } |
| 80 | 75 | |
| 81 | pub fn parse(self: *Archive, macho_file: *MachO) !void { | |
| 76 | pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, file: std.fs.File, fat_arch: ?fat.Arch) !void { | |
| 82 | 77 | const gpa = macho_file.base.comp.gpa; |
| 83 | 78 | |
| 84 | 79 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 85 | 80 | defer arena.deinit(); |
| 86 | 81 | |
| 87 | var stream = std.io.fixedBufferStream(self.data); | |
| 88 | const reader = stream.reader(); | |
| 89 | _ = try reader.readBytesNoEof(SARMAG); | |
| 82 | const offset = if (fat_arch) |ar| ar.offset else 0; | |
| 83 | const size = if (fat_arch) |ar| ar.size else (try file.stat()).size; | |
| 84 | try file.seekTo(offset); | |
| 85 | ||
| 86 | const reader = file.reader(); | |
| 87 | _ = try reader.readBytesNoEof(Archive.SARMAG); | |
| 90 | 88 | |
| 89 | var pos: usize = Archive.SARMAG; | |
| 91 | 90 | while (true) { |
| 92 | if (stream.pos >= self.data.len) break; | |
| 93 | if (!mem.isAligned(stream.pos, 2)) stream.pos += 1; | |
| 91 | if (pos >= size) break; | |
| 92 | if (!mem.isAligned(pos, 2)) { | |
| 93 | try file.seekBy(1); | |
| 94 | pos += 1; | |
| 95 | } | |
| 94 | 96 | |
| 95 | 97 | const hdr = try reader.readStruct(ar_hdr); |
| 98 | pos += @sizeOf(ar_hdr); | |
| 96 | 99 | |
| 97 | 100 | if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { |
| 98 | try macho_file.reportParseError(self.path, "invalid header delimiter: expected '{s}', found '{s}'", .{ | |
| 101 | try macho_file.reportParseError(path, "invalid header delimiter: expected '{s}', found '{s}'", .{ | |
| 99 | 102 | std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag), |
| 100 | 103 | }); |
| 101 | 104 | return error.MalformedArchive; |
| 102 | 105 | } |
| 103 | 106 | |
| 104 | var size = try hdr.size(); | |
| 107 | var hdr_size = try hdr.size(); | |
| 105 | 108 | const name = name: { |
| 106 | 109 | if (hdr.name()) |n| break :name n; |
| 107 | 110 | if (try hdr.nameLength()) |len| { |
| 108 | size -= len; | |
| 111 | hdr_size -= len; | |
| 109 | 112 | const buf = try arena.allocator().alloc(u8, len); |
| 110 | 113 | try reader.readNoEof(buf); |
| 114 | pos += len; | |
| 111 | 115 | const actual_len = mem.indexOfScalar(u8, buf, @as(u8, 0)) orelse len; |
| 112 | 116 | break :name buf[0..actual_len]; |
| 113 | 117 | } |
| 114 | 118 | unreachable; |
| 115 | 119 | }; |
| 116 | 120 | defer { |
| 117 | _ = stream.seekBy(size) catch {}; | |
| 121 | _ = file.seekBy(hdr_size) catch {}; | |
| 122 | pos += hdr_size; | |
| 118 | 123 | } |
| 119 | 124 | |
| 120 | 125 | if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue; |
| 121 | 126 | |
| 122 | 127 | const object = Object{ |
| 123 | .archive = try gpa.dupe(u8, self.path), | |
| 128 | .archive = .{ | |
| 129 | .path = try gpa.dupe(u8, path), | |
| 130 | .offset = offset + pos, | |
| 131 | }, | |
| 124 | 132 | .path = try gpa.dupe(u8, name), |
| 125 | .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]), | |
| 133 | .file = try std.fs.cwd().openFile(path, .{}), | |
| 126 | 134 | .index = undefined, |
| 127 | 135 | .alive = false, |
| 128 | 136 | .mtime = hdr.date() catch 0, |
| 129 | 137 | }; |
| 130 | 138 | |
| 131 | log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, self.path }); | |
| 139 | log.debug("extracting object '{s}' from archive '{s}'", .{ object.path, path }); | |
| 132 | 140 | |
| 133 | 141 | try self.objects.append(gpa, object); |
| 134 | 142 | } |
src/link/MachO/Atom.zig+19-5| ... | ... | @@ -43,26 +43,40 @@ prev_index: Index = 0, |
| 43 | 43 | next_index: Index = 0, |
| 44 | 44 | |
| 45 | 45 | pub fn getName(self: Atom, macho_file: *MachO) [:0]const u8 { |
| 46 | return macho_file.strings.getAssumeExists(self.name); | |
| 46 | return switch (self.getFile(macho_file)) { | |
| 47 | .dylib => unreachable, | |
| 48 | .zig_object => |x| x.strtab.getAssumeExists(self.name), | |
| 49 | inline else => |x| x.getString(self.name), | |
| 50 | }; | |
| 47 | 51 | } |
| 48 | 52 | |
| 49 | 53 | pub fn getFile(self: Atom, macho_file: *MachO) File { |
| 50 | 54 | return macho_file.getFile(self.file).?; |
| 51 | 55 | } |
| 52 | 56 | |
| 57 | pub fn getData(self: Atom, macho_file: *MachO, buffer: []u8) !void { | |
| 58 | assert(buffer.len == self.size); | |
| 59 | switch (self.getFile(macho_file)) { | |
| 60 | .internal => |x| try x.getAtomData(self, buffer), | |
| 61 | .object => |x| try x.getAtomData(self, buffer), | |
| 62 | .zig_object => |x| try x.getAtomData(macho_file, self, buffer), | |
| 63 | else => unreachable, | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| 53 | 67 | pub fn getRelocs(self: Atom, macho_file: *MachO) []const Relocation { |
| 54 | 68 | return switch (self.getFile(macho_file)) { |
| 55 | .zig_object => |x| x.getAtomRelocs(self), | |
| 56 | .object => |x| x.getAtomRelocs(self), | |
| 57 | else => unreachable, | |
| 69 | .dylib => unreachable, | |
| 70 | inline else => |x| x.getAtomRelocs(self), | |
| 58 | 71 | }; |
| 59 | 72 | } |
| 60 | 73 | |
| 61 | 74 | pub fn getInputSection(self: Atom, macho_file: *MachO) macho.section_64 { |
| 62 | 75 | return switch (self.getFile(macho_file)) { |
| 76 | .dylib => unreachable, | |
| 63 | 77 | .zig_object => |x| x.getInputSection(self, macho_file), |
| 64 | 78 | .object => |x| x.sections.items(.header)[self.n_sect], |
| 65 | else => unreachable, | |
| 79 | .internal => |x| x.sections.items(.header)[self.n_sect], | |
| 66 | 80 | }; |
| 67 | 81 | } |
| 68 | 82 |
src/link/MachO/DwarfInfo.zig+39-20| ... | ... | @@ -1,15 +1,17 @@ |
| 1 | debug_info: []const u8, | |
| 2 | debug_abbrev: []const u8, | |
| 3 | debug_str: []const u8, | |
| 4 | ||
| 5 | 1 | /// Abbreviation table indexed by offset in the .debug_abbrev bytestream |
| 6 | 2 | abbrev_tables: std.AutoArrayHashMapUnmanaged(u64, AbbrevTable) = .{}, |
| 7 | 3 | /// List of compile units as they appear in the .debug_info bytestream |
| 8 | 4 | compile_units: std.ArrayListUnmanaged(CompileUnit) = .{}, |
| 9 | ||
| 10 | pub fn init(dw: *DwarfInfo, allocator: Allocator) !void { | |
| 11 | try dw.parseAbbrevTables(allocator); | |
| 12 | try dw.parseCompileUnits(allocator); | |
| 5 | /// Debug info string table | |
| 6 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 7 | /// Debug info data | |
| 8 | di_data: std.ArrayListUnmanaged(u8) = .{}, | |
| 9 | ||
| 10 | pub fn init(dw: *DwarfInfo, allocator: Allocator, di: DebugInfo) !void { | |
| 11 | try dw.strtab.ensureTotalCapacityPrecise(allocator, di.debug_str.len); | |
| 12 | dw.strtab.appendSliceAssumeCapacity(di.debug_str); | |
| 13 | try dw.parseAbbrevTables(allocator, di); | |
| 14 | try dw.parseCompileUnits(allocator, di); | |
| 13 | 15 | } |
| 14 | 16 | |
| 15 | 17 | pub fn deinit(dw: *DwarfInfo, allocator: Allocator) void { |
| ... | ... | @@ -18,18 +20,27 @@ pub fn deinit(dw: *DwarfInfo, allocator: Allocator) void { |
| 18 | 20 | cu.deinit(allocator); |
| 19 | 21 | } |
| 20 | 22 | dw.compile_units.deinit(allocator); |
| 23 | dw.strtab.deinit(allocator); | |
| 24 | dw.di_data.deinit(allocator); | |
| 25 | } | |
| 26 | ||
| 27 | fn appendDiData(dw: *DwarfInfo, allocator: Allocator, values: []const u8) error{OutOfMemory}!u32 { | |
| 28 | const index: u32 = @intCast(dw.di_data.items.len); | |
| 29 | try dw.di_data.ensureUnusedCapacity(allocator, values.len); | |
| 30 | dw.di_data.appendSliceAssumeCapacity(values); | |
| 31 | return index; | |
| 21 | 32 | } |
| 22 | 33 | |
| 23 | 34 | fn getString(dw: DwarfInfo, off: usize) [:0]const u8 { |
| 24 | assert(off < dw.debug_str.len); | |
| 25 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(dw.debug_str.ptr + off)), 0); | |
| 35 | assert(off < dw.strtab.items.len); | |
| 36 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(dw.strtab.items.ptr + off)), 0); | |
| 26 | 37 | } |
| 27 | 38 | |
| 28 | fn parseAbbrevTables(dw: *DwarfInfo, allocator: Allocator) !void { | |
| 39 | fn parseAbbrevTables(dw: *DwarfInfo, allocator: Allocator, di: DebugInfo) !void { | |
| 29 | 40 | const tracy = trace(@src()); |
| 30 | 41 | defer tracy.end(); |
| 31 | 42 | |
| 32 | const debug_abbrev = dw.debug_abbrev; | |
| 43 | const debug_abbrev = di.debug_abbrev; | |
| 33 | 44 | var stream = std.io.fixedBufferStream(debug_abbrev); |
| 34 | 45 | var creader = std.io.countingReader(stream.reader()); |
| 35 | 46 | const reader = creader.reader(); |
| ... | ... | @@ -77,11 +88,11 @@ fn parseAbbrevTables(dw: *DwarfInfo, allocator: Allocator) !void { |
| 77 | 88 | } |
| 78 | 89 | } |
| 79 | 90 | |
| 80 | fn parseCompileUnits(dw: *DwarfInfo, allocator: Allocator) !void { | |
| 91 | fn parseCompileUnits(dw: *DwarfInfo, allocator: Allocator, di: DebugInfo) !void { | |
| 81 | 92 | const tracy = trace(@src()); |
| 82 | 93 | defer tracy.end(); |
| 83 | 94 | |
| 84 | const debug_info = dw.debug_info; | |
| 95 | const debug_info = di.debug_info; | |
| 85 | 96 | var stream = std.io.fixedBufferStream(debug_info); |
| 86 | 97 | var creader = std.io.countingReader(stream.reader()); |
| 87 | 98 | const reader = creader.reader(); |
| ... | ... | @@ -107,7 +118,7 @@ fn parseCompileUnits(dw: *DwarfInfo, allocator: Allocator) !void { |
| 107 | 118 | cu.header.address_size = try reader.readInt(u8, .little); |
| 108 | 119 | |
| 109 | 120 | const table = dw.abbrev_tables.get(cu.header.debug_abbrev_offset).?; |
| 110 | try dw.parseDie(allocator, cu, table, null, &creader); | |
| 121 | try dw.parseDie(allocator, cu, table, di, null, &creader); | |
| 111 | 122 | } |
| 112 | 123 | } |
| 113 | 124 | |
| ... | ... | @@ -116,6 +127,7 @@ fn parseDie( |
| 116 | 127 | allocator: Allocator, |
| 117 | 128 | cu: *CompileUnit, |
| 118 | 129 | table: AbbrevTable, |
| 130 | di: DebugInfo, | |
| 119 | 131 | parent: ?u32, |
| 120 | 132 | creader: anytype, |
| 121 | 133 | ) anyerror!void { |
| ... | ... | @@ -140,19 +152,20 @@ fn parseDie( |
| 140 | 152 | } |
| 141 | 153 | |
| 142 | 154 | const decl = table.decls.get(code) orelse return error.MalformedDwarf; // TODO better errors |
| 143 | const data = dw.debug_info; | |
| 155 | const data = di.debug_info; | |
| 144 | 156 | try cu.diePtr(die).values.ensureTotalCapacityPrecise(allocator, decl.attrs.values().len); |
| 145 | 157 | |
| 146 | 158 | for (decl.attrs.values()) |attr| { |
| 147 | 159 | const start = std.math.cast(usize, creader.bytes_read) orelse return error.Overflow; |
| 148 | 160 | try advanceByFormSize(cu, attr.form, creader); |
| 149 | 161 | const end = std.math.cast(usize, creader.bytes_read) orelse return error.Overflow; |
| 150 | cu.diePtr(die).values.appendAssumeCapacity(data[start..end]); | |
| 162 | const index = try dw.appendDiData(allocator, data[start..end]); | |
| 163 | cu.diePtr(die).values.appendAssumeCapacity(.{ .index = index, .len = @intCast(end - start) }); | |
| 151 | 164 | } |
| 152 | 165 | |
| 153 | 166 | if (decl.children) { |
| 154 | 167 | // Open scope |
| 155 | try dw.parseDie(allocator, cu, table, die, creader); | |
| 168 | try dw.parseDie(allocator, cu, table, di, die, creader); | |
| 156 | 169 | } |
| 157 | 170 | } |
| 158 | 171 | } |
| ... | ... | @@ -340,7 +353,7 @@ pub const CompileUnit = struct { |
| 340 | 353 | |
| 341 | 354 | pub const Die = struct { |
| 342 | 355 | code: Code, |
| 343 | values: std.ArrayListUnmanaged([]const u8) = .{}, | |
| 356 | values: std.ArrayListUnmanaged(struct { index: u32, len: u32 }) = .{}, | |
| 344 | 357 | children: std.ArrayListUnmanaged(Die.Index) = .{}, |
| 345 | 358 | |
| 346 | 359 | pub fn deinit(die: *Die, gpa: Allocator) void { |
| ... | ... | @@ -354,7 +367,7 @@ pub const Die = struct { |
| 354 | 367 | const index = decl.attrs.getIndex(at) orelse return null; |
| 355 | 368 | const attr = decl.attrs.values()[index]; |
| 356 | 369 | const value = die.values.items[index]; |
| 357 | return .{ .attr = attr, .bytes = value }; | |
| 370 | return .{ .attr = attr, .bytes = ctx.di_data.items[value.index..][0..value.len] }; | |
| 358 | 371 | } |
| 359 | 372 | |
| 360 | 373 | pub const Index = u32; |
| ... | ... | @@ -458,6 +471,12 @@ pub const Format = enum { |
| 458 | 471 | dwarf64, |
| 459 | 472 | }; |
| 460 | 473 | |
| 474 | const DebugInfo = struct { | |
| 475 | debug_info: []const u8, | |
| 476 | debug_abbrev: []const u8, | |
| 477 | debug_str: []const u8, | |
| 478 | }; | |
| 479 | ||
| 461 | 480 | const assert = std.debug.assert; |
| 462 | 481 | const dwarf = std.dwarf; |
| 463 | 482 | const leb = std.leb; |
src/link/MachO/Dylib.zig+49-50| ... | ... | @@ -1,8 +1,6 @@ |
| 1 | 1 | path: []const u8, |
| 2 | data: []const u8, | |
| 3 | 2 | index: File.Index, |
| 4 | 3 | |
| 5 | header: ?macho.mach_header_64 = null, | |
| 6 | 4 | exports: std.MultiArrayList(Export) = .{}, |
| 7 | 5 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 8 | 6 | id: ?Id = null, |
| ... | ... | @@ -34,7 +32,6 @@ pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool { |
| 34 | 32 | } |
| 35 | 33 | |
| 36 | 34 | pub fn deinit(self: *Dylib, allocator: Allocator) void { |
| 37 | allocator.free(self.data); | |
| 38 | 35 | allocator.free(self.path); |
| 39 | 36 | self.exports.deinit(allocator); |
| 40 | 37 | self.strtab.deinit(allocator); |
| ... | ... | @@ -44,22 +41,29 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void { |
| 44 | 41 | id.deinit(allocator); |
| 45 | 42 | } |
| 46 | 43 | self.dependents.deinit(allocator); |
| 44 | for (self.rpaths.keys()) |rpath| { | |
| 45 | allocator.free(rpath); | |
| 46 | } | |
| 47 | 47 | self.rpaths.deinit(allocator); |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | pub fn parse(self: *Dylib, macho_file: *MachO) !void { | |
| 50 | pub fn parse(self: *Dylib, macho_file: *MachO, file: std.fs.File, fat_arch: ?fat.Arch) !void { | |
| 51 | 51 | const tracy = trace(@src()); |
| 52 | 52 | defer tracy.end(); |
| 53 | 53 | |
| 54 | 54 | const gpa = macho_file.base.comp.gpa; |
| 55 | var stream = std.io.fixedBufferStream(self.data); | |
| 56 | const reader = stream.reader(); | |
| 55 | const offset = if (fat_arch) |ar| ar.offset else 0; | |
| 57 | 56 | |
| 58 | 57 | log.debug("parsing dylib from binary", .{}); |
| 59 | 58 | |
| 60 | self.header = try reader.readStruct(macho.mach_header_64); | |
| 59 | var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined; | |
| 60 | { | |
| 61 | const amt = try file.preadAll(&header_buffer, offset); | |
| 62 | if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput; | |
| 63 | } | |
| 64 | const header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*; | |
| 61 | 65 | |
| 62 | const this_cpu_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { | |
| 66 | const this_cpu_arch: std.Target.Cpu.Arch = switch (header.cputype) { | |
| 63 | 67 | macho.CPU_TYPE_ARM64 => .aarch64, |
| 64 | 68 | macho.CPU_TYPE_X86_64 => .x86_64, |
| 65 | 69 | else => |x| { |
| ... | ... | @@ -72,39 +76,60 @@ pub fn parse(self: *Dylib, macho_file: *MachO) !void { |
| 72 | 76 | return error.InvalidCpuArch; |
| 73 | 77 | } |
| 74 | 78 | |
| 75 | const lc_id = self.getLoadCommand(.ID_DYLIB) orelse { | |
| 76 | try macho_file.reportParseError2(self.index, "missing LC_ID_DYLIB load command", .{}); | |
| 77 | return error.MalformedDylib; | |
| 78 | }; | |
| 79 | self.id = try Id.fromLoadCommand(gpa, lc_id.cast(macho.dylib_command).?, lc_id.getDylibPathName()); | |
| 79 | const lc_buffer = try gpa.alloc(u8, header.sizeofcmds); | |
| 80 | defer gpa.free(lc_buffer); | |
| 81 | { | |
| 82 | const amt = try file.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64)); | |
| 83 | if (amt != lc_buffer.len) return error.InputOutput; | |
| 84 | } | |
| 80 | 85 | |
| 81 | 86 | var it = LoadCommandIterator{ |
| 82 | .ncmds = self.header.?.ncmds, | |
| 83 | .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds], | |
| 87 | .ncmds = header.ncmds, | |
| 88 | .buffer = lc_buffer, | |
| 84 | 89 | }; |
| 85 | 90 | while (it.next()) |cmd| switch (cmd.cmd()) { |
| 86 | .REEXPORT_DYLIB => if (self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0) { | |
| 91 | .ID_DYLIB => { | |
| 92 | self.id = try Id.fromLoadCommand(gpa, cmd.cast(macho.dylib_command).?, cmd.getDylibPathName()); | |
| 93 | }, | |
| 94 | .REEXPORT_DYLIB => if (header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0) { | |
| 87 | 95 | const id = try Id.fromLoadCommand(gpa, cmd.cast(macho.dylib_command).?, cmd.getDylibPathName()); |
| 88 | 96 | try self.dependents.append(gpa, id); |
| 89 | 97 | }, |
| 90 | 98 | .DYLD_INFO_ONLY => { |
| 91 | 99 | const dyld_cmd = cmd.cast(macho.dyld_info_command).?; |
| 92 | const data = self.data[dyld_cmd.export_off..][0..dyld_cmd.export_size]; | |
| 100 | const data = try gpa.alloc(u8, dyld_cmd.export_size); | |
| 101 | defer gpa.free(data); | |
| 102 | const amt = try file.preadAll(data, dyld_cmd.export_off + offset); | |
| 103 | if (amt != data.len) return error.InputOutput; | |
| 93 | 104 | try self.parseTrie(data, macho_file); |
| 94 | 105 | }, |
| 95 | 106 | .DYLD_EXPORTS_TRIE => { |
| 96 | 107 | const ld_cmd = cmd.cast(macho.linkedit_data_command).?; |
| 97 | const data = self.data[ld_cmd.dataoff..][0..ld_cmd.datasize]; | |
| 108 | const data = try gpa.alloc(u8, ld_cmd.datasize); | |
| 109 | defer gpa.free(data); | |
| 110 | const amt = try file.preadAll(data, ld_cmd.dataoff + offset); | |
| 111 | if (amt != data.len) return error.InputOutput; | |
| 98 | 112 | try self.parseTrie(data, macho_file); |
| 99 | 113 | }, |
| 100 | 114 | .RPATH => { |
| 101 | 115 | const path = cmd.getRpathPathName(); |
| 102 | try self.rpaths.put(gpa, path, {}); | |
| 116 | try self.rpaths.put(gpa, try gpa.dupe(u8, path), {}); | |
| 117 | }, | |
| 118 | .BUILD_VERSION, | |
| 119 | .VERSION_MIN_MACOSX, | |
| 120 | .VERSION_MIN_IPHONEOS, | |
| 121 | .VERSION_MIN_TVOS, | |
| 122 | .VERSION_MIN_WATCHOS, | |
| 123 | => { | |
| 124 | self.platform = MachO.Platform.fromLoadCommand(cmd); | |
| 103 | 125 | }, |
| 104 | 126 | else => {}, |
| 105 | 127 | }; |
| 106 | 128 | |
| 107 | self.initPlatform(); | |
| 129 | if (self.id == null) { | |
| 130 | try macho_file.reportParseError2(self.index, "missing LC_ID_DYLIB load command", .{}); | |
| 131 | return error.MalformedDylib; | |
| 132 | } | |
| 108 | 133 | |
| 109 | 134 | if (self.platform) |platform| { |
| 110 | 135 | if (!macho_file.platform.eqlTarget(platform)) { |
| ... | ... | @@ -168,7 +193,7 @@ const TrieIterator = struct { |
| 168 | 193 | |
| 169 | 194 | pub fn addExport(self: *Dylib, allocator: Allocator, name: []const u8, flags: Export.Flags) !void { |
| 170 | 195 | try self.exports.append(allocator, .{ |
| 171 | .name = try self.insertString(allocator, name), | |
| 196 | .name = try self.addString(allocator, name), | |
| 172 | 197 | .flags = flags, |
| 173 | 198 | }); |
| 174 | 199 | } |
| ... | ... | @@ -479,24 +504,6 @@ pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void { |
| 479 | 504 | } |
| 480 | 505 | } |
| 481 | 506 | |
| 482 | fn initPlatform(self: *Dylib) void { | |
| 483 | var it = LoadCommandIterator{ | |
| 484 | .ncmds = self.header.?.ncmds, | |
| 485 | .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds], | |
| 486 | }; | |
| 487 | self.platform = while (it.next()) |cmd| { | |
| 488 | switch (cmd.cmd()) { | |
| 489 | .BUILD_VERSION, | |
| 490 | .VERSION_MIN_MACOSX, | |
| 491 | .VERSION_MIN_IPHONEOS, | |
| 492 | .VERSION_MIN_TVOS, | |
| 493 | .VERSION_MIN_WATCHOS, | |
| 494 | => break MachO.Platform.fromLoadCommand(cmd), | |
| 495 | else => {}, | |
| 496 | } | |
| 497 | } else null; | |
| 498 | } | |
| 499 | ||
| 500 | 507 | pub fn resolveSymbols(self: *Dylib, macho_file: *MachO) void { |
| 501 | 508 | const tracy = trace(@src()); |
| 502 | 509 | defer tracy.end(); |
| ... | ... | @@ -526,8 +533,10 @@ pub fn resetGlobals(self: *Dylib, macho_file: *MachO) void { |
| 526 | 533 | for (self.symbols.items) |sym_index| { |
| 527 | 534 | const sym = macho_file.getSymbol(sym_index); |
| 528 | 535 | const name = sym.name; |
| 536 | const global = sym.flags.global; | |
| 529 | 537 | sym.* = .{}; |
| 530 | 538 | sym.name = name; |
| 539 | sym.flags.global = global; | |
| 531 | 540 | } |
| 532 | 541 | } |
| 533 | 542 | |
| ... | ... | @@ -589,17 +598,7 @@ pub inline fn getUmbrella(self: Dylib, macho_file: *MachO) *Dylib { |
| 589 | 598 | return macho_file.getFile(self.umbrella).?.dylib; |
| 590 | 599 | } |
| 591 | 600 | |
| 592 | fn getLoadCommand(self: Dylib, lc: macho.LC) ?LoadCommandIterator.LoadCommand { | |
| 593 | var it = LoadCommandIterator{ | |
| 594 | .ncmds = self.header.?.ncmds, | |
| 595 | .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds], | |
| 596 | }; | |
| 597 | while (it.next()) |cmd| { | |
| 598 | if (cmd.cmd() == lc) return cmd; | |
| 599 | } else return null; | |
| 600 | } | |
| 601 | ||
| 602 | fn insertString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 { | |
| 601 | fn addString(self: *Dylib, allocator: Allocator, name: []const u8) !u32 { | |
| 603 | 602 | const off = @as(u32, @intCast(self.strtab.items.len)); |
| 604 | 603 | try self.strtab.writer(allocator).print("{s}\x00", .{name}); |
| 605 | 604 | return off; |
src/link/MachO/InternalObject.zig+41-12| ... | ... | @@ -3,6 +3,7 @@ index: File.Index, |
| 3 | 3 | sections: std.MultiArrayList(Section) = .{}, |
| 4 | 4 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| 5 | 5 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 6 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 6 | 7 | |
| 7 | 8 | objc_methnames: std.ArrayListUnmanaged(u8) = .{}, |
| 8 | 9 | objc_selrefs: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64), |
| ... | ... | @@ -16,6 +17,7 @@ pub fn deinit(self: *InternalObject, allocator: Allocator) void { |
| 16 | 17 | self.sections.deinit(allocator); |
| 17 | 18 | self.atoms.deinit(allocator); |
| 18 | 19 | self.symbols.deinit(allocator); |
| 20 | self.strtab.deinit(allocator); | |
| 19 | 21 | self.objc_methnames.deinit(allocator); |
| 20 | 22 | } |
| 21 | 23 | |
| ... | ... | @@ -26,7 +28,11 @@ pub fn addSymbol(self: *InternalObject, name: [:0]const u8, macho_file: *MachO) |
| 26 | 28 | const gop = try macho_file.getOrCreateGlobal(off); |
| 27 | 29 | self.symbols.addOneAssumeCapacity().* = gop.index; |
| 28 | 30 | const sym = macho_file.getSymbol(gop.index); |
| 29 | sym.* = .{ .name = off, .file = self.index }; | |
| 31 | sym.file = self.index; | |
| 32 | sym.value = 0; | |
| 33 | sym.atom = 0; | |
| 34 | sym.nlist_idx = 0; | |
| 35 | sym.flags = .{ .global = true }; | |
| 30 | 36 | return gop.index; |
| 31 | 37 | } |
| 32 | 38 | |
| ... | ... | @@ -45,7 +51,7 @@ fn addObjcMethnameSection(self: *InternalObject, methname: []const u8, macho_fil |
| 45 | 51 | defer gpa.free(name); |
| 46 | 52 | const atom = macho_file.getAtom(atom_index).?; |
| 47 | 53 | atom.atom_index = atom_index; |
| 48 | atom.name = try macho_file.strings.insert(gpa, name); | |
| 54 | atom.name = try self.addString(gpa, name); | |
| 49 | 55 | atom.file = self.index; |
| 50 | 56 | atom.size = methname.len + 1; |
| 51 | 57 | atom.alignment = .@"1"; |
| ... | ... | @@ -79,7 +85,7 @@ fn addObjcSelrefsSection( |
| 79 | 85 | defer gpa.free(name); |
| 80 | 86 | const atom = macho_file.getAtom(atom_index).?; |
| 81 | 87 | atom.atom_index = atom_index; |
| 82 | atom.name = try macho_file.strings.insert(gpa, name); | |
| 88 | atom.name = try self.addString(gpa, name); | |
| 83 | 89 | atom.file = self.index; |
| 84 | 90 | atom.size = @sizeOf(u64); |
| 85 | 91 | atom.alignment = .@"8"; |
| ... | ... | @@ -158,16 +164,39 @@ fn addSection(self: *InternalObject, allocator: Allocator, segname: []const u8, |
| 158 | 164 | return n_sect; |
| 159 | 165 | } |
| 160 | 166 | |
| 161 | pub fn getSectionData(self: *const InternalObject, index: u32) []const u8 { | |
| 167 | pub fn getAtomData(self: *const InternalObject, atom: Atom, buffer: []u8) !void { | |
| 168 | assert(buffer.len == atom.size); | |
| 162 | 169 | const slice = self.sections.slice(); |
| 163 | assert(index < slice.items(.header).len); | |
| 164 | const sect = slice.items(.header)[index]; | |
| 165 | const extra = slice.items(.extra)[index]; | |
| 166 | if (extra.is_objc_methname) { | |
| 167 | return self.objc_methnames.items[sect.offset..][0..sect.size]; | |
| 168 | } else if (extra.is_objc_selref) { | |
| 169 | return &self.objc_selrefs; | |
| 170 | } else @panic("ref to non-existent section"); | |
| 170 | const sect = slice.items(.header)[atom.n_sect]; | |
| 171 | const extra = slice.items(.extra)[atom.n_sect]; | |
| 172 | const data = if (extra.is_objc_methname) blk: { | |
| 173 | const size = std.math.cast(usize, sect.size) orelse return error.Overflow; | |
| 174 | break :blk self.objc_methnames.items[sect.offset..][0..size]; | |
| 175 | } else if (extra.is_objc_selref) | |
| 176 | &self.objc_selrefs | |
| 177 | else | |
| 178 | @panic("ref to non-existent section"); | |
| 179 | const off = std.math.cast(usize, atom.off) orelse return error.Overflow; | |
| 180 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | |
| 181 | @memcpy(buffer, data[off..][0..size]); | |
| 182 | } | |
| 183 | ||
| 184 | pub fn getAtomRelocs(self: *const InternalObject, atom: Atom) []const Relocation { | |
| 185 | const relocs = self.sections.items(.relocs)[atom.n_sect]; | |
| 186 | return relocs.items[atom.relocs.pos..][0..atom.relocs.len]; | |
| 187 | } | |
| 188 | ||
| 189 | fn addString(self: *InternalObject, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 { | |
| 190 | const off: u32 = @intCast(self.strtab.items.len); | |
| 191 | try self.strtab.ensureUnusedCapacity(allocator, name.len + 1); | |
| 192 | self.strtab.appendSliceAssumeCapacity(name); | |
| 193 | self.strtab.appendAssumeCapacity(0); | |
| 194 | return off; | |
| 195 | } | |
| 196 | ||
| 197 | pub fn getString(self: InternalObject, off: u32) [:0]const u8 { | |
| 198 | assert(off < self.strtab.items.len); | |
| 199 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); | |
| 171 | 200 | } |
| 172 | 201 | |
| 173 | 202 | pub fn asFile(self: *InternalObject) File { |
src/link/MachO/Object.zig+178-117| ... | ... | @@ -1,13 +1,13 @@ |
| 1 | archive: ?[]const u8 = null, | |
| 1 | archive: ?Archive = null, | |
| 2 | 2 | path: []const u8, |
| 3 | file: std.fs.File, | |
| 3 | 4 | mtime: u64, |
| 4 | data: []const u8, | |
| 5 | 5 | index: File.Index, |
| 6 | 6 | |
| 7 | 7 | header: ?macho.mach_header_64 = null, |
| 8 | 8 | sections: std.MultiArrayList(Section) = .{}, |
| 9 | 9 | symtab: std.MultiArrayList(Nlist) = .{}, |
| 10 | strtab: []const u8 = &[0]u8{}, | |
| 10 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 11 | 11 | |
| 12 | 12 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 13 | 13 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| ... | ... | @@ -22,6 +22,7 @@ cies: std.ArrayListUnmanaged(Cie) = .{}, |
| 22 | 22 | fdes: std.ArrayListUnmanaged(Fde) = .{}, |
| 23 | 23 | eh_frame_data: std.ArrayListUnmanaged(u8) = .{}, |
| 24 | 24 | unwind_records: std.ArrayListUnmanaged(UnwindInfo.Record.Index) = .{}, |
| 25 | data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, | |
| 25 | 26 | |
| 26 | 27 | alive: bool = true, |
| 27 | 28 | hidden: bool = false, |
| ... | ... | @@ -29,6 +30,11 @@ hidden: bool = false, |
| 29 | 30 | dynamic_relocs: MachO.DynamicRelocs = .{}, |
| 30 | 31 | output_symtab_ctx: MachO.SymtabCtx = .{}, |
| 31 | 32 | |
| 33 | const Archive = struct { | |
| 34 | path: []const u8, | |
| 35 | offset: u64, | |
| 36 | }; | |
| 37 | ||
| 32 | 38 | pub fn isObject(path: []const u8) !bool { |
| 33 | 39 | const file = try std.fs.cwd().openFile(path, .{}); |
| 34 | 40 | defer file.close(); |
| ... | ... | @@ -37,12 +43,16 @@ pub fn isObject(path: []const u8) !bool { |
| 37 | 43 | } |
| 38 | 44 | |
| 39 | 45 | pub fn deinit(self: *Object, allocator: Allocator) void { |
| 46 | self.file.close(); | |
| 47 | if (self.archive) |*ar| allocator.free(ar.path); | |
| 48 | allocator.free(self.path); | |
| 40 | 49 | for (self.sections.items(.relocs), self.sections.items(.subsections)) |*relocs, *sub| { |
| 41 | 50 | relocs.deinit(allocator); |
| 42 | 51 | sub.deinit(allocator); |
| 43 | 52 | } |
| 44 | 53 | self.sections.deinit(allocator); |
| 45 | 54 | self.symtab.deinit(allocator); |
| 55 | self.strtab.deinit(allocator); | |
| 46 | 56 | self.symbols.deinit(allocator); |
| 47 | 57 | self.atoms.deinit(allocator); |
| 48 | 58 | self.cies.deinit(allocator); |
| ... | ... | @@ -54,7 +64,7 @@ pub fn deinit(self: *Object, allocator: Allocator) void { |
| 54 | 64 | sf.stabs.deinit(allocator); |
| 55 | 65 | } |
| 56 | 66 | self.stab_files.deinit(allocator); |
| 57 | allocator.free(self.data); | |
| 67 | self.data_in_code.deinit(allocator); | |
| 58 | 68 | } |
| 59 | 69 | |
| 60 | 70 | pub fn parse(self: *Object, macho_file: *MachO) !void { |
| ... | ... | @@ -62,10 +72,14 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { |
| 62 | 72 | defer tracy.end(); |
| 63 | 73 | |
| 64 | 74 | const gpa = macho_file.base.comp.gpa; |
| 65 | var stream = std.io.fixedBufferStream(self.data); | |
| 66 | const reader = stream.reader(); | |
| 75 | const offset = if (self.archive) |ar| ar.offset else 0; | |
| 67 | 76 | |
| 68 | self.header = try reader.readStruct(macho.mach_header_64); | |
| 77 | var header_buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined; | |
| 78 | { | |
| 79 | const amt = try self.file.preadAll(&header_buffer, offset); | |
| 80 | if (amt != @sizeOf(macho.mach_header_64)) return error.InputOutput; | |
| 81 | } | |
| 82 | self.header = @as(*align(1) const macho.mach_header_64, @ptrCast(&header_buffer)).*; | |
| 69 | 83 | |
| 70 | 84 | const this_cpu_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { |
| 71 | 85 | macho.CPU_TYPE_ARM64 => .aarch64, |
| ... | ... | @@ -80,35 +94,79 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { |
| 80 | 94 | return error.InvalidCpuArch; |
| 81 | 95 | } |
| 82 | 96 | |
| 83 | if (self.getLoadCommand(.SEGMENT_64)) |lc| { | |
| 84 | const sections = lc.getSections(); | |
| 85 | try self.sections.ensureUnusedCapacity(gpa, sections.len); | |
| 86 | for (sections) |sect| { | |
| 87 | const index = try self.sections.addOne(gpa); | |
| 88 | self.sections.set(index, .{ .header = sect }); | |
| 89 | ||
| 90 | if (mem.eql(u8, sect.sectName(), "__eh_frame")) { | |
| 91 | self.eh_frame_sect_index = @intCast(index); | |
| 92 | } else if (mem.eql(u8, sect.sectName(), "__compact_unwind")) { | |
| 93 | self.compact_unwind_sect_index = @intCast(index); | |
| 94 | } | |
| 95 | } | |
| 96 | } | |
| 97 | if (self.getLoadCommand(.SYMTAB)) |lc| { | |
| 98 | const cmd = lc.cast(macho.symtab_command).?; | |
| 99 | self.strtab = self.data[cmd.stroff..][0..cmd.strsize]; | |
| 100 | ||
| 101 | const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(self.data.ptr + cmd.symoff))[0..cmd.nsyms]; | |
| 102 | try self.symtab.ensureUnusedCapacity(gpa, symtab.len); | |
| 103 | for (symtab) |nlist| { | |
| 104 | self.symtab.appendAssumeCapacity(.{ | |
| 105 | .nlist = nlist, | |
| 106 | .atom = 0, | |
| 107 | .size = 0, | |
| 108 | }); | |
| 109 | } | |
| 97 | const lc_buffer = try gpa.alloc(u8, self.header.?.sizeofcmds); | |
| 98 | defer gpa.free(lc_buffer); | |
| 99 | { | |
| 100 | const amt = try self.file.preadAll(lc_buffer, offset + @sizeOf(macho.mach_header_64)); | |
| 101 | if (amt != self.header.?.sizeofcmds) return error.InputOutput; | |
| 110 | 102 | } |
| 111 | 103 | |
| 104 | var it = LoadCommandIterator{ | |
| 105 | .ncmds = self.header.?.ncmds, | |
| 106 | .buffer = lc_buffer, | |
| 107 | }; | |
| 108 | while (it.next()) |lc| switch (lc.cmd()) { | |
| 109 | .SEGMENT_64 => { | |
| 110 | const sections = lc.getSections(); | |
| 111 | try self.sections.ensureUnusedCapacity(gpa, sections.len); | |
| 112 | for (sections) |sect| { | |
| 113 | const index = try self.sections.addOne(gpa); | |
| 114 | self.sections.set(index, .{ .header = sect }); | |
| 115 | ||
| 116 | if (mem.eql(u8, sect.sectName(), "__eh_frame")) { | |
| 117 | self.eh_frame_sect_index = @intCast(index); | |
| 118 | } else if (mem.eql(u8, sect.sectName(), "__compact_unwind")) { | |
| 119 | self.compact_unwind_sect_index = @intCast(index); | |
| 120 | } | |
| 121 | } | |
| 122 | }, | |
| 123 | .SYMTAB => { | |
| 124 | const cmd = lc.cast(macho.symtab_command).?; | |
| 125 | try self.strtab.resize(gpa, cmd.strsize); | |
| 126 | { | |
| 127 | const amt = try self.file.preadAll(self.strtab.items, cmd.stroff + offset); | |
| 128 | if (amt != self.strtab.items.len) return error.InputOutput; | |
| 129 | } | |
| 130 | ||
| 131 | const symtab_buffer = try gpa.alloc(u8, cmd.nsyms * @sizeOf(macho.nlist_64)); | |
| 132 | defer gpa.free(symtab_buffer); | |
| 133 | { | |
| 134 | const amt = try self.file.preadAll(symtab_buffer, cmd.symoff + offset); | |
| 135 | if (amt != symtab_buffer.len) return error.InputOutput; | |
| 136 | } | |
| 137 | const symtab = @as([*]align(1) const macho.nlist_64, @ptrCast(symtab_buffer.ptr))[0..cmd.nsyms]; | |
| 138 | try self.symtab.ensureUnusedCapacity(gpa, symtab.len); | |
| 139 | for (symtab) |nlist| { | |
| 140 | self.symtab.appendAssumeCapacity(.{ | |
| 141 | .nlist = nlist, | |
| 142 | .atom = 0, | |
| 143 | .size = 0, | |
| 144 | }); | |
| 145 | } | |
| 146 | }, | |
| 147 | .DATA_IN_CODE => { | |
| 148 | const cmd = lc.cast(macho.linkedit_data_command).?; | |
| 149 | const buffer = try gpa.alloc(u8, cmd.datasize); | |
| 150 | defer gpa.free(buffer); | |
| 151 | { | |
| 152 | const amt = try self.file.preadAll(buffer, offset + cmd.dataoff); | |
| 153 | if (amt != buffer.len) return error.InputOutput; | |
| 154 | } | |
| 155 | const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry)); | |
| 156 | const dice = @as([*]align(1) const macho.data_in_code_entry, @ptrCast(buffer.ptr))[0..ndice]; | |
| 157 | try self.data_in_code.appendUnalignedSlice(gpa, dice); | |
| 158 | }, | |
| 159 | .BUILD_VERSION, | |
| 160 | .VERSION_MIN_MACOSX, | |
| 161 | .VERSION_MIN_IPHONEOS, | |
| 162 | .VERSION_MIN_TVOS, | |
| 163 | .VERSION_MIN_WATCHOS, | |
| 164 | => if (self.platform == null) { | |
| 165 | self.platform = MachO.Platform.fromLoadCommand(lc); | |
| 166 | }, | |
| 167 | else => {}, | |
| 168 | }; | |
| 169 | ||
| 112 | 170 | const NlistIdx = struct { |
| 113 | 171 | nlist: macho.nlist_64, |
| 114 | 172 | idx: usize, |
| ... | ... | @@ -170,8 +228,6 @@ pub fn parse(self: *Object, macho_file: *MachO) !void { |
| 170 | 228 | try self.parseUnwindRecords(macho_file); |
| 171 | 229 | } |
| 172 | 230 | |
| 173 | self.initPlatform(); | |
| 174 | ||
| 175 | 231 | if (self.platform) |platform| { |
| 176 | 232 | if (!macho_file.platform.eqlTarget(platform)) { |
| 177 | 233 | try macho_file.reportParseError2(self.index, "invalid platform: {}", .{ |
| ... | ... | @@ -237,7 +293,7 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void { |
| 237 | 293 | defer gpa.free(name); |
| 238 | 294 | const size = if (nlist_start == nlist_end) sect.size else nlists[nlist_start].nlist.n_value - sect.addr; |
| 239 | 295 | const atom_index = try self.addAtom(.{ |
| 240 | .name = name, | |
| 296 | .name = try self.addString(gpa, name), | |
| 241 | 297 | .n_sect = @intCast(n_sect), |
| 242 | 298 | .off = 0, |
| 243 | 299 | .size = size, |
| ... | ... | @@ -267,7 +323,7 @@ fn initSubsections(self: *Object, nlists: anytype, macho_file: *MachO) !void { |
| 267 | 323 | else |
| 268 | 324 | sect.@"align"; |
| 269 | 325 | const atom_index = try self.addAtom(.{ |
| 270 | .name = self.getString(nlist.nlist.n_strx), | |
| 326 | .name = nlist.nlist.n_strx, | |
| 271 | 327 | .n_sect = @intCast(n_sect), |
| 272 | 328 | .off = nlist.nlist.n_value - sect.addr, |
| 273 | 329 | .size = size, |
| ... | ... | @@ -300,7 +356,7 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void { |
| 300 | 356 | defer gpa.free(name); |
| 301 | 357 | |
| 302 | 358 | const atom_index = try self.addAtom(.{ |
| 303 | .name = name, | |
| 359 | .name = try self.addString(gpa, name), | |
| 304 | 360 | .n_sect = @intCast(n_sect), |
| 305 | 361 | .off = 0, |
| 306 | 362 | .size = sect.size, |
| ... | ... | @@ -336,7 +392,7 @@ fn initSections(self: *Object, nlists: anytype, macho_file: *MachO) !void { |
| 336 | 392 | } |
| 337 | 393 | |
| 338 | 394 | const AddAtomArgs = struct { |
| 339 | name: [:0]const u8, | |
| 395 | name: u32, | |
| 340 | 396 | n_sect: u8, |
| 341 | 397 | off: u64, |
| 342 | 398 | size: u64, |
| ... | ... | @@ -349,7 +405,7 @@ fn addAtom(self: *Object, args: AddAtomArgs, macho_file: *MachO) !Atom.Index { |
| 349 | 405 | const atom = macho_file.getAtom(atom_index).?; |
| 350 | 406 | atom.file = self.index; |
| 351 | 407 | atom.atom_index = atom_index; |
| 352 | atom.name = try macho_file.strings.insert(gpa, args.name); | |
| 408 | atom.name = args.name; | |
| 353 | 409 | atom.n_sect = args.n_sect; |
| 354 | 410 | atom.size = args.size; |
| 355 | 411 | atom.alignment = Atom.Alignment.fromLog2Units(args.alignment); |
| ... | ... | @@ -376,7 +432,7 @@ fn initLiteralSections(self: *Object, macho_file: *MachO) !void { |
| 376 | 432 | defer gpa.free(name); |
| 377 | 433 | |
| 378 | 434 | const atom_index = try self.addAtom(.{ |
| 379 | .name = name, | |
| 435 | .name = try self.addString(gpa, name), | |
| 380 | 436 | .n_sect = @intCast(n_sect), |
| 381 | 437 | .off = 0, |
| 382 | 438 | .size = sect.size, |
| ... | ... | @@ -475,10 +531,9 @@ fn initSymbols(self: *Object, macho_file: *MachO) !void { |
| 475 | 531 | const index = try macho_file.addSymbol(); |
| 476 | 532 | self.symbols.appendAssumeCapacity(index); |
| 477 | 533 | const symbol = macho_file.getSymbol(index); |
| 478 | const name = self.getString(nlist.n_strx); | |
| 479 | 534 | symbol.* = .{ |
| 480 | 535 | .value = nlist.n_value, |
| 481 | .name = try macho_file.strings.insert(gpa, name), | |
| 536 | .name = nlist.n_strx, | |
| 482 | 537 | .nlist_idx = @intCast(i), |
| 483 | 538 | .atom = 0, |
| 484 | 539 | .file = self.index, |
| ... | ... | @@ -638,7 +693,10 @@ fn initEhFrameRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { |
| 638 | 693 | const sect = slice.items(.header)[sect_id]; |
| 639 | 694 | const relocs = slice.items(.relocs)[sect_id]; |
| 640 | 695 | |
| 641 | const data = try self.getSectionData(sect_id); | |
| 696 | // TODO: read into buffer directly | |
| 697 | const data = try self.getSectionData(gpa, sect_id); | |
| 698 | defer gpa.free(data); | |
| 699 | ||
| 642 | 700 | try self.eh_frame_data.ensureTotalCapacityPrecise(gpa, data.len); |
| 643 | 701 | self.eh_frame_data.appendSliceAssumeCapacity(data); |
| 644 | 702 | |
| ... | ... | @@ -739,7 +797,8 @@ fn initUnwindRecords(self: *Object, sect_id: u8, macho_file: *MachO) !void { |
| 739 | 797 | }; |
| 740 | 798 | |
| 741 | 799 | const gpa = macho_file.base.comp.gpa; |
| 742 | const data = try self.getSectionData(sect_id); | |
| 800 | const data = try self.getSectionData(gpa, sect_id); | |
| 801 | defer gpa.free(data); | |
| 743 | 802 | const nrecs = @divExact(data.len, @sizeOf(macho.compact_unwind_entry)); |
| 744 | 803 | const recs = @as([*]align(1) const macho.compact_unwind_entry, @ptrCast(data.ptr))[0..nrecs]; |
| 745 | 804 | const sym_lookup = SymbolLookup{ .ctx = self }; |
| ... | ... | @@ -934,24 +993,6 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void { |
| 934 | 993 | } |
| 935 | 994 | } |
| 936 | 995 | |
| 937 | fn initPlatform(self: *Object) void { | |
| 938 | var it = LoadCommandIterator{ | |
| 939 | .ncmds = self.header.?.ncmds, | |
| 940 | .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds], | |
| 941 | }; | |
| 942 | self.platform = while (it.next()) |cmd| { | |
| 943 | switch (cmd.cmd()) { | |
| 944 | .BUILD_VERSION, | |
| 945 | .VERSION_MIN_MACOSX, | |
| 946 | .VERSION_MIN_IPHONEOS, | |
| 947 | .VERSION_MIN_TVOS, | |
| 948 | .VERSION_MIN_WATCHOS, | |
| 949 | => break MachO.Platform.fromLoadCommand(cmd), | |
| 950 | else => {}, | |
| 951 | } | |
| 952 | } else null; | |
| 953 | } | |
| 954 | ||
| 955 | 996 | /// Currently, we only check if a compile unit for this input object file exists |
| 956 | 997 | /// and record that so that we can emit symbol stabs. |
| 957 | 998 | /// TODO in the future, we want parse debug info and debug line sections so that |
| ... | ... | @@ -975,12 +1016,20 @@ fn initDwarfInfo(self: *Object, macho_file: *MachO) !void { |
| 975 | 1016 | |
| 976 | 1017 | if (debug_info_index == null or debug_abbrev_index == null) return; |
| 977 | 1018 | |
| 978 | var dwarf_info = DwarfInfo{ | |
| 979 | .debug_info = try self.getSectionData(@intCast(debug_info_index.?)), | |
| 980 | .debug_abbrev = try self.getSectionData(@intCast(debug_abbrev_index.?)), | |
| 981 | .debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index)) else "", | |
| 982 | }; | |
| 983 | dwarf_info.init(gpa) catch { | |
| 1019 | const debug_info = try self.getSectionData(gpa, @intCast(debug_info_index.?)); | |
| 1020 | defer gpa.free(debug_info); | |
| 1021 | const debug_abbrev = try self.getSectionData(gpa, @intCast(debug_abbrev_index.?)); | |
| 1022 | defer gpa.free(debug_abbrev); | |
| 1023 | const debug_str = if (debug_str_index) |index| try self.getSectionData(gpa, @intCast(index)) else &[0]u8{}; | |
| 1024 | defer gpa.free(debug_str); | |
| 1025 | ||
| 1026 | var dwarf_info = DwarfInfo{}; | |
| 1027 | errdefer dwarf_info.deinit(gpa); | |
| 1028 | dwarf_info.init(gpa, .{ | |
| 1029 | .debug_info = debug_info, | |
| 1030 | .debug_abbrev = debug_abbrev, | |
| 1031 | .debug_str = debug_str, | |
| 1032 | }) catch { | |
| 984 | 1033 | try macho_file.reportParseError2(self.index, "invalid __DWARF info found", .{}); |
| 985 | 1034 | return error.MalformedObject; |
| 986 | 1035 | }; |
| ... | ... | @@ -1049,8 +1098,10 @@ pub fn resetGlobals(self: *Object, macho_file: *MachO) void { |
| 1049 | 1098 | if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue; |
| 1050 | 1099 | const sym = macho_file.getSymbol(sym_index); |
| 1051 | 1100 | const name = sym.name; |
| 1101 | const global = sym.flags.global; | |
| 1052 | 1102 | sym.* = .{}; |
| 1053 | 1103 | sym.name = name; |
| 1104 | sym.flags.global = global; | |
| 1054 | 1105 | } |
| 1055 | 1106 | } |
| 1056 | 1107 | |
| ... | ... | @@ -1137,7 +1188,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void { |
| 1137 | 1188 | defer gpa.free(name); |
| 1138 | 1189 | const atom = macho_file.getAtom(atom_index).?; |
| 1139 | 1190 | atom.atom_index = atom_index; |
| 1140 | atom.name = try macho_file.strings.insert(gpa, name); | |
| 1191 | atom.name = try self.addString(gpa, name); | |
| 1141 | 1192 | atom.file = self.index; |
| 1142 | 1193 | atom.size = nlist.n_value; |
| 1143 | 1194 | atom.alignment = Atom.Alignment.fromLog2Units((nlist.n_desc >> 8) & 0x0f); |
| ... | ... | @@ -1151,6 +1202,7 @@ pub fn convertTentativeDefinitions(self: *Object, macho_file: *MachO) !void { |
| 1151 | 1202 | |
| 1152 | 1203 | sym.value = 0; |
| 1153 | 1204 | sym.atom = atom_index; |
| 1205 | sym.flags.global = true; | |
| 1154 | 1206 | sym.flags.weak = false; |
| 1155 | 1207 | sym.flags.weak_ref = false; |
| 1156 | 1208 | sym.flags.tentative = false; |
| ... | ... | @@ -1219,8 +1271,8 @@ pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void { |
| 1219 | 1271 | self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir |
| 1220 | 1272 | self.output_symtab_ctx.strsize += @as(u32, @intCast(tu_name.len + 1)); // tu_name |
| 1221 | 1273 | |
| 1222 | if (self.archive) |path| { | |
| 1223 | self.output_symtab_ctx.strsize += @as(u32, @intCast(path.len + 1 + self.path.len + 1 + 1)); | |
| 1274 | if (self.archive) |ar| { | |
| 1275 | self.output_symtab_ctx.strsize += @as(u32, @intCast(ar.path.len + 1 + self.path.len + 1 + 1)); | |
| 1224 | 1276 | } else { |
| 1225 | 1277 | self.output_symtab_ctx.strsize += @as(u32, @intCast(self.path.len + 1)); |
| 1226 | 1278 | } |
| ... | ... | @@ -1365,8 +1417,8 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO) error{Overflow}!void |
| 1365 | 1417 | index += 1; |
| 1366 | 1418 | // N_OSO path |
| 1367 | 1419 | n_strx = @as(u32, @intCast(macho_file.strtab.items.len)); |
| 1368 | if (self.archive) |path| { | |
| 1369 | macho_file.strtab.appendSliceAssumeCapacity(path); | |
| 1420 | if (self.archive) |ar| { | |
| 1421 | macho_file.strtab.appendSliceAssumeCapacity(ar.path); | |
| 1370 | 1422 | macho_file.strtab.appendAssumeCapacity('('); |
| 1371 | 1423 | macho_file.strtab.appendSliceAssumeCapacity(self.path); |
| 1372 | 1424 | macho_file.strtab.appendAssumeCapacity(')'); |
| ... | ... | @@ -1532,30 +1584,26 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO) error{Overflow}!void |
| 1532 | 1584 | } |
| 1533 | 1585 | } |
| 1534 | 1586 | |
| 1535 | fn getLoadCommand(self: Object, lc: macho.LC) ?LoadCommandIterator.LoadCommand { | |
| 1536 | var it = LoadCommandIterator{ | |
| 1537 | .ncmds = self.header.?.ncmds, | |
| 1538 | .buffer = self.data[@sizeOf(macho.mach_header_64)..][0..self.header.?.sizeofcmds], | |
| 1539 | }; | |
| 1540 | while (it.next()) |cmd| { | |
| 1541 | if (cmd.cmd() == lc) return cmd; | |
| 1542 | } else return null; | |
| 1543 | } | |
| 1544 | ||
| 1545 | pub fn getSectionData(self: *const Object, index: u32) error{Overflow}![]const u8 { | |
| 1587 | fn getSectionData(self: *const Object, allocator: Allocator, index: u32) ![]u8 { | |
| 1546 | 1588 | const slice = self.sections.slice(); |
| 1547 | 1589 | assert(index < slice.items(.header).len); |
| 1548 | 1590 | const sect = slice.items(.header)[index]; |
| 1549 | const off = math.cast(usize, sect.offset) orelse return error.Overflow; | |
| 1591 | const offset = if (self.archive) |ar| ar.offset else 0; | |
| 1550 | 1592 | const size = math.cast(usize, sect.size) orelse return error.Overflow; |
| 1551 | return self.data[off..][0..size]; | |
| 1593 | const buffer = try allocator.alloc(u8, size); | |
| 1594 | errdefer allocator.free(buffer); | |
| 1595 | const amt = try self.file.preadAll(buffer, sect.offset + offset); | |
| 1596 | if (amt != buffer.len) return error.InputOutput; | |
| 1597 | return buffer; | |
| 1552 | 1598 | } |
| 1553 | 1599 | |
| 1554 | pub fn getAtomData(self: *const Object, atom: Atom) error{Overflow}![]const u8 { | |
| 1555 | const data = try self.getSectionData(atom.n_sect); | |
| 1556 | const off = math.cast(usize, atom.off) orelse return error.Overflow; | |
| 1557 | const size = math.cast(usize, atom.size) orelse return error.Overflow; | |
| 1558 | return data[off..][0..size]; | |
| 1600 | pub fn getAtomData(self: *const Object, atom: Atom, buffer: []u8) !void { | |
| 1601 | assert(buffer.len == atom.size); | |
| 1602 | const slice = self.sections.slice(); | |
| 1603 | const offset = if (self.archive) |ar| ar.offset else 0; | |
| 1604 | const sect = slice.items(.header)[atom.n_sect]; | |
| 1605 | const amt = try self.file.preadAll(buffer, sect.offset + offset + atom.off); | |
| 1606 | if (amt != buffer.len) return error.InputOutput; | |
| 1559 | 1607 | } |
| 1560 | 1608 | |
| 1561 | 1609 | pub fn getAtomRelocs(self: *const Object, atom: Atom) []const Relocation { |
| ... | ... | @@ -1563,9 +1611,17 @@ pub fn getAtomRelocs(self: *const Object, atom: Atom) []const Relocation { |
| 1563 | 1611 | return relocs.items[atom.relocs.pos..][0..atom.relocs.len]; |
| 1564 | 1612 | } |
| 1565 | 1613 | |
| 1566 | fn getString(self: Object, off: u32) [:0]const u8 { | |
| 1567 | assert(off < self.strtab.len); | |
| 1568 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.ptr + off)), 0); | |
| 1614 | fn addString(self: *Object, allocator: Allocator, name: [:0]const u8) error{OutOfMemory}!u32 { | |
| 1615 | const off: u32 = @intCast(self.strtab.items.len); | |
| 1616 | try self.strtab.ensureUnusedCapacity(allocator, name.len + 1); | |
| 1617 | self.strtab.appendSliceAssumeCapacity(name); | |
| 1618 | self.strtab.appendAssumeCapacity(0); | |
| 1619 | return off; | |
| 1620 | } | |
| 1621 | ||
| 1622 | pub fn getString(self: Object, off: u32) [:0]const u8 { | |
| 1623 | assert(off < self.strtab.items.len); | |
| 1624 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); | |
| 1569 | 1625 | } |
| 1570 | 1626 | |
| 1571 | 1627 | pub fn hasUnwindRecords(self: Object) bool { |
| ... | ... | @@ -1600,15 +1656,8 @@ pub fn hasObjc(self: Object) bool { |
| 1600 | 1656 | return false; |
| 1601 | 1657 | } |
| 1602 | 1658 | |
| 1603 | pub fn getDataInCode(self: Object) []align(1) const macho.data_in_code_entry { | |
| 1604 | const lc = self.getLoadCommand(.DATA_IN_CODE) orelse return &[0]macho.data_in_code_entry{}; | |
| 1605 | const cmd = lc.cast(macho.linkedit_data_command).?; | |
| 1606 | const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry)); | |
| 1607 | const dice = @as( | |
| 1608 | [*]align(1) const macho.data_in_code_entry, | |
| 1609 | @ptrCast(self.data.ptr + cmd.dataoff), | |
| 1610 | )[0..ndice]; | |
| 1611 | return dice; | |
| 1659 | pub fn getDataInCode(self: Object) []const macho.data_in_code_entry { | |
| 1660 | return self.data_in_code.items; | |
| 1612 | 1661 | } |
| 1613 | 1662 | |
| 1614 | 1663 | pub inline fn hasSubsections(self: Object) bool { |
| ... | ... | @@ -1762,8 +1811,8 @@ fn formatPath( |
| 1762 | 1811 | ) !void { |
| 1763 | 1812 | _ = unused_fmt_string; |
| 1764 | 1813 | _ = options; |
| 1765 | if (object.archive) |path| { | |
| 1766 | try writer.writeAll(path); | |
| 1814 | if (object.archive) |ar| { | |
| 1815 | try writer.writeAll(ar.path); | |
| 1767 | 1816 | try writer.writeByte('('); |
| 1768 | 1817 | try writer.writeAll(object.path); |
| 1769 | 1818 | try writer.writeByte(')'); |
| ... | ... | @@ -1831,11 +1880,17 @@ const x86_64 = struct { |
| 1831 | 1880 | ) !void { |
| 1832 | 1881 | const gpa = macho_file.base.comp.gpa; |
| 1833 | 1882 | |
| 1834 | const relocs = @as( | |
| 1835 | [*]align(1) const macho.relocation_info, | |
| 1836 | @ptrCast(self.data.ptr + sect.reloff), | |
| 1837 | )[0..sect.nreloc]; | |
| 1838 | const code = try self.getSectionData(@intCast(n_sect)); | |
| 1883 | const offset = if (self.archive) |ar| ar.offset else 0; | |
| 1884 | const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); | |
| 1885 | defer gpa.free(relocs_buffer); | |
| 1886 | { | |
| 1887 | const amt = try self.file.preadAll(relocs_buffer, sect.reloff + offset); | |
| 1888 | if (amt != relocs_buffer.len) return error.InputOutput; | |
| 1889 | } | |
| 1890 | const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; | |
| 1891 | ||
| 1892 | const code = try self.getSectionData(gpa, @intCast(n_sect)); | |
| 1893 | defer gpa.free(code); | |
| 1839 | 1894 | |
| 1840 | 1895 | try out.ensureTotalCapacityPrecise(gpa, relocs.len); |
| 1841 | 1896 | |
| ... | ... | @@ -1987,11 +2042,17 @@ const aarch64 = struct { |
| 1987 | 2042 | ) !void { |
| 1988 | 2043 | const gpa = macho_file.base.comp.gpa; |
| 1989 | 2044 | |
| 1990 | const relocs = @as( | |
| 1991 | [*]align(1) const macho.relocation_info, | |
| 1992 | @ptrCast(self.data.ptr + sect.reloff), | |
| 1993 | )[0..sect.nreloc]; | |
| 1994 | const code = try self.getSectionData(@intCast(n_sect)); | |
| 2045 | const offset = if (self.archive) |ar| ar.offset else 0; | |
| 2046 | const relocs_buffer = try gpa.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); | |
| 2047 | defer gpa.free(relocs_buffer); | |
| 2048 | { | |
| 2049 | const amt = try self.file.preadAll(relocs_buffer, sect.reloff + offset); | |
| 2050 | if (amt != relocs_buffer.len) return error.InputOutput; | |
| 2051 | } | |
| 2052 | const relocs = @as([*]align(1) const macho.relocation_info, @ptrCast(relocs_buffer.ptr))[0..sect.nreloc]; | |
| 2053 | ||
| 2054 | const code = try self.getSectionData(gpa, @intCast(n_sect)); | |
| 2055 | defer gpa.free(code); | |
| 1995 | 2056 | |
| 1996 | 2057 | try out.ensureTotalCapacityPrecise(gpa, relocs.len); |
| 1997 | 2058 |
src/link/MachO/Symbol.zig+11-1| ... | ... | @@ -55,7 +55,12 @@ pub fn weakRef(symbol: Symbol, macho_file: *MachO) bool { |
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | pub fn getName(symbol: Symbol, macho_file: *MachO) [:0]const u8 { |
| 58 | return macho_file.strings.getAssumeExists(symbol.name); | |
| 58 | if (symbol.flags.global) return macho_file.strings.getAssumeExists(symbol.name); | |
| 59 | return switch (symbol.getFile(macho_file).?) { | |
| 60 | .dylib => unreachable, // There are no local symbols for dylibs | |
| 61 | .zig_object => |x| x.strtab.getAssumeExists(symbol.name), | |
| 62 | inline else => |x| x.getString(symbol.name), | |
| 63 | }; | |
| 59 | 64 | } |
| 60 | 65 | |
| 61 | 66 | pub fn getAtom(symbol: Symbol, macho_file: *MachO) ?*Atom { |
| ... | ... | @@ -341,6 +346,11 @@ pub const Flags = packed struct { |
| 341 | 346 | /// Whether the symbol is exported at runtime. |
| 342 | 347 | @"export": bool = false, |
| 343 | 348 | |
| 349 | /// Whether the symbol is effectively an extern and takes part in global | |
| 350 | /// symbol resolution. Then, its name will be saved in global string interning | |
| 351 | /// table. | |
| 352 | global: bool = false, | |
| 353 | ||
| 344 | 354 | /// Whether this symbol is weak. |
| 345 | 355 | weak: bool = false, |
| 346 | 356 |
src/link/MachO/ZigObject.zig+23-30| ... | ... | @@ -3,6 +3,7 @@ path: []const u8, |
| 3 | 3 | index: File.Index, |
| 4 | 4 | |
| 5 | 5 | symtab: std.MultiArrayList(Nlist) = .{}, |
| 6 | strtab: StringTable = .{}, | |
| 6 | 7 | |
| 7 | 8 | symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 8 | 9 | atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, |
| ... | ... | @@ -52,10 +53,12 @@ pub fn init(self: *ZigObject, macho_file: *MachO) !void { |
| 52 | 53 | const gpa = comp.gpa; |
| 53 | 54 | |
| 54 | 55 | try self.atoms.append(gpa, 0); // null input section |
| 56 | try self.strtab.buffer.append(gpa, 0); | |
| 55 | 57 | } |
| 56 | 58 | |
| 57 | 59 | pub fn deinit(self: *ZigObject, allocator: Allocator) void { |
| 58 | 60 | self.symtab.deinit(allocator); |
| 61 | self.strtab.deinit(allocator); | |
| 59 | 62 | self.symbols.deinit(allocator); |
| 60 | 63 | self.atoms.deinit(allocator); |
| 61 | 64 | self.globals_lookup.deinit(allocator); |
| ... | ... | @@ -136,37 +139,24 @@ pub fn addAtom(self: *ZigObject, macho_file: *MachO) !Symbol.Index { |
| 136 | 139 | return symbol_index; |
| 137 | 140 | } |
| 138 | 141 | |
| 139 | /// Caller owns the memory. | |
| 140 | pub fn getAtomDataAlloc( | |
| 141 | self: ZigObject, | |
| 142 | macho_file: *MachO, | |
| 143 | allocator: Allocator, | |
| 144 | atom: Atom, | |
| 145 | ) ![]u8 { | |
| 142 | pub fn getAtomData(self: ZigObject, macho_file: *MachO, atom: Atom, buffer: []u8) !void { | |
| 146 | 143 | assert(atom.file == self.index); |
| 144 | assert(atom.size == buffer.len); | |
| 147 | 145 | const sect = macho_file.sections.items(.header)[atom.out_n_sect]; |
| 148 | 146 | assert(!sect.isZerofill()); |
| 149 | 147 | |
| 150 | 148 | switch (sect.type()) { |
| 151 | 149 | macho.S_THREAD_LOCAL_REGULAR => { |
| 152 | 150 | const tlv = self.tlv_initializers.get(atom.atom_index).?; |
| 153 | const data = try allocator.dupe(u8, tlv.data); | |
| 154 | return data; | |
| 151 | @memcpy(buffer, tlv.data); | |
| 155 | 152 | }, |
| 156 | 153 | macho.S_THREAD_LOCAL_VARIABLES => { |
| 157 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | |
| 158 | const data = try allocator.alloc(u8, size); | |
| 159 | @memset(data, 0); | |
| 160 | return data; | |
| 154 | @memset(buffer, 0); | |
| 161 | 155 | }, |
| 162 | 156 | else => { |
| 163 | 157 | const file_offset = sect.offset + atom.value - sect.addr; |
| 164 | const size = std.math.cast(usize, atom.size) orelse return error.Overflow; | |
| 165 | const data = try allocator.alloc(u8, size); | |
| 166 | errdefer allocator.free(data); | |
| 167 | const amt = try macho_file.base.file.?.preadAll(data, file_offset); | |
| 168 | if (amt != data.len) return error.InputOutput; | |
| 169 | return data; | |
| 158 | const amt = try macho_file.base.file.?.preadAll(buffer, file_offset); | |
| 159 | if (amt != buffer.len) return error.InputOutput; | |
| 170 | 160 | }, |
| 171 | 161 | } |
| 172 | 162 | } |
| ... | ... | @@ -242,8 +232,10 @@ pub fn resetGlobals(self: *ZigObject, macho_file: *MachO) void { |
| 242 | 232 | if (!self.symtab.items(.nlist)[nlist_idx].ext()) continue; |
| 243 | 233 | const sym = macho_file.getSymbol(sym_index); |
| 244 | 234 | const name = sym.name; |
| 235 | const global = sym.flags.global; | |
| 245 | 236 | sym.* = .{}; |
| 246 | 237 | sym.name = name; |
| 238 | sym.flags.global = global; | |
| 247 | 239 | } |
| 248 | 240 | } |
| 249 | 241 | |
| ... | ... | @@ -686,7 +678,7 @@ fn updateDeclCode( |
| 686 | 678 | sym.out_n_sect = sect_index; |
| 687 | 679 | atom.out_n_sect = sect_index; |
| 688 | 680 | |
| 689 | sym.name = try macho_file.strings.insert(gpa, decl_name); | |
| 681 | sym.name = try self.strtab.insert(gpa, decl_name); | |
| 690 | 682 | atom.flags.alive = true; |
| 691 | 683 | atom.name = sym.name; |
| 692 | 684 | nlist.n_strx = sym.name; |
| ... | ... | @@ -796,7 +788,7 @@ fn createTlvInitializer( |
| 796 | 788 | atom.out_n_sect = sect_index; |
| 797 | 789 | |
| 798 | 790 | sym.value = 0; |
| 799 | sym.name = try macho_file.strings.insert(gpa, sym_name); | |
| 791 | sym.name = try self.strtab.insert(gpa, sym_name); | |
| 800 | 792 | atom.flags.alive = true; |
| 801 | 793 | atom.name = sym.name; |
| 802 | 794 | nlist.n_strx = sym.name; |
| ... | ... | @@ -849,7 +841,7 @@ fn createTlvDescriptor( |
| 849 | 841 | atom.out_n_sect = sect_index; |
| 850 | 842 | |
| 851 | 843 | sym.value = 0; |
| 852 | sym.name = try macho_file.strings.insert(gpa, name); | |
| 844 | sym.name = try self.strtab.insert(gpa, name); | |
| 853 | 845 | atom.flags.alive = true; |
| 854 | 846 | atom.name = sym.name; |
| 855 | 847 | nlist.n_strx = sym.name; |
| ... | ... | @@ -1019,7 +1011,7 @@ fn lowerConst( |
| 1019 | 1011 | }; |
| 1020 | 1012 | |
| 1021 | 1013 | const sym = macho_file.getSymbol(sym_index); |
| 1022 | const name_str_index = try macho_file.strings.insert(gpa, name); | |
| 1014 | const name_str_index = try self.strtab.insert(gpa, name); | |
| 1023 | 1015 | sym.name = name_str_index; |
| 1024 | 1016 | sym.out_n_sect = output_section_index; |
| 1025 | 1017 | |
| ... | ... | @@ -1110,7 +1102,7 @@ pub fn updateExports( |
| 1110 | 1102 | } |
| 1111 | 1103 | |
| 1112 | 1104 | const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); |
| 1113 | const global_nlist_index = if (metadata.@"export"(self, macho_file, exp_name)) |exp_index| | |
| 1105 | const global_nlist_index = if (metadata.@"export"(self, exp_name)) |exp_index| | |
| 1114 | 1106 | exp_index.* |
| 1115 | 1107 | else blk: { |
| 1116 | 1108 | const global_nlist_index = try self.getGlobalSymbol(macho_file, exp_name, null); |
| ... | ... | @@ -1159,7 +1151,7 @@ fn updateLazySymbol( |
| 1159 | 1151 | lazy_sym.ty.fmt(mod), |
| 1160 | 1152 | }); |
| 1161 | 1153 | defer gpa.free(name); |
| 1162 | break :blk try macho_file.strings.insert(gpa, name); | |
| 1154 | break :blk try self.strtab.insert(gpa, name); | |
| 1163 | 1155 | }; |
| 1164 | 1156 | |
| 1165 | 1157 | const src = if (lazy_sym.ty.getOwnerDeclOrNull(mod)) |owner_decl| |
| ... | ... | @@ -1247,7 +1239,7 @@ pub fn deleteDeclExport( |
| 1247 | 1239 | |
| 1248 | 1240 | const mod = macho_file.base.comp.module.?; |
| 1249 | 1241 | const exp_name = mod.intern_pool.stringToSlice(name); |
| 1250 | const nlist_index = metadata.@"export"(self, macho_file, exp_name) orelse return; | |
| 1242 | const nlist_index = metadata.@"export"(self, exp_name) orelse return; | |
| 1251 | 1243 | |
| 1252 | 1244 | log.debug("deleting export '{s}'", .{exp_name}); |
| 1253 | 1245 | |
| ... | ... | @@ -1268,7 +1260,7 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l |
| 1268 | 1260 | const gpa = macho_file.base.comp.gpa; |
| 1269 | 1261 | const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); |
| 1270 | 1262 | defer gpa.free(sym_name); |
| 1271 | const off = try macho_file.strings.insert(gpa, sym_name); | |
| 1263 | const off = try self.strtab.insert(gpa, sym_name); | |
| 1272 | 1264 | const lookup_gop = try self.globals_lookup.getOrPut(gpa, off); |
| 1273 | 1265 | if (!lookup_gop.found_existing) { |
| 1274 | 1266 | const nlist_index = try self.addNlist(gpa); |
| ... | ... | @@ -1276,7 +1268,8 @@ pub fn getGlobalSymbol(self: *ZigObject, macho_file: *MachO, name: []const u8, l |
| 1276 | 1268 | nlist.n_strx = off; |
| 1277 | 1269 | nlist.n_type = macho.N_EXT; |
| 1278 | 1270 | lookup_gop.value_ptr.* = nlist_index; |
| 1279 | const gop = try macho_file.getOrCreateGlobal(off); | |
| 1271 | const global_name_off = try macho_file.strings.insert(gpa, sym_name); | |
| 1272 | const gop = try macho_file.getOrCreateGlobal(global_name_off); | |
| 1280 | 1273 | try self.symbols.append(gpa, gop.index); |
| 1281 | 1274 | } |
| 1282 | 1275 | return lookup_gop.value_ptr.*; |
| ... | ... | @@ -1406,10 +1399,10 @@ const DeclMetadata = struct { |
| 1406 | 1399 | /// A list of all exports aliases of this Decl. |
| 1407 | 1400 | exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, |
| 1408 | 1401 | |
| 1409 | fn @"export"(m: DeclMetadata, zig_object: *ZigObject, macho_file: *MachO, name: []const u8) ?*u32 { | |
| 1402 | fn @"export"(m: DeclMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 { | |
| 1410 | 1403 | for (m.exports.items) |*exp| { |
| 1411 | 1404 | const nlist = zig_object.symtab.items(.nlist)[exp.*]; |
| 1412 | const exp_name = macho_file.strings.getAssumeExists(nlist.n_strx); | |
| 1405 | const exp_name = zig_object.strtab.getAssumeExists(nlist.n_strx); | |
| 1413 | 1406 | if (mem.eql(u8, name, exp_name)) return exp; |
| 1414 | 1407 | } |
| 1415 | 1408 | return null; |
src/link/MachO/relocatable.zig+1-2| ... | ... | @@ -290,8 +290,7 @@ fn writeAtoms(macho_file: *MachO) !void { |
| 290 | 290 | assert(atom.flags.alive); |
| 291 | 291 | const off = math.cast(usize, atom.value - header.addr) orelse return error.Overflow; |
| 292 | 292 | const atom_size = math.cast(usize, atom.size) orelse return error.Overflow; |
| 293 | const atom_data = try atom.getFile(macho_file).object.getAtomData(atom.*); | |
| 294 | @memcpy(code[off..][0..atom_size], atom_data); | |
| 293 | try atom.getFile(macho_file).object.getAtomData(atom.*, code[off..][0..atom_size]); | |
| 295 | 294 | try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs); |
| 296 | 295 | } |
| 297 | 296 |