aboutsummaryrefslogtreecommitdiff
path: root/src/link/MachO/Object.zig
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2024-05-24 15:34:30 +0200
committerJakub Konka <kubkon@jakubkonka.com>2024-05-24 15:34:48 +0200
commited7073c6300d6ae3ec4b370c1e13c2bbf11bc08c (patch)
tree2c6dcf4349e4f710a3d71cec3b47b8b31636726f /src/link/MachO/Object.zig
parentfb88cfdf6aa3fabba700d8340f025e4a3e0d3fb2 (diff)
downloadzig-ed7073c6300d6ae3ec4b370c1e13c2bbf11bc08c.tar.gz
zig-ed7073c6300d6ae3ec4b370c1e13c2bbf11bc08c.zip
link/macho: fix perf bug in DWARF parsing
Diffstat (limited to 'src/link/MachO/Object.zig')
-rw-r--r--src/link/MachO/Object.zig157
1 files changed, 126 insertions, 31 deletions
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 28c3c127e3..c856c65d4e 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -13,7 +13,7 @@ symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
atoms: std.ArrayListUnmanaged(Atom.Index) = .{},
platform: ?MachO.Platform = null,
-dwarf_info: ?DwarfInfo = null,
+compile_unit: ?CompileUnit = null,
stab_files: std.ArrayListUnmanaged(StabFile) = .{},
eh_frame_sect_index: ?u8 = null,
@@ -31,12 +31,6 @@ dynamic_relocs: MachO.DynamicRelocs = .{},
output_symtab_ctx: MachO.SymtabCtx = .{},
output_ar_state: Archive.ArState = .{},
-const InArchive = struct {
- path: []const u8,
- offset: u64,
- size: u32,
-};
-
pub fn isObject(path: []const u8) !bool {
const file = try std.fs.cwd().openFile(path, .{});
defer file.close();
@@ -60,7 +54,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void {
self.fdes.deinit(allocator);
self.eh_frame_data.deinit(allocator);
self.unwind_records.deinit(allocator);
- if (self.dwarf_info) |*dw| dw.deinit(allocator);
for (self.stab_files.items) |*sf| {
sf.stabs.deinit(allocator);
}
@@ -251,8 +244,6 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
// }
}
- try self.initDwarfInfo(macho_file);
-
for (self.atoms.items) |atom_index| {
const atom = macho_file.getAtom(atom_index).?;
const isec = atom.getInputSection(macho_file);
@@ -1214,7 +1205,7 @@ fn parseUnwindRecords(self: *Object, macho_file: *MachO) !void {
/// and record that so that we can emit symbol stabs.
/// TODO in the future, we want parse debug info and debug line sections so that
/// we can provide nice error locations to the user.
-fn initDwarfInfo(self: *Object, macho_file: *MachO) !void {
+pub fn parseDebugInfo(self: *Object, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -1240,17 +1231,107 @@ fn initDwarfInfo(self: *Object, macho_file: *MachO) !void {
const debug_str = if (debug_str_index) |index| try self.getSectionData(@intCast(index), macho_file) else &[0]u8{};
defer gpa.free(debug_str);
- var dwarf_info = DwarfInfo{};
- errdefer dwarf_info.deinit(gpa);
- dwarf_info.init(gpa, .{
+ self.compile_unit = self.findCompileUnit(.{
+ .gpa = gpa,
.debug_info = debug_info,
.debug_abbrev = debug_abbrev,
.debug_str = debug_str,
- }) catch {
- try macho_file.reportParseError2(self.index, "invalid __DWARF info found", .{});
- return error.MalformedObject;
+ }) catch null; // TODO figure out what errors are fatal, and when we silently fail
+}
+
+fn findCompileUnit(self: *Object, args: struct {
+ gpa: Allocator,
+ debug_info: []const u8,
+ debug_abbrev: []const u8,
+ debug_str: []const u8,
+}) !CompileUnit {
+ var cu_wip: struct {
+ comp_dir: ?[:0]const u8 = null,
+ tu_name: ?[:0]const u8 = null,
+ } = .{};
+
+ const gpa = args.gpa;
+ var info_reader = dwarf.InfoReader{ .bytes = args.debug_info, .strtab = args.debug_str };
+ var abbrev_reader = dwarf.AbbrevReader{ .bytes = args.debug_abbrev };
+
+ const cuh = try info_reader.readCompileUnitHeader();
+ try abbrev_reader.seekTo(cuh.debug_abbrev_offset);
+
+ const cu_decl = (try abbrev_reader.readDecl()) orelse return error.Eof;
+ if (cu_decl.tag != dwarf.TAG.compile_unit) return error.UnexpectedTag;
+
+ try info_reader.seekToDie(cu_decl.code, cuh, &abbrev_reader);
+
+ while (try abbrev_reader.readAttr()) |attr| switch (attr.at) {
+ dwarf.AT.name => {
+ cu_wip.tu_name = try info_reader.readString(attr.form, cuh);
+ },
+ dwarf.AT.comp_dir => {
+ cu_wip.comp_dir = try info_reader.readString(attr.form, cuh);
+ },
+ else => switch (attr.form) {
+ dwarf.FORM.sec_offset,
+ dwarf.FORM.ref_addr,
+ => {
+ _ = try info_reader.readOffset(cuh.format);
+ },
+
+ dwarf.FORM.addr => {
+ _ = try info_reader.readNBytes(cuh.address_size);
+ },
+
+ dwarf.FORM.block1,
+ dwarf.FORM.block2,
+ dwarf.FORM.block4,
+ dwarf.FORM.block,
+ => {
+ _ = try info_reader.readBlock(attr.form);
+ },
+
+ dwarf.FORM.exprloc => {
+ _ = try info_reader.readExprLoc();
+ },
+
+ dwarf.FORM.flag_present => {},
+
+ dwarf.FORM.data1,
+ dwarf.FORM.ref1,
+ dwarf.FORM.flag,
+ dwarf.FORM.data2,
+ dwarf.FORM.ref2,
+ dwarf.FORM.data4,
+ dwarf.FORM.ref4,
+ dwarf.FORM.data8,
+ dwarf.FORM.ref8,
+ dwarf.FORM.ref_sig8,
+ dwarf.FORM.udata,
+ dwarf.FORM.ref_udata,
+ dwarf.FORM.sdata,
+ => {
+ _ = try info_reader.readConstant(attr.form);
+ },
+
+ dwarf.FORM.strp,
+ dwarf.FORM.string,
+ => {
+ _ = try info_reader.readString(attr.form, cuh);
+ },
+
+ else => {
+ // TODO actual errors?
+ log.err("unhandled DW_FORM_* value with identifier {x}", .{attr.form});
+ return error.UnhandledForm;
+ },
+ },
+ };
+
+ if (cu_wip.comp_dir == null) return error.MissingCompDir;
+ if (cu_wip.tu_name == null) return error.MissingTuName;
+
+ return .{
+ .comp_dir = try self.addString(gpa, cu_wip.comp_dir.?),
+ .tu_name = try self.addString(gpa, cu_wip.tu_name.?),
};
- self.dwarf_info = dwarf_info;
}
pub fn resolveSymbols(self: *Object, macho_file: *MachO) void {
@@ -1591,10 +1672,9 @@ pub fn calcSymtabSize(self: *Object, macho_file: *MachO) !void {
}
pub fn calcStabsSize(self: *Object, macho_file: *MachO) error{Overflow}!void {
- if (self.dwarf_info) |dw| {
- const cu = dw.compile_units.items[0];
- const comp_dir = try cu.getCompileDir(dw) orelse return;
- const tu_name = try cu.getSourceFile(dw) orelse return;
+ if (self.compile_unit) |cu| {
+ const comp_dir = cu.getCompDir(self);
+ const tu_name = cu.getTuName(self);
self.output_symtab_ctx.nstabs += 4; // N_SO, N_SO, N_OSO, N_SO
self.output_symtab_ctx.strsize += @as(u32, @intCast(comp_dir.len + 1)); // comp_dir
@@ -1709,10 +1789,9 @@ pub fn writeStabs(self: *const Object, macho_file: *MachO, ctx: anytype) error{O
var index = self.output_symtab_ctx.istab;
- if (self.dwarf_info) |dw| {
- const cu = dw.compile_units.items[0];
- const comp_dir = try cu.getCompileDir(dw) orelse return;
- const tu_name = try cu.getSourceFile(dw) orelse return;
+ if (self.compile_unit) |cu| {
+ const comp_dir = cu.getCompDir(self);
+ const tu_name = cu.getTuName(self);
// Open scope
// N_SO comp_dir
@@ -1958,10 +2037,7 @@ pub fn hasEhFrameRecords(self: Object) bool {
}
pub fn hasDebugInfo(self: Object) bool {
- if (self.dwarf_info) |dw| {
- return dw.compile_units.items.len > 0;
- }
- return self.hasSymbolStabs();
+ return self.compile_unit != null or self.hasSymbolStabs();
}
fn hasSymbolStabs(self: Object) bool {
@@ -2194,6 +2270,25 @@ const StabFile = struct {
};
};
+const CompileUnit = struct {
+ comp_dir: u32,
+ tu_name: u32,
+
+ fn getCompDir(cu: CompileUnit, object: *const Object) [:0]const u8 {
+ return object.getString(cu.comp_dir);
+ }
+
+ fn getTuName(cu: CompileUnit, object: *const Object) [:0]const u8 {
+ return object.getString(cu.tu_name);
+ }
+};
+
+const InArchive = struct {
+ path: []const u8,
+ offset: u64,
+ size: u32,
+};
+
const x86_64 = struct {
fn parseRelocs(
self: *const Object,
@@ -2548,6 +2643,7 @@ const aarch64 = struct {
};
const assert = std.debug.assert;
+const dwarf = @import("dwarf.zig");
const eh_frame = @import("eh_frame.zig");
const log = std.log.scoped(.link);
const macho = std.macho;
@@ -2560,7 +2656,6 @@ const Allocator = mem.Allocator;
const Archive = @import("Archive.zig");
const Atom = @import("Atom.zig");
const Cie = eh_frame.Cie;
-const DwarfInfo = @import("DwarfInfo.zig");
const Fde = eh_frame.Fde;
const File = @import("file.zig").File;
const LoadCommandIterator = macho.LoadCommandIterator;