zld: replace parsed reloc with a simple wrapper around macho.relocation_info

This commit is contained in:
Jakub Konka 2021-07-16 17:18:53 +02:00
parent 5a2bea2931
commit 54a403d4ff
6 changed files with 320 additions and 597 deletions

View File

@ -581,7 +581,6 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/src/link/MachO/DebugSymbols.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.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"

View File

@ -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;

View File

@ -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;
{
// 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;
};
// // 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 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_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);
// 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";
// 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);
}
// 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);
// 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,
});
}
}
// 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;
// // 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);
}
// 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);
}
// 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,
};
// 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;
}
}
// 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);
}
}
// 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;
// // 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);
}
// 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);
}
// try self.text_blocks.append(self.allocator, block);
// }
break :next;
}
// 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);
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;
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);
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,
};
}
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;
}
// TODO
// 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,
});
}
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) {

View File

@ -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;
}

View File

@ -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,
};
}

View File

@ -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");
}