zld: reuse string table for symbol names

rather than manage allocs separately per symbol.
This commit is contained in:
Jakub Konka
2021-07-15 07:41:59 +02:00
parent ec874a9b2b
commit f8678c48ff
4 changed files with 107 additions and 116 deletions

View File

@@ -389,7 +389,7 @@ const TextBlockParser = struct {
return switch (rreg.linkage) {
.global => true,
.linkage_unit => lreg.linkage == .translation_unit,
else => lsym.isTemp(),
else => lsym.isTemp(context.zld),
};
}
@@ -417,7 +417,7 @@ const TextBlockParser = struct {
const sym = self.object.symbols.items[nlist_with_index.index];
if (sym.payload != .regular) {
log.err("expected a regular symbol, found {s}", .{sym.payload});
log.err(" when remapping {s}", .{sym.name});
log.err(" when remapping {s}", .{self.zld.getString(sym.strx)});
return error.SymbolIsNotRegular;
}
assert(sym.payload.regular.local_sym_index != 0); // This means the symbol has not been properly resolved.
@@ -463,7 +463,7 @@ const TextBlockParser = struct {
}
}
}
if (self.zld.globals.contains(senior_sym.name)) break :blk .global;
if (self.zld.globals.contains(self.zld.getString(senior_sym.strx))) break :blk .global;
break :blk .static;
} else null;
@@ -598,7 +598,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
sectionName(sect),
});
defer self.allocator.free(name);
const symbol = try Symbol.new(self.allocator, name);
const symbol = try zld.allocator.create(Symbol);
symbol.* = .{
.strx = try zld.makeString(name),
.payload = .{ .undef = .{} },
};
try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
break :symbol symbol;
};
@@ -684,7 +688,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
const reg = &sym.payload.regular;
if (reg.file) |file| {
if (file != self) {
log.debug("deduping definition of {s} in {s}", .{ sym.name, self.name.? });
log.debug("deduping definition of {s} in {s}", .{ zld.getString(sym.strx), self.name.? });
block.deinit();
self.allocator.destroy(block);
continue;
@@ -739,7 +743,11 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
sectionName(sect),
});
defer self.allocator.free(name);
const symbol = try Symbol.new(self.allocator, name);
const symbol = try zld.allocator.create(Symbol);
symbol.* = .{
.strx = try zld.makeString(name),
.payload = .{ .undef = .{} },
};
try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);
break :symbol symbol;
};
@@ -812,7 +820,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void {
}
}
}
if (zld.globals.contains(sym.name)) break :blk .global;
if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
break :blk .static;
} else null;
@@ -870,7 +878,7 @@ fn parseRelocs(
try parser.parse();
}
pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol {
pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*Symbol {
const symbol = blk: {
if (rel.r_extern == 1) {
break :blk self.symbols.items[rel.r_symbolnum];
@@ -888,12 +896,15 @@ pub fn symbolFromReloc(self: *Object, rel: macho.relocation_info) !*Symbol {
sectionName(sect),
});
defer self.allocator.free(name);
const symbol = try Symbol.new(self.allocator, name);
symbol.payload = .{
.regular = .{
.linkage = .translation_unit,
.address = sect.addr,
.file = self,
const symbol = try zld.allocator.create(Symbol);
symbol.* = .{
.strx = try zld.makeString(name),
.payload = .{
.regular = .{
.linkage = .translation_unit,
.address = sect.addr,
.file = self,
},
},
};
try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol);

View File

@@ -11,8 +11,8 @@ const Dylib = @import("Dylib.zig");
const Object = @import("Object.zig");
const Zld = @import("Zld.zig");
/// Symbol name. Owned slice.
name: []const u8,
/// Offset into the string table.
strx: u32,
/// Index in GOT table for indirection.
got_index: ?u32 = null,
@@ -160,26 +160,11 @@ pub const Undefined = struct {
}
};
/// Create new undefined symbol.
pub fn new(allocator: *Allocator, name: []const u8) !*Symbol {
const new_sym = try allocator.create(Symbol);
errdefer allocator.destroy(new_sym);
new_sym.* = .{
.name = try allocator.dupe(u8, name),
.payload = .{
.undef = .{},
},
};
return new_sym;
}
pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
_ = fmt;
_ = options;
try std.fmt.format(writer, "Symbol {{", .{});
try std.fmt.format(writer, ".name = {s}, ", .{self.name});
try std.fmt.format(writer, ".strx = {d}, ", .{self.strx});
if (self.got_index) |got_index| {
try std.fmt.format(writer, ".got_index = {}, ", .{got_index});
}
@@ -190,11 +175,12 @@ pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOpt
try std.fmt.format(writer, "}}", .{});
}
pub fn isTemp(symbol: Symbol) bool {
pub fn isTemp(symbol: Symbol, zld: *Zld) bool {
const sym_name = zld.getString(symbol.strx);
switch (symbol.payload) {
.regular => |regular| {
if (regular.linkage == .translation_unit) {
return mem.startsWith(u8, symbol.name, "l") or mem.startsWith(u8, symbol.name, "L");
return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
}
},
else => {},
@@ -202,24 +188,12 @@ 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, zld: *Zld) !macho.nlist_64 {
const n_strx = try zld.makeString(symbol.name);
const nlist = nlist: {
switch (symbol.payload) {
.regular => |regular| {
var nlist = macho.nlist_64{
.n_strx = n_strx,
.n_strx = symbol.strx,
.n_type = macho.N_SECT,
.n_sect = regular.sectionId(zld),
.n_desc = 0,
@@ -239,7 +213,7 @@ pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
.tentative => {
// TODO
break :nlist macho.nlist_64{
.n_strx = n_strx,
.n_strx = symbol.strx,
.n_type = macho.N_UNDF,
.n_sect = 0,
.n_desc = 0,
@@ -248,7 +222,7 @@ pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
},
.proxy => |proxy| {
break :nlist macho.nlist_64{
.n_strx = n_strx,
.n_strx = symbol.strx,
.n_type = macho.N_UNDF | macho.N_EXT,
.n_sect = 0,
.n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY,
@@ -258,7 +232,7 @@ pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
.undef => {
// TODO
break :nlist macho.nlist_64{
.n_strx = n_strx,
.n_strx = symbol.strx,
.n_type = macho.N_UNDF,
.n_sect = 0,
.n_desc = 0,
@@ -270,10 +244,6 @@ pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
return nlist;
}
pub fn deinit(symbol: *Symbol, allocator: *Allocator) void {
allocator.free(symbol.name);
}
pub fn isStab(sym: macho.nlist_64) bool {
return (macho.N_STAB & sym.n_type) != 0;
}

View File

@@ -113,7 +113,6 @@ stub_helper_stubs_start_off: ?u64 = null,
blocks: std.AutoHashMapUnmanaged(MatchingSection, *TextBlock) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
strtab_cache: std.StringHashMapUnmanaged(u32) = .{},
has_dices: bool = false,
has_stabs: bool = false,
@@ -171,7 +170,7 @@ pub const TextBlock = struct {
.n_value = reg.address,
});
nlists.appendAssumeCapacity(.{
.n_strx = try zld.makeString(sym.name),
.n_strx = sym.strx,
.n_type = macho.N_FUN,
.n_sect = section_id,
.n_desc = 0,
@@ -194,7 +193,7 @@ pub const TextBlock = struct {
},
.global => {
try nlists.append(.{
.n_strx = try zld.makeString(sym.name),
.n_strx = sym.strx,
.n_type = macho.N_GSYM,
.n_sect = 0,
.n_desc = 0,
@@ -203,7 +202,7 @@ pub const TextBlock = struct {
},
.static => {
try nlists.append(.{
.n_strx = try zld.makeString(sym.name),
.n_strx = sym.strx,
.n_type = macho.N_STSYM,
.n_sect = reg.sectionId(zld),
.n_desc = 0,
@@ -349,26 +348,20 @@ pub fn deinit(self: *Zld) void {
self.dylibs.deinit(self.allocator);
for (self.imports.items) |sym| {
sym.deinit(self.allocator);
self.allocator.destroy(sym);
}
self.imports.deinit(self.allocator);
for (self.locals.items) |sym| {
sym.deinit(self.allocator);
self.allocator.destroy(sym);
}
self.locals.deinit(self.allocator);
for (self.globals.keys()) |key| {
self.allocator.free(key);
}
self.globals.deinit(self.allocator);
{
var it = self.strtab_cache.keyIterator();
while (it.next()) |key| {
self.allocator.free(key.*);
}
}
self.strtab_cache.deinit(self.allocator);
self.strtab.deinit(self.allocator);
// TODO dealloc all blocks
@@ -1168,7 +1161,7 @@ fn allocateTextBlocks(self: *Zld) !void {
sym.payload.regular.address = base_addr;
log.debug(" {s}: start=0x{x}, end=0x{x}, size={}, align={}", .{
sym.name,
self.getString(sym.strx),
base_addr,
base_addr + block.size,
block.size,
@@ -1231,7 +1224,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={}", .{
sym.name,
self.getString(sym.strx),
aligned_base_off,
aligned_base_off + block.size,
block.size,
@@ -1552,14 +1545,17 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
if (Symbol.isSect(sym) and !Symbol.isExt(sym)) {
// Regular symbol local to translation unit
const symbol = try Symbol.new(self.allocator, sym_name);
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),
const symbol = try self.allocator.create(Symbol);
symbol.* = .{
.strx = try self.makeString(sym_name),
.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),
},
},
};
try self.locals.append(self.allocator, symbol);
@@ -1569,9 +1565,13 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
const symbol = self.globals.get(sym_name) orelse symbol: {
// Insert new global symbol.
const symbol = try Symbol.new(self.allocator, sym_name);
symbol.payload.undef.file = object;
try self.globals.putNoClobber(self.allocator, symbol.name, symbol);
const symbol = try self.allocator.create(Symbol);
symbol.* = .{
.strx = try self.makeString(sym_name),
.payload = .{ .undef = .{ .file = object } },
};
const alloc_name = try self.allocator.dupe(u8, sym_name);
try self.globals.putNoClobber(self.allocator, alloc_name, symbol);
break :symbol symbol;
};
@@ -1628,7 +1628,8 @@ fn resolveSymbolsInObject(self: *Zld, object: *Object) !void {
fn resolveSymbols(self: *Zld) !void {
// TODO mimicking insertion of null symbol from incremental linker.
// This will need to moved.
const null_sym = try Symbol.new(self.allocator, "");
const null_sym = try self.allocator.create(Symbol);
null_sym.* = .{ .strx = 0, .payload = .{ .undef = .{} } };
try self.locals.append(self.allocator, null_sym);
// First pass, resolve symbols in provided objects.
@@ -1639,12 +1640,13 @@ fn resolveSymbols(self: *Zld) !void {
// Second pass, resolve symbols in static libraries.
var sym_it = self.globals.iterator();
while (sym_it.next()) |entry| {
const sym_name = entry.key_ptr.*;
const symbol = entry.value_ptr.*;
if (symbol.payload != .undef) continue;
for (self.archives.items) |archive| {
// Check if the entry exists in a static archive.
const offsets = archive.toc.get(symbol.name) orelse {
const offsets = archive.toc.get(sym_name) orelse {
// No hit.
continue;
};
@@ -1734,21 +1736,27 @@ fn resolveSymbols(self: *Zld) !void {
// Third pass, resolve symbols in dynamic libraries.
{
// Put dyld_stub_binder as an undefined special symbol.
const symbol = try Symbol.new(self.allocator, "dyld_stub_binder");
const symbol = try self.allocator.create(Symbol);
symbol.* = .{
.strx = try self.makeString("dyld_stub_binder"),
.payload = .{ .undef = .{} },
};
const index = @intCast(u32, self.got_entries.items.len);
symbol.got_index = index;
try self.got_entries.append(self.allocator, symbol);
try self.globals.putNoClobber(self.allocator, symbol.name, symbol);
const alloc_name = try self.allocator.dupe(u8, "dyld_stub_binder");
try self.globals.putNoClobber(self.allocator, alloc_name, symbol);
}
var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
defer referenced.deinit();
loop: for (self.globals.values()) |symbol| {
loop: for (self.globals.keys()) |sym_name| {
const symbol = self.globals.get(sym_name).?;
if (symbol.payload != .undef) continue;
for (self.dylibs.items) |dylib| {
if (!dylib.symbols.contains(symbol.name)) continue;
if (!dylib.symbols.contains(sym_name)) continue;
try referenced.put(dylib, {});
const index = @intCast(u32, self.imports.items.len);
@@ -1798,10 +1806,11 @@ fn resolveSymbols(self: *Zld) !void {
}
var has_undefined = false;
for (self.globals.values()) |symbol| {
for (self.globals.keys()) |sym_name| {
const symbol = self.globals.get(sym_name).?;
if (symbol.payload != .undef) continue;
log.err("undefined reference to symbol '{s}'", .{symbol.name});
log.err("undefined reference to symbol '{s}'", .{sym_name});
if (symbol.payload.undef.file) |file| {
log.err(" | referenced in {s}", .{file.name.?});
}
@@ -2344,7 +2353,7 @@ fn writeBindInfoTable(self: *Zld) !void {
.offset = base_offset + sym.got_index.? * @sizeOf(u64),
.segment_id = segment_id,
.dylib_ordinal = proxy.dylibOrdinal(),
.name = sym.name,
.name = self.getString(sym.strx),
});
}
}
@@ -2372,7 +2381,7 @@ fn writeBindInfoTable(self: *Zld) !void {
.offset = binding.offset + base_offset,
.segment_id = match.seg,
.dylib_ordinal = proxy.dylibOrdinal(),
.name = bind_sym.name,
.name = self.getString(bind_sym.strx),
});
}
@@ -2395,7 +2404,7 @@ fn writeBindInfoTable(self: *Zld) !void {
.offset = base_offset,
.segment_id = segment_id,
.dylib_ordinal = proxy.dylibOrdinal(),
.name = sym.name,
.name = self.getString(sym.strx),
});
}
@@ -2435,7 +2444,7 @@ fn writeLazyBindInfoTable(self: *Zld) !void {
.offset = base_offset + sym.stubs_index.? * @sizeOf(u64),
.segment_id = segment_id,
.dylib_ordinal = proxy.dylibOrdinal(),
.name = sym.name,
.name = self.getString(sym.strx),
});
}
}
@@ -2547,7 +2556,7 @@ fn writeExportInfo(self: *Zld) !void {
if (sym.payload != .regular) continue;
const reg = sym.payload.regular;
if (reg.linkage != .global) continue;
try sorted_globals.append(sym.name);
try sorted_globals.append(self.getString(sym.strx));
}
std.sort.sort([]const u8, sorted_globals.items, {}, Sorter.lessThan);
@@ -2556,10 +2565,10 @@ fn writeExportInfo(self: *Zld) !void {
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 });
log.debug(" | putting '{s}' defined at 0x{x}", .{ sym_name, reg.address });
try trie.put(.{
.name = sym.name,
.name = sym_name,
.vmaddr_offset = reg.address - base_address,
.export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
});
@@ -2597,7 +2606,7 @@ fn writeSymbolTable(self: *Zld) !void {
for (self.locals.items) |symbol, i| {
if (i == 0) continue; // skip null symbol
if (symbol.isTemp()) continue; // TODO when merging codepaths, this should go into freelist
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);
@@ -2673,7 +2682,7 @@ fn writeSymbolTable(self: *Zld) !void {
const nlist = try sym.asNlist(self);
const id = @intCast(u32, undefs.items.len);
try undefs.append(nlist);
try undef_dir.putNoClobber(sym.name, id);
try undef_dir.putNoClobber(self.getString(sym.strx), id);
}
const nlocals = locals.items.len;
@@ -2735,7 +2744,8 @@ fn writeSymbolTable(self: *Zld) !void {
stubs.reserved1 = 0;
for (self.stubs.items) |sym| {
const id = undef_dir.get(sym.name) orelse unreachable;
const sym_name = self.getString(sym.strx);
const id = undef_dir.get(sym_name) orelse unreachable;
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
}
@@ -2743,7 +2753,8 @@ fn writeSymbolTable(self: *Zld) !void {
for (self.got_entries.items) |sym| {
switch (sym.payload) {
.proxy => {
const id = undef_dir.get(sym.name) orelse unreachable;
const sym_name = self.getString(sym.strx);
const id = undef_dir.get(sym_name) orelse unreachable;
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
},
else => {
@@ -2754,7 +2765,8 @@ fn writeSymbolTable(self: *Zld) !void {
la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
for (self.stubs.items) |sym| {
const id = undef_dir.get(sym.name) orelse unreachable;
const sym_name = self.getString(sym.strx);
const id = undef_dir.get(sym_name) orelse unreachable;
try writer.writeIntLittle(u32, dysymtab.iundefsym + id);
}
@@ -2940,11 +2952,6 @@ fn writeHeader(self: *Zld) !void {
}
pub fn makeString(self: *Zld, string: []const u8) !u32 {
if (self.strtab_cache.get(string)) |off| {
log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
return off;
}
try self.strtab.ensureUnusedCapacity(self.allocator, string.len + 1);
const new_off = @intCast(u32, self.strtab.items.len);
@@ -2953,12 +2960,10 @@ pub fn makeString(self: *Zld, string: []const u8) !u32 {
self.strtab.appendSliceAssumeCapacity(string);
self.strtab.appendAssumeCapacity(0);
try self.strtab_cache.putNoClobber(self.allocator, try self.allocator.dupe(u8, string), new_off);
return new_off;
}
pub fn getString(self: *Zld, off: u32) ?[]const u8 {
pub fn getString(self: *Zld, off: u32) []const u8 {
assert(off < self.strtab.items.len);
return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off));
}

View File

@@ -407,7 +407,7 @@ pub const Relocation = struct {
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 = self.target.got_index orelse {
log.err("expected GOT entry for symbol '{s}'", .{self.target.name});
log.err("expected GOT entry for symbol '{s}'", .{zld.getString(self.target.strx)});
log.err(" this is an internal linker error", .{});
return error.FailedToResolveRelocationTarget;
};
@@ -446,8 +446,8 @@ pub const Relocation = struct {
break :blk reg.address;
},
.proxy => |proxy| {
if (mem.eql(u8, self.target.name, "__tlv_bootstrap")) {
.proxy => {
if (mem.eql(u8, zld.getString(self.target.strx), "__tlv_bootstrap")) {
break :blk 0; // Dynamically bound by dyld.
}
@@ -460,7 +460,9 @@ pub const Relocation = struct {
break :blk stubs.addr + stubs_index * stubs.reserved2;
},
else => {
log.err("failed to resolve symbol '{s}' as a relocation target", .{self.target.name});
log.err("failed to resolve symbol '{s}' as a relocation target", .{
zld.getString(self.target.strx),
});
log.err(" this is an internal linker error", .{});
return error.FailedToResolveRelocationTarget;
},
@@ -634,7 +636,10 @@ pub const Parser = struct {
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 });
log.debug("adding GOT entry for symbol {s} at index {}", .{
self.zld.getString(out_rel.target.strx),
index,
});
} else if (out_rel.payload == .unsigned) {
const sym = out_rel.target;
switch (sym.payload) {
@@ -697,14 +702,14 @@ pub const Parser = struct {
sym.stubs_index = index;
try self.zld.stubs.append(self.zld.allocator, sym);
log.debug("adding stub entry for symbol {s} at index {}", .{ sym.name, index });
log.debug("adding stub entry for symbol {s} at index {}", .{ self.zld.getString(sym.strx), index });
}
}
}
fn parseBaseRelInfo(self: *Parser, rel: macho.relocation_info) !Relocation {
const offset = @intCast(u32, @intCast(u64, rel.r_address) - self.base_addr);
const target = try self.object.symbolFromReloc(rel);
const target = try self.object.symbolFromReloc(self.zld, rel);
return Relocation{
.offset = offset,
.target = target,
@@ -888,7 +893,7 @@ pub const Parser = struct {
assert(rel.r_pcrel == 0);
assert(self.subtractor == null);
self.subtractor = try self.object.symbolFromReloc(rel);
self.subtractor = try self.object.symbolFromReloc(self.zld, rel);
}
fn parseLoad(self: *Parser, rel: macho.relocation_info) !Relocation {