zld: fixup flush function

This commit is contained in:
Jakub Konka 2021-07-17 17:30:16 +02:00
parent d81783375c
commit 97914d93a9
2 changed files with 134 additions and 157 deletions

View File

@ -1,11 +1,14 @@
const TextBlock = @This();
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(.text_block);
const macho = std.macho;
const math = std.math;
const mem = std.mem;
const meta = std.meta;
const Allocator = mem.Allocator;
const Arch = std.Target.Cpu.Arch;
@ -44,25 +47,22 @@ pub const Stab = union(enum) {
defer nlists.deinit();
const sym = zld.locals.items[local_sym_index];
const reg = sym.payload.regular;
switch (stab) {
.function => |size| {
try nlists.ensureUnusedCapacity(4);
const section_id = reg.sectionId(zld);
nlists.appendAssumeCapacity(.{
.n_strx = 0,
.n_type = macho.N_BNSYM,
.n_sect = section_id,
.n_sect = sym.n_sect,
.n_desc = 0,
.n_value = reg.address,
.n_value = sym.n_value,
});
nlists.appendAssumeCapacity(.{
.n_strx = sym.strx,
.n_strx = sym.n_strx,
.n_type = macho.N_FUN,
.n_sect = section_id,
.n_sect = sym.n_sect,
.n_desc = 0,
.n_value = reg.address,
.n_value = sym.n_value,
});
nlists.appendAssumeCapacity(.{
.n_strx = 0,
@ -74,14 +74,14 @@ pub const Stab = union(enum) {
nlists.appendAssumeCapacity(.{
.n_strx = 0,
.n_type = macho.N_ENSYM,
.n_sect = section_id,
.n_sect = sym.n_sect,
.n_desc = 0,
.n_value = size,
});
},
.global => {
try nlists.append(.{
.n_strx = sym.strx,
.n_strx = sym.n_strx,
.n_type = macho.N_GSYM,
.n_sect = 0,
.n_desc = 0,
@ -90,11 +90,11 @@ pub const Stab = union(enum) {
},
.static => {
try nlists.append(.{
.n_strx = sym.strx,
.n_strx = sym.n_strx,
.n_type = macho.N_STSYM,
.n_sect = reg.sectionId(zld),
.n_sect = sym.n_sect,
.n_desc = 0,
.n_value = reg.address,
.n_value = sym.n_value,
});
},
}
@ -1006,7 +1006,10 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
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 = zld.got_entries.getIndex(.{
.where = rel.where,
.where = switch (rel.where) {
.local => .local,
.import => .import,
},
.where_index = rel.where_index,
}) orelse {
const sym = switch (rel.where) {
@ -1024,8 +1027,8 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
.local => {
const sym = zld.locals.items[rel.where_index];
const is_tlv = is_tlv: {
const sym = zld.locals.items[self.local_sym_index];
const match = zld.unpackSectionId(sym.n_sect);
const source_sym = zld.locals.items[self.local_sym_index];
const match = zld.unpackSectionId(source_sym.n_sect);
const seg = zld.load_commands.items[match.seg].Segment;
const sect = seg.sections.items[match.sect];
break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
@ -1073,7 +1076,13 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
log.warn(" | source_addr = 0x{x}", .{source_addr});
log.warn(" | target_addr = 0x{x}", .{target_addr});
try rel.resolve(self, source_addr, target_addr);
try rel.resolve(.{
.block = self,
.offset = rel.offset,
.source_addr = source_addr,
.target_addr = target_addr,
.zld = zld,
});
}
}

View File

@ -233,6 +233,16 @@ 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();
{
// Add dyld_stub_binder as the final GOT entry.
const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable;
try self.got_entries.putNoClobber(self.allocator, .{
.where = .import,
.where_index = resolv.where_index,
}, {});
}
try self.sortSections();
try self.addRpaths(args.rpaths);
try self.addDataInCodeLC();
@ -296,9 +306,7 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg
entry.value_ptr.*.print(self);
}
return error.TODO;
// try self.flush();
try self.flush();
}
fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u8) !void {
@ -1097,7 +1105,7 @@ fn writeTextBlocks(self: *Zld) !void {
const sym = self.locals.items[block.local_sym_index];
log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
self.getString(sym.strx),
self.getString(sym.n_strx),
aligned_base_off,
aligned_base_off + block.size,
block.size,
@ -1154,8 +1162,12 @@ fn writeStubHelperCommon(self: *Zld) !void {
code[9] = 0xff;
code[10] = 0x25;
{
const dyld_stub_binder = self.globals.get("dyld_stub_binder").?;
const addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64));
const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable;
const got_index = self.got_entries.getIndex(.{
.where = .import,
.where_index = resolv.where_index,
}) orelse unreachable;
const addr = got.addr + got_index * @sizeOf(u64);
const displacement = try math.cast(u32, addr - stub_helper.addr - code_size);
mem.writeIntLittle(u32, code[11..], displacement);
}
@ -1198,9 +1210,13 @@ fn writeStubHelperCommon(self: *Zld) !void {
code[10] = 0xbf;
code[11] = 0xa9;
binder_blk_outer: {
const dyld_stub_binder = self.globals.get("dyld_stub_binder").?;
const resolv = self.symbol_resolver.get("dyld_stub_binder") orelse unreachable;
const got_index = self.got_entries.getIndex(.{
.where = .import,
.where_index = resolv.where_index,
}) orelse unreachable;
const this_addr = stub_helper.addr + 3 * @sizeOf(u32);
const target_addr = (got.addr + dyld_stub_binder.got_index.? * @sizeOf(u64));
const target_addr = got.addr + got_index * @sizeOf(u64);
binder_blk: {
const displacement = math.divExact(u64, target_addr - this_addr, 4) catch break :binder_blk;
const literal = math.cast(u18, displacement) catch break :binder_blk;
@ -1251,12 +1267,11 @@ fn writeStubHelperCommon(self: *Zld) !void {
}
};
for (self.stubs.items) |sym| {
for (self.stubs.keys()) |key| {
// TODO weak bound pointers
const index = sym.stubs_index orelse unreachable;
try self.writeLazySymbolPointer(index);
try self.writeStub(index);
try self.writeStubInStubHelper(index);
try self.writeLazySymbolPointer(key);
try self.writeStub(key);
try self.writeStubInStubHelper(key);
}
}
@ -1764,7 +1779,7 @@ fn resolveSymbols(self: *Zld) !void {
.n_strx = undef.n_strx,
.n_type = macho.N_UNDF | macho.N_EXT,
.n_sect = 0,
.n_desc = (dylib.ordinal.? * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
.n_desc = packDylibOrdinal(dylib.ordinal.?),
.n_value = 0,
});
resolv.* = .{
@ -2218,16 +2233,16 @@ fn writeGotEntries(self: *Zld) !void {
const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
const sect = seg.sections.items[self.got_section_index.?];
var buffer = try self.allocator.alloc(u8, self.got_entries.items.len * @sizeOf(u64));
var buffer = try self.allocator.alloc(u8, self.got_entries.count() * @sizeOf(u64));
defer self.allocator.free(buffer);
var stream = std.io.fixedBufferStream(buffer);
var writer = stream.writer();
for (self.got_entries.items) |sym| {
const address: u64 = switch (sym.payload) {
.regular => |reg| reg.address,
else => 0,
for (self.got_entries.keys()) |key| {
const address: u64 = switch (key.where) {
.local => self.locals.items[key.where_index].n_value,
.import => 0,
};
try writer.writeIntLittle(u64, address);
}
@ -2243,9 +2258,14 @@ fn setEntryPoint(self: *Zld) !void {
// TODO we should respect the -entry flag passed in by the user to set a custom
// entrypoint. For now, assume default of `_main`.
const seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const sym = self.globals.get("_main") orelse return error.MissingMainEntrypoint;
const resolv = self.symbol_resolver.get("_main") orelse {
log.err("'_main' export not found", .{});
return error.MissingMainEntrypoint;
};
assert(resolv.where == .global);
const sym = self.globals.items[resolv.where_index];
const ec = &self.load_commands.items[self.main_cmd_index.?].Main;
ec.entryoff = @intCast(u32, sym.payload.regular.address - seg.inner.vmaddr);
ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
ec.stacksize = self.stack_size;
}
@ -2265,8 +2285,7 @@ fn writeRebaseInfoTable(self: *Zld) !void {
while (true) {
const sym = self.locals.items[block.local_sym_index];
assert(sym.payload == .regular);
const base_offset = sym.payload.regular.address - seg.inner.vmaddr;
const base_offset = sym.n_value - seg.inner.vmaddr;
for (block.rebases.items) |offset| {
try pointers.append(.{
@ -2288,11 +2307,11 @@ fn writeRebaseInfoTable(self: *Zld) !void {
const base_offset = sect.addr - seg.inner.vmaddr;
const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
for (self.got_entries.items) |sym| {
if (sym.payload == .proxy) continue;
for (self.got_entries.keys()) |key, i| {
if (key.where == .import) continue;
try pointers.append(.{
.offset = base_offset + sym.got_index.? * @sizeOf(u64),
.offset = base_offset + i * @sizeOf(u64),
.segment_id = segment_id,
});
}
@ -2304,10 +2323,10 @@ fn writeRebaseInfoTable(self: *Zld) !void {
const base_offset = sect.addr - seg.inner.vmaddr;
const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
try pointers.ensureCapacity(pointers.items.len + self.stubs.items.len);
for (self.stubs.items) |sym| {
try pointers.ensureCapacity(pointers.items.len + self.stubs.count());
for (self.stubs.keys()) |_, i| {
pointers.appendAssumeCapacity(.{
.offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
.offset = base_offset + i * @sizeOf(u64),
.segment_id = segment_id,
});
}
@ -2343,15 +2362,15 @@ fn writeBindInfoTable(self: *Zld) !void {
const base_offset = sect.addr - seg.inner.vmaddr;
const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
for (self.got_entries.items) |sym| {
if (sym.payload != .proxy) continue;
for (self.got_entries.keys()) |key, i| {
if (key.where == .local) continue;
const proxy = sym.payload.proxy;
const sym = self.imports.items[key.where_index];
try pointers.append(.{
.offset = base_offset + sym.got_index.? * @sizeOf(u64),
.offset = base_offset + i * @sizeOf(u64),
.segment_id = segment_id,
.dylib_ordinal = proxy.dylibOrdinal(),
.name = self.getString(sym.strx),
.dylib_ordinal = unpackDylibOrdinal(sym.n_desc),
.name = self.getString(sym.n_strx),
});
}
}
@ -2368,18 +2387,15 @@ fn writeBindInfoTable(self: *Zld) !void {
while (true) {
const sym = self.locals.items[block.local_sym_index];
assert(sym.payload == .regular);
const base_offset = sym.payload.regular.address - seg.inner.vmaddr;
const base_offset = sym.n_value - seg.inner.vmaddr;
for (block.bindings.items) |binding| {
const bind_sym = self.imports.items[binding.local_sym_index];
const proxy = bind_sym.payload.proxy;
try pointers.append(.{
.offset = binding.offset + base_offset,
.segment_id = match.seg,
.dylib_ordinal = proxy.dylibOrdinal(),
.name = self.getString(bind_sym.strx),
.dylib_ordinal = unpackDylibOrdinal(bind_sym.n_desc),
.name = self.getString(bind_sym.n_strx),
});
}
@ -2390,21 +2406,21 @@ fn writeBindInfoTable(self: *Zld) !void {
}
}
if (self.tlv_section_index) |idx| {
const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const sect = seg.sections.items[idx];
const base_offset = sect.addr - seg.inner.vmaddr;
const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
// if (self.tlv_section_index) |idx| {
// const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
// const sect = seg.sections.items[idx];
// const base_offset = sect.addr - seg.inner.vmaddr;
// const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
const sym = self.globals.get("__tlv_bootstrap") orelse unreachable;
const proxy = sym.payload.proxy;
try pointers.append(.{
.offset = base_offset,
.segment_id = segment_id,
.dylib_ordinal = proxy.dylibOrdinal(),
.name = self.getString(sym.strx),
});
}
// const sym = self.globals.get("__tlv_bootstrap") orelse unreachable;
// const proxy = sym.payload.proxy;
// try pointers.append(.{
// .offset = base_offset,
// .segment_id = segment_id,
// .dylib_ordinal = proxy.dylibOrdinal(),
// .name = self.getString(sym.strx),
// });
// }
const size = try bindInfoSize(pointers.items);
var buffer = try self.allocator.alloc(u8, @intCast(usize, size));
@ -2434,15 +2450,15 @@ fn writeLazyBindInfoTable(self: *Zld) !void {
const base_offset = sect.addr - seg.inner.vmaddr;
const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
try pointers.ensureCapacity(self.stubs.items.len);
try pointers.ensureCapacity(self.stubs.count());
for (self.stubs.items) |sym| {
const proxy = sym.payload.proxy;
for (self.stubs.keys()) |key, i| {
const sym = self.imports.items[key];
pointers.appendAssumeCapacity(.{
.offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
.offset = base_offset + i * @sizeOf(u64),
.segment_id = segment_id,
.dylib_ordinal = proxy.dylibOrdinal(),
.name = self.getString(sym.strx),
.dylib_ordinal = unpackDylibOrdinal(sym.n_desc),
.name = self.getString(sym.n_strx),
});
}
}
@ -2510,7 +2526,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
else => {},
}
}
assert(self.stubs.items.len <= offsets.items.len);
assert(self.stubs.count() <= offsets.items.len);
const stub_size: u4 = switch (self.target.?.cpu.arch) {
.x86_64 => 10,
@ -2523,8 +2539,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
else => unreachable,
};
var buf: [@sizeOf(u32)]u8 = undefined;
for (self.stubs.items) |sym| {
const index = sym.stubs_index orelse unreachable;
for (self.stubs.keys()) |_, index| {
const placeholder_off = self.stub_helper_stubs_start_off.? + index * stub_size + off;
mem.writeIntLittle(u32, &buf, offsets.items[index]);
try self.file.?.pwriteAll(&buf, placeholder_off);
@ -2541,33 +2556,13 @@ fn writeExportInfo(self: *Zld) !void {
// TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
log.debug("writing export trie", .{});
const Sorter = struct {
fn lessThan(_: void, a: []const u8, b: []const u8) bool {
return mem.lessThan(u8, a, b);
}
};
var sorted_globals = std.ArrayList([]const u8).init(self.allocator);
defer sorted_globals.deinit();
for (self.globals.values()) |sym| {
if (sym.payload != .regular) continue;
const reg = sym.payload.regular;
if (reg.linkage != .global) continue;
try sorted_globals.append(self.getString(sym.strx));
}
std.sort.sort([]const u8, sorted_globals.items, {}, Sorter.lessThan);
for (sorted_globals.items) |sym_name| {
const sym = self.globals.get(sym_name) orelse unreachable;
const reg = sym.payload.regular;
log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, reg.address });
for (self.globals.items) |sym| {
const sym_name = self.getString(sym.n_strx);
log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, sym.n_value });
try trie.put(.{
.name = sym_name,
.vmaddr_offset = reg.address - base_address,
.vmaddr_offset = sym.n_value - base_address,
.export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
});
}
@ -2598,23 +2593,7 @@ fn writeSymbolTable(self: *Zld) !void {
var locals = std.ArrayList(macho.nlist_64).init(self.allocator);
defer locals.deinit();
var exports = std.ArrayList(macho.nlist_64).init(self.allocator);
defer exports.deinit();
for (self.locals.items) |symbol, i| {
if (i == 0) continue; // skip null symbol
if (symbol.isTemp(self)) continue; // TODO when merging codepaths, this should go into freelist
const reg = symbol.payload.regular;
const nlist = try symbol.asNlist(self);
if (reg.linkage == .translation_unit) {
try locals.append(nlist);
} else {
try exports.append(nlist);
}
}
try locals.appendSlice(self.locals.items);
if (self.has_stabs) {
for (self.objects.items) |object| {
@ -2671,21 +2650,9 @@ fn writeSymbolTable(self: *Zld) !void {
}
}
var undefs = std.ArrayList(macho.nlist_64).init(self.allocator);
defer undefs.deinit();
var undef_dir = std.StringHashMap(u32).init(self.allocator);
defer undef_dir.deinit();
for (self.imports.items) |sym| {
const nlist = try sym.asNlist(self);
const id = @intCast(u32, undefs.items.len);
try undefs.append(nlist);
try undef_dir.putNoClobber(self.getString(sym.strx), id);
}
const nlocals = locals.items.len;
const nexports = exports.items.len;
const nundefs = undefs.items.len;
const nexports = self.globals.items.len;
const nundefs = self.imports.items.len;
const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);
const locals_size = nlocals * @sizeOf(macho.nlist_64);
@ -2695,12 +2662,12 @@ fn writeSymbolTable(self: *Zld) !void {
const exports_off = locals_off + locals_size;
const exports_size = nexports * @sizeOf(macho.nlist_64);
log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
try self.file.?.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
try self.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off);
const undefs_off = exports_off + exports_size;
const undefs_size = nundefs * @sizeOf(macho.nlist_64);
log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
try self.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
try self.file.?.pwriteAll(mem.sliceAsBytes(self.imports.items), undefs_off);
symtab.nsyms += @intCast(u32, nlocals + nexports + nundefs);
seg.inner.filesize += locals_size + exports_size + undefs_size;
@ -2720,8 +2687,8 @@ fn writeSymbolTable(self: *Zld) !void {
const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
const nstubs = @intCast(u32, self.stubs.items.len);
const ngot_entries = @intCast(u32, self.got_entries.items.len);
const nstubs = @intCast(u32, self.stubs.count());
const ngot_entries = @intCast(u32, self.got_entries.count());
dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
dysymtab.nindirectsyms = nstubs * 2 + ngot_entries;
@ -2741,31 +2708,25 @@ fn writeSymbolTable(self: *Zld) !void {
var writer = stream.writer();
stubs.reserved1 = 0;
for (self.stubs.items) |sym| {
const sym_name = self.getString(sym.strx);
const id = undef_dir.get(sym_name) orelse unreachable;
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
for (self.stubs.keys()) |key| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
}
got.reserved1 = nstubs;
for (self.got_entries.items) |sym| {
switch (sym.payload) {
.proxy => {
const sym_name = self.getString(sym.strx);
const id = undef_dir.get(sym_name) orelse unreachable;
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
for (self.got_entries.keys()) |key| {
switch (key.where) {
.import => {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key.where_index);
},
else => {
.local => {
try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
},
}
}
la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
for (self.stubs.items) |sym| {
const sym_name = self.getString(sym.strx);
const id = undef_dir.get(sym_name) orelse unreachable;
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
for (self.stubs.keys()) |key| {
try writer.writeIntLittle(u32, dysymtab.iundefsym + key);
}
try self.file.?.pwriteAll(buf, dysymtab.indirectsymoff);
@ -2813,8 +2774,7 @@ fn writeDices(self: *Zld) !void {
while (true) {
if (block.dices.items.len > 0) {
const sym = self.locals.items[block.local_sym_index];
const reg = sym.payload.regular;
const base_off = try math.cast(u32, reg.address - text_sect.addr + text_sect.offset);
const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset);
try buf.ensureUnusedCapacity(block.dices.items.len * @sizeOf(macho.data_in_code_entry));
for (block.dices.items) |dice| {
@ -3052,6 +3012,14 @@ pub fn unpackSectionId(self: Zld, section_id: u8) MatchingSection {
return match;
}
fn packDylibOrdinal(ordinal: u16) u16 {
return ordinal * macho.N_SYMBOL_RESOLVER;
}
fn unpackDylibOrdinal(pack: u16) u16 {
return @divExact(pack, macho.N_SYMBOL_RESOLVER);
}
pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anytype) usize {
if (!@hasDecl(@TypeOf(predicate), "predicate"))
@compileError("Predicate is required to define fn predicate(@This(), T) bool");