aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2021-07-16 17:18:53 +0200
committerJakub Konka <kubkon@jakubkonka.com>2021-07-16 17:18:53 +0200
commit54a403d4ff9e20daf1843725012ae44ec828a833 (patch)
tree1c1679ee843c9caeb1986062a400cc42a645d560 /src
parent5a2bea29315158bc05fb4b09842bbb9ae0ddfada (diff)
downloadzig-54a403d4ff9e20daf1843725012ae44ec828a833.tar.gz
zig-54a403d4ff9e20daf1843725012ae44ec828a833.zip
zld: replace parsed reloc with a simple wrapper around macho.relocation_info
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO/Dylib.zig4
-rw-r--r--src/link/MachO/Object.zig529
-rw-r--r--src/link/MachO/Symbol.zig285
-rw-r--r--src/link/MachO/TextBlock.zig64
-rw-r--r--src/link/MachO/Zld.zig74
5 files changed, 340 insertions, 616 deletions
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig
index b751249ce4..ca71b7613c 100644
--- a/src/link/MachO/Dylib.zig
+++ b/src/link/MachO/Dylib.zig
@@ -12,8 +12,8 @@ const fat = @import("fat.zig");
const Allocator = mem.Allocator;
const Arch = std.Target.Cpu.Arch;
-const Symbol = @import("Symbol.zig");
const LibStub = @import("../tapi.zig").LibStub;
+const Zld = @import("Zld.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 = Symbol.isExt(sym) and (Symbol.isSect(sym) or Symbol.isIndr(sym));
+ const add_to_symtab = Zld.symbolIsExt(sym) and (Zld.symbolIsSect(sym) or Zld.symbolIsIndr(sym));
if (!add_to_symtab) continue;
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index e87a74e80c..1c074a97c7 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -9,13 +9,10 @@ const log = std.log.scoped(.object);
const macho = std.macho;
const math = std.math;
const mem = std.mem;
-const reloc = @import("reloc.zig");
const sort = std.sort;
const Allocator = mem.Allocator;
const Arch = std.Target.Cpu.Arch;
-const Relocation = reloc.Relocation;
-const Symbol = @import("Symbol.zig");
const TextBlock = @import("TextBlock.zig");
const Zld = @import("Zld.zig");
@@ -57,6 +54,8 @@ tu_comp_dir: ?[]const u8 = null,
mtime: ?u64 = null,
text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{},
+sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
+symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
const DebugInfo = struct {
inner: dwarf.DwarfInfo,
@@ -163,6 +162,8 @@ pub fn deinit(self: *Object) void {
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);
if (self.debug_info) |*db| {
db.deinit(self.allocator);
@@ -372,20 +373,17 @@ const TextBlockParser = struct {
}
const SeniorityContext = struct {
- zld: *Zld,
+ object: *Object,
};
fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {
- const lsym = context.zld.locals.items[lhs.index];
- const rsym = context.zld.locals.items[rhs.index];
- const lreg = lsym.payload.regular;
- const rreg = rsym.payload.regular;
-
- return switch (rreg.linkage) {
- .global => true,
- .linkage_unit => lreg.linkage == .translation_unit,
- else => lsym.isTemp(context.zld),
- };
+ 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);
+ } else {
+ return true;
+ }
}
pub fn next(self: *TextBlockParser) !?*TextBlock {
@@ -409,6 +407,7 @@ const TextBlockParser = struct {
} else null;
for (aliases.items) |*nlist_with_index| {
+ nlist_with_index.index = self.symbol_mapping.get(nlist_with_index.index);
const sym = self.object.symbols.items[nlist_with_index.index];
if (sym.payload != .regular) {
log.err("expected a regular symbol, found {s}", .{sym.payload});
@@ -424,7 +423,7 @@ const TextBlockParser = struct {
sort.sort(
NlistWithIndex,
aliases.items,
- SeniorityContext{ .zld = self.zld },
+ SeniorityContext{ .object = self.object },
@This().lessThanBySeniority,
);
}
@@ -515,7 +514,7 @@ const TextBlockParser = struct {
pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
const seg = self.load_commands.items[self.segment_cmd_index.?].Segment;
- log.debug("analysing {s}", .{self.name.?});
+ log.warn("analysing {s}", .{self.name.?});
const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
// We only care about defined symbols, so filter every other out.
@@ -536,14 +535,14 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
for (seg.sections.items) |sect, id| {
const sect_id = @intCast(u8, id);
- log.debug("putting section '{s},{s}' as a TextBlock", .{
+ log.warn("putting section '{s},{s}' as a TextBlock", .{
segmentName(sect),
sectionName(sect),
});
// Get matching segment/section in the final artifact.
const match = (try zld.getMatchingSection(sect)) orelse {
- log.debug("unhandled section", .{});
+ log.warn("unhandled section", .{});
continue;
};
@@ -577,200 +576,249 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
};
zld.has_stabs = zld.has_stabs or self.debug_info != null;
- next: {
- if (is_splittable) blocks: {
- if (filtered_nlists.len == 0) break :blocks;
-
- // If the first nlist does not match the start of the section,
- // then we need encapsulate the memory range [section start, first symbol)
- // as a temporary symbol and insert the matching TextBlock.
- const first_nlist = filtered_nlists[0].nlist;
- if (first_nlist.n_value > sect.addr) {
- const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: {
- const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
- self.name.?,
- segmentName(sect),
- sectionName(sect),
- });
- defer self.allocator.free(name);
- const symbol = try zld.allocator.create(Symbol);
- symbol.* = .{
- .strx = try zld.makeString(name),
- .payload = .{ .undef = .{} },
- };
- try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
- break :symbol symbol;
- };
-
- const local_sym_index = @intCast(u32, zld.locals.items.len);
- symbol.payload = .{
- .regular = .{
- .linkage = .translation_unit,
- .address = sect.addr,
- .segment_id = match.seg,
- .section_id = match.sect,
- .file = self,
- .local_sym_index = local_sym_index,
- },
- };
- try zld.locals.append(zld.allocator, symbol);
-
- const block_code = code[0 .. first_nlist.n_value - sect.addr];
- const block_size = block_code.len;
-
- const block = try self.allocator.create(TextBlock);
- errdefer self.allocator.destroy(block);
-
- block.* = TextBlock.init(self.allocator);
- block.local_sym_index = local_sym_index;
- block.code = try self.allocator.dupe(u8, block_code);
- block.size = block_size;
- block.alignment = sect.@"align";
-
- const block_relocs = filterRelocs(relocs, 0, block_size);
- if (block_relocs.len > 0) {
- try self.parseRelocs(zld, block_relocs, block, 0);
- }
-
- if (zld.has_dices) {
- const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size);
- try block.dices.ensureTotalCapacity(dices.len);
-
- for (dices) |dice| {
- block.dices.appendAssumeCapacity(.{
- .offset = dice.offset - try math.cast(u32, sect.addr),
- .length = dice.length,
- .kind = dice.kind,
- });
- }
- }
-
- // 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 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);
- const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
- tsect.size = new_size;
- tsect.@"align" = new_alignment;
-
- if (zld.blocks.getPtr(match)) |last| {
- last.*.next = block;
- block.prev = last.*;
- last.* = block;
- } else {
- try zld.blocks.putNoClobber(zld.allocator, match, block);
- }
-
- try self.text_blocks.append(self.allocator, block);
- }
-
- var parser = TextBlockParser{
- .allocator = self.allocator,
- .section = sect,
- .code = code,
- .relocs = relocs,
- .object = self,
- .zld = zld,
- .nlists = filtered_nlists,
- .match = match,
- };
-
- while (try parser.next()) |block| {
- const sym = zld.locals.items[block.local_sym_index];
- const reg = &sym.payload.regular;
- if (reg.file) |file| {
- if (file != self) {
- log.debug("deduping definition of {s} in {s}", .{ zld.getString(sym.strx), self.name.? });
- block.deinit();
- self.allocator.destroy(block);
- continue;
- }
- }
-
- if (reg.address == sect.addr) {
- if (self.sections_as_symbols.get(sect_id)) |alias| {
- // Add alias.
- const local_sym_index = @intCast(u32, zld.locals.items.len);
- const reg_alias = &alias.payload.regular;
- reg_alias.segment_id = match.seg;
- reg_alias.section_id = match.sect;
- reg_alias.local_sym_index = local_sym_index;
- try block.aliases.append(local_sym_index);
- try zld.locals.append(zld.allocator, alias);
- }
- }
-
- // 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 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);
- const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
- tsect.size = new_size;
- tsect.@"align" = new_alignment;
-
- if (zld.blocks.getPtr(match)) |last| {
- last.*.next = block;
- block.prev = last.*;
- last.* = block;
- } else {
- try zld.blocks.putNoClobber(zld.allocator, match, block);
- }
-
- try self.text_blocks.append(self.allocator, block);
- }
-
- break :next;
- }
+ {
+ // next: {
+ // if (is_splittable) blocks: {
+ // if (filtered_nlists.len == 0) break :blocks;
+
+ // // If the first nlist does not match the start of the section,
+ // // then we need encapsulate the memory range [section start, first symbol)
+ // // as a temporary symbol and insert the matching TextBlock.
+ // const first_nlist = filtered_nlists[0].nlist;
+ // if (first_nlist.n_value > sect.addr) {
+ // const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: {
+ // const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
+ // self.name.?,
+ // segmentName(sect),
+ // sectionName(sect),
+ // });
+ // defer self.allocator.free(name);
+ // const symbol = try zld.allocator.create(Symbol);
+ // symbol.* = .{
+ // .strx = try zld.makeString(name),
+ // .payload = .{ .undef = .{} },
+ // };
+ // try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
+ // break :symbol symbol;
+ // };
+
+ // const local_sym_index = @intCast(u32, zld.locals.items.len);
+ // symbol.payload = .{
+ // .regular = .{
+ // .linkage = .translation_unit,
+ // .address = sect.addr,
+ // .segment_id = match.seg,
+ // .section_id = match.sect,
+ // .file = self,
+ // .local_sym_index = local_sym_index,
+ // },
+ // };
+ // try zld.locals.append(zld.allocator, symbol);
+
+ // const block_code = code[0 .. first_nlist.n_value - sect.addr];
+ // const block_size = block_code.len;
+
+ // const block = try self.allocator.create(TextBlock);
+ // errdefer self.allocator.destroy(block);
+
+ // block.* = TextBlock.init(self.allocator);
+ // block.local_sym_index = local_sym_index;
+ // block.code = try self.allocator.dupe(u8, block_code);
+ // block.size = block_size;
+ // block.alignment = sect.@"align";
+
+ // const block_relocs = filterRelocs(relocs, 0, block_size);
+ // if (block_relocs.len > 0) {
+ // try self.parseRelocs(zld, block_relocs, block, 0);
+ // }
+
+ // if (zld.has_dices) {
+ // const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size);
+ // try block.dices.ensureTotalCapacity(dices.len);
+
+ // for (dices) |dice| {
+ // block.dices.appendAssumeCapacity(.{
+ // .offset = dice.offset - try math.cast(u32, sect.addr),
+ // .length = dice.length,
+ // .kind = dice.kind,
+ // });
+ // }
+ // }
+
+ // // 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 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);
+ // const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
+ // tsect.size = new_size;
+ // tsect.@"align" = new_alignment;
+
+ // if (zld.blocks.getPtr(match)) |last| {
+ // last.*.next = block;
+ // block.prev = last.*;
+ // last.* = block;
+ // } else {
+ // try zld.blocks.putNoClobber(zld.allocator, match, block);
+ // }
+
+ // try self.text_blocks.append(self.allocator, block);
+ // }
+
+ // var parser = TextBlockParser{
+ // .allocator = self.allocator,
+ // .section = sect,
+ // .code = code,
+ // .relocs = relocs,
+ // .object = self,
+ // .zld = zld,
+ // .nlists = filtered_nlists,
+ // .match = match,
+ // };
+
+ // while (try parser.next()) |block| {
+ // const sym = zld.locals.items[block.local_sym_index];
+ // const reg = &sym.payload.regular;
+ // if (reg.file) |file| {
+ // if (file != self) {
+ // log.debug("deduping definition of {s} in {s}", .{ zld.getString(sym.strx), self.name.? });
+ // block.deinit();
+ // self.allocator.destroy(block);
+ // continue;
+ // }
+ // }
+
+ // if (reg.address == sect.addr) {
+ // if (self.sections_as_symbols.get(sect_id)) |alias| {
+ // // Add alias.
+ // const local_sym_index = @intCast(u32, zld.locals.items.len);
+ // const reg_alias = &alias.payload.regular;
+ // reg_alias.segment_id = match.seg;
+ // reg_alias.section_id = match.sect;
+ // reg_alias.local_sym_index = local_sym_index;
+ // try block.aliases.append(local_sym_index);
+ // try zld.locals.append(zld.allocator, alias);
+ // }
+ // }
+
+ // // 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 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);
+ // const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size;
+ // tsect.size = new_size;
+ // tsect.@"align" = new_alignment;
+
+ // if (zld.blocks.getPtr(match)) |last| {
+ // last.*.next = block;
+ // block.prev = last.*;
+ // last.* = block;
+ // } else {
+ // try zld.blocks.putNoClobber(zld.allocator, match, block);
+ // }
+
+ // try self.text_blocks.append(self.allocator, block);
+ // }
+
+ // break :next;
+ // }
// 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 symbol = self.sections_as_symbols.get(sect_id) orelse symbol: {
- const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
- self.name.?,
- segmentName(sect),
- sectionName(sect),
- });
- defer self.allocator.free(name);
- const symbol = try zld.allocator.create(Symbol);
- symbol.* = .{
- .strx = try zld.makeString(name),
- .payload = .{ .undef = .{} },
- };
- try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
- break :symbol symbol;
- };
-
- const local_sym_index = @intCast(u32, zld.locals.items.len);
- symbol.payload = .{
- .regular = .{
- .linkage = .translation_unit,
- .address = sect.addr,
- .segment_id = match.seg,
- .section_id = match.sect,
- .file = self,
- .local_sym_index = local_sym_index,
- },
- };
- try zld.locals.append(zld.allocator, symbol);
+ const block_local_sym_index = @intCast(u32, zld.locals.items.len);
+ const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
+ self.name.?,
+ segmentName(sect),
+ sectionName(sect),
+ });
+ defer self.allocator.free(sym_name);
+ try zld.locals.append(zld.allocator, .{
+ .n_strx = try zld.makeString(sym_name),
+ .n_type = macho.N_SECT,
+ .n_sect = zld.sectionId(match),
+ .n_desc = 0,
+ .n_value = sect.addr,
+ });
+ const block_local = &zld.locals.items[block_local_sym_index];
+ block_local.n_sect = zld.sectionId(match);
const block = try self.allocator.create(TextBlock);
errdefer self.allocator.destroy(block);
block.* = TextBlock.init(self.allocator);
- block.local_sym_index = local_sym_index;
+ block.local_sym_index = block_local_sym_index;
block.code = try self.allocator.dupe(u8, code);
block.size = sect.size;
block.alignment = sect.@"align";
- if (relocs.len > 0) {
- try self.parseRelocs(zld, relocs, block, 0);
+ try block.relocs.ensureTotalCapacity(relocs.len);
+ for (relocs) |rel| {
+ const out_rel: TextBlock.Relocation = outer: {
+ if (rel.r_extern == 0) {
+ const rel_sect_id = @intCast(u16, rel.r_symbolnum - 1);
+ const sect_sym_index = self.sections_as_symbols.get(rel_sect_id) orelse blk: {
+ const sect_sym_index = @intCast(u32, zld.locals.items.len);
+ const sect_sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{
+ self.name.?,
+ segmentName(sect),
+ sectionName(sect),
+ });
+ defer self.allocator.free(sect_sym_name);
+ try zld.locals.append(zld.allocator, .{
+ .n_strx = try zld.makeString(sect_sym_name),
+ .n_type = macho.N_SECT,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ });
+ try self.sections_as_symbols.putNoClobber(self.allocator, rel_sect_id, sect_sym_index);
+ break :blk sect_sym_index;
+ };
+ break :outer .{
+ .inner = rel,
+ .where = .local,
+ .where_index = sect_sym_index,
+ };
+ }
+
+ const rel_sym = self.symtab.items[rel.r_symbolnum];
+ const rel_sym_name = self.getString(rel_sym.n_strx);
+
+ if (Zld.symbolIsSect(rel_sym) and !Zld.symbolIsExt(rel_sym)) {
+ const where_index = self.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
+ break :outer .{
+ .inner = rel,
+ .where = .local,
+ .where_index = where_index,
+ };
+ }
+
+ const resolv = zld.symbol_resolver.get(rel_sym_name) orelse unreachable;
+ switch (resolv.where) {
+ .global => {
+ break :outer .{
+ .inner = rel,
+ .where = .local,
+ .where_index = resolv.local_sym_index,
+ };
+ },
+ .import => {
+ break :outer .{
+ .inner = rel,
+ .where = .import,
+ .where_index = resolv.where_index,
+ };
+ },
+ else => unreachable,
+ }
+ };
+ block.relocs.appendAssumeCapacity(out_rel);
}
if (zld.has_dices) {
@@ -791,44 +839,41 @@ 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.
- if (filtered_nlists.len > 0) {
- var contained = std.ArrayList(TextBlock.SymbolAtOffset).init(self.allocator);
- defer contained.deinit();
- try contained.ensureTotalCapacity(filtered_nlists.len);
-
- for (filtered_nlists) |nlist_with_index| {
- const sym = self.symbols.items[nlist_with_index.index];
- assert(sym.payload == .regular);
- const reg = &sym.payload.regular;
-
- reg.segment_id = match.seg;
- reg.section_id = match.sect;
-
- const stab: ?TextBlock.Stab = if (self.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| {
- if (reg.address >= range.start and reg.address < range.end) {
- break :blk TextBlock.Stab{
- .function = range.end - range.start,
- };
- }
+ var contained = std.ArrayList(TextBlock.SymbolAtOffset).init(self.allocator);
+ defer contained.deinit();
+ try contained.ensureTotalCapacity(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 stab: ?TextBlock.Stab = if (self.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| {
+ if (nlist.n_value >= range.start and nlist.n_value < range.end) {
+ break :blk TextBlock.Stab{
+ .function = range.end - range.start,
+ };
}
}
- if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
- break :blk .static;
- } else null;
-
- contained.appendAssumeCapacity(.{
- .local_sym_index = reg.local_sym_index,
- .offset = nlist_with_index.nlist.n_value - sect.addr,
- .stab = stab,
- });
- }
+ }
+ // TODO
+ // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
+ break :blk .static;
+ } else null;
- block.contained = contained.toOwnedSlice();
+ 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?
@@ -853,26 +898,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
}
}
-fn parseRelocs(
- self: *Object,
- zld: *Zld,
- relocs: []const macho.relocation_info,
- block: *TextBlock,
- base_addr: u64,
-) !void {
- var it = reloc.RelocIterator{
- .buffer = relocs,
- };
- var parser = reloc.Parser{
- .object = self,
- .zld = zld,
- .it = &it,
- .block = block,
- .base_addr = base_addr,
- };
- try parser.parse();
-}
-
pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*Symbol {
const symbol = blk: {
if (rel.r_extern == 1) {
diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig
deleted file mode 100644
index 37072b5618..0000000000
--- a/src/link/MachO/Symbol.zig
+++ /dev/null
@@ -1,285 +0,0 @@
-const Symbol = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const commands = @import("commands.zig");
-const macho = std.macho;
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const Dylib = @import("Dylib.zig");
-const Object = @import("Object.zig");
-const Zld = @import("Zld.zig");
-
-/// Offset into the string table.
-strx: u32,
-
-/// Index in GOT table for indirection.
-got_index: ?u32 = null,
-
-/// Index in stubs table for late binding.
-stubs_index: ?u32 = null,
-
-payload: union(enum) {
- regular: Regular,
- tentative: Tentative,
- proxy: Proxy,
- undef: Undefined,
-
- pub fn format(self: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- return switch (self) {
- .regular => |p| p.format(fmt, options, writer),
- .tentative => |p| p.format(fmt, options, writer),
- .proxy => |p| p.format(fmt, options, writer),
- .undef => |p| p.format(fmt, options, writer),
- };
- }
-},
-
-pub const Regular = struct {
- /// Linkage type.
- linkage: Linkage,
-
- /// Symbol address.
- address: u64 = 0,
-
- /// Segment ID
- segment_id: u16 = 0,
-
- /// Section ID
- section_id: u16 = 0,
-
- /// Whether the symbol is a weak ref.
- weak_ref: bool = false,
-
- /// Object file where to locate this symbol.
- /// null means self-reference.
- file: ?*Object = null,
-
- local_sym_index: u32 = 0,
-
- pub const Linkage = enum {
- translation_unit,
- linkage_unit,
- global,
- };
-
- pub fn format(self: Regular, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- try std.fmt.format(writer, "Regular {{ ", .{});
- try std.fmt.format(writer, ".linkage = {s}, ", .{self.linkage});
- try std.fmt.format(writer, ".address = 0x{x}, ", .{self.address});
- try std.fmt.format(writer, ".segment_id = {}, ", .{self.segment_id});
- try std.fmt.format(writer, ".section_id = {}, ", .{self.section_id});
- if (self.weak_ref) {
- try std.fmt.format(writer, ".weak_ref, ", .{});
- }
- if (self.file) |file| {
- try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
- }
- try std.fmt.format(writer, ".local_sym_index = {}, ", .{self.local_sym_index});
- try std.fmt.format(writer, "}}", .{});
- }
-
- pub fn sectionId(self: Regular, zld: *Zld) u8 {
- // TODO there might be a more generic way of doing this.
- var section: u8 = 0;
- for (zld.load_commands.items) |cmd, cmd_id| {
- if (cmd != .Segment) break;
- if (cmd_id == self.segment_id) {
- section += @intCast(u8, self.section_id) + 1;
- break;
- }
- section += @intCast(u8, cmd.Segment.sections.items.len);
- }
- return section;
- }
-};
-
-pub const Tentative = struct {
- /// Symbol size.
- size: u64,
-
- /// Symbol alignment as power of two.
- alignment: u16,
-
- /// File where this symbol was referenced.
- file: ?*Object = null,
-
- pub fn format(self: Tentative, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- try std.fmt.format(writer, "Tentative {{ ", .{});
- try std.fmt.format(writer, ".size = 0x{x}, ", .{self.size});
- try std.fmt.format(writer, ".alignment = 0x{x}, ", .{self.alignment});
- if (self.file) |file| {
- try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
- }
- try std.fmt.format(writer, "}}", .{});
- }
-};
-
-pub const Proxy = struct {
- /// Dylib where to locate this symbol.
- /// null means self-reference.
- file: ?*Dylib = null,
-
- local_sym_index: u32 = 0,
-
- pub fn dylibOrdinal(proxy: Proxy) u16 {
- const dylib = proxy.file orelse return 0;
- return dylib.ordinal.?;
- }
-
- pub fn format(self: Proxy, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- try std.fmt.format(writer, "Proxy {{ ", .{});
- if (self.file) |file| {
- try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
- }
- try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index});
- try std.fmt.format(writer, "}}", .{});
- }
-};
-
-pub const Undefined = struct {
- /// File where this symbol was referenced.
- /// null means synthetic, e.g., dyld_stub_binder.
- file: ?*Object = null,
-
- pub fn format(self: Undefined, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- try std.fmt.format(writer, "Undefined {{ ", .{});
- if (self.file) |file| {
- try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
- }
- try std.fmt.format(writer, "}}", .{});
- }
-};
-
-pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
- _ = fmt;
- _ = options;
- try std.fmt.format(writer, "Symbol {{", .{});
- try std.fmt.format(writer, ".strx = {d}, ", .{self.strx});
- if (self.got_index) |got_index| {
- try std.fmt.format(writer, ".got_index = {}, ", .{got_index});
- }
- if (self.stubs_index) |stubs_index| {
- try std.fmt.format(writer, ".stubs_index = {}, ", .{stubs_index});
- }
- try std.fmt.format(writer, "{}, ", .{self.payload});
- try std.fmt.format(writer, "}}", .{});
-}
-
-pub fn isTemp(symbol: Symbol, zld: *Zld) bool {
- const sym_name = zld.getString(symbol.strx);
- switch (symbol.payload) {
- .regular => |regular| {
- if (regular.linkage == .translation_unit) {
- return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
- }
- },
- else => {},
- }
- return false;
-}
-
-pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
- const nlist = nlist: {
- switch (symbol.payload) {
- .regular => |regular| {
- var nlist = macho.nlist_64{
- .n_strx = symbol.strx,
- .n_type = macho.N_SECT,
- .n_sect = regular.sectionId(zld),
- .n_desc = 0,
- .n_value = regular.address,
- };
-
- if (regular.linkage != .translation_unit) {
- nlist.n_type |= macho.N_EXT;
- }
- if (regular.linkage == .linkage_unit) {
- nlist.n_type |= macho.N_PEXT;
- nlist.n_desc |= macho.N_WEAK_DEF;
- }
-
- break :nlist nlist;
- },
- .tentative => {
- // TODO
- break :nlist macho.nlist_64{
- .n_strx = symbol.strx,
- .n_type = macho.N_UNDF,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = 0,
- };
- },
- .proxy => |proxy| {
- break :nlist macho.nlist_64{
- .n_strx = symbol.strx,
- .n_type = macho.N_UNDF | macho.N_EXT,
- .n_sect = 0,
- .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
- .n_value = 0,
- };
- },
- .undef => {
- // TODO
- break :nlist macho.nlist_64{
- .n_strx = symbol.strx,
- .n_type = macho.N_UNDF,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = 0,
- };
- },
- }
- };
- return nlist;
-}
-
-pub fn isStab(sym: macho.nlist_64) bool {
- return (macho.N_STAB & sym.n_type) != 0;
-}
-
-pub fn isPext(sym: macho.nlist_64) bool {
- return (macho.N_PEXT & sym.n_type) != 0;
-}
-
-pub fn isExt(sym: macho.nlist_64) bool {
- return (macho.N_EXT & sym.n_type) != 0;
-}
-
-pub fn isSect(sym: macho.nlist_64) bool {
- const type_ = macho.N_TYPE & sym.n_type;
- return type_ == macho.N_SECT;
-}
-
-pub fn isUndf(sym: macho.nlist_64) bool {
- const type_ = macho.N_TYPE & sym.n_type;
- return type_ == macho.N_UNDF;
-}
-
-pub fn isIndr(sym: macho.nlist_64) bool {
- const type_ = macho.N_TYPE & sym.n_type;
- return type_ == macho.N_INDR;
-}
-
-pub fn isAbs(sym: macho.nlist_64) bool {
- const type_ = macho.N_TYPE & sym.n_type;
- return type_ == macho.N_ABS;
-}
-
-pub fn isWeakDef(sym: macho.nlist_64) bool {
- return (sym.n_desc & macho.N_WEAK_DEF) != 0;
-}
-
-pub fn isWeakRef(sym: macho.nlist_64) bool {
- return (sym.n_desc & macho.N_WEAK_REF) != 0;
-}
diff --git a/src/link/MachO/TextBlock.zig b/src/link/MachO/TextBlock.zig
index 93763ded18..04cb33855c 100644
--- a/src/link/MachO/TextBlock.zig
+++ b/src/link/MachO/TextBlock.zig
@@ -5,10 +5,9 @@ const commands = @import("commands.zig");
const log = std.log.scoped(.text_block);
const macho = std.macho;
const mem = std.mem;
-const reloc = @import("reloc.zig");
const Allocator = mem.Allocator;
-const Relocation = reloc.Relocation;
+const Arch = std.Target.Cpu.Arch;
const Zld = @import("Zld.zig");
allocator: *Allocator,
@@ -102,6 +101,15 @@ pub const Stab = union(enum) {
}
};
+pub const Relocation = struct {
+ inner: macho.relocation_info,
+ where: enum {
+ local,
+ import,
+ },
+ where_index: u32,
+};
+
pub fn init(allocator: *Allocator) TextBlock {
return .{
.allocator = allocator,
@@ -137,18 +145,10 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
const source_addr = blk: {
const sym = zld.locals.items[self.local_sym_index];
- break :blk sym.payload.regular.address + rel.offset;
+ break :blk sym.n_value + rel.offset;
};
const target_addr = blk: {
- const is_via_got = switch (rel.payload) {
- .pointer_to_got => true,
- .page => |page| page.kind == .got,
- .page_off => |page_off| page_off.kind == .got,
- .load => |load| load.kind == .got,
- else => false,
- };
-
- if (is_via_got) {
+ if (isGotIndirection(rel, zld.target.?.cpu.arch)) {
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 = rel.target.got_index orelse {
@@ -228,31 +228,18 @@ pub fn print_this(self: *const TextBlock, zld: *Zld) void {
log.warn(" stab: {}", .{stab});
}
if (self.aliases.items.len > 0) {
- log.warn(" aliases:", .{});
- for (self.aliases.items) |index| {
- log.warn(" {}: {}", .{ index, zld.locals.items[index] });
- }
+ log.warn(" aliases: {any}", .{self.aliases.items});
}
if (self.references.count() > 0) {
- log.warn(" references:", .{});
- for (self.references.keys()) |index| {
- log.warn(" {}: {}", .{ index, zld.locals.items[index] });
- }
+ log.warn(" references: {any}", .{self.references.keys()});
}
if (self.contained) |contained| {
log.warn(" contained symbols:", .{});
for (contained) |sym_at_off| {
if (sym_at_off.stab) |stab| {
- log.warn(" {}: {}, stab: {}\n", .{
- sym_at_off.offset,
- zld.locals.items[sym_at_off.local_sym_index],
- stab,
- });
+ log.warn(" {}: {}, stab: {}", .{ sym_at_off.offset, sym_at_off.local_sym_index, stab });
} else {
- log.warn(" {}: {}\n", .{
- sym_at_off.offset,
- zld.locals.items[sym_at_off.local_sym_index],
- });
+ log.warn(" {}: {}", .{ sym_at_off.offset, sym_at_off.local_sym_index });
}
}
}
@@ -282,3 +269,22 @@ pub fn print(self: *const TextBlock, zld: *Zld) void {
}
self.print_this(zld);
}
+
+fn isGotIndirection(rel: macho.relocation_info, arch: Arch) bool {
+ return switch (arch) {
+ .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) {
+ .ARM64_RELOC_POINTER_TO_GOT,
+ .ARM64_RELOC_GOT_LOAD_PAGE21,
+ .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
+ => true,
+ else => false,
+ },
+ .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) {
+ .X86_64_RELOC_GOT,
+ .X86_64_RELOC_GOT_LOAD,
+ => true,
+ else => false,
+ },
+ else => unreachable,
+ };
+}
diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig
index d6dd9f597c..862e6b5b0c 100644
--- a/src/link/MachO/Zld.zig
+++ b/src/link/MachO/Zld.zig
@@ -105,7 +105,6 @@ imports: std.ArrayListUnmanaged(macho.nlist_64) = .{},
undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{},
symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
-object_mapping: std.AutoHashMapUnmanaged(u16, []u32) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
@@ -199,14 +198,6 @@ pub fn deinit(self: *Zld) void {
}
self.symbol_resolver.deinit(self.allocator);
- {
- var it = self.object_mapping.valueIterator();
- while (it.next()) |value_ptr| {
- self.allocator.free(value_ptr.*);
- }
- }
- self.object_mapping.deinit(self.allocator);
-
self.strtab.deinit(self.allocator);
// TODO dealloc all blocks
@@ -251,33 +242,33 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
try self.resolveSymbols();
log.warn("locals", .{});
- for (self.locals.items) |sym| {
- log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
+ for (self.locals.items) |sym, id| {
+ log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
log.warn("globals", .{});
- for (self.globals.items) |sym| {
- log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
+ for (self.globals.items) |sym, id| {
+ log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
log.warn("tentatives", .{});
- for (self.tentatives.items) |sym| {
- log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
+ for (self.tentatives.items) |sym, id| {
+ log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
log.warn("undefines", .{});
- for (self.undefs.items) |sym| {
- log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
+ for (self.undefs.items) |sym, id| {
+ log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
log.warn("imports", .{});
- for (self.imports.items) |sym| {
- log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym });
+ for (self.imports.items) |sym, id| {
+ log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym });
}
log.warn("symbol resolver", .{});
for (self.symbol_resolver.keys()) |key| {
- log.warn(" | {s} => {}", .{ key, self.symbol_resolver.get(key).? });
+ log.warn(" {s} => {}", .{ key, self.symbol_resolver.get(key).? });
}
log.warn("mappings", .{});
@@ -285,7 +276,7 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
const object_id = @intCast(u16, id);
log.warn(" in object {s}", .{object.name.?});
for (object.symtab.items) |sym, sym_id| {
- if (self.localSymIndex(object_id, @intCast(u32, sym_id))) |local_id| {
+ if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| {
log.warn(" | {d} => {d}", .{ sym_id, local_id });
} else {
log.warn(" | {d} no local mapping for {s}", .{ sym_id, object.getString(sym.n_strx) });
@@ -293,8 +284,19 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
}
}
+ try self.parseTextBlocks();
+
+ var it = self.blocks.iterator();
+ while (it.next()) |entry| {
+ const seg = self.load_commands.items[entry.key_ptr.seg].Segment;
+ const sect = seg.sections.items[entry.key_ptr.sect];
+
+ log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) });
+ log.warn(" {}", .{sect});
+ entry.value_ptr.*.print(self);
+ }
+
return error.TODO;
- // try self.parseTextBlocks();
// try self.sortSections();
// try self.addRpaths(args.rpaths);
// try self.addDataInCodeLC();
@@ -305,16 +307,6 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
// self.allocateLinkeditSegment();
// try self.allocateTextBlocks();
- // // var it = self.blocks.iterator();
- // // while (it.next()) |entry| {
- // // const seg = self.load_commands.items[entry.key_ptr.seg].Segment;
- // // const sect = seg.sections.items[entry.key_ptr.sect];
-
- // // log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) });
- // // log.warn(" {}", .{sect});
- // // entry.value_ptr.*.print(self);
- // // }
-
// try self.flush();
}
@@ -1414,10 +1406,6 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void {
log.warn("resolving symbols in '{s}'", .{object.name});
- const mapping = try self.allocator.alloc(u32, object.symtab.items.len);
- mem.set(u32, mapping, 0);
- try self.object_mapping.putNoClobber(self.allocator, object_id, mapping);
-
for (object.symtab.items) |sym, id| {
const sym_id = @intCast(u32, id);
const sym_name = object.getString(sym.n_strx);
@@ -1464,7 +1452,7 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void {
.n_desc = 0,
.n_value = sym.n_value,
});
- mapping[sym_id] = local_sym_index;
+ try object.symbol_mapping.putNoClobber(self.allocator, sym_id, local_sym_index);
// If the symbol's scope is not local aka translation unit, then we need work out
// if we should save the symbol as a global, or potentially flag the error.
@@ -2987,15 +2975,6 @@ pub fn getString(self: *Zld, off: u32) []const u8 {
return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));
}
-fn localSymIndex(self: Zld, object_id: u16, orig_id: u32) ?u32 {
- const mapping = self.object_mapping.get(object_id) orelse return null;
- const local_sym_index = mapping[orig_id];
- if (local_sym_index == 0) {
- return null;
- }
- return local_sym_index;
-}
-
pub fn symbolIsStab(sym: macho.nlist_64) bool {
return (macho.N_STAB & sym.n_type) != 0;
}
@@ -3045,10 +3024,9 @@ pub fn symbolIsNull(sym: macho.nlist_64) bool {
return sym.n_value == 0 and sym.n_desc == 0 and sym.n_type == 0 and sym.n_strx == 0 and sym.n_sect == 0;
}
-pub fn symbolIsTemp(self: Zld, sym: macho.nlist_64) bool {
+pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool {
if (!symbolIsSect(sym)) return false;
if (symbolIsExt(sym)) return false;
- const sym_name = self.getString(sym.n_strx);
return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
}