zld: resolve stubs and GOT entries

This commit is contained in:
Jakub Konka 2021-04-07 13:58:12 +02:00
parent 46cc214f2d
commit b667fe2c62
3 changed files with 149 additions and 19 deletions

View File

@ -207,7 +207,7 @@ pub fn parseSections(self: *Object) !void {
};
// Parse relocations
var relocs: ?[]*Relocation = if (sect.nreloc > 0) relocs: {
section.relocs = if (sect.nreloc > 0) relocs: {
var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc);
defer self.allocator.free(raw_relocs);

View File

@ -87,8 +87,14 @@ mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
const GotEntry = struct {
tag: enum {
local,
import,
},
index: u32,
target_addr: u64,
file: u16,
local_index: u32,
};
const MappingKey = struct {
@ -273,7 +279,8 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
try self.allocateDataSegment();
self.allocateLinkeditSegment();
try self.allocateSymbols();
self.printSymtab();
try self.allocateStubsAndGotEntries();
self.printDebug();
// try self.writeStubHelperCommon();
// try self.resolveRelocsAndWriteSections();
// try self.flush();
@ -957,7 +964,7 @@ fn allocateSymbols(self: *Zld) !void {
const target_addr = target_sect.addr + target_mapping.offset;
const n_value = source_sym.inner.n_value - source_sect.addr + target_addr;
log.warn("resolving '{s}' symbol at 0x{x}", .{ entry.key, n_value });
log.debug("resolving '{s}' symbol at 0x{x}", .{ entry.key, n_value });
// TODO there might be a more generic way of doing this.
var n_sect: u8 = 0;
@ -979,6 +986,41 @@ fn allocateSymbols(self: *Zld) !void {
}
}
fn allocateStubsAndGotEntries(self: *Zld) !void {
for (self.got_entries.items()) |*entry| {
if (entry.value.tag == .import) continue;
const object = self.objects.items[entry.value.file];
const sym = object.symtab.items[entry.value.local_index];
const sym_name = object.getString(sym.inner.n_strx);
assert(mem.eql(u8, sym_name, entry.key));
// TODO clean this up
entry.value.target_addr = target_addr: {
if (sym.tag != .Local) {
const glob = self.symtab.get(sym_name) orelse unreachable;
break :target_addr glob.inner.n_value;
}
const target_mapping = self.mappings.get(.{
.object_id = entry.value.file,
.source_sect_id = sym.inner.n_sect - 1,
}) orelse unreachable;
const source_sect = object.sections.items[target_mapping.source_sect_id];
const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment;
const target_sect = target_seg.sections.items[target_mapping.target_sect_id];
const target_sect_addr = target_sect.addr + target_mapping.offset;
break :target_addr target_sect_addr + sym.inner.n_value - source_sect.inner.addr;
};
log.warn("resolving GOT entry '{s}' at 0x{x}", .{
entry.key,
entry.value.target_addr,
});
}
}
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.?];
@ -1385,9 +1427,85 @@ fn resolveSymbols(self: *Zld) !void {
if (has_unresolved) {
return error.UndefinedSymbolReference;
}
// Finally put dyld_stub_binder as an Import
var name = try self.allocator.dupe(u8, "dyld_stub_binder");
try self.symtab.putNoClobber(self.allocator, name, .{
.tag = .Import,
.inner = .{
.n_strx = 0, // This will be populated once we write the string table.
.n_type = macho.N_UNDF | macho.N_EXT,
.n_sect = 0,
.n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
.n_value = 0,
},
.file = 0,
});
}
fn resolveStubsAndGotEntries(self: *Zld) !void {}
fn resolveStubsAndGotEntries(self: *Zld) !void {
for (self.objects.items) |object, object_id| {
log.debug("\nresolving stubs and got entries from {s}", .{object.name});
for (object.sections.items) |sect| {
const relocs = sect.relocs orelse continue;
for (relocs) |reloc| {
switch (reloc.@"type") {
.unsigned => continue,
.got_page, .got_page_off => {
const sym = object.symtab.items[reloc.target.symbol];
const sym_name = object.getString(sym.inner.n_strx);
if (self.got_entries.contains(sym_name)) continue;
const is_import = self.symtab.get(sym_name).?.tag == .Import;
var name = try self.allocator.dupe(u8, sym_name);
const index = @intCast(u32, self.got_entries.items().len);
try self.got_entries.putNoClobber(self.allocator, name, .{
.tag = if (is_import) .import else .local,
.index = index,
.target_addr = 0,
.file = if (is_import) 0 else @intCast(u16, object_id),
.local_index = if (is_import) 0 else reloc.target.symbol,
});
log.debug(" | found GOT entry {s}: {}", .{ sym_name, self.got_entries.get(sym_name) });
},
else => {
const sym = object.symtab.items[reloc.target.symbol];
const sym_name = object.getString(sym.inner.n_strx);
if (sym.tag != .Undef) continue;
const in_globals = self.symtab.get(sym_name) orelse unreachable;
if (in_globals.tag != .Import) continue;
if (self.stubs.contains(sym_name)) continue;
var name = try self.allocator.dupe(u8, sym_name);
const index = @intCast(u32, self.stubs.items().len);
try self.stubs.putNoClobber(self.allocator, name, index);
log.debug(" | found stub {s}: {}", .{ sym_name, self.stubs.get(sym_name) });
},
}
}
}
}
// Finally, put dyld_stub_binder as the final GOT entry
var name = try self.allocator.dupe(u8, "dyld_stub_binder");
const index = @intCast(u32, self.got_entries.items().len);
try self.got_entries.putNoClobber(self.allocator, name, .{
.tag = .import,
.index = index,
.target_addr = 0,
.file = 0,
.local_index = 0,
});
log.debug(" | found GOT entry dyld_stub_binder: {}", .{self.got_entries.get("dyld_stub_binder")});
}
fn resolveRelocsAndWriteSections(self: *Zld) !void {
for (self.objects.items) |object, object_id| {
@ -2871,9 +2989,21 @@ fn aarch64IsArithmetic(inst: *const [4]u8) callconv(.Inline) bool {
return ((group_decode >> 2) == 4);
}
fn printSymtab(self: Zld) void {
fn printDebug(self: Zld) void {
log.warn("symtab", .{});
for (self.symtab.items()) |entry| {
log.warn(" | {s} => {any}", .{ entry.key, entry.value });
}
log.warn("\n", .{});
log.warn("GOT entries", .{});
for (self.got_entries.items()) |entry| {
log.warn(" | {s} => {any}", .{ entry.key, entry.value });
}
log.warn("\n", .{});
log.warn("stubs", .{});
for (self.stubs.items()) |entry| {
log.warn(" | {s} => {any}", .{ entry.key, entry.value });
}
}

View File

@ -292,12 +292,12 @@ const RelocIterator = struct {
self.index += 1;
if (self.index < self.buffer.len) {
const reloc = self.buffer[@intCast(u64, self.index)];
log.warn("{s}", .{@intToEnum(macho.reloc_type_arm64, reloc.r_type)});
log.warn(" | offset = {}", .{reloc.r_address});
log.warn(" | PC = {}", .{reloc.r_pcrel == 1});
log.warn(" | length = {}", .{reloc.r_length});
log.warn(" | symbolnum = {}", .{reloc.r_symbolnum});
log.warn(" | extern = {}", .{reloc.r_extern == 1});
log.debug("{s}", .{@intToEnum(macho.reloc_type_arm64, reloc.r_type)});
log.debug(" | offset = {}", .{reloc.r_address});
log.debug(" | PC = {}", .{reloc.r_pcrel == 1});
log.debug(" | length = {}", .{reloc.r_length});
log.debug(" | symbolnum = {}", .{reloc.r_symbolnum});
log.debug(" | extern = {}", .{reloc.r_extern == 1});
return reloc;
}
return null;
@ -419,7 +419,7 @@ const Parser = struct {
.inst = parsed_inst,
};
log.warn(" | emitting {}", .{branch});
log.debug(" | emitting {}", .{branch});
try parser.parsed.append(&branch.base);
}
@ -458,7 +458,7 @@ const Parser = struct {
.inst = parsed_inst,
};
log.warn(" | emitting {}", .{page});
log.debug(" | emitting {}", .{page});
break :ptr &page.base;
},
@ -476,7 +476,7 @@ const Parser = struct {
.inst = parsed_inst,
};
log.warn(" | emitting {}", .{page});
log.debug(" | emitting {}", .{page});
break :ptr &page.base;
},
@ -494,7 +494,7 @@ const Parser = struct {
.inst = parsed_inst,
};
log.warn(" | emitting {}", .{page});
log.debug(" | emitting {}", .{page});
break :ptr &page.base;
},
@ -551,7 +551,7 @@ const Parser = struct {
.addend = parser.addend,
};
log.warn(" | emitting {}", .{page_off});
log.debug(" | emitting {}", .{page_off});
try parser.parsed.append(&page_off.base);
}
@ -588,7 +588,7 @@ const Parser = struct {
},
};
log.warn(" | emitting {}", .{page_off});
log.debug(" | emitting {}", .{page_off});
try parser.parsed.append(&page_off.base);
}
@ -655,7 +655,7 @@ const Parser = struct {
},
};
log.warn(" | emitting {}", .{page_off});
log.debug(" | emitting {}", .{page_off});
try parser.parsed.append(&page_off.base);
}
@ -716,7 +716,7 @@ const Parser = struct {
.addend = addend,
};
log.warn(" | emitting {}", .{unsigned});
log.debug(" | emitting {}", .{unsigned});
try parser.parsed.append(&unsigned.base);
}
};