zld: new approach at handling static inits

This commit is contained in:
Jakub Konka
2021-04-23 10:33:32 +02:00
parent b00d08b667
commit 4ec6d174ad
2 changed files with 66 additions and 133 deletions

View File

@@ -14,7 +14,6 @@ const Allocator = mem.Allocator;
const Relocation = reloc.Relocation;
const Symbol = @import("Symbol.zig");
const parseName = @import("Zld.zig").parseName;
const CppStatic = @import("Zld.zig").CppStatic;
usingnamespace @import("commands.zig");
@@ -33,7 +32,9 @@ symtab_cmd_index: ?u16 = null,
dysymtab_cmd_index: ?u16 = null,
build_version_cmd_index: ?u16 = null,
data_in_code_cmd_index: ?u16 = null,
text_section_index: ?u16 = null,
mod_init_func_section_index: ?u16 = null,
// __DWARF segment sections
dwarf_debug_info_index: ?u16 = null,
@@ -50,6 +51,7 @@ stabs: std.ArrayListUnmanaged(Stab) = .{},
tu_path: ?[]const u8 = null,
tu_mtime: ?u64 = null,
initializers: std.ArrayListUnmanaged(CppStatic) = .{},
data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
pub const Section = struct {
@@ -69,6 +71,11 @@ pub const Section = struct {
}
};
const CppStatic = struct {
symbol: u32,
target_addr: u64,
};
const Stab = struct {
tag: Tag,
symbol: u32,
@@ -171,6 +178,7 @@ pub fn deinit(self: *Object) void {
self.strtab.deinit(self.allocator);
self.stabs.deinit(self.allocator);
self.data_in_code_entries.deinit(self.allocator);
self.initializers.deinit(self.allocator);
if (self.name) |n| {
self.allocator.free(n);
@@ -252,6 +260,10 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void {
if (mem.eql(u8, sectname, "__text")) {
self.text_section_index = index;
}
} else if (mem.eql(u8, segname, "__DATA")) {
if (mem.eql(u8, sectname, "__mod_init_func")) {
self.mod_init_func_section_index = index;
}
}
sect.offset += offset;
@@ -323,16 +335,27 @@ pub fn parseSections(self: *Object) !void {
}
pub fn parseInitializers(self: *Object) !void {
for (self.sections.items) |section| {
if (section.inner.flags != macho.S_MOD_INIT_FUNC_POINTERS) continue;
log.warn("parsing initializers in {s}", .{self.name.?});
// Parse C++ initializers
const relocs = section.relocs orelse unreachable;
for (relocs) |rel| {
const sym = self.symtab.items[rel.target.symbol];
const sym_name = self.getString(sym.n_strx);
log.warn(" | {s}", .{sym_name});
}
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(.{
.symbol = rel.target.symbol,
.target_addr = undefined,
});
}
mem.reverse(CppStatic, self.initializers.items);
for (self.initializers.items) |initializer| {
const sym = self.symtab.items[initializer.symbol];
const sym_name = self.getString(sym.n_strx);
log.debug(" | {s}", .{sym_name});
}
}

View File

@@ -82,20 +82,12 @@ threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
stubs: std.StringArrayHashMapUnmanaged(u32) = .{},
got_entries: std.StringArrayHashMapUnmanaged(GotEntry) = .{},
cpp_initializers: std.StringArrayHashMapUnmanaged(CppStatic) = .{},
cpp_finalizers: std.StringArrayHashMapUnmanaged(CppStatic) = .{},
stub_helper_stubs_start_off: ?u64 = null,
mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{},
unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{},
pub const CppStatic = struct {
index: u32,
target_addr: u64,
file: u16,
};
const GotEntry = struct {
tag: enum {
local,
@@ -143,16 +135,6 @@ pub fn deinit(self: *Zld) void {
}
self.got_entries.deinit(self.allocator);
for (self.cpp_initializers.items()) |entry| {
self.allocator.free(entry.key);
}
self.cpp_initializers.deinit(self.allocator);
for (self.cpp_finalizers.items()) |entry| {
self.allocator.free(entry.key);
}
self.cpp_finalizers.deinit(self.allocator);
for (self.load_commands.items) |*lc| {
lc.deinit(self.allocator);
}
@@ -243,6 +225,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
self.allocateLinkeditSegment();
try self.allocateSymbols();
try self.allocateStubsAndGotEntries();
try self.allocateCppStatics();
try self.writeStubHelperCommon();
try self.resolveRelocsAndWriteSections();
try self.flush();
@@ -1007,37 +990,20 @@ fn allocateStubsAndGotEntries(self: *Zld) !void {
entry.value.target_addr,
});
}
}
for (self.cpp_initializers.items()) |*entry| {
const object = self.objects.items[entry.value.file];
entry.value.target_addr = target_addr: {
if (object.locals.get(entry.key)) |local| {
break :target_addr local.address;
}
const global = self.symtab.get(entry.key) orelse unreachable;
break :target_addr global.address;
};
fn allocateCppStatics(self: *Zld) !void {
for (self.objects.items) |*object| {
for (object.initializers.items) |*initializer| {
const sym = object.symtab.items[initializer.symbol];
const sym_name = object.getString(sym.n_strx);
initializer.target_addr = object.locals.get(sym_name).?.address;
log.debug("resolving C++ initializer '{s}' at 0x{x}", .{
entry.key,
entry.value.target_addr,
});
}
for (self.cpp_finalizers.items()) |*entry| {
const object = self.objects.items[entry.value.file];
entry.value.target_addr = target_addr: {
if (object.locals.get(entry.key)) |local| {
break :target_addr local.address;
}
const global = self.symtab.get(entry.key) orelse unreachable;
break :target_addr global.address;
};
log.debug("resolving C++ finalizer '{s}' at 0x{x}", .{
entry.key,
entry.value.target_addr,
});
log.debug("resolving C++ initializer '{s}' at 0x{x}", .{
sym_name,
initializer.target_addr,
});
}
}
}
@@ -1453,34 +1419,7 @@ fn resolveStubsAndGotEntries(self: *Zld) !void {
const relocs = sect.relocs orelse continue;
for (relocs) |rel| {
switch (rel.@"type") {
.unsigned => {
if (rel.target != .symbol) continue;
const sym = object.symtab.items[rel.target.symbol];
const sym_name = object.getString(sym.n_strx);
if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS) {
if (self.cpp_initializers.contains(sym_name)) continue;
var name = try self.allocator.dupe(u8, sym_name);
const index = @intCast(u32, self.cpp_initializers.items().len);
try self.cpp_initializers.putNoClobber(self.allocator, name, .{
.index = index,
.target_addr = 0,
.file = @intCast(u16, object_id),
});
} else if (sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) {
if (self.cpp_finalizers.contains(sym_name)) continue;
var name = try self.allocator.dupe(u8, sym_name);
const index = @intCast(u32, self.cpp_finalizers.items().len);
try self.cpp_finalizers.putNoClobber(self.allocator, name, .{
.index = index,
.target_addr = 0,
.file = @intCast(u16, object_id),
});
} else continue;
},
.unsigned => continue,
.got_page, .got_page_off, .got_load, .got => {
const sym = object.symtab.items[rel.target.symbol];
const sym_name = object.getString(sym.n_strx);
@@ -2194,36 +2133,18 @@ fn flush(self: *Zld) !void {
const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
const sect = &seg.sections.items[index];
var buffer = try self.allocator.alloc(u8, self.cpp_initializers.items().len * @sizeOf(u64));
defer self.allocator.free(buffer);
var initializers = std.ArrayList(u64).init(self.allocator);
defer initializers.deinit();
var stream = std.io.fixedBufferStream(buffer);
var writer = stream.writer();
for (self.cpp_initializers.items()) |entry| {
try writer.writeIntLittle(u64, entry.value.target_addr);
// TODO sort the initializers globally
for (self.objects.items) |object| {
for (object.initializers.items) |initializer| {
try initializers.append(initializer.target_addr);
}
}
_ = try self.file.?.pwriteAll(buffer, sect.offset);
sect.size = @intCast(u32, buffer.len);
}
if (self.mod_term_func_section_index) |index| {
const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
const sect = &seg.sections.items[index];
var buffer = try self.allocator.alloc(u8, self.cpp_finalizers.items().len * @sizeOf(u64));
defer self.allocator.free(buffer);
var stream = std.io.fixedBufferStream(buffer);
var writer = stream.writer();
for (self.cpp_finalizers.items()) |entry| {
try writer.writeIntLittle(u64, entry.value.target_addr);
}
_ = try self.file.?.pwriteAll(buffer, sect.offset);
sect.size = @intCast(u32, buffer.len);
_ = try self.file.?.pwriteAll(mem.sliceAsBytes(initializers.items), sect.offset);
sect.size = @intCast(u32, initializers.items.len * @sizeOf(u64));
}
try self.writeGotEntries();
@@ -2328,26 +2249,15 @@ 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.cpp_initializers.items()) |entry| {
try pointers.append(.{
.offset = base_offset + entry.value.index * @sizeOf(u64),
.segment_id = segment_id,
});
}
}
if (self.mod_term_func_section_index) |idx| {
// TODO audit and investigate this.
const seg = self.load_commands.items[self.data_const_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_const_segment_cmd_index.?);
for (self.cpp_finalizers.items()) |entry| {
try pointers.append(.{
.offset = base_offset + entry.value.index * @sizeOf(u64),
.segment_id = segment_id,
});
var index: u64 = 0;
for (self.objects.items) |object| {
for (object.initializers.items) |_| {
try pointers.append(.{
.offset = base_offset + index * @sizeOf(u64),
.segment_id = segment_id,
});
index += 1;
}
}
}