zld: move contents of Zld into MachO module

This commit is contained in:
Jakub Konka 2021-07-18 17:48:00 +02:00
parent e0b53ad3c9
commit f6d13e9d6f
6 changed files with 2649 additions and 3370 deletions

View File

@ -583,7 +583,6 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/TextBlock.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/commands.zig"
"${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ const fat = @import("fat.zig");
const Allocator = mem.Allocator;
const Arch = std.Target.Cpu.Arch;
const LibStub = @import("../tapi.zig").LibStub;
const Zld = @import("Zld.zig");
const MachO = @import("../MachO.zig");
usingnamespace @import("commands.zig");
@ -324,7 +324,7 @@ fn parseSymbols(self: *Dylib) !void {
_ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
for (slice) |sym| {
const add_to_symtab = Zld.symbolIsExt(sym) and (Zld.symbolIsSect(sym) or Zld.symbolIsIndr(sym));
const add_to_symtab = MachO.symbolIsExt(sym) and (MachO.symbolIsSect(sym) or MachO.symbolIsIndr(sym));
if (!add_to_symtab) continue;

View File

@ -13,8 +13,8 @@ const sort = std.sort;
const Allocator = mem.Allocator;
const Arch = std.Target.Cpu.Arch;
const MachO = @import("../MachO.zig");
const TextBlock = @import("TextBlock.zig");
const Zld = @import("Zld.zig");
usingnamespace @import("commands.zig");
@ -307,8 +307,8 @@ const NlistWithIndex = struct {
}
};
const start = Zld.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr });
const end = Zld.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size });
const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr });
const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size });
return symbols[start..end];
}
@ -323,8 +323,8 @@ fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64)
}
};
const start = Zld.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
const end = Zld.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
return dices[start..end];
}
@ -335,10 +335,10 @@ const TextBlockParser = struct {
code: []u8,
relocs: []macho.relocation_info,
object: *Object,
zld: *Zld,
macho_file: *MachO,
nlists: []NlistWithIndex,
index: u32 = 0,
match: Zld.MatchingSection,
match: MachO.MatchingSection,
fn peek(self: *TextBlockParser) ?NlistWithIndex {
return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
@ -349,10 +349,10 @@ const TextBlockParser = struct {
};
fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {
if (!Zld.symbolIsExt(rhs.nlist)) {
return Zld.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx));
} else if (Zld.symbolIsPext(rhs.nlist) or Zld.symbolIsWeakDef(rhs.nlist)) {
return !Zld.symbolIsExt(lhs.nlist);
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)) {
return !MachO.symbolIsExt(lhs.nlist);
} else {
return true;
}
@ -383,7 +383,7 @@ const TextBlockParser = struct {
const sym = self.object.symbols.items[nlist_with_index.index];
if (sym.payload != .regular) {
log.err("expected a regular symbol, found {s}", .{sym.payload});
log.err(" when remapping {s}", .{self.zld.getString(sym.strx)});
log.err(" when remapping {s}", .{self.macho_file.getString(sym.strx)});
return error.SymbolIsNotRegular;
}
assert(sym.payload.regular.local_sym_index != 0); // This means the symbol has not been properly resolved.
@ -401,7 +401,7 @@ const TextBlockParser = struct {
}
const senior_nlist = aliases.pop();
const senior_sym = self.zld.locals.items[senior_nlist.index];
const senior_sym = self.macho_file.locals.items[senior_nlist.index];
assert(senior_sym.payload == .regular);
senior_sym.payload.regular.segment_id = self.match.seg;
senior_sym.payload.regular.section_id = self.match.sect;
@ -429,7 +429,7 @@ const TextBlockParser = struct {
}
}
}
if (self.zld.globals.contains(self.zld.getString(senior_sym.strx))) break :blk .global;
if (self.macho_file.globals.contains(self.macho_file.getString(senior_sym.strx))) break :blk .global;
break :blk .static;
} else null;
@ -448,19 +448,19 @@ const TextBlockParser = struct {
for (aliases.items) |alias| {
block.aliases.appendAssumeCapacity(alias.index);
const sym = self.zld.locals.items[alias.index];
const sym = self.macho_file.locals.items[alias.index];
const reg = &sym.payload.regular;
reg.segment_id = self.match.seg;
reg.section_id = self.match.sect;
}
}
try block.parseRelocsFromObject(relocs, object, .{
try block.parseRelocsFromObject(self.allocator, relocs, object, .{
.base_addr = start_addr,
.zld = self.zld,
.macho_file = self.macho_file,
});
if (self.zld.has_dices) {
if (self.macho_file.has_dices) {
const dices = filterDice(
self.object.data_in_code_entries.items,
senior_nlist.nlist.n_value,
@ -483,7 +483,7 @@ const TextBlockParser = struct {
}
};
pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
pub fn parseTextBlocks(self: *Object, macho_file: *MachO) !void {
const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
log.debug("analysing {s}", .{self.name.?});
@ -513,7 +513,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
});
// Get matching segment/section in the final artifact.
const match = (try zld.getMatchingSection(sect)) orelse {
const match = (try macho_file.getMatchingSection(sect)) orelse {
log.debug("unhandled section", .{});
continue;
};
@ -538,7 +538,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
// duplicates at all? Need some benchmarks!
// const is_splittable = false;
zld.has_dices = blk: {
macho_file.has_dices = blk: {
if (self.text_section_index) |index| {
if (index != id) break :blk false;
if (self.data_in_code_entries.items.len == 0) break :blk false;
@ -546,7 +546,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
}
break :blk false;
};
zld.has_stabs = zld.has_stabs or self.debug_info != null;
macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
{
// next: {
@ -711,11 +711,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
defer self.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, zld.locals.items.len);
try zld.locals.append(zld.allocator, .{
.n_strx = try zld.makeString(sym_name),
const block_local_sym_index = @intCast(u32, macho_file.locals.items.len);
try macho_file.locals.append(macho_file.base.allocator, .{
.n_strx = try macho_file.makeString(sym_name),
.n_type = macho.N_SECT,
.n_sect = zld.sectionId(match),
.n_sect = macho_file.sectionId(match),
.n_desc = 0,
.n_value = sect.addr,
});
@ -726,20 +726,20 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
const block = try self.allocator.create(TextBlock);
errdefer self.allocator.destroy(block);
block.* = TextBlock.init(self.allocator);
block.* = TextBlock.empty;
block.local_sym_index = block_local_sym_index;
block.code = try self.allocator.dupe(u8, code);
block.size = sect.size;
block.alignment = sect.@"align";
try block.parseRelocsFromObject(relocs, self, .{
try block.parseRelocsFromObject(self.allocator, relocs, self, .{
.base_addr = 0,
.zld = zld,
.macho_file = macho_file,
});
if (zld.has_dices) {
if (macho_file.has_dices) {
const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);
try block.dices.ensureTotalCapacity(dices.len);
try block.dices.ensureTotalCapacity(self.allocator, dices.len);
for (dices) |dice| {
block.dices.appendAssumeCapacity(.{
@ -755,15 +755,13 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !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.
var contained = std.ArrayList(TextBlock.SymbolAtOffset).init(self.allocator);
defer contained.deinit();
try contained.ensureTotalCapacity(filtered_nlists.len);
try block.contained.ensureTotalCapacity(self.allocator, filtered_nlists.len);
for (filtered_nlists) |nlist_with_index| {
const nlist = nlist_with_index.nlist;
const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
const local = &zld.locals.items[local_sym_index];
local.n_sect = zld.sectionId(match);
const local = &macho_file.locals.items[local_sym_index];
local.n_sect = macho_file.sectionId(match);
const stab: ?TextBlock.Stab = if (self.debug_info) |di| blk: {
// TODO there has to be a better to handle this.
@ -781,19 +779,17 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
break :blk .static;
} else null;
contained.appendAssumeCapacity(.{
block.contained.appendAssumeCapacity(.{
.local_sym_index = local_sym_index,
.offset = nlist.n_value - sect.addr,
.stab = stab,
});
}
block.contained = contained.toOwnedSlice();
// Update target section's metadata
// TODO should we update segment's size here too?
// How does it tie with incremental space allocs?
const tseg = &zld.load_commands.items[match.seg].Segment;
const tseg = &macho_file.load_commands.items[match.seg].Segment;
const tsect = &tseg.sections.items[match.sect];
const new_alignment = math.max(tsect.@"align", block.alignment);
const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment);
@ -801,12 +797,12 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
tsect.size = new_size;
tsect.@"align" = new_alignment;
if (zld.blocks.getPtr(match)) |last| {
if (macho_file.blocks.getPtr(match)) |last| {
last.*.next = block;
block.prev = last.*;
last.* = block;
} else {
try zld.blocks.putNoClobber(zld.allocator, match, block);
try macho_file.blocks.putNoClobber(self.allocator, match, block);
}
try self.text_blocks.append(self.allocator, block);
@ -814,7 +810,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
}
}
pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*Symbol {
pub fn symbolFromReloc(self: *Object, macho_file: *MachO, rel: macho.relocation_info) !*Symbol {
const symbol = blk: {
if (rel.r_extern == 1) {
break :blk self.symbols.items[rel.r_symbolnum];
@ -832,9 +828,9 @@ pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*S
sectionName(sect),
});
defer self.allocator.free(name);
const symbol = try zld.allocator.create(Symbol);
const symbol = try macho_file.allocator.create(Symbol);
symbol.* = .{
.strx = try zld.makeString(name),
.strx = try macho_file.makeString(name),
.payload = .{
.regular = .{
.linkage = .translation_unit,

View File

@ -12,24 +12,64 @@ const meta = std.meta;
const Allocator = mem.Allocator;
const Arch = std.Target.Cpu.Arch;
const MachO = @import("../MachO.zig");
const Object = @import("Object.zig");
const Zld = @import("Zld.zig");
allocator: *Allocator,
/// Each decl always gets a local symbol with the fully qualified name.
/// The vaddr and size are found here directly.
/// The file offset is found by computing the vaddr offset from the section vaddr
/// the symbol references, and adding that to the file offset of the section.
/// If this field is 0, it means the codegen size = 0 and there is no symbol or
/// offset table entry.
local_sym_index: u32,
stab: ?Stab = null,
aliases: std.ArrayList(u32),
references: std.AutoArrayHashMap(u32, void),
contained: ?[]SymbolAtOffset = null,
/// List of symbol aliases pointing to the same block via different nlists
aliases: std.ArrayListUnmanaged(u32) = .{},
/// List of symbols contained within this block
contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
/// Code (may be non-relocated) this block represents
code: []u8,
relocs: std.ArrayList(Relocation),
/// Size and alignment of this text block
/// Unlike in Elf, we need to store the size of this symbol as part of
/// the TextBlock since macho.nlist_64 lacks this information.
size: u64,
alignment: u32,
rebases: std.ArrayList(u64),
bindings: std.ArrayList(SymbolAtOffset),
dices: std.ArrayList(macho.data_in_code_entry),
next: ?*TextBlock = null,
prev: ?*TextBlock = null,
relocs: std.ArrayListUnmanaged(Relocation) = .{},
/// List of offsets contained within this block that need rebasing by the dynamic
/// loader in presence of ASLR
rebases: std.ArrayListUnmanaged(u64) = .{},
/// List of offsets contained within this block that will be dynamically bound
/// by the dynamic loader and contain pointers to resolved (at load time) extern
/// symbols (aka proxies aka imports)
bindings: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
/// List of data-in-code entries. This is currently specific to x86_64 only.
dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
/// Stab entry for this block. This is currently specific to a binary created
/// by linking object files in a traditional sense - in incremental sense, we
/// bypass stabs altogether to produce dSYM bundle directly with fully relocated
/// DWARF sections.
stab: ?Stab = null,
/// Points to the previous and next neighbours
next: ?*TextBlock,
prev: ?*TextBlock,
/// Previous/next linked list pointers.
/// This is the linked list node for this Decl's corresponding .debug_info tag.
dbg_info_prev: ?*TextBlock,
dbg_info_next: ?*TextBlock,
/// Offset into .debug_info pointing to the tag for this Decl.
dbg_info_off: u32,
/// Size of the .debug_info tag for this Decl, not including padding.
dbg_info_len: u32,
pub const SymbolAtOffset = struct {
local_sym_index: u32,
@ -42,11 +82,11 @@ pub const Stab = union(enum) {
static,
global,
pub fn asNlists(stab: Stab, local_sym_index: u32, zld: *Zld) ![]macho.nlist_64 {
var nlists = std.ArrayList(macho.nlist_64).init(zld.allocator);
pub fn asNlists(stab: Stab, local_sym_index: u32, macho_file: anytype) ![]macho.nlist_64 {
var nlists = std.ArrayList(macho.nlist_64).init(macho_file.base.allocator);
defer nlists.deinit();
const sym = zld.locals.items[local_sym_index];
const sym = macho_file.locals.items[local_sym_index];
switch (stab) {
.function => |size| {
try nlists.ensureUnusedCapacity(4);
@ -130,7 +170,7 @@ pub const Relocation = struct {
offset: u32,
source_addr: u64,
target_addr: u64,
zld: *Zld,
macho_file: *MachO,
};
pub const Unsigned = struct {
@ -148,7 +188,7 @@ pub const Relocation = struct {
pub fn resolve(self: Unsigned, args: ResolveArgs) !void {
const result = blk: {
if (self.subtractor) |subtractor| {
const sym = args.zld.locals.items[subtractor];
const sym = args.macho_file.locals.items[subtractor];
break :blk @intCast(i64, args.target_addr) - @intCast(i64, sym.n_value) + self.addend;
} else {
break :blk @intCast(i64, args.target_addr) + self.addend;
@ -500,38 +540,59 @@ pub const Relocation = struct {
}
};
pub fn init(allocator: *Allocator) TextBlock {
return .{
.allocator = allocator,
.local_sym_index = undefined,
.aliases = std.ArrayList(u32).init(allocator),
.references = std.AutoArrayHashMap(u32, void).init(allocator),
.code = undefined,
.relocs = std.ArrayList(Relocation).init(allocator),
.size = undefined,
.alignment = undefined,
.rebases = std.ArrayList(u64).init(allocator),
.bindings = std.ArrayList(SymbolAtOffset).init(allocator),
.dices = std.ArrayList(macho.data_in_code_entry).init(allocator),
};
pub const empty = TextBlock{
.local_sym_index = 0,
.code = undefined,
.size = 0,
.alignment = 0,
.prev = null,
.next = null,
.dbg_info_prev = null,
.dbg_info_next = null,
.dbg_info_off = undefined,
.dbg_info_len = undefined,
};
pub fn deinit(self: *TextBlock, allocator: *Allocator) void {
self.dices.deinit(allocator);
self.bindings.deinit(allocator);
self.rebases.deinit(allocator);
self.relocs.deinit(allocator);
self.allocator.free(self.code);
self.contained.deinit(allocator);
self.aliases.deinit(allocator);
}
pub fn deinit(self: *TextBlock) void {
self.aliases.deinit();
self.references.deinit();
if (self.contained) |contained| {
self.allocator.free(contained);
/// Returns how much room there is to grow in virtual address space.
/// File offset relocation happens transparently, so it is not included in
/// this calculation.
pub fn capacity(self: TextBlock, macho_file: MachO) u64 {
const self_sym = macho_file.locals.items[self.local_sym_index];
if (self.next) |next| {
const next_sym = macho_file.locals.items[next.local_sym_index];
return next_sym.n_value - self_sym.n_value;
} else {
// We are the last block.
// The capacity is limited only by virtual address space.
return std.math.maxInt(u64) - self_sym.n_value;
}
self.allocator.free(self.code);
self.relocs.deinit();
self.rebases.deinit();
self.bindings.deinit();
self.dices.deinit();
}
pub fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
// No need to keep a free list node for the last block.
const next = self.next orelse return false;
const self_sym = macho_file.locals.items[self.local_sym_index];
const next_sym = macho_file.locals.items[next.local_sym_index];
const cap = next_sym.n_value - self_sym.n_value;
const ideal_cap = MachO.padToIdeal(self.size);
if (cap <= ideal_cap) return false;
const surplus = cap - ideal_cap;
return surplus >= MachO.min_text_capacity;
}
const RelocContext = struct {
base_addr: u64 = 0,
zld: *Zld,
macho_file: *MachO,
};
fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocContext) !Relocation {
@ -548,19 +609,19 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo
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 sect = seg.sections.items[sect_id];
const match = (try ctx.zld.getMatchingSection(sect)) orelse unreachable;
const local_sym_index = @intCast(u32, ctx.zld.locals.items.len);
const sym_name = try std.fmt.allocPrint(ctx.zld.allocator, "l_{s}_{s}_{s}", .{
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.?,
commands.segmentName(sect),
commands.sectionName(sect),
});
defer ctx.zld.allocator.free(sym_name);
defer ctx.macho_file.base.allocator.free(sym_name);
try ctx.zld.locals.append(ctx.zld.allocator, .{
.n_strx = try ctx.zld.makeString(sym_name),
try ctx.macho_file.locals.append(ctx.macho_file.base.allocator, .{
.n_strx = try ctx.macho_file.makeString(sym_name),
.n_type = macho.N_SECT,
.n_sect = ctx.zld.sectionId(match),
.n_sect = ctx.macho_file.sectionId(match),
.n_desc = 0,
.n_value = sect.addr,
});
@ -574,12 +635,12 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo
const sym = object.symtab.items[rel.r_symbolnum];
const sym_name = object.getString(sym.n_strx);
if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) {
if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
parsed_rel.where = .local;
parsed_rel.where_index = where_index;
} else {
const resolv = ctx.zld.symbol_resolver.get(sym_name) orelse unreachable;
const resolv = ctx.macho_file.symbol_resolver.get(sym_name) orelse unreachable;
switch (resolv.where) {
.global => {
parsed_rel.where = .local;
@ -599,6 +660,7 @@ fn initRelocFromObject(rel: macho.relocation_info, object: *Object, ctx: RelocCo
pub fn parseRelocsFromObject(
self: *TextBlock,
allocator: *Allocator,
relocs: []macho.relocation_info,
object: *Object,
ctx: RelocContext,
@ -638,11 +700,11 @@ pub fn parseRelocsFromObject(
const sym = object.symtab.items[rel.r_symbolnum];
const sym_name = object.getString(sym.n_strx);
if (Zld.symbolIsSect(sym) and !Zld.symbolIsExt(sym)) {
if (MachO.symbolIsSect(sym) and !MachO.symbolIsExt(sym)) {
const where_index = object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
subtractor = where_index;
} else {
const resolv = ctx.zld.symbol_resolver.get(sym_name) orelse unreachable;
const resolv = ctx.macho_file.symbol_resolver.get(sym_name) orelse unreachable;
assert(resolv.where == .global);
subtractor = resolv.local_sym_index;
}
@ -732,11 +794,7 @@ pub fn parseRelocsFromObject(
else => unreachable,
}
try self.relocs.append(parsed_rel);
if (parsed_rel.where == .local) {
try self.references.put(parsed_rel.where_index, {});
}
try self.relocs.append(allocator, parsed_rel);
const is_via_got = switch (parsed_rel.payload) {
.pointer_to_got => true,
@ -747,28 +805,30 @@ pub fn parseRelocsFromObject(
};
if (is_via_got) blk: {
const key = Zld.GotIndirectionKey{
const key = MachO.GotIndirectionKey{
.where = switch (parsed_rel.where) {
.local => .local,
.import => .import,
},
.where_index = parsed_rel.where_index,
};
if (ctx.zld.got_entries.contains(key)) break :blk;
if (ctx.macho_file.got_entries_map.contains(key)) break :blk;
try ctx.zld.got_entries.putNoClobber(ctx.zld.allocator, key, {});
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);
} else if (parsed_rel.payload == .unsigned) {
switch (parsed_rel.where) {
.import => {
try self.bindings.append(.{
try self.bindings.append(allocator, .{
.local_sym_index = parsed_rel.where_index,
.offset = parsed_rel.offset,
});
},
.local => {
const source_sym = ctx.zld.locals.items[self.local_sym_index];
const match = ctx.zld.unpackSectionId(source_sym.n_sect);
const seg = ctx.zld.load_commands.items[match.seg].Segment;
const source_sym = ctx.macho_file.locals.items[self.local_sym_index];
const match = ctx.macho_file.unpackSectionId(source_sym.n_sect);
const seg = ctx.macho_file.load_commands.items[match.seg].Segment;
const sect = seg.sections.items[match.sect];
const sect_type = commands.sectionType(sect);
@ -778,12 +838,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.zld.data_segment_cmd_index) |idx| {
if (ctx.macho_file.data_segment_cmd_index) |idx| {
if (match.seg == idx) {
break :blk true;
}
}
if (ctx.zld.data_const_segment_cmd_index) |idx| {
if (ctx.macho_file.data_const_segment_cmd_index) |idx| {
if (match.seg == idx) {
break :blk true;
}
@ -804,15 +864,17 @@ pub fn parseRelocsFromObject(
};
if (should_rebase) {
try self.rebases.append(parsed_rel.offset);
try self.rebases.append(allocator, parsed_rel.offset);
}
},
}
} else if (parsed_rel.payload == .branch) blk: {
if (parsed_rel.where != .import) break :blk;
if (ctx.zld.stubs.contains(parsed_rel.where_index)) break :blk;
if (ctx.macho_file.stubs_map.contains(parsed_rel.where_index)) break :blk;
try ctx.zld.stubs.putNoClobber(ctx.zld.allocator, parsed_rel.where_index, {});
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);
}
}
}
@ -852,7 +914,7 @@ fn parseUnsigned(
if (rel.r_extern == 0) {
assert(out.where == .local);
const target_sym = ctx.zld.locals.items[out.where_index];
const target_sym = ctx.macho_file.locals.items[out.where_index];
addend -= @intCast(i64, target_sym.n_value);
}
@ -872,7 +934,7 @@ fn parseBranch(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct
out.payload = .{
.branch = .{
.arch = ctx.zld.target.?.cpu.arch,
.arch = ctx.macho_file.base.options.target.cpu.arch,
},
};
}
@ -948,10 +1010,10 @@ fn parseSigned(self: TextBlock, rel: macho.relocation_info, out: *Relocation, ct
var addend: i64 = mem.readIntLittle(i32, self.code[out.offset..][0..4]) + correction;
if (rel.r_extern == 0) {
const source_sym = ctx.zld.locals.items[self.local_sym_index];
const source_sym = ctx.macho_file.locals.items[self.local_sym_index];
const target_sym = switch (out.where) {
.local => ctx.zld.locals.items[out.where_index],
.import => ctx.zld.imports.items[out.where_index],
.local => ctx.macho_file.locals.items[out.where_index],
.import => ctx.macho_file.imports.items[out.where_index],
};
addend = @intCast(i64, source_sym.n_value + out.offset + 4) + addend - @intCast(i64, target_sym.n_value);
}
@ -986,12 +1048,12 @@ fn parseLoad(self: TextBlock, rel: macho.relocation_info, out: *Relocation) void
};
}
pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
pub fn resolveRelocs(self: *TextBlock, macho_file: *MachO) !void {
for (self.relocs.items) |rel| {
log.debug("relocating {}", .{rel});
const source_addr = blk: {
const sym = zld.locals.items[self.local_sym_index];
const sym = macho_file.locals.items[self.local_sym_index];
break :blk sym.n_value + rel.offset;
};
const target_addr = blk: {
@ -1004,9 +1066,9 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
};
if (is_via_got) {
const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
const got = dc_seg.sections.items[zld.got_section_index.?];
const got_index = zld.got_entries.getIndex(.{
const dc_seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment;
const got = dc_seg.sections.items[macho_file.got_section_index.?];
const got_index = macho_file.got_entries_map.get(.{
.where = switch (rel.where) {
.local => .local,
.import => .import,
@ -1014,10 +1076,10 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
.where_index = rel.where_index,
}) orelse {
const sym = switch (rel.where) {
.local => zld.locals.items[rel.where_index],
.import => zld.imports.items[rel.where_index],
.local => macho_file.locals.items[rel.where_index],
.import => macho_file.imports.items[rel.where_index],
};
log.err("expected GOT entry for symbol '{s}'", .{zld.getString(sym.n_strx)});
log.err("expected GOT entry for symbol '{s}'", .{macho_file.getString(sym.n_strx)});
log.err(" this is an internal linker error", .{});
return error.FailedToResolveRelocationTarget;
};
@ -1026,11 +1088,11 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
switch (rel.where) {
.local => {
const sym = zld.locals.items[rel.where_index];
const sym = macho_file.locals.items[rel.where_index];
const is_tlv = is_tlv: {
const source_sym = zld.locals.items[self.local_sym_index];
const match = zld.unpackSectionId(source_sym.n_sect);
const seg = zld.load_commands.items[match.seg].Segment;
const source_sym = macho_file.locals.items[self.local_sym_index];
const match = macho_file.unpackSectionId(source_sym.n_sect);
const seg = macho_file.load_commands.items[match.seg].Segment;
const sect = seg.sections.items[match.sect];
break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
};
@ -1040,11 +1102,11 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
// defined TLV template init section in the following order:
// * wrt to __thread_data if defined, then
// * wrt to __thread_bss
const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].Segment;
const base_address = inner: {
if (zld.tlv_data_section_index) |i| {
if (macho_file.tlv_data_section_index) |i| {
break :inner seg.sections.items[i].addr;
} else if (zld.tlv_bss_section_index) |i| {
} else if (macho_file.tlv_bss_section_index) |i| {
break :inner seg.sections.items[i].addr;
} else {
log.err("threadlocal variables present but no initializer sections found", .{});
@ -1059,12 +1121,12 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
break :blk sym.n_value;
},
.import => {
const stubs_index = zld.stubs.getIndex(rel.where_index) orelse {
const stubs_index = macho_file.stubs_map.get(rel.where_index) orelse {
// TODO verify in TextBlock that the symbol is indeed dynamically bound.
break :blk 0; // Dynamically bound by dyld.
};
const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
const stubs = segment.sections.items[zld.stubs_section_index.?];
const segment = macho_file.load_commands.items[macho_file.text_segment_cmd_index.?].Segment;
const stubs = segment.sections.items[macho_file.stubs_section_index.?];
break :blk stubs.addr + stubs_index * stubs.reserved2;
},
}
@ -1078,14 +1140,14 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
.offset = rel.offset,
.source_addr = source_addr,
.target_addr = target_addr,
.zld = zld,
.macho_file = macho_file,
});
}
}
pub fn print_this(self: *const TextBlock, zld: *Zld) void {
pub fn print_this(self: *const TextBlock, macho_file: MachO) void {
log.warn("TextBlock", .{});
log.warn(" {}: {}", .{ self.local_sym_index, zld.locals.items[self.local_sym_index] });
log.warn(" {}: {}", .{ self.local_sym_index, macho_file.locals.items[self.local_sym_index] });
if (self.stab) |stab| {
log.warn(" stab: {}", .{stab});
}
@ -1125,11 +1187,11 @@ pub fn print_this(self: *const TextBlock, zld: *Zld) void {
log.warn(" align = {}", .{self.alignment});
}
pub fn print(self: *const TextBlock, zld: *Zld) void {
pub fn print(self: *const TextBlock, macho_file: MachO) void {
if (self.prev) |prev| {
prev.print(zld);
prev.print(macho_file);
}
self.print_this(zld);
self.print_this(macho_file);
}
const RelocIterator = struct {
@ -1159,8 +1221,8 @@ fn filterRelocs(relocs: []macho.relocation_info, start_addr: u64, end_addr: u64)
}
};
const start = Zld.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
const end = Zld.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
return relocs[start..end];
}

File diff suppressed because it is too large Load Diff