Commit c30cc4dbbf
Changed files (4)
src
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;
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);
- }
-}
-
-pub fn closeFile(self: Object) void {
- if (self.file) |f| {
- f.close();
+ allocator.free(n);
}
}
-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;
}
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);
}
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 });