zld: move should_rebase logic into Symbol

This commit is contained in:
Jakub Konka 2021-07-07 10:36:41 +02:00
parent dbd2eb7c7f
commit 555b66c255
4 changed files with 112 additions and 153 deletions

View File

@ -339,6 +339,7 @@ const TextBlockParser = struct {
zld: *Zld,
nlists: []NlistWithIndex,
index: u32 = 0,
match: Zld.MatchingSection,
fn peek(self: *TextBlockParser) ?NlistWithIndex {
return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null;
@ -405,6 +406,8 @@ const TextBlockParser = struct {
const senior_nlist = aliases.pop();
const senior_sym = self.zld.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;
const start_addr = senior_nlist.nlist.n_value - self.section.addr;
const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size;
@ -417,6 +420,11 @@ const TextBlockParser = struct {
try out.ensureTotalCapacity(aliases.items.len);
for (aliases.items) |alias| {
out.appendAssumeCapacity(alias.index);
const sym = self.zld.locals.items[alias.index];
const reg = &sym.payload.regular;
reg.segment_id = self.match.seg;
reg.section_id = self.match.sect;
}
break :blk out.toOwnedSlice();
} else null;
@ -439,6 +447,18 @@ const TextBlockParser = struct {
try self.object.parseRelocs(self.zld, relocs, block, start_addr);
}
const is_zerofill = blk: {
const tseg = self.zld.load_commands.items[self.match.seg].Segment;
const tsect = tseg.sections.items[self.match.sect];
const tsect_type = sectionType(tsect);
break :blk tsect_type == macho.S_ZEROFILL or
tsect_type == macho.S_THREAD_LOCAL_ZEROFILL or
tsect_type == macho.S_THREAD_LOCAL_VARIABLES;
};
if (is_zerofill) {
mem.set(u8, block.code, 0);
}
self.index += 1;
return block;
@ -511,28 +531,16 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
.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.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? });
continue;
}
}
reg.segment_id = match.seg;
reg.section_id = match.sect;
}
if (block.aliases) |aliases| {
for (aliases) |alias| {
const sym = zld.locals.items[alias];
const reg = &sym.payload.regular;
reg.segment_id = match.seg;
reg.section_id = match.sect;
const sym = zld.locals.items[block.local_sym_index];
const reg = &sym.payload.regular;
if (reg.file) |file| {
if (file != self) {
log.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? });
continue;
}
}
@ -587,6 +595,18 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
try self.parseRelocs(zld, relocs, block, 0);
}
const is_zerofill = blk: {
const tseg = zld.load_commands.items[match.seg].Segment;
const tsect = tseg.sections.items[match.sect];
const tsect_type = sectionType(tsect);
break :blk tsect_type == macho.S_ZEROFILL or
tsect_type == macho.S_THREAD_LOCAL_ZEROFILL or
tsect_type == macho.S_THREAD_LOCAL_VARIABLES;
};
if (is_zerofill) {
mem.set(u8, block.code, 0);
}
if (zld.last_text_block) |last| {
last.next = block;
block.prev = last;

View File

@ -2,6 +2,7 @@ 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;
@ -57,6 +58,8 @@ pub const Regular = struct {
local_sym_index: u32 = 0,
should_rebase: bool = false,
pub const Linkage = enum {
translation_unit,
linkage_unit,
@ -74,6 +77,9 @@ pub const Regular = struct {
if (self.weak_ref) {
try std.fmt.format(writer, ".weak_ref, ", .{});
}
if (self.should_rebase) {
try std.fmt.format(writer, ".should_rebase, ", .{});
}
if (self.file) |file| {
try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
}
@ -108,8 +114,8 @@ pub const Proxy = struct {
/// Dynamic binding info - spots within the final
/// executable where this proxy is referenced from.
bind_info: std.ArrayListUnmanaged(struct {
segment_id: u16,
address: u64,
local_sym_index: u32,
offset: u32,
}) = .{},
/// Dylib where to locate this symbol.
@ -198,6 +204,17 @@ pub fn isTemp(symbol: Symbol) bool {
return false;
}
pub fn needsTlvOffset(self: Symbol, zld: *Zld) bool {
if (self.payload != .regular) return false;
const reg = self.payload.regular;
const seg = zld.load_command.items[reg.segment_id].Segment;
const sect = seg.sections.items[reg.section_id];
const sect_type = commands.sectionType(sect);
return sect_type == macho.S_THREAD_LOCAL_VARIABLES;
}
pub fn asNlist(symbol: *Symbol, strtab: *StringTable) !macho.nlist_64 {
const n_strx = try strtab.getOrPut(symbol.name);
const nlist = nlist: {

View File

@ -107,8 +107,6 @@ locals: std.ArrayListUnmanaged(*Symbol) = .{},
imports: std.ArrayListUnmanaged(*Symbol) = .{},
globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{},
threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction
local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
stubs: std.ArrayListUnmanaged(*Symbol) = .{},
got_entries: std.ArrayListUnmanaged(*Symbol) = .{},
@ -197,8 +195,6 @@ pub fn init(allocator: *Allocator) !Zld {
}
pub fn deinit(self: *Zld) void {
self.threadlocal_offsets.deinit(self.allocator);
self.local_rebases.deinit(self.allocator);
self.stubs.deinit(self.allocator);
self.got_entries.deinit(self.allocator);
@ -225,8 +221,6 @@ pub fn deinit(self: *Zld) void {
}
self.dylibs.deinit(self.allocator);
self.globals.deinit(self.allocator);
for (self.imports.items) |sym| {
sym.deinit(self.allocator);
self.allocator.destroy(sym);
@ -239,6 +233,7 @@ pub fn deinit(self: *Zld) void {
}
self.locals.deinit(self.allocator);
self.globals.deinit(self.allocator);
self.strtab.deinit();
}
@ -290,7 +285,6 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
// try self.allocateDataSegment();
// self.allocateLinkeditSegment();
// try self.allocateSymbols();
// try self.allocateProxyBindAddresses();
// try self.flush();
}
@ -449,7 +443,7 @@ fn updateMetadata(self: *Zld) !void {
}
}
const MatchingSection = struct {
pub const MatchingSection = struct {
seg: u16,
sect: u16,
};
@ -1140,31 +1134,6 @@ fn allocateSymbols(self: *Zld) !void {
}
}
fn allocateProxyBindAddresses(self: *Zld) !void {
for (self.objects.items) |object| {
for (object.sections.items) |sect| {
const relocs = sect.relocs orelse continue;
for (relocs) |rel| {
if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased
if (rel.target != .symbol) continue;
const sym = object.symbols.items[rel.target.symbol];
if (sym.payload != .proxy) continue;
const target_map = sect.target_map orelse continue;
const target_seg = self.load_commands.items[target_map.segment_id].Segment;
const target_sect = target_seg.sections.items[target_map.section_id];
try sym.payload.proxy.bind_info.append(self.allocator, .{
.segment_id = target_map.segment_id,
.address = target_sect.addr + target_map.offset + rel.offset,
});
}
}
}
}
fn writeStubHelperCommon(self: *Zld) !void {
const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
@ -1748,72 +1717,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
args.source_source_sect_addr = sect.inner.addr;
args.source_target_sect_addr = source_sect.inner.addr;
}
const sect_type = sectionType(target_sect);
const should_rebase = rebase: {
if (!unsigned.is_64bit) break :rebase false;
// 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 (self.data_segment_cmd_index) |idx| {
if (target_map.segment_id == idx) {
break :blk true;
}
}
if (self.data_const_segment_cmd_index) |idx| {
if (target_map.segment_id == idx) {
break :blk true;
}
}
break :blk false;
};
if (!is_right_segment) break :rebase false;
if (sect_type != macho.S_LITERAL_POINTERS and
sect_type != macho.S_REGULAR)
{
break :rebase false;
}
if (rel.target == .symbol) {
const sym = object.symbols.items[rel.target.symbol];
if (sym.payload == .proxy) {
break :rebase false;
}
}
break :rebase true;
};
if (should_rebase) {
try self.local_rebases.append(self.allocator, .{
.offset = source_addr - target_seg.inner.vmaddr,
.segment_id = target_map.segment_id,
});
}
// TLV is handled via a separate offset mechanism.
// Calculate the offset to the initializer.
if (sect_type == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
// TODO we don't want to save offset to tlv_bootstrap
if (mem.eql(u8, object.symbols.items[rel.target.symbol].name, "__tlv_bootstrap")) break :tlv;
const base_addr = blk: {
if (self.tlv_data_section_index) |index| {
const tlv_data = target_seg.sections.items[index];
break :blk tlv_data.addr;
} else {
const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
break :blk tlv_bss.addr;
}
};
// Since we require TLV data to always preceed TLV bss section, we calculate
// offsets wrt to the former if it is defined; otherwise, wrt to the latter.
try self.threadlocal_offsets.append(self.allocator, .{
.source_addr = args.source_addr,
.offset = args.target_addr - base_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;
@ -1839,34 +1742,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void {
try rel.resolve(args);
}
}
log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{
segname,
sectname,
object.name,
target_sect_off,
target_sect_off + sect.code.len,
});
if (sectionType(target_sect) == macho.S_ZEROFILL or
sectionType(target_sect) == macho.S_THREAD_LOCAL_ZEROFILL or
sectionType(target_sect) == macho.S_THREAD_LOCAL_VARIABLES)
{
log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{
segmentName(target_sect),
sectionName(target_sect),
target_sect_off,
target_sect_off + sect.code.len,
});
// Zero-out the space
var zeroes = try self.allocator.alloc(u8, sect.code.len);
defer self.allocator.free(zeroes);
mem.set(u8, zeroes, 0);
try self.file.?.pwriteAll(zeroes, target_sect_off);
} else {
try self.file.?.pwriteAll(sect.code, target_sect_off);
}
}
}
}

View File

@ -1,6 +1,7 @@
const std = @import("std");
const aarch64 = @import("../../codegen/aarch64.zig");
const assert = std.debug.assert;
const commands = @import("commands.zig");
const log = std.log.scoped(.reloc);
const macho = std.macho;
const math = std.math;
@ -567,14 +568,60 @@ pub const Parser = struct {
const index = @intCast(u32, self.zld.got_entries.items.len);
out_rel.target.got_index = index;
try self.zld.got_entries.append(self.zld.allocator, out_rel.target);
log.debug("adding GOT entry for symbol {s} at index {}", .{ out_rel.target.name, index });
}
if (out_rel.payload == .branch) {
log.debug("adding GOT entry for symbol {s} at index {}", .{ out_rel.target.name, index });
} else if (out_rel.payload == .unsigned) {
const sym = out_rel.target;
switch (sym.payload) {
.proxy => {
try sym.payload.proxy.bind_info.append(self.zld.allocator, .{
.local_sym_index = self.block.local_sym_index,
.offset = out_rel.offset,
});
},
else => {
const source_sym = self.zld.locals.items[self.block.local_sym_index];
const source_reg = &source_sym.payload.regular;
const seg = self.zld.load_commands.items[source_reg.segment_id].Segment;
const sect = seg.sections.items[source_reg.section_id];
const sect_type = commands.sectionType(sect);
const should_rebase = rebase: {
if (!out_rel.payload.unsigned.is_64bit) break :rebase false;
// 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 (self.zld.data_segment_cmd_index) |idx| {
if (source_reg.segment_id == idx) {
break :blk true;
}
}
if (self.zld.data_const_segment_cmd_index) |idx| {
if (source_reg.segment_id == idx) {
break :blk true;
}
}
break :blk false;
};
if (!is_right_segment) break :rebase false;
if (sect_type != macho.S_LITERAL_POINTERS and
sect_type != macho.S_REGULAR)
{
break :rebase false;
}
break :rebase true;
};
source_reg.should_rebase = should_rebase;
},
}
} else if (out_rel.payload == .branch) blk: {
const sym = out_rel.target;
if (sym.stubs_index != null) continue;
if (sym.payload != .proxy) continue;
if (sym.stubs_index != null) break :blk;
if (sym.payload != .proxy) break :blk;
const index = @intCast(u32, self.zld.stubs.items.len);
sym.stubs_index = index;