diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 1f2e0616ba..78c926f5f1 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2004,21 +2004,21 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void { if (symbolIsStab(sym)) { log.err("unhandled symbol type: stab", .{}); log.err(" symbol '{s}'", .{sym_name}); - log.err(" first definition in '{s}'", .{object.name.?}); + log.err(" first definition in '{s}'", .{object.name}); return error.UnhandledSymbolType; } if (symbolIsIndr(sym)) { log.err("unhandled symbol type: indirect", .{}); log.err(" symbol '{s}'", .{sym_name}); - log.err(" first definition in '{s}'", .{object.name.?}); + log.err(" first definition in '{s}'", .{object.name}); return error.UnhandledSymbolType; } if (symbolIsAbs(sym)) { log.err("unhandled symbol type: absolute", .{}); log.err(" symbol '{s}'", .{sym_name}); - log.err(" first definition in '{s}'", .{object.name.?}); + log.err(" first definition in '{s}'", .{object.name}); return error.UnhandledSymbolType; } @@ -2068,8 +2068,8 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void { !(symbolIsWeakDef(global.*) or symbolIsPext(global.*))) { log.err("symbol '{s}' defined multiple times", .{sym_name}); - log.err(" first definition in '{s}'", .{self.objects.items[resolv.file].name.?}); - log.err(" next definition in '{s}'", .{object.name.?}); + log.err(" first definition in '{s}'", .{self.objects.items[resolv.file].name}); + log.err(" next definition in '{s}'", .{object.name}); return error.MultipleSymbolDefinitions; } @@ -2448,7 +2448,7 @@ fn resolveSymbols(self: *MachO) !void { const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable; log.err("undefined reference to symbol '{s}'", .{sym_name}); - log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name.?}); + log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name}); has_undefined = true; } @@ -2457,7 +2457,7 @@ fn resolveSymbols(self: *MachO) !void { fn parseTextBlocks(self: *MachO) !void { for (self.objects.items) |object| { - try object.parseTextBlocks(self); + try object.parseTextBlocks(self.base.allocator, self); } } @@ -3190,7 +3190,7 @@ fn writeSymbolTable(self: *MachO) !void { .n_value = 0, }); locals.appendAssumeCapacity(.{ - .n_strx = try self.makeString(object.name.?), + .n_strx = try self.makeString(object.name), .n_type = macho.N_OSO, .n_sect = 0, .n_desc = 1, @@ -3334,7 +3334,7 @@ pub fn deinit(self: *MachO) void { self.symbol_resolver.deinit(self.base.allocator); for (self.objects.items) |object| { - object.deinit(); + object.deinit(self.base.allocator); self.base.allocator.destroy(object); } self.objects.deinit(self.base.allocator); @@ -3372,7 +3372,7 @@ pub fn deinit(self: *MachO) void { pub fn closeFiles(self: MachO) void { for (self.objects.items) |object| { - object.closeFile(); + object.file.close(); } for (self.archives.items) |archive| { archive.closeFile(); @@ -5913,7 +5913,7 @@ fn printSymtabAndTextBlock(self: *MachO) void { log.debug("mappings", .{}); for (self.objects.items) |object| { - log.debug(" in object {s}", .{object.name.?}); + log.debug(" in object {s}", .{object.name}); for (object.symtab.items) |sym, sym_id| { if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| { log.debug(" | {d} => {d}", .{ sym_id, local_id }); diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 4004cdaefc..c5fab6a1d7 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -264,14 +264,12 @@ pub fn parseObject(self: Archive, offset: u32) !*Object { errdefer self.allocator.destroy(object); object.* = .{ - .allocator = self.allocator, - .arch = self.arch.?, .file = try fs.cwd().openFile(self.name.?, .{}), .name = name, .file_offset = @intCast(u32, try reader.context.getPos()), .mtime = try self.header.?.date(), }; - try object.parse(); + try object.parse(self.allocator, self.arch.?); try reader.context.seekTo(0); return object; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index fc17669e04..1250f015ed 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -10,20 +10,22 @@ const macho = std.macho; const math = std.math; const mem = std.mem; const sort = std.sort; +const commands = @import("commands.zig"); +const segmentName = commands.segmentName; +const sectionName = commands.sectionName; const Allocator = mem.Allocator; const Arch = std.Target.Cpu.Arch; +const LoadCommand = commands.LoadCommand; const MachO = @import("../MachO.zig"); const TextBlock = @import("TextBlock.zig"); -usingnamespace @import("commands.zig"); +file: fs.File, +name: []const u8, -allocator: *Allocator, -arch: ?Arch = null, -header: ?macho.mach_header_64 = null, -file: ?fs.File = null, file_offset: ?u32 = null, -name: ?[]const u8 = null, + +header: ?macho.mach_header_64 = null, load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, @@ -139,15 +141,13 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u errdefer allocator.free(name); object.* = .{ - .allocator = allocator, - .arch = arch, .name = name, .file = file, }; - object.parse() catch |err| switch (err) { + object.parse(allocator, arch) catch |err| switch (err) { error.EndOfStream, error.NotObject => { - object.deinit(); + object.deinit(allocator); allocator.destroy(object); return null; }, @@ -157,44 +157,35 @@ pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u return object; } -pub fn deinit(self: *Object) void { +pub fn deinit(self: *Object, allocator: *Allocator) void { for (self.load_commands.items) |*lc| { - lc.deinit(self.allocator); + lc.deinit(allocator); } - self.load_commands.deinit(self.allocator); - self.data_in_code_entries.deinit(self.allocator); - self.symtab.deinit(self.allocator); - self.strtab.deinit(self.allocator); - self.text_blocks.deinit(self.allocator); - self.sections_as_symbols.deinit(self.allocator); - self.symbol_mapping.deinit(self.allocator); - self.reverse_symbol_mapping.deinit(self.allocator); + self.load_commands.deinit(allocator); + self.data_in_code_entries.deinit(allocator); + self.symtab.deinit(allocator); + self.strtab.deinit(allocator); + self.text_blocks.deinit(allocator); + self.sections_as_symbols.deinit(allocator); + self.symbol_mapping.deinit(allocator); + self.reverse_symbol_mapping.deinit(allocator); + allocator.free(self.name); if (self.debug_info) |*db| { - db.deinit(self.allocator); + db.deinit(allocator); } if (self.tu_name) |n| { - self.allocator.free(n); + allocator.free(n); } if (self.tu_comp_dir) |n| { - self.allocator.free(n); - } - - if (self.name) |n| { - self.allocator.free(n); + allocator.free(n); } } -pub fn closeFile(self: Object) void { - if (self.file) |f| { - f.close(); - } -} - -pub fn parse(self: *Object) !void { - var reader = self.file.?.reader(); +pub fn parse(self: *Object, allocator: *Allocator, arch: Arch) !void { + var reader = self.file.reader(); if (self.file_offset) |offset| { try reader.context.seekTo(offset); } @@ -214,26 +205,28 @@ pub fn parse(self: *Object) !void { return error.UnsupportedCpuArchitecture; }, }; - if (this_arch != self.arch.?) { - log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch }); + if (this_arch != arch) { + log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch }); return error.MismatchedCpuArchitecture; } self.header = header; - try self.readLoadCommands(reader); - try self.parseSymtab(); - try self.parseDataInCode(); - try self.parseDebugInfo(); + try self.readLoadCommands(allocator, reader); + try self.parseSymtab(allocator); + try self.parseDataInCode(allocator); + try self.parseDebugInfo(allocator); } -pub fn readLoadCommands(self: *Object, reader: anytype) !void { +pub fn readLoadCommands(self: *Object, allocator: *Allocator, reader: anytype) !void { + const header = self.header orelse unreachable; // Unreachable here signifies a fatal unexplored condition. const offset = self.file_offset orelse 0; - try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); + + try self.load_commands.ensureCapacity(allocator, header.ncmds); var i: u16 = 0; - while (i < self.header.?.ncmds) : (i += 1) { - var cmd = try LoadCommand.read(self.allocator, reader); + while (i < header.ncmds) : (i += 1) { + var cmd = try LoadCommand.read(allocator, reader); switch (cmd.cmd()) { macho.LC_SEGMENT_64 => { self.segment_cmd_index = i; @@ -347,26 +340,25 @@ fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) return dices[start..end]; } -const TextBlockParser = struct { +const Context = struct { allocator: *Allocator, + object: *Object, + macho_file: *MachO, + match: MachO.MatchingSection, +}; + +const TextBlockParser = struct { section: macho.section_64, code: []u8, relocs: []macho.relocation_info, - object: *Object, - macho_file: *MachO, nlists: []NlistWithIndex, index: u32 = 0, - match: MachO.MatchingSection, - fn peek(self: *TextBlockParser) ?NlistWithIndex { + fn peek(self: TextBlockParser) ?NlistWithIndex { return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null; } - const SeniorityContext = struct { - object: *Object, - }; - - fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool { + fn lessThanBySeniority(context: Context, lhs: NlistWithIndex, rhs: NlistWithIndex) bool { if (!MachO.symbolIsExt(rhs.nlist)) { return MachO.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx)); } else if (MachO.symbolIsPext(rhs.nlist) or MachO.symbolIsWeakDef(rhs.nlist)) { @@ -376,10 +368,10 @@ const TextBlockParser = struct { } } - pub fn next(self: *TextBlockParser) !?*TextBlock { + pub fn next(self: *TextBlockParser, context: Context) !?*TextBlock { if (self.index == self.nlists.len) return null; - var aliases = std.ArrayList(NlistWithIndex).init(self.allocator); + var aliases = std.ArrayList(NlistWithIndex).init(context.allocator); defer aliases.deinit(); const next_nlist: ?NlistWithIndex = blk: while (true) { @@ -397,7 +389,7 @@ const TextBlockParser = struct { } else null; for (aliases.items) |*nlist_with_index| { - nlist_with_index.index = self.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable; + nlist_with_index.index = context.object.symbol_mapping.get(nlist_with_index.index) orelse unreachable; } if (aliases.items.len > 1) { @@ -405,14 +397,14 @@ const TextBlockParser = struct { sort.sort( NlistWithIndex, aliases.items, - SeniorityContext{ .object = self.object }, + context, TextBlockParser.lessThanBySeniority, ); } const senior_nlist = aliases.pop(); - const senior_sym = &self.macho_file.locals.items[senior_nlist.index]; - senior_sym.n_sect = self.macho_file.section_to_ordinal.get(self.match) orelse unreachable; + const senior_sym = &context.macho_file.locals.items[senior_nlist.index]; + senior_sym.n_sect = context.macho_file.section_to_ordinal.get(context.match) orelse unreachable; const start_addr = senior_nlist.nlist.n_value - self.section.addr; const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size; @@ -426,7 +418,7 @@ const TextBlockParser = struct { else max_align; - const stab: ?TextBlock.Stab = if (self.object.debug_info) |di| blk: { + const stab: ?TextBlock.Stab = if (context.object.debug_info) |di| blk: { // TODO there has to be a better to handle this. for (di.inner.func_list.items) |func| { if (func.pc_range) |range| { @@ -442,35 +434,37 @@ const TextBlockParser = struct { break :blk .static; } else null; - const block = try self.macho_file.base.allocator.create(TextBlock); + const block = try context.allocator.create(TextBlock); block.* = TextBlock.empty; block.local_sym_index = senior_nlist.index; block.stab = stab; block.size = size; block.alignment = actual_align; - try self.macho_file.managed_blocks.append(self.macho_file.base.allocator, block); + try context.macho_file.managed_blocks.append(context.allocator, block); - try block.code.appendSlice(self.macho_file.base.allocator, code); + try block.code.appendSlice(context.allocator, code); - try block.aliases.ensureTotalCapacity(self.macho_file.base.allocator, aliases.items.len); + try block.aliases.ensureTotalCapacity(context.allocator, aliases.items.len); for (aliases.items) |alias| { block.aliases.appendAssumeCapacity(alias.index); - const sym = &self.macho_file.locals.items[alias.index]; - sym.n_sect = self.macho_file.section_to_ordinal.get(self.match) orelse unreachable; + const sym = &context.macho_file.locals.items[alias.index]; + sym.n_sect = context.macho_file.section_to_ordinal.get(context.match) orelse unreachable; } - try block.parseRelocsFromObject(self.macho_file.base.allocator, self.relocs, self.object, .{ + try block.parseRelocs(self.relocs, .{ .base_addr = start_addr, - .macho_file = self.macho_file, + .allocator = context.allocator, + .object = context.object, + .macho_file = context.macho_file, }); - if (self.macho_file.has_dices) { + if (context.macho_file.has_dices) { const dices = filterDice( - self.object.data_in_code_entries.items, + context.object.data_in_code_entries.items, senior_nlist.nlist.n_value, senior_nlist.nlist.n_value + size, ); - try block.dices.ensureTotalCapacity(self.macho_file.base.allocator, dices.len); + try block.dices.ensureTotalCapacity(context.allocator, dices.len); for (dices) |dice| { block.dices.appendAssumeCapacity(.{ @@ -487,16 +481,16 @@ const TextBlockParser = struct { } }; -pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { +pub fn parseTextBlocks(self: *Object, allocator: *Allocator, macho_file: *MachO) !void { const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; - log.debug("analysing {s}", .{self.name.?}); + log.debug("analysing {s}", .{self.name}); // You would expect that the symbol table is at least pre-sorted based on symbol's type: // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance, // the GO compiler does not necessarily respect that therefore we sort immediately by type // and address within. - var sorted_all_nlists = std.ArrayList(NlistWithIndex).init(self.allocator); + var sorted_all_nlists = std.ArrayList(NlistWithIndex).init(allocator); defer sorted_all_nlists.deinit(); try sorted_all_nlists.ensureTotalCapacity(self.symtab.items.len); @@ -540,14 +534,14 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { }; // Read section's code - var code = try self.allocator.alloc(u8, @intCast(usize, sect.size)); - defer self.allocator.free(code); - _ = try self.file.?.preadAll(code, sect.offset); + var code = try allocator.alloc(u8, @intCast(usize, sect.size)); + defer allocator.free(code); + _ = try self.file.preadAll(code, sect.offset); // Read section's list of relocations - var raw_relocs = try self.allocator.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); - defer self.allocator.free(raw_relocs); - _ = try self.file.?.preadAll(raw_relocs, sect.reloff); + var raw_relocs = try allocator.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info)); + defer allocator.free(raw_relocs); + _ = try self.file.preadAll(raw_relocs, sect.reloff); const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs); // Symbols within this section only. @@ -579,46 +573,48 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { // as a temporary symbol and insert the matching TextBlock. const first_nlist = filtered_nlists[0].nlist; if (first_nlist.n_value > sect.addr) { - const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ - self.name.?, + const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{ + self.name, segmentName(sect), sectionName(sect), }); - defer self.allocator.free(sym_name); + defer allocator.free(sym_name); const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { const block_local_sym_index = @intCast(u32, macho_file.locals.items.len); - try macho_file.locals.append(macho_file.base.allocator, .{ + try macho_file.locals.append(allocator, .{ .n_strx = try macho_file.makeString(sym_name), .n_type = macho.N_SECT, .n_sect = macho_file.section_to_ordinal.get(match) orelse unreachable, .n_desc = 0, .n_value = sect.addr, }); - try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, block_local_sym_index); + try self.sections_as_symbols.putNoClobber(allocator, sect_id, block_local_sym_index); break :blk block_local_sym_index; }; const block_code = code[0 .. first_nlist.n_value - sect.addr]; const block_size = block_code.len; - const block = try macho_file.base.allocator.create(TextBlock); + const block = try allocator.create(TextBlock); block.* = TextBlock.empty; block.local_sym_index = block_local_sym_index; block.size = block_size; block.alignment = sect.@"align"; - try macho_file.managed_blocks.append(macho_file.base.allocator, block); + try macho_file.managed_blocks.append(allocator, block); - try block.code.appendSlice(macho_file.base.allocator, block_code); + try block.code.appendSlice(allocator, block_code); - try block.parseRelocsFromObject(self.allocator, relocs, self, .{ + try block.parseRelocs(relocs, .{ .base_addr = 0, + .allocator = allocator, + .object = self, .macho_file = macho_file, }); if (macho_file.has_dices) { const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size); - try block.dices.ensureTotalCapacity(macho_file.base.allocator, dices.len); + try block.dices.ensureTotalCapacity(allocator, dices.len); for (dices) |dice| { block.dices.appendAssumeCapacity(.{ @@ -645,24 +641,25 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { block.prev = last.*; last.* = block; } else { - try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block); + try macho_file.blocks.putNoClobber(allocator, match, block); } - try self.text_blocks.append(self.allocator, block); + try self.text_blocks.append(allocator, block); } var parser = TextBlockParser{ - .allocator = self.allocator, .section = sect, .code = code, .relocs = relocs, - .object = self, - .macho_file = macho_file, .nlists = filtered_nlists, - .match = match, }; - while (try parser.next()) |block| { + while (try parser.next(.{ + .allocator = allocator, + .object = self, + .macho_file = macho_file, + .match = match, + })) |block| { const sym = macho_file.locals.items[block.local_sym_index]; const is_ext = blk: { const orig_sym_id = self.reverse_symbol_mapping.get(block.local_sym_index) orelse unreachable; @@ -675,9 +672,9 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { if (global_object != self) { log.debug("deduping definition of {s} in {s}", .{ macho_file.getString(sym.n_strx), - self.name.?, + self.name, }); - log.debug(" already defined in {s}", .{global_object.name.?}); + log.debug(" already defined in {s}", .{global_object.name}); continue; } } @@ -688,7 +685,7 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { // In x86_64 relocs, it can so happen that the compiler refers to the same // atom by both the actual assigned symbol and the start of the section. In this // case, we need to link the two together so add an alias. - try block.aliases.append(macho_file.base.allocator, alias); + try block.aliases.append(allocator, alias); } } @@ -708,10 +705,10 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { block.prev = last.*; last.* = block; } else { - try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block); + try macho_file.blocks.putNoClobber(allocator, match, block); } - try self.text_blocks.append(self.allocator, block); + try self.text_blocks.append(allocator, block); } break :next; @@ -720,43 +717,45 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { // Since there is no symbol to refer to this block, we create // a temp one, unless we already did that when working out the relocations // of other text blocks. - const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ - self.name.?, + const sym_name = try std.fmt.allocPrint(allocator, "l_{s}_{s}_{s}", .{ + self.name, segmentName(sect), sectionName(sect), }); - defer self.allocator.free(sym_name); + defer allocator.free(sym_name); const block_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: { const block_local_sym_index = @intCast(u32, macho_file.locals.items.len); - try macho_file.locals.append(macho_file.base.allocator, .{ + try macho_file.locals.append(allocator, .{ .n_strx = try macho_file.makeString(sym_name), .n_type = macho.N_SECT, .n_sect = macho_file.section_to_ordinal.get(match) orelse unreachable, .n_desc = 0, .n_value = sect.addr, }); - try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, block_local_sym_index); + try self.sections_as_symbols.putNoClobber(allocator, sect_id, block_local_sym_index); break :blk block_local_sym_index; }; - const block = try macho_file.base.allocator.create(TextBlock); + const block = try allocator.create(TextBlock); block.* = TextBlock.empty; block.local_sym_index = block_local_sym_index; block.size = sect.size; block.alignment = sect.@"align"; - try macho_file.managed_blocks.append(macho_file.base.allocator, block); + try macho_file.managed_blocks.append(allocator, block); - try block.code.appendSlice(macho_file.base.allocator, code); + try block.code.appendSlice(allocator, code); - try block.parseRelocsFromObject(self.allocator, relocs, self, .{ + try block.parseRelocs(relocs, .{ .base_addr = 0, + .allocator = allocator, + .object = self, .macho_file = macho_file, }); if (macho_file.has_dices) { const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size); - try block.dices.ensureTotalCapacity(macho_file.base.allocator, dices.len); + try block.dices.ensureTotalCapacity(allocator, dices.len); for (dices) |dice| { block.dices.appendAssumeCapacity(.{ @@ -772,7 +771,7 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { // the filtered symbols and note which symbol is contained within so that // we can properly allocate addresses down the line. // While we're at it, we need to update segment,section mapping of each symbol too. - try block.contained.ensureTotalCapacity(self.allocator, filtered_nlists.len); + try block.contained.ensureTotalCapacity(allocator, filtered_nlists.len); for (filtered_nlists) |nlist_with_index| { const nlist = nlist_with_index.nlist; @@ -819,35 +818,35 @@ pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void { block.prev = last.*; last.* = block; } else { - try macho_file.blocks.putNoClobber(macho_file.base.allocator, match, block); + try macho_file.blocks.putNoClobber(allocator, match, block); } - try self.text_blocks.append(self.allocator, block); + try self.text_blocks.append(allocator, block); } } } -fn parseSymtab(self: *Object) !void { +fn parseSymtab(self: *Object, allocator: *Allocator) !void { const index = self.symtab_cmd_index orelse return; const symtab_cmd = self.load_commands.items[index].Symtab; - var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); - defer self.allocator.free(symtab); - _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff); + var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); + defer allocator.free(symtab); + _ = try self.file.preadAll(symtab, symtab_cmd.symoff); const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); - try self.symtab.appendSlice(self.allocator, slice); + try self.symtab.appendSlice(allocator, slice); - var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); - defer self.allocator.free(strtab); - _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff); - try self.strtab.appendSlice(self.allocator, strtab); + var strtab = try allocator.alloc(u8, symtab_cmd.strsize); + defer allocator.free(strtab); + _ = try self.file.preadAll(strtab, symtab_cmd.stroff); + try self.strtab.appendSlice(allocator, strtab); } -pub fn parseDebugInfo(self: *Object) !void { - log.debug("parsing debug info in '{s}'", .{self.name.?}); +pub fn parseDebugInfo(self: *Object, allocator: *Allocator) !void { + log.debug("parsing debug info in '{s}'", .{self.name}); var debug_info = blk: { - var di = try DebugInfo.parseFromObject(self.allocator, self); + var di = try DebugInfo.parseFromObject(allocator, self); break :blk di orelse return; }; @@ -855,7 +854,7 @@ pub fn parseDebugInfo(self: *Object) !void { const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) { error.MissingDebugInfo => { // TODO audit cases with missing debug info and audit our dwarf.zig module. - log.debug("invalid or missing debug info in {s}; skipping", .{self.name.?}); + log.debug("invalid or missing debug info in {s}; skipping", .{self.name}); return; }, else => |e| return e, @@ -864,26 +863,25 @@ pub fn parseDebugInfo(self: *Object) !void { const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir); self.debug_info = debug_info; - self.tu_name = try self.allocator.dupe(u8, name); - self.tu_comp_dir = try self.allocator.dupe(u8, comp_dir); + self.tu_name = try allocator.dupe(u8, name); + self.tu_comp_dir = try allocator.dupe(u8, comp_dir); if (self.mtime == null) { self.mtime = mtime: { - const file = self.file orelse break :mtime 0; - const stat = file.stat() catch break :mtime 0; + const stat = self.file.stat() catch break :mtime 0; break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000)); }; } } -pub fn parseDataInCode(self: *Object) !void { +pub fn parseDataInCode(self: *Object, allocator: *Allocator) !void { const index = self.data_in_code_cmd_index orelse return; const data_in_code = self.load_commands.items[index].LinkeditData; - var buffer = try self.allocator.alloc(u8, data_in_code.datasize); - defer self.allocator.free(buffer); + var buffer = try allocator.alloc(u8, data_in_code.datasize); + defer allocator.free(buffer); - _ = try self.file.?.preadAll(buffer, data_in_code.dataoff); + _ = try self.file.preadAll(buffer, data_in_code.dataoff); var stream = io.fixedBufferStream(buffer); var reader = stream.reader(); @@ -892,7 +890,7 @@ pub fn parseDataInCode(self: *Object) !void { error.EndOfStream => break, else => |e| return e, }; - try self.data_in_code_entries.append(self.allocator, dice); + try self.data_in_code_entries.append(allocator, dice); } } @@ -900,7 +898,7 @@ fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; const sect = seg.sections.items[index]; var buffer = try allocator.alloc(u8, @intCast(usize, sect.size)); - _ = try self.file.?.preadAll(buffer, sect.offset); + _ = try self.file.preadAll(buffer, sect.offset); return buffer; } diff --git a/src/link/MachO/TextBlock.zig b/src/link/MachO/TextBlock.zig index 8dca7bc37b..ef10d3fe09 100644 --- a/src/link/MachO/TextBlock.zig +++ b/src/link/MachO/TextBlock.zig @@ -606,12 +606,14 @@ pub fn freeListEligible(self: TextBlock, macho_file: MachO) bool { const RelocContext = struct { base_addr: u64 = 0, + allocator: *Allocator, + object: *Object, macho_file: *MachO, }; -fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocContext) !Relocation { +fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation { var parsed_rel = Relocation{ - .offset = @intCast(u32, @intCast(u64, rel.r_address) - ctx.base_addr), + .offset = @intCast(u32, @intCast(u64, rel.r_address) - context.base_addr), .where = undefined, .where_index = undefined, .payload = undefined, @@ -620,44 +622,44 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo if (rel.r_extern == 0) { const sect_id = @intCast(u16, rel.r_symbolnum - 1); - const local_sym_index = object.sections_as_symbols.get(sect_id) orelse blk: { - const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; + const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: { + const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].Segment; const sect = seg.sections.items[sect_id]; - const match = (try ctx.macho_file.getMatchingSection(sect)) orelse unreachable; - const local_sym_index = @intCast(u32, ctx.macho_file.locals.items.len); - const sym_name = try std.fmt.allocPrint(ctx.macho_file.base.allocator, "l_{s}_{s}_{s}", .{ - object.name.?, + const match = (try context.macho_file.getMatchingSection(sect)) orelse unreachable; + const local_sym_index = @intCast(u32, context.macho_file.locals.items.len); + const sym_name = try std.fmt.allocPrint(context.allocator, "l_{s}_{s}_{s}", .{ + context.object.name, commands.segmentName(sect), commands.sectionName(sect), }); - defer ctx.macho_file.base.allocator.free(sym_name); + defer context.allocator.free(sym_name); - try ctx.macho_file.locals.append(ctx.macho_file.base.allocator, .{ - .n_strx = try ctx.macho_file.makeString(sym_name), + try context.macho_file.locals.append(context.allocator, .{ + .n_strx = try context.macho_file.makeString(sym_name), .n_type = macho.N_SECT, - .n_sect = ctx.macho_file.section_to_ordinal.get(match) orelse unreachable, + .n_sect = context.macho_file.section_to_ordinal.get(match) orelse unreachable, .n_desc = 0, .n_value = sect.addr, }); - try object.sections_as_symbols.putNoClobber(object.allocator, sect_id, local_sym_index); + try context.object.sections_as_symbols.putNoClobber(context.allocator, sect_id, local_sym_index); break :blk local_sym_index; }; parsed_rel.where = .local; parsed_rel.where_index = local_sym_index; } else { - const sym = object.symtab.items[rel.r_symbolnum]; - const sym_name = object.getString(sym.n_strx); + const sym = context.object.symtab.items[rel.r_symbolnum]; + const sym_name = context.object.getString(sym.n_strx); if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) { - const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; + const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; parsed_rel.where = .local; parsed_rel.where_index = where_index; } else { - const n_strx = ctx.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{ - .strtab = &ctx.macho_file.strtab, + const n_strx = context.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{ + .strtab = &context.macho_file.strtab, }) orelse unreachable; - const resolv = ctx.macho_file.symbol_resolver.get(n_strx) orelse unreachable; + const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable; switch (resolv.where) { .global => { parsed_rel.where = .local; @@ -675,29 +677,24 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo return parsed_rel; } -pub fn parseRelocsFromObject( - self: *TextBlock, - allocator: *Allocator, - relocs: []macho.relocation_info, - object: *Object, - ctx: RelocContext, -) !void { - const filtered_relocs = filterRelocs(relocs, ctx.base_addr, ctx.base_addr + self.size); +pub fn parseRelocs(self: *TextBlock, relocs: []macho.relocation_info, context: RelocContext) !void { + const filtered_relocs = filterRelocs(relocs, context.base_addr, context.base_addr + self.size); var it = RelocIterator{ .buffer = filtered_relocs, }; var addend: u32 = 0; var subtractor: ?u32 = null; + const arch = context.macho_file.base.options.target.cpu.arch; while (it.next()) |rel| { - if (isAddend(rel, object.arch.?)) { + if (isAddend(rel, arch)) { // Addend is not a relocation with effect on the TextBlock, so // parse it and carry on. assert(addend == 0); // Oh no, addend was not reset! addend = rel.r_symbolnum; - // Verify ADDEND is followed by a load. + // Verify ADDEND is followed by a PAGE21 or PAGEOFF12. const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type); switch (next) { .ARM64_RELOC_PAGE21, .ARM64_RELOC_PAGEOFF12 => {}, @@ -709,28 +706,28 @@ pub fn parseRelocsFromObject( continue; } - if (isSubtractor(rel, object.arch.?)) { + if (isSubtractor(rel, arch)) { // Subtractor is not a relocation with effect on the TextBlock, so // parse it and carry on. assert(subtractor == null); // Oh no, subtractor was not reset! assert(rel.r_extern == 1); - const sym = object.symtab.items[rel.r_symbolnum]; - const sym_name = object.getString(sym.n_strx); + const sym = context.object.symtab.items[rel.r_symbolnum]; + const sym_name = context.object.getString(sym.n_strx); if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) { - const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; + const where_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; subtractor = where_index; } else { - const n_strx = ctx.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{ - .strtab = &ctx.macho_file.strtab, + const n_strx = context.macho_file.strtab_dir.getAdapted(@as([]const u8, sym_name), MachO.StringSliceAdapter{ + .strtab = &context.macho_file.strtab, }) orelse unreachable; - const resolv = ctx.macho_file.symbol_resolver.get(n_strx) orelse unreachable; + const resolv = context.macho_file.symbol_resolver.get(n_strx) orelse unreachable; assert(resolv.where == .global); subtractor = resolv.local_sym_index; } // Verify SUBTRACTOR is followed by UNSIGNED. - switch (object.arch.?) { + switch (arch) { .aarch64 => { const next = @intToEnum(macho.reloc_type_arm64, it.peek().r_type); if (next != .ARM64_RELOC_UNSIGNED) { @@ -750,19 +747,19 @@ pub fn parseRelocsFromObject( continue; } - var parsed_rel = try initRelocFromObject(rel, object, ctx); + var parsed_rel = try initRelocFromObject(rel, context); - switch (object.arch.?) { + switch (arch) { .aarch64 => { const rel_type = @intToEnum(macho.reloc_type_arm64, rel.r_type); switch (rel_type) { .ARM64_RELOC_ADDEND => unreachable, .ARM64_RELOC_SUBTRACTOR => unreachable, .ARM64_RELOC_BRANCH26 => { - self.parseBranch(rel, &parsed_rel, ctx); + self.parseBranch(rel, &parsed_rel, context); }, .ARM64_RELOC_UNSIGNED => { - self.parseUnsigned(rel, &parsed_rel, subtractor, ctx); + self.parseUnsigned(rel, &parsed_rel, subtractor, context); subtractor = null; }, .ARM64_RELOC_PAGE21, @@ -790,10 +787,10 @@ pub fn parseRelocsFromObject( switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { .X86_64_RELOC_SUBTRACTOR => unreachable, .X86_64_RELOC_BRANCH => { - self.parseBranch(rel, &parsed_rel, ctx); + self.parseBranch(rel, &parsed_rel, context); }, .X86_64_RELOC_UNSIGNED => { - self.parseUnsigned(rel, &parsed_rel, subtractor, ctx); + self.parseUnsigned(rel, &parsed_rel, subtractor, context); subtractor = null; }, .X86_64_RELOC_SIGNED, @@ -801,7 +798,7 @@ pub fn parseRelocsFromObject( .X86_64_RELOC_SIGNED_2, .X86_64_RELOC_SIGNED_4, => { - self.parseSigned(rel, &parsed_rel, ctx); + self.parseSigned(rel, &parsed_rel, context); }, .X86_64_RELOC_GOT_LOAD, .X86_64_RELOC_GOT, @@ -814,7 +811,7 @@ pub fn parseRelocsFromObject( else => unreachable, } - try self.relocs.append(allocator, parsed_rel); + try self.relocs.append(context.allocator, parsed_rel); const is_via_got = switch (parsed_rel.payload) { .pointer_to_got => true, @@ -832,23 +829,23 @@ pub fn parseRelocsFromObject( }, .where_index = parsed_rel.where_index, }; - if (ctx.macho_file.got_entries_map.contains(key)) break :blk; + if (context.macho_file.got_entries_map.contains(key)) break :blk; - const got_index = @intCast(u32, ctx.macho_file.got_entries.items.len); - try ctx.macho_file.got_entries.append(ctx.macho_file.base.allocator, key); - try ctx.macho_file.got_entries_map.putNoClobber(ctx.macho_file.base.allocator, key, got_index); + const got_index = @intCast(u32, context.macho_file.got_entries.items.len); + try context.macho_file.got_entries.append(context.allocator, key); + try context.macho_file.got_entries_map.putNoClobber(context.allocator, key, got_index); } else if (parsed_rel.payload == .unsigned) { switch (parsed_rel.where) { .import => { - try self.bindings.append(allocator, .{ + try self.bindings.append(context.allocator, .{ .local_sym_index = parsed_rel.where_index, .offset = parsed_rel.offset, }); }, .local => { - const source_sym = ctx.macho_file.locals.items[self.local_sym_index]; - const match = ctx.macho_file.section_ordinals.items[source_sym.n_sect]; - const seg = ctx.macho_file.load_commands.items[match.seg].Segment; + const source_sym = context.macho_file.locals.items[self.local_sym_index]; + const match = context.macho_file.section_ordinals.items[source_sym.n_sect]; + const seg = context.macho_file.load_commands.items[match.seg].Segment; const sect = seg.sections.items[match.sect]; const sect_type = commands.sectionType(sect); @@ -858,12 +855,12 @@ pub fn parseRelocsFromObject( // TODO actually, a check similar to what dyld is doing, that is, verifying // that the segment is writable should be enough here. const is_right_segment = blk: { - if (ctx.macho_file.data_segment_cmd_index) |idx| { + if (context.macho_file.data_segment_cmd_index) |idx| { if (match.seg == idx) { break :blk true; } } - if (ctx.macho_file.data_const_segment_cmd_index) |idx| { + if (context.macho_file.data_const_segment_cmd_index) |idx| { if (match.seg == idx) { break :blk true; } @@ -884,17 +881,17 @@ pub fn parseRelocsFromObject( }; if (should_rebase) { - try self.rebases.append(allocator, parsed_rel.offset); + try self.rebases.append(context.allocator, parsed_rel.offset); } }, } } else if (parsed_rel.payload == .branch) blk: { if (parsed_rel.where != .import) break :blk; - if (ctx.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk; + if (context.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk; - const stubs_index = @intCast(u32, ctx.macho_file.stubs.items.len); - try ctx.macho_file.stubs.append(ctx.macho_file.base.allocator, parsed_rel.where_index); - try ctx.macho_file.stubs_map.putNoClobber(ctx.macho_file.base.allocator, parsed_rel.where_index, stubs_index); + const stubs_index = @intCast(u32, context.macho_file.stubs.items.len); + try context.macho_file.stubs.append(context.allocator, parsed_rel.where_index); + try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stubs_index); } } } @@ -917,7 +914,7 @@ fn parseUnsigned( rel: macho.relocation_info, out: *Relocation, subtractor: ?u32, - ctx: RelocContext, + context: RelocContext, ) void { assert(rel.r_pcrel == 0); @@ -934,7 +931,7 @@ fn parseUnsigned( if (rel.r_extern == 0) { assert(out.where == .local); - const target_sym = ctx.macho_file.locals.items[out.where_index]; + const target_sym = context.macho_file.locals.items[out.where_index]; addend -= @intCast(i64, target_sym.n_value); } @@ -947,14 +944,14 @@ fn parseUnsigned( }; } -fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void { +fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void { _ = self; assert(rel.r_pcrel == 1); assert(rel.r_length == 2); out.payload = .{ .branch = .{ - .arch = ctx.macho_file.base.options.target.cpu.arch, + .arch = context.macho_file.base.options.target.cpu.arch, }, }; } @@ -1015,7 +1012,7 @@ fn parsePointerToGot(self: TextBlock, rel: macho.relocation_info, out: *Relocati }; } -fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ctx: RelocContext) void { +fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, context: RelocContext) void { assert(rel.r_pcrel == 1); assert(rel.r_length == 2); @@ -1030,10 +1027,10 @@ fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct var addend: i64 = mem.readIntLittle(i32, self.code.items[out.offset..][0..4]) + correction; if (rel.r_extern == 0) { - const source_sym = ctx.macho_file.locals.items[self.local_sym_index]; + const source_sym = context.macho_file.locals.items[self.local_sym_index]; const target_sym = switch (out.where) { - .local => ctx.macho_file.locals.items[out.where_index], - .import => ctx.macho_file.imports.items[out.where_index], + .local => context.macho_file.locals.items[out.where_index], + .import => context.macho_file.imports.items[out.where_index], }; addend = @intCast(i64, source_sym.n_value + out.offset + 4) + addend - @intCast(i64, target_sym.n_value); }