zld: add Symbol.Stab and move nlist creation logic there

This commit is contained in:
Jakub Konka 2021-07-02 00:13:46 +02:00
parent 2b3bda43e3
commit ee6e25bc13
5 changed files with 216 additions and 178 deletions

View File

@ -81,6 +81,11 @@ const ar_hdr = extern struct {
}
}
fn date(self: ar_hdr) !u64 {
const value = getValue(&self.ar_date);
return std.fmt.parseInt(u64, value, 10);
}
fn size(self: ar_hdr) !u32 {
const value = getValue(&self.ar_size);
return std.fmt.parseInt(u32, value, 10);
@ -264,6 +269,7 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
.file = try fs.cwd().openFile(self.name.?, .{}),
.name = name,
.file_offset = @intCast(u32, try reader.context.getPos()),
.mtime = try self.header.?.date(),
};
try object.parse();
try reader.context.seekTo(0);

View File

@ -18,7 +18,6 @@ const LibStub = @import("../tapi.zig").LibStub;
usingnamespace @import("commands.zig");
allocator: *Allocator,
arch: ?Arch = null,
header: ?macho.mach_header_64 = null,
file: ?fs.File = null,

View File

@ -24,6 +24,7 @@ header: ?macho.mach_header_64 = null,
file: ?fs.File = null,
file_offset: ?u32 = null,
name: ?[]const u8 = null,
mtime: ?u64 = null,
load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
sections: std.ArrayListUnmanaged(Section) = .{},
@ -45,12 +46,10 @@ dwarf_debug_line_index: ?u16 = null,
dwarf_debug_ranges_index: ?u16 = null,
symbols: std.ArrayListUnmanaged(*Symbol) = .{},
stabs: std.ArrayListUnmanaged(*Symbol) = .{},
initializers: std.ArrayListUnmanaged(*Symbol) = .{},
data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
tu_path: ?[]const u8 = null,
tu_mtime: ?u64 = null,
pub const Section = struct {
inner: macho.section_64,
code: []u8,
@ -223,16 +222,18 @@ pub fn deinit(self: *Object) void {
}
self.symbols.deinit(self.allocator);
for (self.stabs.items) |stab| {
stab.deinit(self.allocator);
self.allocator.destroy(stab);
}
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);
}
if (self.tu_path) |tu_path| {
self.allocator.free(tu_path);
}
}
pub fn closeFile(self: Object) void {
@ -484,11 +485,33 @@ pub fn parseDebugInfo(self: *Object) !void {
const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
self.tu_path = try std.fs.path.join(self.allocator, &[_][]const u8{ comp_dir, name });
self.tu_mtime = mtime: {
const stat = try self.file.?.stat();
break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
};
if (self.mtime == null) {
self.mtime = mtime: {
const file = self.file orelse break :mtime 0;
const stat = file.stat() catch break :mtime 0;
break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
};
}
try self.stabs.ensureUnusedCapacity(self.allocator, self.symbols.items.len + 4);
// Current dir
self.stabs.appendAssumeCapacity(try Symbol.Stab.new(self.allocator, comp_dir, .{
.kind = .so,
.file = self,
}));
// Artifact name
self.stabs.appendAssumeCapacity(try Symbol.Stab.new(self.allocator, name, .{
.kind = .so,
.file = self,
}));
// Path to object file with debug info
self.stabs.appendAssumeCapacity(try Symbol.Stab.new(self.allocator, self.name.?, .{
.kind = .oso,
.file = self,
}));
for (self.symbols.items) |sym| {
if (sym.cast(Symbol.Regular)) |reg| {
@ -500,7 +523,7 @@ pub fn parseDebugInfo(self: *Object) !void {
}
} else 0;
reg.stab = .{
const stab = try Symbol.Stab.new(self.allocator, sym.name, .{
.kind = kind: {
if (size > 0) break :kind .function;
switch (reg.linkage) {
@ -509,9 +532,27 @@ pub fn parseDebugInfo(self: *Object) !void {
}
},
.size = size,
};
.symbol = sym,
.file = self,
});
self.stabs.appendAssumeCapacity(stab);
} else if (sym.cast(Symbol.Tentative)) |_| {
const stab = try Symbol.Stab.new(self.allocator, sym.name, .{
.kind = .global,
.size = 0,
.symbol = sym,
.file = self,
});
self.stabs.appendAssumeCapacity(stab);
}
}
// Closing delimiter.
const delim_stab = try Symbol.Stab.new(self.allocator, "", .{
.kind = .so,
.file = self,
});
self.stabs.appendAssumeCapacity(delim_stab);
}
fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 {

View File

@ -10,6 +10,7 @@ const Object = @import("Object.zig");
const StringTable = @import("StringTable.zig");
pub const Type = enum {
stab,
regular,
proxy,
unresolved,
@ -31,6 +32,151 @@ got_index: ?u32 = null,
/// Index in stubs table for late binding.
stubs_index: ?u32 = null,
pub const Stab = struct {
base: Symbol,
// Symbol kind: function, etc.
kind: Kind,
// Size of stab.
size: u64,
// Base regular symbol for this stub if defined.
symbol: ?*Symbol = null,
// null means self-reference.
file: ?*Object = null,
pub const base_type: Symbol.Type = .stab;
pub const Kind = enum {
so,
oso,
function,
global,
static,
};
const Opts = struct {
kind: Kind = .so,
size: u64 = 0,
symbol: ?*Symbol = null,
file: ?*Object = null,
};
pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
const stab = try allocator.create(Stab);
errdefer allocator.destroy(stab);
stab.* = .{
.base = .{
.@"type" = .stab,
.name = try allocator.dupe(u8, name),
},
.kind = opts.kind,
.size = opts.size,
.symbol = opts.symbol,
.file = opts.file,
};
return &stab.base;
}
pub fn asNlists(stab: *Stab, allocator: *Allocator, strtab: *StringTable) ![]macho.nlist_64 {
var out = std.ArrayList(macho.nlist_64).init(allocator);
defer out.deinit();
if (stab.kind == .so) {
try out.append(.{
.n_strx = try strtab.getOrPut(stab.base.name),
.n_type = macho.N_SO,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
} else if (stab.kind == .oso) {
const mtime = mtime: {
const object = stab.file orelse break :mtime 0;
break :mtime object.mtime orelse 0;
};
try out.append(.{
.n_strx = try strtab.getOrPut(stab.base.name),
.n_type = macho.N_OSO,
.n_sect = 0,
.n_desc = 1,
.n_value = mtime,
});
} else outer: {
const symbol = stab.symbol orelse unreachable;
const regular = symbol.getTopmostAlias().cast(Regular) orelse unreachable;
const is_match = blk: {
if (regular.file == null and stab.file == null) break :blk true;
if (regular.file) |f1| {
if (stab.file) |f2| {
if (f1 == f2) break :blk true;
}
}
break :blk false;
};
if (!is_match) break :outer;
switch (stab.kind) {
.function => {
try out.ensureUnusedCapacity(4);
out.appendAssumeCapacity(.{
.n_strx = 0,
.n_type = macho.N_BNSYM,
.n_sect = regular.section,
.n_desc = 0,
.n_value = regular.address,
});
out.appendAssumeCapacity(.{
.n_strx = try strtab.getOrPut(stab.base.name),
.n_type = macho.N_FUN,
.n_sect = regular.section,
.n_desc = 0,
.n_value = regular.address,
});
out.appendAssumeCapacity(.{
.n_strx = 0,
.n_type = macho.N_FUN,
.n_sect = 0,
.n_desc = 0,
.n_value = stab.size,
});
out.appendAssumeCapacity(.{
.n_strx = 0,
.n_type = macho.N_ENSYM,
.n_sect = regular.section,
.n_desc = 0,
.n_value = stab.size,
});
},
.global => {
try out.append(.{
.n_strx = try strtab.getOrPut(stab.base.name),
.n_type = macho.N_GSYM,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
},
.static => {
try out.append(.{
.n_strx = try strtab.getOrPut(stab.base.name),
.n_type = macho.N_STSYM,
.n_sect = regular.section,
.n_desc = 0,
.n_value = regular.address,
});
},
.so, .oso => unreachable,
}
}
return out.toOwnedSlice();
}
};
pub const Regular = struct {
base: Symbol,
@ -50,9 +196,6 @@ pub const Regular = struct {
/// null means self-reference.
file: ?*Object = null,
/// Debug stab if defined.
stab: ?Stab = null,
/// True if symbol was already committed into the final
/// symbol table.
visited: bool = false,
@ -65,25 +208,12 @@ pub const Regular = struct {
global,
};
pub const Stab = struct {
/// Stab kind
kind: enum {
function,
global,
static,
},
/// Size of the stab.
size: u64,
};
const Opts = struct {
linkage: Linkage = .translation_unit,
address: u64 = 0,
section: u8 = 0,
weak_ref: bool = false,
file: ?*Object = null,
stab: ?Stab = null,
};
pub fn new(allocator: *Allocator, name: []const u8, opts: Opts) !*Symbol {
@ -100,7 +230,6 @@ pub const Regular = struct {
.section = opts.section,
.weak_ref = opts.weak_ref,
.file = opts.file,
.stab = opts.stab,
};
return &reg.base;
@ -304,15 +433,6 @@ pub fn getTopmostAlias(base: *Symbol) *Symbol {
return base;
}
pub fn asNlist(base: *Symbol, strtab: *StringTable) !macho.nlist_64 {
return switch (base.tag) {
.regular => @fieldParentPtr(Regular, "base", base).asNlist(strtab),
.proxy => @fieldParentPtr(Proxy, "base", base).asNlist(strtab),
.unresolved => @fieldParentPtr(Unresolved, "base", base).asNlist(strtab),
.tentative => @fieldParentPtr(Tentative, "base", base).asNlist(strtab),
};
}
pub fn isStab(sym: macho.nlist_64) bool {
return (macho.N_STAB & sym.n_type) != 0;
}

View File

@ -1137,10 +1137,6 @@ fn allocateTentativeSymbols(self: *Zld) !void {
.section = section,
.weak_ref = false,
.file = tent.file,
.stab = .{
.kind = .global,
.size = 0,
},
});
reg.got_index = tent.base.got_index;
reg.stubs_index = tent.base.stubs_index;
@ -2338,7 +2334,6 @@ fn flush(self: *Zld) !void {
symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
}
try self.writeDebugInfo();
try self.writeSymbolTable();
try self.writeStringTable();
@ -2711,138 +2706,6 @@ fn writeExportInfo(self: *Zld) !void {
try self.file.?.pwriteAll(buffer, dyld_info.export_off);
}
fn writeDebugInfo(self: *Zld) !void {
var stabs = std.ArrayList(macho.nlist_64).init(self.allocator);
defer stabs.deinit();
for (self.objects.items) |object| {
const tu_path = object.tu_path orelse continue;
const tu_mtime = object.tu_mtime orelse continue;
_ = tu_mtime;
const dirname = std.fs.path.dirname(tu_path) orelse "./";
// Current dir
try stabs.append(.{
.n_strx = try self.strtab.getOrPut(tu_path[0 .. dirname.len + 1]),
.n_type = macho.N_SO,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
// Artifact name
try stabs.append(.{
.n_strx = try self.strtab.getOrPut(tu_path[dirname.len + 1 ..]),
.n_type = macho.N_SO,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
// Path to object file with debug info
try stabs.append(.{
.n_strx = try self.strtab.getOrPut(object.name.?),
.n_type = macho.N_OSO,
.n_sect = 0,
.n_desc = 1,
.n_value = 0, //tu_mtime, TODO figure out why precalculated mtime value doesn't work
});
for (object.symbols.items) |sym| {
const reg = reg: {
switch (sym.@"type") {
.regular => break :reg sym.cast(Symbol.Regular) orelse unreachable,
.tentative => {
const final = sym.getTopmostAlias().cast(Symbol.Regular) orelse unreachable;
if (object != final.file) continue;
break :reg final;
},
else => continue,
}
};
if (reg.isTemp() or reg.stab == null) continue;
const stab = reg.stab orelse unreachable;
switch (stab.kind) {
.function => {
try stabs.append(.{
.n_strx = 0,
.n_type = macho.N_BNSYM,
.n_sect = reg.section,
.n_desc = 0,
.n_value = reg.address,
});
try stabs.append(.{
.n_strx = try self.strtab.getOrPut(sym.name),
.n_type = macho.N_FUN,
.n_sect = reg.section,
.n_desc = 0,
.n_value = reg.address,
});
try stabs.append(.{
.n_strx = 0,
.n_type = macho.N_FUN,
.n_sect = 0,
.n_desc = 0,
.n_value = stab.size,
});
try stabs.append(.{
.n_strx = 0,
.n_type = macho.N_ENSYM,
.n_sect = reg.section,
.n_desc = 0,
.n_value = stab.size,
});
},
.global => {
try stabs.append(.{
.n_strx = try self.strtab.getOrPut(sym.name),
.n_type = macho.N_GSYM,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
},
.static => {
try stabs.append(.{
.n_strx = try self.strtab.getOrPut(sym.name),
.n_type = macho.N_STSYM,
.n_sect = reg.section,
.n_desc = 0,
.n_value = reg.address,
});
},
}
}
// Close the source file!
try stabs.append(.{
.n_strx = 0,
.n_type = macho.N_SO,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
}
if (stabs.items.len == 0) return;
// Write stabs into the symbol table
const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
symtab.nsyms = @intCast(u32, stabs.items.len);
const stabs_off = symtab.symoff;
const stabs_size = symtab.nsyms * @sizeOf(macho.nlist_64);
log.debug("writing symbol stabs from 0x{x} to 0x{x}", .{ stabs_off, stabs_size + stabs_off });
try self.file.?.pwriteAll(mem.sliceAsBytes(stabs.items), stabs_off);
linkedit.inner.filesize += stabs_size;
// Update dynamic symbol table.
const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
dysymtab.nlocalsym = symtab.nsyms;
}
fn writeSymbolTable(self: *Zld) !void {
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
@ -2854,6 +2717,15 @@ fn writeSymbolTable(self: *Zld) !void {
defer exports.deinit();
for (self.objects.items) |object| {
for (object.stabs.items) |sym| {
const stab = sym.cast(Symbol.Stab) orelse unreachable;
const nlists = try stab.asNlists(self.allocator, &self.strtab);
defer self.allocator.free(nlists);
try locals.appendSlice(nlists);
}
for (object.symbols.items) |sym| {
const final = sym.getTopmostAlias();
if (final.@"type" != .regular) continue;