zld: update relocs and start prepping for segment allocs

This commit is contained in:
Jakub Konka 2021-07-07 13:47:04 +02:00
parent dfa69e3c30
commit a04bc1ed14
3 changed files with 140 additions and 305 deletions

View File

@ -48,8 +48,6 @@ dwarf_debug_ranges_index: ?u16 = null,
symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
initializers: std.ArrayListUnmanaged(u32) = .{},
data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
symbols: std.ArrayListUnmanaged(*Symbol) = .{},
@ -157,7 +155,6 @@ pub fn deinit(self: *Object) void {
}
self.load_commands.deinit(self.allocator);
self.data_in_code_entries.deinit(self.allocator);
self.initializers.deinit(self.allocator);
self.symtab.deinit(self.allocator);
self.strtab.deinit(self.allocator);
self.symbols.deinit(self.allocator);
@ -573,6 +570,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
symbol.payload = .{
.regular = .{
.linkage = .translation_unit,
.address = sect.addr,
.segment_id = match.seg,
.section_id = match.sect,
.file = self,
@ -657,6 +655,13 @@ pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol {
});
defer self.allocator.free(name);
const symbol = try Symbol.new(self.allocator, name);
symbol.payload = .{
.regular = .{
.linkage = .translation_unit,
.address = sect.addr,
.file = self,
},
};
try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
break :symbol symbol;
};
@ -666,22 +671,6 @@ pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol {
return symbol;
}
pub fn parseInitializers(self: *Object) !void {
const index = self.mod_init_func_section_index orelse return;
const section = self.sections.items[index];
log.debug("parsing initializers in {s}", .{self.name.?});
// Parse C++ initializers
const relocs = section.relocs orelse unreachable;
try self.initializers.ensureCapacity(self.allocator, relocs.len);
for (relocs) |rel| {
self.initializers.appendAssumeCapacity(rel.target.symbol);
}
mem.reverse(u32, self.initializers.items);
}
fn parseSymtab(self: *Object) !void {
const index = self.symtab_cmd_index orelse return;
const symtab_cmd = self.load_commands.items[index].Symtab;

View File

@ -120,16 +120,6 @@ pub const Output = struct {
install_name: ?[]const u8 = null,
};
const TlvOffset = struct {
source_addr: u64,
offset: u64,
fn cmp(context: void, a: TlvOffset, b: TlvOffset) bool {
_ = context;
return a.source_addr < b.source_addr;
}
};
pub const TextBlock = struct {
local_sym_index: u32,
aliases: ?[]u32 = null,
@ -274,12 +264,11 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
try self.parseLibs(args.libs, args.syslibroot);
try self.resolveSymbols();
try self.parseTextBlocks();
try self.sortSections();
try self.addRpaths(args.rpaths);
try self.addDataInCodeLC();
try self.addCodeSignatureLC();
return error.TODO;
// try self.updateMetadata();
// try self.sortSections();
// try self.addRpaths(args.rpaths);
// try self.addDataInCodeLC();
// try self.addCodeSignatureLC();
// try self.allocateTextSegment();
// try self.allocateDataConstSegment();
// try self.allocateDataSegment();
@ -343,106 +332,6 @@ fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !voi
}
}
fn mapAndUpdateSections(
self: *Zld,
object: *Object,
source_sect_id: u16,
target_seg_id: u16,
target_sect_id: u16,
) !void {
const source_sect = &object.sections.items[source_sect_id];
const target_seg = &self.load_commands.items[target_seg_id].Segment;
const target_sect = &target_seg.sections.items[target_sect_id];
const alignment = try math.powi(u32, 2, target_sect.@"align");
const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment);
const size = mem.alignForwardGeneric(u64, source_sect.inner.size, alignment);
log.debug("{s}: '{s},{s}' mapped to '{s},{s}' from 0x{x} to 0x{x}", .{
object.name.?,
segmentName(source_sect.inner),
sectionName(source_sect.inner),
segmentName(target_sect.*),
sectionName(target_sect.*),
offset,
offset + size,
});
log.debug(" | flags 0x{x}", .{source_sect.inner.flags});
source_sect.target_map = .{
.segment_id = target_seg_id,
.section_id = target_sect_id,
.offset = @intCast(u32, offset),
};
target_sect.size = offset + size;
}
fn updateMetadata(self: *Zld) !void {
for (self.objects.items) |object| {
// Find ideal section alignment and update section mappings
for (object.sections.items) |sect, sect_id| {
const match = (try self.getMatchingSection(sect.inner)) orelse {
log.debug("{s}: unhandled section type 0x{x} for '{s},{s}'", .{
object.name.?,
sect.inner.flags,
segmentName(sect.inner),
sectionName(sect.inner),
});
continue;
};
const target_seg = &self.load_commands.items[match.seg].Segment;
const target_sect = &target_seg.sections.items[match.sect];
target_sect.@"align" = math.max(target_sect.@"align", sect.inner.@"align");
try self.mapAndUpdateSections(object, @intCast(u16, sect_id), match.seg, match.sect);
}
}
tlv_align: {
const has_tlv =
self.tlv_section_index != null or
self.tlv_data_section_index != null or
self.tlv_bss_section_index != null;
if (!has_tlv) break :tlv_align;
const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
if (self.tlv_section_index) |index| {
const sect = &seg.sections.items[index];
sect.@"align" = 3; // __thread_vars is always 8byte aligned
}
// Apparently __tlv_data and __tlv_bss need to have matching alignment, so fix it up.
// <rdar://problem/24221680> All __thread_data and __thread_bss sections must have same alignment
// https://github.com/apple-opensource/ld64/blob/e28c028b20af187a16a7161d89e91868a450cadc/src/ld/ld.cpp#L1172
const data_align: u32 = data: {
if (self.tlv_data_section_index) |index| {
const sect = &seg.sections.items[index];
break :data sect.@"align";
}
break :tlv_align;
};
const bss_align: u32 = bss: {
if (self.tlv_bss_section_index) |index| {
const sect = &seg.sections.items[index];
break :bss sect.@"align";
}
break :tlv_align;
};
const max_align = math.max(data_align, bss_align);
if (self.tlv_data_section_index) |index| {
const sect = &seg.sections.items[index];
sect.@"align" = max_align;
}
if (self.tlv_bss_section_index) |index| {
const sect = &seg.sections.items[index];
sect.@"align" = max_align;
}
}
}
pub const MatchingSection = struct {
seg: u16,
sect: u16,
@ -946,36 +835,6 @@ fn sortSections(self: *Zld) !void {
maybe_index.* = new_index;
}
}
for (self.objects.items) |object| {
for (object.sections.items) |*sect| {
const target_map = sect.target_map orelse continue;
const new_index = blk: {
if (self.text_segment_cmd_index.? == target_map.segment_id) {
break :blk text_index_mapping.get(target_map.section_id) orelse unreachable;
} else if (self.data_const_segment_cmd_index.? == target_map.segment_id) {
break :blk data_const_index_mapping.get(target_map.section_id) orelse unreachable;
} else if (self.data_segment_cmd_index.? == target_map.segment_id) {
break :blk data_index_mapping.get(target_map.section_id) orelse unreachable;
} else unreachable;
};
log.debug("remapping in {s}: '{s},{s}': {} => {}", .{
object.name.?,
segmentName(sect.inner),
sectionName(sect.inner),
target_map.section_id,
new_index,
});
sect.target_map = .{
.segment_id = target_map.segment_id,
.section_id = new_index,
.offset = target_map.offset,
};
}
}
}
fn allocateTextSegment(self: *Zld) !void {
@ -1431,6 +1290,7 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
symbol.payload = .{
.regular = .{
.linkage = .translation_unit,
.address = sym.n_value,
.weak_ref = Symbol.isWeakRef(sym),
.file = object,
.local_sym_index = @intCast(u32, self.locals.items.len),
@ -1470,6 +1330,7 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
symbol.payload = .{
.regular = .{
.linkage = linkage,
.address = sym.n_value,
.weak_ref = Symbol.isWeakRef(sym),
.file = object,
},
@ -1672,80 +1533,6 @@ fn parseTextBlocks(self: *Zld) !void {
}
}
fn resolveRelocsAndWriteSections(self: *Zld) !void {
for (self.objects.items) |object| {
log.debug("relocating object {s}", .{object.name});
for (object.sections.items) |sect| {
if (sectionType(sect.inner) == macho.S_MOD_INIT_FUNC_POINTERS or
sectionType(sect.inner) == macho.S_MOD_TERM_FUNC_POINTERS) continue;
const segname = segmentName(sect.inner);
const sectname = sectionName(sect.inner);
log.debug("relocating section '{s},{s}'", .{ segname, sectname });
// Get target mapping
const target_map = sect.target_map orelse {
log.debug("no mapping for '{s},{s}'; skipping", .{ segname, sectname });
continue;
};
const target_seg = self.load_commands.items[target_map.segment_id].Segment;
const target_sect = target_seg.sections.items[target_map.section_id];
const target_sect_addr = target_sect.addr + target_map.offset;
const target_sect_off = target_sect.offset + target_map.offset;
if (sect.relocs) |relocs| {
for (relocs) |rel| {
const source_addr = target_sect_addr + rel.offset;
var args: reloc.Relocation.ResolveArgs = .{
.source_addr = source_addr,
.target_addr = undefined,
};
switch (rel.@"type") {
.unsigned => {
args.target_addr = try self.relocTargetAddr(object, rel.target);
const unsigned = rel.cast(reloc.Unsigned) orelse unreachable;
if (unsigned.subtractor) |subtractor| {
args.subtractor = try self.relocTargetAddr(object, subtractor);
}
if (rel.target == .section) {
const source_sect = object.sections.items[rel.target.section];
args.source_source_sect_addr = sect.inner.addr;
args.source_target_sect_addr = source_sect.inner.addr;
}
},
.got_page, .got_page_off, .got_load, .got, .pointer_to_got => {
const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
const got = dc_seg.sections.items[self.got_section_index.?];
const sym = object.symbols.items[rel.target.symbol];
const got_index = sym.got_index orelse {
log.err("expected GOT index relocating symbol '{s}'", .{sym.name});
log.err("this is an internal linker error", .{});
return error.FailedToResolveRelocationTarget;
};
args.target_addr = got.addr + got_index * @sizeOf(u64);
},
else => |tt| {
if (tt == .signed and rel.target == .section) {
const source_sect = object.sections.items[rel.target.section];
args.source_source_sect_addr = sect.inner.addr;
args.source_target_sect_addr = source_sect.inner.addr;
}
args.target_addr = try self.relocTargetAddr(object, rel.target);
},
}
try rel.resolve(args);
}
}
}
}
}
fn populateMetadata(self: *Zld) !void {
if (self.pagezero_segment_cmd_index == null) {
self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);

View File

@ -48,33 +48,24 @@ pub const Relocation = struct {
/// => * is unreachable
is_64bit: bool,
source_sect_addr: ?u64 = null,
pub fn resolve(self: Unsigned, base: Relocation, source_addr: u64, target_addr: u64) !void {
// const addend = if (unsigned.base.target == .section)
// unsigned.addend - @intCast(i64, args.source_target_sect_addr.?)
// else
// unsigned.addend;
const addend = if (self.source_sect_addr) |addr|
self.addend - addr
else
self.addend;
// const result = if (args.subtractor) |subtractor|
// @intCast(i64, args.target_addr) - @intCast(i64, subtractor) + addend
// else
// @intCast(i64, args.target_addr) + addend;
const result = if (self.subtractor) |subtractor|
@intCast(i64, target_addr) - @intCast(i64, subtractor.payload.regular.address) + addend
else
@intCast(i64, target_addr) + addend;
// log.debug(" | calculated addend 0x{x}", .{addend});
// log.debug(" | calculated unsigned value 0x{x}", .{result});
// if (unsigned.is_64bit) {
// mem.writeIntLittle(
// u64,
// unsigned.base.code[0..8],
// @bitCast(u64, result),
// );
// } else {
// mem.writeIntLittle(
// u32,
// unsigned.base.code[0..4],
// @truncate(u32, @bitCast(u64, result)),
// );
// }
if (self.is_64bit) {
mem.writeIntLittle(u64, base.block.code[base.offset..][0..8], @bitCast(u64, result));
} else {
mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], @truncate(u32, @bitCast(u64, result)));
}
}
pub fn format(self: Unsigned, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
@ -191,56 +182,119 @@ pub const Relocation = struct {
pub fn resolve(self: PageOff, base: Relocation, source_addr: u64, target_addr: u64) !void {
switch (self.kind) {
.page => {
// const target_addr = if (page_off.addend) |addend| args.target_addr + addend else args.target_addr;
// const narrowed = @truncate(u12, target_addr);
const actual_target_addr = if (self.addend) |addend| target_addr + addend else target_addr;
const narrowed = @truncate(u12, actual_target_addr);
// log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
// log.debug(" | {s} opcode", .{page_off.op_kind});
const op_kind = self.op_kind orelse unreachable;
var inst: aarch64.Instruction = blk: {
switch (op_kind) {
.arithmetic => {
break :blk .{
.add_subtract_immediate = mem.bytesToValue(
meta.TagPayload(
aarch64.Instruction,
aarch64.Instruction.add_subtract_immediate,
),
base.block.code[base.offset..][0..4],
),
};
},
.load => {
break :blk .{
.load_store_register = mem.bytesToValue(
meta.TagPayload(
aarch64.Instruction,
aarch64.Instruction.load_store_register,
),
base.block.code[base.offset..][0..4],
),
};
},
}
};
// var inst = page_off.inst;
// if (page_off.op_kind == .arithmetic) {
// inst.add_subtract_immediate.imm12 = narrowed;
// } else {
// const offset: u12 = blk: {
// if (inst.load_store_register.size == 0) {
// if (inst.load_store_register.v == 1) {
// // 128-bit SIMD is scaled by 16.
// break :blk try math.divExact(u12, narrowed, 16);
// }
// // Otherwise, 8-bit SIMD or ldrb.
// break :blk narrowed;
// } else {
// const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
// break :blk try math.divExact(u12, narrowed, denom);
// }
// };
// inst.load_store_register.offset = offset;
// }
// mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
if (op_kind == .arithmetic) {
inst.add_subtract_immediate.imm12 = narrowed;
} else {
const offset: u12 = blk: {
if (inst.load_store_register.size == 0) {
if (inst.load_store_register.v == 1) {
// 128-bit SIMD is scaled by 16.
break :blk try math.divExact(u12, narrowed, 16);
}
// Otherwise, 8-bit SIMD or ldrb.
break :blk narrowed;
} else {
const denom: u4 = try math.powi(u4, 2, inst.load_store_register.size);
break :blk try math.divExact(u12, narrowed, denom);
}
};
inst.load_store_register.offset = offset;
}
mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
},
.got => {
// const narrowed = @truncate(u12, args.target_addr);
// log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
// var inst = page_off.inst;
// const offset = try math.divExact(u12, narrowed, 8);
// inst.load_store_register.offset = offset;
// mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
const narrowed = @truncate(u12, target_addr);
var inst = mem.bytesToValue(
meta.TagPayload(
aarch64.Instruction,
aarch64.Instruction.load_store_register,
),
base.block.code[base.offset..][0..4],
);
const offset = try math.divExact(u12, narrowed, 8);
inst.load_store_register.offset = offset;
mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
},
.tlvp => {
// const narrowed = @truncate(u12, args.target_addr);
// log.debug(" | narrowed address within the page 0x{x}", .{narrowed});
// var inst = page_off.inst;
// inst.add_subtract_immediate.imm12 = narrowed;
// mem.writeIntLittle(u32, page_off.base.code[0..4], inst.toU32());
const RegInfo = struct {
rd: u5,
rn: u5,
size: u1,
};
const reg_info: RegInfo = blk: {
if (isArithmeticOp(base.block.code[base.offset..][0..4])) {
const inst = mem.bytesToValue(
meta.TagPayload(
aarch64.Instruction,
aarch64.Instruction.add_subtract_immediate,
),
base.block.code[base.offset..][0..4],
);
break :blk .{
.rd = inst.rd,
.rn = inst.rn,
.size = inst.sf,
};
} else {
const inst = mem.bytesToValue(
meta.TagPayload(
aarch64.Instruction,
aarch64.Instruction.load_store_register,
),
base.block.code[base.offset..][0..4],
);
break :blk .{
.rd = inst.rt,
.rn = inst.rn,
.size = @truncate(u1, inst.size),
};
}
};
const narrowed = @truncate(u12, target_addr);
var inst = aarch64.Instruction{
.add_subtract_immediate = .{
.rd = reg_info.rd,
.rn = reg_info.rn,
.imm12 = narrowed,
.sh = 0,
.s = 0,
.op = 0,
.sf = reg_info.size,
},
};
mem.writeIntLittle(u32, base.block.code[base.offset..][0..4], inst.toU32());
},
}
}
@ -661,12 +715,17 @@ pub const Parser = struct {
mem.readIntLittle(i64, self.block.code[parsed.offset..][0..8])
else
mem.readIntLittle(i32, self.block.code[parsed.offset..][0..4]);
const source_sect_addr = if (rel.r_extern == 0) blk: {
if (parsed.target.payload == .regular) break :blk parsed.target.payload.regular.address;
break :blk null;
} else null;
parsed.payload = .{
.unsigned = .{
.subtractor = self.subtractor,
.is_64bit = is_64bit,
.addend = addend,
.source_sect_addr = source_sect_addr,
},
};