Commit cba3389d90
Changed files (17)
src/link/Elf/Atom.zig
@@ -631,15 +631,12 @@ fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 {
}
fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) RelocError!void {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("fatal linker error: unhandled relocation type {} at offset 0x{x}", .{
relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
rel.r_offset,
});
- try err.addNote(elf_file, "in {}:{s}", .{
- self.file(elf_file).?.fmtPath(),
- self.name(elf_file),
- });
+ try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
return error.RelocFailure;
}
@@ -649,15 +646,12 @@ fn reportTextRelocError(
rel: elf.Elf64_Rela,
elf_file: *Elf,
) RelocError!void {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
rel.r_offset,
symbol.name(elf_file),
});
- try err.addNote(elf_file, "in {}:{s}", .{
- self.file(elf_file).?.fmtPath(),
- self.name(elf_file),
- });
+ try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
return error.RelocFailure;
}
@@ -667,16 +661,13 @@ fn reportPicError(
rel: elf.Elf64_Rela,
elf_file: *Elf,
) RelocError!void {
- var err = try elf_file.addErrorWithNotes(2);
- try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
+ var err = try elf_file.base.addErrorWithNotes(2);
+ try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
rel.r_offset,
symbol.name(elf_file),
});
- try err.addNote(elf_file, "in {}:{s}", .{
- self.file(elf_file).?.fmtPath(),
- self.name(elf_file),
- });
- try err.addNote(elf_file, "recompile with -fPIC", .{});
+ try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
+ try err.addNote("recompile with -fPIC", .{});
return error.RelocFailure;
}
@@ -686,16 +677,13 @@ fn reportNoPicError(
rel: elf.Elf64_Rela,
elf_file: *Elf,
) RelocError!void {
- var err = try elf_file.addErrorWithNotes(2);
- try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
+ var err = try elf_file.base.addErrorWithNotes(2);
+ try err.addMsg("relocation at offset 0x{x} against symbol '{s}' cannot be used", .{
rel.r_offset,
symbol.name(elf_file),
});
- try err.addNote(elf_file, "in {}:{s}", .{
- self.file(elf_file).?.fmtPath(),
- self.name(elf_file),
- });
- try err.addNote(elf_file, "recompile with -fno-PIC", .{});
+ try err.addNote("in {}:{s}", .{ self.file(elf_file).?.fmtPath(), self.name(elf_file) });
+ try err.addNote("recompile with -fno-PIC", .{});
return error.RelocFailure;
}
@@ -1332,9 +1320,9 @@ const x86_64 = struct {
try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little);
} else {
x86_64.relaxGotPcTlsDesc(code[r_offset - 3 ..]) catch {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "could not relax {s}", .{@tagName(r_type)});
- try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("could not relax {s}", .{@tagName(r_type)});
+ try err.addNote("in {}:{s} at offset 0x{x}", .{
atom.file(elf_file).?.fmtPath(),
atom.name(elf_file),
rel.r_offset,
@@ -1479,12 +1467,12 @@ const x86_64 = struct {
},
else => {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("TODO: rewrite {} when followed by {}", .{
relocation.fmtRelocType(rels[0].r_type(), .x86_64),
relocation.fmtRelocType(rels[1].r_type(), .x86_64),
});
- try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
+ try err.addNote("in {}:{s} at offset 0x{x}", .{
self.file(elf_file).?.fmtPath(),
self.name(elf_file),
rels[0].r_offset,
@@ -1534,12 +1522,12 @@ const x86_64 = struct {
},
else => {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "TODO: rewrite {} when followed by {}", .{
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("TODO: rewrite {} when followed by {}", .{
relocation.fmtRelocType(rels[0].r_type(), .x86_64),
relocation.fmtRelocType(rels[1].r_type(), .x86_64),
});
- try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
+ try err.addNote("in {}:{s} at offset 0x{x}", .{
self.file(elf_file).?.fmtPath(),
self.name(elf_file),
rels[0].r_offset,
@@ -1630,12 +1618,12 @@ const x86_64 = struct {
},
else => {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("fatal linker error: rewrite {} when followed by {}", .{
relocation.fmtRelocType(rels[0].r_type(), .x86_64),
relocation.fmtRelocType(rels[1].r_type(), .x86_64),
});
- try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
+ try err.addNote("in {}:{s} at offset 0x{x}", .{
self.file(elf_file).?.fmtPath(),
self.name(elf_file),
rels[0].r_offset,
@@ -1824,9 +1812,9 @@ const aarch64 = struct {
aarch64_util.writeAdrpInst(pages, code);
} else {
// TODO: relax
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "TODO: relax ADR_GOT_PAGE", .{});
- try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("TODO: relax ADR_GOT_PAGE", .{});
+ try err.addNote("in {}:{s} at offset 0x{x}", .{
atom.file(elf_file).?.fmtPath(),
atom.name(elf_file),
r_offset,
@@ -2118,9 +2106,9 @@ const riscv = struct {
if (S == atom_addr + @as(i64, @intCast(pair.r_offset))) break pair;
} else {
// TODO: implement searching forward
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "TODO: find HI20 paired reloc scanning forward", .{});
- try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("TODO: find HI20 paired reloc scanning forward", .{});
+ try err.addNote("in {}:{s} at offset 0x{x}", .{
atom.file(elf_file).?.fmtPath(),
atom.name(elf_file),
rel.r_offset,
src/link/Elf/eh_frame.zig
@@ -591,12 +591,12 @@ const riscv = struct {
};
fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "invalid relocation type {} at offset 0x{x}", .{
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("invalid relocation type {} at offset 0x{x}", .{
relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch),
rel.r_offset,
});
- try err.addNote(elf_file, "in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});
+ try err.addNote("in {}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()});
return error.RelocFailure;
}
src/link/Elf/Object.zig
@@ -704,9 +704,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
var end = start;
while (end < data.len - sh_entsize and !isNull(data[end .. end + sh_entsize])) : (end += sh_entsize) {}
if (!isNull(data[end .. end + sh_entsize])) {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "string not null terminated", .{});
- try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("string not null terminated", .{});
+ try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
return error.MalformedObject;
}
end += sh_entsize;
@@ -719,9 +719,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void {
const sh_entsize: u32 = @intCast(shdr.sh_entsize);
if (sh_entsize == 0) continue; // Malformed, don't split but don't error out
if (shdr.sh_size % sh_entsize != 0) {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "size not a multiple of sh_entsize", .{});
- try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("size not a multiple of sh_entsize", .{});
+ try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
return error.MalformedObject;
}
@@ -779,10 +779,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
if (imsec.offsets.items.len == 0) continue;
const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse {
- var err = try elf_file.addErrorWithNotes(2);
- try err.addMsg(elf_file, "invalid symbol value: {x}", .{esym.st_value});
- try err.addNote(elf_file, "for symbol {s}", .{sym.name(elf_file)});
- try err.addNote(elf_file, "in {}", .{self.fmtPath()});
+ var err = try elf_file.base.addErrorWithNotes(2);
+ try err.addMsg("invalid symbol value: {x}", .{esym.st_value});
+ try err.addNote("for symbol {s}", .{sym.name(elf_file)});
+ try err.addNote("in {}", .{self.fmtPath()});
return error.MalformedObject;
};
@@ -804,9 +804,9 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
const imsec = elf_file.inputMergeSection(imsec_index) orelse continue;
if (imsec.offsets.items.len == 0) continue;
const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse {
- var err = try elf_file.addErrorWithNotes(1);
- try err.addMsg(elf_file, "invalid relocation at offset 0x{x}", .{rel.r_offset});
- try err.addNote(elf_file, "in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
+ var err = try elf_file.base.addErrorWithNotes(1);
+ try err.addMsg("invalid relocation at offset 0x{x}", .{rel.r_offset});
+ try err.addNote("in {}:{s}", .{ self.fmtPath(), atom_ptr.name(elf_file) });
return error.MalformedObject;
};
const msub = elf_file.mergeSubsection(msub_index);
src/link/Elf/relocatable.zig
@@ -29,7 +29,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
};
}
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (elf_file.base.hasErrors()) return error.FlushFailure;
// First, we flush relocatable object file generated with our backends.
if (elf_file.zigObjectPtr()) |zig_object| {
@@ -146,7 +146,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
try elf_file.base.file.?.setEndPos(total_size);
try elf_file.base.file.?.pwriteAll(buffer.items, 0);
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (elf_file.base.hasErrors()) return error.FlushFailure;
}
pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
@@ -177,7 +177,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
};
}
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (elf_file.base.hasErrors()) return error.FlushFailure;
// Now, we are ready to resolve the symbols across all input files.
// We will first resolve the files in the ZigObject, next in the parsed
@@ -216,7 +216,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const
try elf_file.writeShdrTable();
try elf_file.writeElfHeader();
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (elf_file.base.hasErrors()) return error.FlushFailure;
}
fn parsePositional(elf_file: *Elf, path: []const u8) Elf.ParseError!void {
src/link/MachO/Archive.zig
@@ -1,21 +1,10 @@
objects: std.ArrayListUnmanaged(Object) = .{},
-pub fn isArchive(path: []const u8, fat_arch: ?fat.Arch) !bool {
- const file = try std.fs.cwd().openFile(path, .{});
- defer file.close();
- if (fat_arch) |arch| {
- try file.seekTo(arch.offset);
- }
- const magic = file.reader().readBytesNoEof(SARMAG) catch return false;
- if (!mem.eql(u8, &magic, ARMAG)) return false;
- return true;
-}
-
pub fn deinit(self: *Archive, allocator: Allocator) void {
self.objects.deinit(allocator);
}
-pub fn parse(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
+pub fn unpack(self: *Archive, macho_file: *MachO, path: []const u8, handle_index: File.HandleIndex, fat_arch: ?fat.Arch) !void {
const gpa = macho_file.base.comp.gpa;
var arena = std.heap.ArenaAllocator.init(gpa);
src/link/MachO/Atom.zig
@@ -906,15 +906,15 @@ const x86_64 = struct {
encode(&.{inst}, code) catch return error.RelaxFail;
},
else => |x| {
- var err = try macho_file.addErrorWithNotes(2);
- try err.addMsg(macho_file, "{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
+ var err = try macho_file.base.addErrorWithNotes(2);
+ try err.addMsg("{s}: 0x{x}: 0x{x}: failed to relax relocation of type {}", .{
self.getName(macho_file),
self.getAddress(macho_file),
rel.offset,
rel.fmtPretty(.x86_64),
});
- try err.addNote(macho_file, "expected .mov instruction but found .{s}", .{@tagName(x)});
- try err.addNote(macho_file, "while parsing {}", .{self.getFile(macho_file).fmtPath()});
+ try err.addNote("expected .mov instruction but found .{s}", .{@tagName(x)});
+ try err.addNote("while parsing {}", .{self.getFile(macho_file).fmtPath()});
return error.RelaxFailUnexpectedInstruction;
},
}
src/link/MachO/Dylib.zig
@@ -1,5 +1,9 @@
+/// Non-zero for fat dylibs
+offset: u64,
path: []const u8,
index: File.Index,
+file_handle: File.HandleIndex,
+tag: enum { dylib, tbd },
exports: std.MultiArrayList(Export) = .{},
strtab: std.ArrayListUnmanaged(u8) = .{},
@@ -11,7 +15,7 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{},
globals: std.ArrayListUnmanaged(MachO.SymbolResolver.Index) = .{},
dependents: std.ArrayListUnmanaged(Id) = .{},
rpaths: std.StringArrayHashMapUnmanaged(void) = .{},
-umbrella: File.Index = 0,
+umbrella: File.Index,
platform: ?MachO.Platform = null,
needed: bool,
@@ -23,16 +27,6 @@ referenced: bool = false,
output_symtab_ctx: MachO.SymtabCtx = .{},
-pub fn isDylib(path: []const u8, fat_arch: ?fat.Arch) !bool {
- const file = try std.fs.cwd().openFile(path, .{});
- defer file.close();
- if (fat_arch) |arch| {
- try file.seekTo(arch.offset);
- }
- const header = file.reader().readStruct(macho.mach_header_64) catch return false;
- return header.filetype == macho.MH_DYLIB;
-}
-
pub fn deinit(self: *Dylib, allocator: Allocator) void {
allocator.free(self.path);
self.exports.deinit(allocator);
@@ -51,12 +45,21 @@ pub fn deinit(self: *Dylib, allocator: Allocator) void {
self.rpaths.deinit(allocator);
}
-pub fn parse(self: *Dylib, macho_file: *MachO, file: std.fs.File, fat_arch: ?fat.Arch) !void {
+pub fn parse(self: *Dylib, macho_file: *MachO) !void {
+ switch (self.tag) {
+ .tbd => try self.parseTbd(macho_file),
+ .dylib => try self.parseBinary(macho_file),
+ }
+ try self.initSymbols(macho_file);
+}
+
+fn parseBinary(self: *Dylib, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
const gpa = macho_file.base.comp.gpa;
- const offset = if (fat_arch) |ar| ar.offset else 0;
+ const file = macho_file.getFileHandle(self.file_handle);
+ const offset = self.offset;
log.debug("parsing dylib from binary: {s}", .{self.path});
@@ -258,13 +261,7 @@ fn parseTrie(self: *Dylib, data: []const u8, macho_file: *MachO) !void {
try self.parseTrieNode(&it, gpa, arena.allocator(), "");
}
-pub fn parseTbd(
- self: *Dylib,
- cpu_arch: std.Target.Cpu.Arch,
- platform: MachO.Platform,
- lib_stub: LibStub,
- macho_file: *MachO,
-) !void {
+fn parseTbd(self: *Dylib, macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -272,6 +269,9 @@ pub fn parseTbd(
log.debug("parsing dylib from stub: {s}", .{self.path});
+ const file = macho_file.getFileHandle(self.file_handle);
+ var lib_stub = LibStub.loadFromFile(gpa, file) catch return error.NotLibStub;
+ defer lib_stub.deinit();
const umbrella_lib = lib_stub.inner[0];
{
@@ -290,7 +290,8 @@ pub fn parseTbd(
log.debug(" (install_name '{s}')", .{umbrella_lib.installName()});
- self.platform = platform;
+ const cpu_arch = macho_file.getTarget().cpu.arch;
+ self.platform = macho_file.platform;
var matcher = try TargetMatcher.init(gpa, cpu_arch, self.platform.?.toApplePlatform());
defer matcher.deinit();
@@ -495,7 +496,7 @@ fn addObjCExport(
try self.addExport(allocator, full_name, .{});
}
-pub fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
+fn initSymbols(self: *Dylib, macho_file: *MachO) !void {
const gpa = macho_file.base.comp.gpa;
const nsyms = self.exports.items(.name).len;
src/link/MachO/fat.zig
@@ -8,11 +8,17 @@ const native_endian = builtin.target.cpu.arch.endian();
const MachO = @import("../MachO.zig");
-pub fn isFatLibrary(path: []const u8) !bool {
- const file = try std.fs.cwd().openFile(path, .{});
- defer file.close();
- const hdr = file.reader().readStructEndian(macho.fat_header, .big) catch return false;
- return hdr.magic == macho.FAT_MAGIC;
+pub fn readFatHeader(file: std.fs.File) !macho.fat_header {
+ return readFatHeaderGeneric(macho.fat_header, file, 0);
+}
+
+fn readFatHeaderGeneric(comptime Hdr: type, file: std.fs.File, offset: usize) !Hdr {
+ var buffer: [@sizeOf(Hdr)]u8 = undefined;
+ const nread = try file.preadAll(&buffer, offset);
+ if (nread != buffer.len) return error.InputOutput;
+ var hdr = @as(*align(1) const Hdr, @ptrCast(&buffer)).*;
+ mem.byteSwapAllFields(Hdr, &hdr);
+ return hdr;
}
pub const Arch = struct {
@@ -21,17 +27,12 @@ pub const Arch = struct {
size: u32,
};
-pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {
- const file = try std.fs.cwd().openFile(path, .{});
- defer file.close();
- const reader = file.reader();
- const fat_header = try reader.readStructEndian(macho.fat_header, .big);
- assert(fat_header.magic == macho.FAT_MAGIC);
-
+pub fn parseArchs(file: std.fs.File, fat_header: macho.fat_header, out: *[2]Arch) ![]const Arch {
var count: usize = 0;
var fat_arch_index: u32 = 0;
- while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) {
- const fat_arch = try reader.readStructEndian(macho.fat_arch, .big);
+ while (fat_arch_index < fat_header.nfat_arch and count < out.len) : (fat_arch_index += 1) {
+ const offset = @sizeOf(macho.fat_header) + @sizeOf(macho.fat_arch) * fat_arch_index;
+ const fat_arch = try readFatHeaderGeneric(macho.fat_arch, file, offset);
// If we come across an architecture that we do not know how to handle, that's
// fine because we can keep looking for one that might match.
const arch: std.Target.Cpu.Arch = switch (fat_arch.cputype) {
@@ -39,9 +40,9 @@ pub fn parseArchs(path: []const u8, buffer: *[2]Arch) ![]const Arch {
macho.CPU_TYPE_X86_64 => if (fat_arch.cpusubtype == macho.CPU_SUBTYPE_X86_64_ALL) .x86_64 else continue,
else => continue,
};
- buffer[count] = .{ .tag = arch, .offset = fat_arch.offset, .size = fat_arch.size };
+ out[count] = .{ .tag = arch, .offset = fat_arch.offset, .size = fat_arch.size };
count += 1;
}
- return buffer[0..count];
+ return out[0..count];
}
src/link/MachO/file.zig
@@ -335,6 +335,21 @@ pub const File = union(enum) {
};
}
+ pub fn parse(file: File, macho_file: *MachO) !void {
+ return switch (file) {
+ .internal, .zig_object => unreachable,
+ .object => |x| x.parse(macho_file),
+ .dylib => |x| x.parse(macho_file),
+ };
+ }
+
+ pub fn parseAr(file: File, macho_file: *MachO) !void {
+ return switch (file) {
+ .internal, .zig_object, .dylib => unreachable,
+ .object => |x| x.parseAr(macho_file),
+ };
+ }
+
pub const Index = u32;
pub const Entry = union(enum) {
src/link/MachO/Object.zig
@@ -38,13 +38,6 @@ compact_unwind_ctx: CompactUnwindCtx = .{},
output_symtab_ctx: MachO.SymtabCtx = .{},
output_ar_state: Archive.ArState = .{},
-pub fn isObject(path: []const u8) !bool {
- const file = try std.fs.cwd().openFile(path, .{});
- defer file.close();
- const header = file.reader().readStruct(macho.mach_header_64) catch return false;
- return header.filetype == macho.MH_OBJECT;
-}
-
pub fn deinit(self: *Object, allocator: Allocator) void {
if (self.in_archive) |*ar| allocator.free(ar.path);
allocator.free(self.path);
@@ -273,6 +266,9 @@ pub fn parse(self: *Object, macho_file: *MachO) !void {
atom.flags.alive = false;
}
}
+
+ // Finally, we do a post-parse check for -ObjC to see if we need to force load this member anyhow.
+ self.alive = self.alive or (macho_file.force_load_objc and self.hasObjC());
}
pub fn isCstringLiteral(sect: macho.section_64) bool {
@@ -2325,7 +2321,7 @@ fn hasSymbolStabs(self: Object) bool {
return self.stab_files.items.len > 0;
}
-pub fn hasObjc(self: Object) bool {
+fn hasObjC(self: Object) bool {
for (self.symtab.items(.nlist)) |nlist| {
const name = self.getString(nlist.n_strx);
if (mem.startsWith(u8, name, "_OBJC_CLASS_$_")) return true;
src/link/MachO/relocatable.zig
@@ -27,22 +27,21 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]c
}
for (positionals.items) |obj| {
- macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
- error.MalformedObject,
- error.MalformedArchive,
- error.InvalidCpuArch,
- error.InvalidTarget,
- => continue, // already reported
- error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
+ macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
+ error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
else => |e| try macho_file.reportParseError(
obj.path,
- "unexpected error: parsing input file failed with error {s}",
+ "unexpected error: reading input file failed with error {s}",
.{@errorName(e)},
),
};
}
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (macho_file.base.hasErrors()) return error.FlushFailure;
+
+ try macho_file.parseInputFiles();
+
+ if (macho_file.base.hasErrors()) return error.FlushFailure;
try macho_file.resolveSymbols();
try macho_file.dedupLiterals();
@@ -93,22 +92,21 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
}
for (positionals.items) |obj| {
- parsePositional(macho_file, obj.path) catch |err| switch (err) {
- error.MalformedObject,
- error.MalformedArchive,
- error.InvalidCpuArch,
- error.InvalidTarget,
- => continue, // already reported
- error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an object file", .{}),
+ macho_file.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
+ error.UnknownFileType => try macho_file.reportParseError(obj.path, "unknown file type for an input file", .{}),
else => |e| try macho_file.reportParseError(
obj.path,
- "unexpected error: parsing input file failed with error {s}",
+ "unexpected error: reading input file failed with error {s}",
.{@errorName(e)},
),
};
}
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (macho_file.base.hasErrors()) return error.FlushFailure;
+
+ try parseInputFilesAr(macho_file);
+
+ if (macho_file.base.hasErrors()) return error.FlushFailure;
// First, we flush relocatable object file generated with our backends.
if (macho_file.getZigObject()) |zo| {
@@ -225,79 +223,19 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation, module_obj_path: ?
try macho_file.base.file.?.setEndPos(total_size);
try macho_file.base.file.?.pwriteAll(buffer.items, 0);
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (macho_file.base.hasErrors()) return error.FlushFailure;
}
-fn parsePositional(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
+fn parseInputFilesAr(macho_file: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
- if (try Object.isObject(path)) {
- try parseObject(macho_file, path);
- } else if (try fat.isFatLibrary(path)) {
- const fat_arch = try macho_file.parseFatLibrary(path);
- if (try Archive.isArchive(path, fat_arch)) {
- try parseArchive(macho_file, path, fat_arch);
- } else return error.UnknownFileType;
- } else if (try Archive.isArchive(path, null)) {
- try parseArchive(macho_file, path, null);
- } else return error.UnknownFileType;
-}
-fn parseObject(macho_file: *MachO, path: []const u8) MachO.ParseError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const gpa = macho_file.base.comp.gpa;
- const file = try std.fs.cwd().openFile(path, .{});
- errdefer file.close();
- const handle = try macho_file.addFileHandle(file);
- const mtime: u64 = mtime: {
- const stat = file.stat() catch break :mtime 0;
- break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
- };
- const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
- macho_file.files.set(index, .{
- .object = .{
- .offset = 0, // TODO FAT objects
- .path = try gpa.dupe(u8, path),
- .file_handle = handle,
- .mtime = mtime,
- .index = index,
- },
- });
- try macho_file.objects.append(gpa, index);
-
- const object = macho_file.getFile(index).?.object;
- try object.parseAr(macho_file);
-}
-
-fn parseArchive(macho_file: *MachO, path: []const u8, fat_arch: ?fat.Arch) MachO.ParseError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const gpa = macho_file.base.comp.gpa;
-
- const file = try std.fs.cwd().openFile(path, .{});
- errdefer file.close();
- const handle = try macho_file.addFileHandle(file);
-
- var archive = Archive{};
- defer archive.deinit(gpa);
- try archive.parse(macho_file, path, handle, fat_arch);
-
- var has_parse_error = false;
- for (archive.objects.items) |extracted| {
- const index = @as(File.Index, @intCast(try macho_file.files.addOne(gpa)));
- macho_file.files.set(index, .{ .object = extracted });
- const object = &macho_file.files.items(.data)[index].object;
- object.index = index;
- object.parseAr(macho_file) catch |err| switch (err) {
- error.InvalidCpuArch => has_parse_error = true,
- else => |e| return e,
+ for (macho_file.objects.items) |index| {
+ macho_file.getFile(index).?.parseAr(macho_file) catch |err| switch (err) {
+ error.InvalidCpuArch => {}, // already reported
+ else => |e| try macho_file.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}),
};
- try macho_file.objects.append(gpa, index);
}
- if (has_parse_error) return error.MalformedArchive;
}
fn markExports(macho_file: *MachO) void {
src/link/Wasm/Object.zig
@@ -235,27 +235,27 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
if (object.imported_tables_count == table_count) return null;
if (table_count != 0) {
- var err = try wasm_file.addErrorWithNotes(1);
- try err.addMsg(wasm_file, "Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
+ var err = try wasm_file.base.addErrorWithNotes(1);
+ try err.addMsg("Expected a table entry symbol for each of the {d} table(s), but instead got {d} symbols.", .{
object.imported_tables_count,
table_count,
});
- try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
+ try err.addNote("defined in '{s}'", .{object.path});
return error.MissingTableSymbols;
}
// MVP object files cannot have any table definitions, only imports (for the indirect function table).
if (object.tables.len > 0) {
- var err = try wasm_file.addErrorWithNotes(1);
- try err.addMsg(wasm_file, "Unexpected table definition without representing table symbols.", .{});
- try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
+ var err = try wasm_file.base.addErrorWithNotes(1);
+ try err.addMsg("Unexpected table definition without representing table symbols.", .{});
+ try err.addNote("defined in '{s}'", .{object.path});
return error.UnexpectedTable;
}
if (object.imported_tables_count != 1) {
- var err = try wasm_file.addErrorWithNotes(1);
- try err.addMsg(wasm_file, "Found more than one table import, but no representing table symbols", .{});
- try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
+ var err = try wasm_file.base.addErrorWithNotes(1);
+ try err.addMsg("Found more than one table import, but no representing table symbols", .{});
+ try err.addNote("defined in '{s}'", .{object.path});
return error.MissingTableSymbols;
}
@@ -266,9 +266,9 @@ fn checkLegacyIndirectFunctionTable(object: *Object, wasm_file: *const Wasm) !?S
} else unreachable;
if (!std.mem.eql(u8, object.string_table.get(table_import.name), "__indirect_function_table")) {
- var err = try wasm_file.addErrorWithNotes(1);
- try err.addMsg(wasm_file, "Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
- try err.addNote(wasm_file, "defined in '{s}'", .{object.path});
+ var err = try wasm_file.base.addErrorWithNotes(1);
+ try err.addMsg("Non-indirect function table import '{s}' is missing a corresponding symbol", .{object.string_table.get(table_import.name)});
+ try err.addNote("defined in '{s}'", .{object.path});
return error.MissingTableSymbols;
}
@@ -596,9 +596,9 @@ fn Parser(comptime ReaderType: type) type {
try reader.readNoEof(name);
const tag = types.known_features.get(name) orelse {
- var err = try parser.wasm_file.addErrorWithNotes(1);
- try err.addMsg(parser.wasm_file, "Object file contains unknown feature: {s}", .{name});
- try err.addNote(parser.wasm_file, "defined in '{s}'", .{parser.object.path});
+ var err = try parser.wasm_file.base.addErrorWithNotes(1);
+ try err.addMsg("Object file contains unknown feature: {s}", .{name});
+ try err.addNote("defined in '{s}'", .{parser.object.path});
return error.UnknownFeature;
};
feature.* = .{
src/link/Elf.zig
@@ -995,12 +995,12 @@ pub fn growAllocSection(self: *Elf, shdr_index: u32, needed_size: u64) !void {
if (maybe_phdr) |phdr| {
const mem_capacity = self.allocatedVirtualSize(phdr.p_vaddr);
if (needed_size > mem_capacity) {
- var err = try self.addErrorWithNotes(2);
- try err.addMsg(self, "fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
+ var err = try self.base.addErrorWithNotes(2);
+ try err.addMsg("fatal linker error: cannot expand load segment phdr({d}) in virtual memory", .{
self.phdr_to_shdr_table.get(shdr_index).?,
});
- try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
- try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
+ try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
+ try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
}
phdr.p_memsz = needed_size;
@@ -1276,7 +1276,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
};
}
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (self.base.hasErrors()) return error.FlushFailure;
// Dedup shared objects
{
@@ -1423,7 +1423,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
try self.writeElfHeader();
}
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (self.base.hasErrors()) return error.FlushFailure;
}
/// --verbose-link output
@@ -2852,9 +2852,9 @@ fn writePhdrTable(self: *Elf) !void {
}
pub fn writeElfHeader(self: *Elf) !void {
- const comp = self.base.comp;
- if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
+ if (self.base.hasErrors()) return; // We had errors, so skip flushing to render the output unusable
+ const comp = self.base.comp;
var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
var index: usize = 0;
@@ -4298,9 +4298,9 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
// (revisit getMaxNumberOfPhdrs())
// 2. shift everything in file to free more space for EHDR + PHDR table
// TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, "fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
- try err.addNote(self, "required 0x{x}, available 0x{x}", .{ needed_size, available_space });
+ var err = try self.base.addErrorWithNotes(1);
+ try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
+ try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space });
}
phdr_table_load.p_filesz = needed_size + ehsize;
@@ -5863,56 +5863,6 @@ pub fn tlsAddress(self: *Elf) i64 {
return @intCast(phdr.p_vaddr);
}
-const ErrorWithNotes = struct {
- /// Allocated index in comp.link_errors array.
- index: usize,
-
- /// Next available note slot.
- note_slot: usize = 0,
-
- pub fn addMsg(
- err: ErrorWithNotes,
- elf_file: *Elf,
- comptime format: []const u8,
- args: anytype,
- ) error{OutOfMemory}!void {
- const comp = elf_file.base.comp;
- const gpa = comp.gpa;
- const err_msg = &comp.link_errors.items[err.index];
- err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
- }
-
- pub fn addNote(
- err: *ErrorWithNotes,
- elf_file: *Elf,
- comptime format: []const u8,
- args: anytype,
- ) error{OutOfMemory}!void {
- const comp = elf_file.base.comp;
- const gpa = comp.gpa;
- const err_msg = &comp.link_errors.items[err.index];
- assert(err.note_slot < err_msg.notes.len);
- err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
- err.note_slot += 1;
- }
-};
-
-pub fn addErrorWithNotes(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
- const comp = self.base.comp;
- const gpa = comp.gpa;
- try comp.link_errors.ensureUnusedCapacity(gpa, 1);
- return self.addErrorWithNotesAssumeCapacity(note_count);
-}
-
-fn addErrorWithNotesAssumeCapacity(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
- const comp = self.base.comp;
- const gpa = comp.gpa;
- const index = comp.link_errors.items.len;
- const err = comp.link_errors.addOneAssumeCapacity();
- err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
- return .{ .index = index };
-}
-
pub fn getShString(self: Elf, off: u32) [:0]const u8 {
assert(off < self.shstrtab.items.len);
return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0);
@@ -5940,11 +5890,10 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
}
fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
- const comp = self.base.comp;
- const gpa = comp.gpa;
+ const gpa = self.base.comp.gpa;
const max_notes = 4;
- try comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
+ try self.base.comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
var it = undefs.iterator();
while (it.next()) |entry| {
@@ -5953,18 +5902,18 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
const natoms = @min(atoms.len, max_notes);
const nnotes = natoms + @intFromBool(atoms.len > max_notes);
- var err = try self.addErrorWithNotesAssumeCapacity(nnotes);
- try err.addMsg(self, "undefined symbol: {s}", .{self.symbol(undef_index).name(self)});
+ var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes);
+ try err.addMsg("undefined symbol: {s}", .{self.symbol(undef_index).name(self)});
for (atoms[0..natoms]) |atom_index| {
const atom_ptr = self.atom(atom_index).?;
const file_ptr = self.file(atom_ptr.file_index).?;
- try err.addNote(self, "referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
+ try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
}
if (atoms.len > max_notes) {
const remaining = atoms.len - max_notes;
- try err.addNote(self, "referenced {d} more times", .{remaining});
+ try err.addNote("referenced {d} more times", .{remaining});
}
}
}
@@ -5978,19 +5927,19 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
const notes = entry.value_ptr.*;
const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
- var err = try self.addErrorWithNotes(nnotes + 1);
- try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.name(self)});
- try err.addNote(self, "defined by {}", .{sym.file(self).?.fmtPath()});
+ var err = try self.base.addErrorWithNotes(nnotes + 1);
+ try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)});
+ try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()});
var inote: usize = 0;
while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
const file_ptr = self.file(notes.items[inote]).?;
- try err.addNote(self, "defined by {}", .{file_ptr.fmtPath()});
+ try err.addNote("defined by {}", .{file_ptr.fmtPath()});
}
if (notes.items.len > max_notes) {
const remaining = notes.items.len - max_notes;
- try err.addNote(self, "defined {d} more times", .{remaining});
+ try err.addNote("defined {d} more times", .{remaining});
}
has_dupes = true;
@@ -6005,16 +5954,16 @@ fn reportMissingLibraryError(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(checked_paths.len);
- try err.addMsg(self, format, args);
+ var err = try self.base.addErrorWithNotes(checked_paths.len);
+ try err.addMsg(format, args);
for (checked_paths) |path| {
- try err.addNote(self, "tried {s}", .{path});
+ try err.addNote("tried {s}", .{path});
}
}
pub fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(0);
- try err.addMsg(self, "fatal linker error: unsupported CPU architecture {s}", .{
+ var err = try self.base.addErrorWithNotes(0);
+ try err.addMsg("fatal linker error: unsupported CPU architecture {s}", .{
@tagName(self.getTarget().cpu.arch),
});
}
@@ -6025,9 +5974,9 @@ pub fn reportParseError(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, format, args);
- try err.addNote(self, "while parsing {s}", .{path});
+ var err = try self.base.addErrorWithNotes(1);
+ try err.addMsg(format, args);
+ try err.addNote("while parsing {s}", .{path});
}
pub fn reportParseError2(
@@ -6036,9 +5985,9 @@ pub fn reportParseError2(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, format, args);
- try err.addNote(self, "while parsing {}", .{self.file(file_index).?.fmtPath()});
+ var err = try self.base.addErrorWithNotes(1);
+ try err.addMsg(format, args);
+ try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
}
const FormatShdrCtx = struct {
src/link/MachO.zig
@@ -395,17 +395,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
}
for (positionals.items) |obj| {
- self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
- error.MalformedObject,
- error.MalformedArchive,
- error.MalformedDylib,
- error.InvalidCpuArch,
- error.InvalidTarget,
- => continue, // already reported
- error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an object file", .{}),
+ self.classifyInputFile(obj.path, .{ .path = obj.path }, obj.must_link) catch |err| switch (err) {
+ error.UnknownFileType => try self.reportParseError(obj.path, "unknown file type for an input file", .{}),
else => |e| try self.reportParseError(
obj.path,
- "unexpected error: parsing input file failed with error {s}",
+ "unexpected error: reading input file failed with error {s}",
.{@errorName(e)},
),
};
@@ -448,15 +442,11 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
};
for (system_libs.items) |lib| {
- self.parseLibrary(lib, false) catch |err| switch (err) {
- error.MalformedArchive,
- error.MalformedDylib,
- error.InvalidCpuArch,
- => continue, // already reported
- error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for a library", .{}),
+ self.classifyInputFile(lib.path, lib, false) catch |err| switch (err) {
+ error.UnknownFileType => try self.reportParseError(lib.path, "unknown file type for an input file", .{}),
else => |e| try self.reportParseError(
lib.path,
- "unexpected error: parsing library failed with error {s}",
+ "unexpected error: parsing input file failed with error {s}",
.{@errorName(e)},
),
};
@@ -469,13 +459,8 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
break :blk null;
};
if (compiler_rt_path) |path| {
- self.parsePositional(path, false) catch |err| switch (err) {
- error.MalformedObject,
- error.MalformedArchive,
- error.InvalidCpuArch,
- error.InvalidTarget,
- => {}, // already reported
- error.UnknownFileType => try self.reportParseError(path, "unknown file type for a library", .{}),
+ self.classifyInputFile(path, .{ .path = path }, false) catch |err| switch (err) {
+ error.UnknownFileType => try self.reportParseError(path, "unknown file type for an input file", .{}),
else => |e| try self.reportParseError(
path,
"unexpected error: parsing input file failed with error {s}",
@@ -484,30 +469,20 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
};
}
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (self.base.hasErrors()) return error.FlushFailure;
- for (self.dylibs.items) |index| {
- self.getFile(index).?.dylib.umbrella = index;
- }
-
- if (self.dylibs.items.len > 0) {
- self.parseDependentDylibs() catch |err| {
- switch (err) {
- error.MissingLibraryDependencies => {},
- else => |e| try self.reportUnexpectedError(
- "unexpected error while parsing dependent libraries: {s}",
- .{@errorName(e)},
- ),
- }
- return error.FlushFailure;
- };
- }
+ try self.parseInputFiles();
+ self.parseDependentDylibs() catch |err| {
+ switch (err) {
+ error.MissingLibraryDependencies => {},
+ else => |e| try self.reportUnexpectedError(
+ "unexpected error while parsing dependent libraries: {s}",
+ .{@errorName(e)},
+ ),
+ }
+ };
- for (self.dylibs.items) |index| {
- const dylib = self.getFile(index).?.dylib;
- if (!dylib.explicit and !dylib.hoisted) continue;
- try dylib.initSymbols(self);
- }
+ if (self.base.hasErrors()) return error.FlushFailure;
{
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
@@ -841,181 +816,173 @@ pub fn resolveLibSystem(
});
}
-pub const ParseError = error{
- MalformedObject,
- MalformedArchive,
- MalformedDylib,
- MalformedTbd,
- NotLibStub,
- InvalidCpuArch,
- InvalidTarget,
- InvalidTargetFatLibrary,
- IncompatibleDylibVersion,
- OutOfMemory,
- Overflow,
- InputOutput,
- EndOfStream,
- FileSystem,
- NotSupported,
- Unhandled,
- UnknownFileType,
-} || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError || tapi.TapiError;
-
-pub fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void {
+pub fn classifyInputFile(self: *MachO, path: []const u8, lib: SystemLib, must_link: bool) !void {
const tracy = trace(@src());
defer tracy.end();
- if (try Object.isObject(path)) {
- try self.parseObject(path);
- } else {
- try self.parseLibrary(.{ .path = path }, must_link);
+
+ log.debug("classifying input file {s}", .{path});
+
+ const file = try std.fs.cwd().openFile(path, .{});
+ const fh = try self.addFileHandle(file);
+ var buffer: [Archive.SARMAG]u8 = undefined;
+
+ const fat_arch: ?fat.Arch = try self.parseFatFile(file, path);
+ const offset = if (fat_arch) |fa| fa.offset else 0;
+
+ if (readMachHeader(file, offset) catch null) |h| blk: {
+ if (h.magic != macho.MH_MAGIC_64) break :blk;
+ switch (h.filetype) {
+ macho.MH_OBJECT => try self.addObject(path, fh, offset),
+ macho.MH_DYLIB => _ = try self.addDylib(lib, true, fh, offset),
+ else => return error.UnknownFileType,
+ }
+ return;
+ }
+ if (readArMagic(file, offset, &buffer) catch null) |ar_magic| blk: {
+ if (!mem.eql(u8, ar_magic, Archive.ARMAG)) break :blk;
+ try self.addArchive(lib, must_link, fh, fat_arch);
+ return;
}
+ _ = try self.addTbd(lib, true, fh);
}
-fn parseLibrary(self: *MachO, lib: SystemLib, must_link: bool) ParseError!void {
- const tracy = trace(@src());
- defer tracy.end();
- if (try fat.isFatLibrary(lib.path)) {
- const fat_arch = try self.parseFatLibrary(lib.path);
- if (try Archive.isArchive(lib.path, fat_arch)) {
- try self.parseArchive(lib, must_link, fat_arch);
- } else if (try Dylib.isDylib(lib.path, fat_arch)) {
- _ = try self.parseDylib(lib, true, fat_arch);
- } else return error.UnknownFileType;
- } else if (try Archive.isArchive(lib.path, null)) {
- try self.parseArchive(lib, must_link, null);
- } else if (try Dylib.isDylib(lib.path, null)) {
- _ = try self.parseDylib(lib, true, null);
- } else {
- _ = self.parseTbd(lib, true) catch |err| switch (err) {
- error.MalformedTbd => return error.UnknownFileType,
- else => |e| return e,
- };
+fn parseFatFile(self: *MachO, file: std.fs.File, path: []const u8) !?fat.Arch {
+ const fat_h = fat.readFatHeader(file) catch return null;
+ if (fat_h.magic != macho.FAT_MAGIC and fat_h.magic != macho.FAT_MAGIC_64) return null;
+ var fat_archs_buffer: [2]fat.Arch = undefined;
+ const fat_archs = try fat.parseArchs(file, fat_h, &fat_archs_buffer);
+ const cpu_arch = self.getTarget().cpu.arch;
+ for (fat_archs) |arch| {
+ if (arch.tag == cpu_arch) return arch;
}
+ try self.reportParseError(path, "missing arch in universal file: expected {s}", .{
+ @tagName(cpu_arch),
+ });
+ return error.MissingCpuArch;
+}
+
+pub fn readMachHeader(file: std.fs.File, offset: usize) !macho.mach_header_64 {
+ var buffer: [@sizeOf(macho.mach_header_64)]u8 = undefined;
+ const nread = try file.preadAll(&buffer, offset);
+ if (nread != buffer.len) return error.InputOutput;
+ const hdr = @as(*align(1) const macho.mach_header_64, @ptrCast(&buffer)).*;
+ return hdr;
}
-fn parseObject(self: *MachO, path: []const u8) ParseError!void {
+pub fn readArMagic(file: std.fs.File, offset: usize, buffer: *[Archive.SARMAG]u8) ![]const u8 {
+ const nread = try file.preadAll(buffer, offset);
+ if (nread != buffer.len) return error.InputOutput;
+ return buffer[0..Archive.SARMAG];
+}
+
+fn addObject(self: *MachO, path: []const u8, handle: File.HandleIndex, offset: u64) !void {
const tracy = trace(@src());
defer tracy.end();
const gpa = self.base.comp.gpa;
- const file = try fs.cwd().openFile(path, .{});
- const handle = try self.addFileHandle(file);
const mtime: u64 = mtime: {
+ const file = self.getFileHandle(handle);
const stat = file.stat() catch break :mtime 0;
break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
};
const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
- self.files.set(index, .{
- .object = .{
- .offset = 0, // TODO FAT objects
- .path = try gpa.dupe(u8, path),
- .file_handle = handle,
- .mtime = mtime,
- .index = index,
- },
- });
+ self.files.set(index, .{ .object = .{
+ .offset = offset,
+ .path = try gpa.dupe(u8, path),
+ .file_handle = handle,
+ .mtime = mtime,
+ .index = index,
+ } });
try self.objects.append(gpa, index);
-
- const object = self.getFile(index).?.object;
- try object.parse(self);
}
-pub fn parseFatLibrary(self: *MachO, path: []const u8) !fat.Arch {
- var buffer: [2]fat.Arch = undefined;
- const fat_archs = try fat.parseArchs(path, &buffer);
- const cpu_arch = self.getTarget().cpu.arch;
- for (fat_archs) |arch| {
- if (arch.tag == cpu_arch) return arch;
+pub fn parseInputFiles(self: *MachO) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ for (self.objects.items) |index| {
+ self.getFile(index).?.parse(self) catch |err| switch (err) {
+ error.MalformedObject,
+ error.InvalidCpuArch,
+ error.InvalidTarget,
+ => {}, // already reported
+ else => |e| try self.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}),
+ };
+ }
+ for (self.dylibs.items) |index| {
+ self.getFile(index).?.parse(self) catch |err| switch (err) {
+ error.MalformedDylib,
+ error.InvalidCpuArch,
+ error.InvalidTarget,
+ => {}, // already reported
+ else => |e| try self.reportParseError2(index, "unexpected error: parsing input file failed with error {s}", .{@errorName(e)}),
+ };
}
- try self.reportParseError(path, "missing arch in universal file: expected {s}", .{@tagName(cpu_arch)});
- return error.InvalidCpuArch;
}
-fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Arch) ParseError!void {
+fn addArchive(self: *MachO, lib: SystemLib, must_link: bool, handle: File.HandleIndex, fat_arch: ?fat.Arch) !void {
const tracy = trace(@src());
defer tracy.end();
const gpa = self.base.comp.gpa;
- const file = try fs.cwd().openFile(lib.path, .{});
- const handle = try self.addFileHandle(file);
-
var archive = Archive{};
defer archive.deinit(gpa);
- try archive.parse(self, lib.path, handle, fat_arch);
+ try archive.unpack(self, lib.path, handle, fat_arch);
- var has_parse_error = false;
- for (archive.objects.items) |extracted| {
- const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
- self.files.set(index, .{ .object = extracted });
+ for (archive.objects.items) |unpacked| {
+ const index: File.Index = @intCast(try self.files.addOne(gpa));
+ self.files.set(index, .{ .object = unpacked });
const object = &self.files.items(.data)[index].object;
object.index = index;
object.alive = must_link or lib.needed; // TODO: or self.options.all_load;
object.hidden = lib.hidden;
- object.parse(self) catch |err| switch (err) {
- error.MalformedObject,
- error.InvalidCpuArch,
- error.InvalidTarget,
- => has_parse_error = true,
- else => |e| return e,
- };
try self.objects.append(gpa, index);
-
- // Finally, we do a post-parse check for -ObjC to see if we need to force load this member
- // anyhow.
- object.alive = object.alive or (self.force_load_objc and object.hasObjc());
}
- if (has_parse_error) return error.MalformedArchive;
}
-fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) ParseError!File.Index {
+fn addDylib(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleIndex, offset: u64) !File.Index {
const tracy = trace(@src());
defer tracy.end();
const gpa = self.base.comp.gpa;
- const file = try fs.cwd().openFile(lib.path, .{});
- defer file.close();
-
- const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
+ const index: File.Index = @intCast(try self.files.addOne(gpa));
self.files.set(index, .{ .dylib = .{
+ .offset = offset,
+ .file_handle = handle,
+ .tag = .dylib,
.path = try gpa.dupe(u8, lib.path),
.index = index,
.needed = lib.needed,
.weak = lib.weak,
.reexport = lib.reexport,
.explicit = explicit,
+ .umbrella = index,
} });
- const dylib = &self.files.items(.data)[index].dylib;
- try dylib.parse(self, file, fat_arch);
-
try self.dylibs.append(gpa, index);
return index;
}
-fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!File.Index {
+fn addTbd(self: *MachO, lib: SystemLib, explicit: bool, handle: File.HandleIndex) !File.Index {
const tracy = trace(@src());
defer tracy.end();
const gpa = self.base.comp.gpa;
- const file = try fs.cwd().openFile(lib.path, .{});
- defer file.close();
-
- var lib_stub = LibStub.loadFromFile(gpa, file) catch return error.MalformedTbd; // TODO actually handle different errors
- defer lib_stub.deinit();
-
- const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
+ const index: File.Index = @intCast(try self.files.addOne(gpa));
self.files.set(index, .{ .dylib = .{
+ .offset = 0,
+ .file_handle = handle,
+ .tag = .tbd,
.path = try gpa.dupe(u8, lib.path),
.index = index,
.needed = lib.needed,
.weak = lib.weak,
.reexport = lib.reexport,
.explicit = explicit,
+ .umbrella = index,
} });
- const dylib = &self.files.items(.data)[index].dylib;
- try dylib.parseTbd(self.getTarget().cpu.arch, self.platform, lib_stub, self);
try self.dylibs.append(gpa, index);
return index;
@@ -1092,6 +1059,8 @@ fn parseDependentDylibs(self: *MachO) !void {
const tracy = trace(@src());
defer tracy.end();
+ if (self.dylibs.items.len == 0) return;
+
const gpa = self.base.comp.gpa;
const lib_dirs = self.lib_dirs;
const framework_dirs = self.framework_dirs;
@@ -1108,7 +1077,7 @@ fn parseDependentDylibs(self: *MachO) !void {
while (index < self.dylibs.items.len) : (index += 1) {
const dylib_index = self.dylibs.items[index];
- var dependents = std.ArrayList(struct { id: Dylib.Id, file: File.Index }).init(gpa);
+ var dependents = std.ArrayList(File.Index).init(gpa);
defer dependents.deinit();
try dependents.ensureTotalCapacityPrecise(self.getFile(dylib_index).?.dylib.dependents.items.len);
@@ -1199,38 +1168,34 @@ fn parseDependentDylibs(self: *MachO) !void {
.path = full_path,
.weak = is_weak,
};
+ const file = try std.fs.cwd().openFile(lib.path, .{});
+ const fh = try self.addFileHandle(file);
+ const fat_arch = try self.parseFatFile(file, lib.path);
+ const offset = if (fat_arch) |fa| fa.offset else 0;
const file_index = file_index: {
- if (try fat.isFatLibrary(lib.path)) {
- const fat_arch = try self.parseFatLibrary(lib.path);
- if (try Dylib.isDylib(lib.path, fat_arch)) {
- break :file_index try self.parseDylib(lib, false, fat_arch);
- } else break :file_index @as(File.Index, 0);
- } else if (try Dylib.isDylib(lib.path, null)) {
- break :file_index try self.parseDylib(lib, false, null);
- } else {
- const file_index = self.parseTbd(lib, false) catch |err| switch (err) {
- error.MalformedTbd => @as(File.Index, 0),
- else => |e| return e,
- };
- break :file_index file_index;
+ if (readMachHeader(file, offset) catch null) |h| blk: {
+ if (h.magic != macho.MH_MAGIC_64) break :blk;
+ switch (h.filetype) {
+ macho.MH_DYLIB => break :file_index try self.addDylib(lib, false, fh, offset),
+ else => break :file_index @as(File.Index, 0),
+ }
}
+ break :file_index try self.addTbd(lib, false, fh);
};
- dependents.appendAssumeCapacity(.{ .id = id, .file = file_index });
+ dependents.appendAssumeCapacity(file_index);
}
const dylib = self.getFile(dylib_index).?.dylib;
- for (dependents.items) |entry| {
- const id = entry.id;
- const file_index = entry.file;
+ for (dylib.dependents.items, dependents.items) |id, file_index| {
if (self.getFile(file_index)) |file| {
const dep_dylib = file.dylib;
+ try dep_dylib.parse(self); // TODO in parallel
dep_dylib.hoisted = self.isHoisted(id.name);
- if (self.getFile(dep_dylib.umbrella) == null) {
- dep_dylib.umbrella = dylib.umbrella;
- }
+ dep_dylib.umbrella = dylib.umbrella;
if (!dep_dylib.hoisted) {
const umbrella = dep_dylib.getUmbrella(self);
for (dep_dylib.exports.items(.name), dep_dylib.exports.items(.flags)) |off, flags| {
+ // TODO rethink this entire algorithm
try umbrella.addExport(gpa, dep_dylib.getString(off), flags);
}
try umbrella.rpaths.ensureUnusedCapacity(gpa, dep_dylib.rpaths.keys().len);
@@ -1238,15 +1203,13 @@ fn parseDependentDylibs(self: *MachO) !void {
umbrella.rpaths.putAssumeCapacity(try gpa.dupe(u8, rpath), {});
}
}
- } else {
- try self.reportDependencyError(
- dylib.getUmbrella(self).index,
- id.name,
- "unable to resolve dependency",
- .{},
- );
- has_errors = true;
- }
+ } else try self.reportDependencyError(
+ dylib.getUmbrella(self).index,
+ id.name,
+ "unable to resolve dependency",
+ .{},
+ );
+ has_errors = true;
}
}
@@ -1533,8 +1496,8 @@ fn reportUndefs(self: *MachO) !void {
const notes = entry.value_ptr.*;
const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
- var err = try self.addErrorWithNotes(nnotes);
- try err.addMsg(self, "undefined symbol: {s}", .{undef_sym.getName(self)});
+ var err = try self.base.addErrorWithNotes(nnotes);
+ try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)});
has_undefs = true;
var inote: usize = 0;
@@ -1542,12 +1505,12 @@ fn reportUndefs(self: *MachO) !void {
const note = notes.items[inote];
const file = self.getFile(note.file).?;
const atom = note.getAtom(self).?;
- try err.addNote(self, "referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
+ try err.addNote("referenced by {}:{s}", .{ file.fmtPath(), atom.getName(self) });
}
if (notes.items.len > max_notes) {
const remaining = notes.items.len - max_notes;
- try err.addNote(self, "referenced {d} more times", .{remaining});
+ try err.addNote("referenced {d} more times", .{remaining});
}
}
if (has_undefs) return error.HasUndefinedSymbols;
@@ -3323,13 +3286,13 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !vo
const mem_capacity = self.allocatedSizeVirtual(seg.vmaddr);
if (needed_size > mem_capacity) {
- var err = try self.addErrorWithNotes(2);
- try err.addMsg(self, "fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
+ var err = try self.base.addErrorWithNotes(2);
+ try err.addMsg("fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
seg_id,
seg.segName(),
});
- try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
- try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
+ try err.addNote("TODO: emit relocations to memory locations in self-hosted backends", .{});
+ try err.addNote("as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
}
seg.vmsize = needed_size;
@@ -3618,65 +3581,15 @@ pub fn eatPrefix(path: []const u8, prefix: []const u8) ?[]const u8 {
return null;
}
-const ErrorWithNotes = struct {
- /// Allocated index in comp.link_errors array.
- index: usize,
-
- /// Next available note slot.
- note_slot: usize = 0,
-
- pub fn addMsg(
- err: ErrorWithNotes,
- macho_file: *MachO,
- comptime format: []const u8,
- args: anytype,
- ) error{OutOfMemory}!void {
- const comp = macho_file.base.comp;
- const gpa = comp.gpa;
- const err_msg = &comp.link_errors.items[err.index];
- err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
- }
-
- pub fn addNote(
- err: *ErrorWithNotes,
- macho_file: *MachO,
- comptime format: []const u8,
- args: anytype,
- ) error{OutOfMemory}!void {
- const comp = macho_file.base.comp;
- const gpa = comp.gpa;
- const err_msg = &comp.link_errors.items[err.index];
- assert(err.note_slot < err_msg.notes.len);
- err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
- err.note_slot += 1;
- }
-};
-
-pub fn addErrorWithNotes(self: *MachO, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
- const comp = self.base.comp;
- const gpa = comp.gpa;
- try comp.link_errors.ensureUnusedCapacity(gpa, 1);
- return self.addErrorWithNotesAssumeCapacity(note_count);
-}
-
-fn addErrorWithNotesAssumeCapacity(self: *MachO, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
- const comp = self.base.comp;
- const gpa = comp.gpa;
- const index = comp.link_errors.items.len;
- const err = comp.link_errors.addOneAssumeCapacity();
- err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
- return .{ .index = index };
-}
-
pub fn reportParseError(
self: *MachO,
path: []const u8,
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, format, args);
- try err.addNote(self, "while parsing {s}", .{path});
+ var err = try self.base.addErrorWithNotes(1);
+ try err.addMsg(format, args);
+ try err.addNote("while parsing {s}", .{path});
}
pub fn reportParseError2(
@@ -3685,9 +3598,9 @@ pub fn reportParseError2(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, format, args);
- try err.addNote(self, "while parsing {}", .{self.getFile(file_index).?.fmtPath()});
+ var err = try self.base.addErrorWithNotes(1);
+ try err.addMsg(format, args);
+ try err.addNote("while parsing {}", .{self.getFile(file_index).?.fmtPath()});
}
fn reportMissingLibraryError(
@@ -3696,10 +3609,10 @@ fn reportMissingLibraryError(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(checked_paths.len);
- try err.addMsg(self, format, args);
+ var err = try self.base.addErrorWithNotes(checked_paths.len);
+ try err.addMsg(format, args);
for (checked_paths) |path| {
- try err.addNote(self, "tried {s}", .{path});
+ try err.addNote("tried {s}", .{path});
}
}
@@ -3711,12 +3624,12 @@ fn reportMissingDependencyError(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(2 + checked_paths.len);
- try err.addMsg(self, format, args);
- try err.addNote(self, "while resolving {s}", .{path});
- try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
+ var err = try self.base.addErrorWithNotes(2 + checked_paths.len);
+ try err.addMsg(format, args);
+ try err.addNote("while resolving {s}", .{path});
+ try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
for (checked_paths) |p| {
- try err.addNote(self, "tried {s}", .{p});
+ try err.addNote("tried {s}", .{p});
}
}
@@ -3727,16 +3640,16 @@ fn reportDependencyError(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(2);
- try err.addMsg(self, format, args);
- try err.addNote(self, "while parsing {s}", .{path});
- try err.addNote(self, "a dependency of {}", .{self.getFile(parent).?.fmtPath()});
+ var err = try self.base.addErrorWithNotes(2);
+ try err.addMsg(format, args);
+ try err.addNote("while parsing {s}", .{path});
+ try err.addNote("a dependency of {}", .{self.getFile(parent).?.fmtPath()});
}
pub fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytype) error{OutOfMemory}!void {
- var err = try self.addErrorWithNotes(1);
- try err.addMsg(self, format, args);
- try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
+ var err = try self.base.addErrorWithNotes(1);
+ try err.addMsg(format, args);
+ try err.addNote("please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{});
}
fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
@@ -3752,20 +3665,20 @@ fn reportDuplicates(self: *MachO) error{ HasDuplicates, OutOfMemory }!void {
const notes = entry.value_ptr.*;
const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
- var err = try self.addErrorWithNotes(nnotes + 1);
- try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)});
- try err.addNote(self, "defined by {}", .{sym.getFile(self).?.fmtPath()});
+ var err = try self.base.addErrorWithNotes(nnotes + 1);
+ try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)});
+ try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()});
has_dupes = true;
var inote: usize = 0;
while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
const file = self.getFile(notes.items[inote]).?;
- try err.addNote(self, "defined by {}", .{file.fmtPath()});
+ try err.addNote("defined by {}", .{file.fmtPath()});
}
if (notes.items.len > max_notes) {
const remaining = notes.items.len - max_notes;
- try err.addNote(self, "defined {d} more times", .{remaining});
+ try err.addNote("defined {d} more times", .{remaining});
}
}
if (has_dupes) return error.HasDuplicates;
src/link/Wasm.zig
@@ -658,9 +658,9 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
var object = Object.create(wasm, obj_file, path, null) catch |err| switch (err) {
error.InvalidMagicByte, error.NotObjectFile => return false,
else => |e| {
- var err_note = try wasm.addErrorWithNotes(1);
- try err_note.addMsg(wasm, "Failed parsing object file: {s}", .{@errorName(e)});
- try err_note.addNote(wasm, "while parsing '{s}'", .{path});
+ var err_note = try wasm.base.addErrorWithNotes(1);
+ try err_note.addMsg("Failed parsing object file: {s}", .{@errorName(e)});
+ try err_note.addNote("while parsing '{s}'", .{path});
return error.FlushFailure;
},
};
@@ -714,9 +714,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
return false;
},
else => |e| {
- var err_note = try wasm.addErrorWithNotes(1);
- try err_note.addMsg(wasm, "Failed parsing archive: {s}", .{@errorName(e)});
- try err_note.addNote(wasm, "while parsing archive {s}", .{path});
+ var err_note = try wasm.base.addErrorWithNotes(1);
+ try err_note.addMsg("Failed parsing archive: {s}", .{@errorName(e)});
+ try err_note.addNote("while parsing archive {s}", .{path});
return error.FlushFailure;
},
};
@@ -741,9 +741,9 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool {
for (offsets.keys()) |file_offset| {
var object = archive.parseObject(wasm, file_offset) catch |e| {
- var err_note = try wasm.addErrorWithNotes(1);
- try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
- try err_note.addNote(wasm, "while parsing object in archive {s}", .{path});
+ var err_note = try wasm.base.addErrorWithNotes(1);
+ try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
+ try err_note.addNote("while parsing object in archive {s}", .{path});
return error.FlushFailure;
};
object.index = @enumFromInt(wasm.files.len);
@@ -779,9 +779,9 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
if (symbol.isLocal()) {
if (symbol.isUndefined()) {
- var err = try wasm.addErrorWithNotes(1);
- try err.addMsg(wasm, "Local symbols are not allowed to reference imports", .{});
- try err.addNote(wasm, "symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
+ var err = try wasm.base.addErrorWithNotes(1);
+ try err.addMsg("Local symbols are not allowed to reference imports", .{});
+ try err.addNote("symbol '{s}' defined in '{s}'", .{ sym_name, obj_file.path() });
}
try wasm.resolved_symbols.putNoClobber(gpa, location, {});
continue;
@@ -816,10 +816,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
break :outer; // existing is weak, while new one isn't. Replace it.
}
// both are defined and weak, we have a symbol collision.
- var err = try wasm.addErrorWithNotes(2);
- try err.addMsg(wasm, "symbol '{s}' defined multiple times", .{sym_name});
- try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
- try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
+ var err = try wasm.base.addErrorWithNotes(2);
+ try err.addMsg("symbol '{s}' defined multiple times", .{sym_name});
+ try err.addNote("first definition in '{s}'", .{existing_file_path});
+ try err.addNote("next definition in '{s}'", .{obj_file.path()});
}
try wasm.discarded.put(gpa, location, existing_loc);
@@ -827,10 +827,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
}
if (symbol.tag != existing_sym.tag) {
- var err = try wasm.addErrorWithNotes(2);
- try err.addMsg(wasm, "symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
- try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
- try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
+ var err = try wasm.base.addErrorWithNotes(2);
+ try err.addMsg("symbol '{s}' mismatching types '{s}' and '{s}'", .{ sym_name, @tagName(symbol.tag), @tagName(existing_sym.tag) });
+ try err.addNote("first definition in '{s}'", .{existing_file_path});
+ try err.addNote("next definition in '{s}'", .{obj_file.path()});
}
if (existing_sym.isUndefined() and symbol.isUndefined()) {
@@ -847,14 +847,14 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
const imp = obj_file.import(sym_index);
const module_name = obj_file.string(imp.module_name);
if (!mem.eql(u8, existing_name, module_name)) {
- var err = try wasm.addErrorWithNotes(2);
- try err.addMsg(wasm, "symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
+ var err = try wasm.base.addErrorWithNotes(2);
+ try err.addMsg("symbol '{s}' module name mismatch. Expected '{s}', but found '{s}'", .{
sym_name,
existing_name,
module_name,
});
- try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
- try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
+ try err.addNote("first definition in '{s}'", .{existing_file_path});
+ try err.addNote("next definition in '{s}'", .{obj_file.path()});
}
}
@@ -867,10 +867,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
const existing_ty = wasm.getGlobalType(existing_loc);
const new_ty = wasm.getGlobalType(location);
if (existing_ty.mutable != new_ty.mutable or existing_ty.valtype != new_ty.valtype) {
- var err = try wasm.addErrorWithNotes(2);
- try err.addMsg(wasm, "symbol '{s}' mismatching global types", .{sym_name});
- try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
- try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
+ var err = try wasm.base.addErrorWithNotes(2);
+ try err.addMsg("symbol '{s}' mismatching global types", .{sym_name});
+ try err.addNote("first definition in '{s}'", .{existing_file_path});
+ try err.addNote("next definition in '{s}'", .{obj_file.path()});
}
}
@@ -878,11 +878,11 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
const existing_ty = wasm.getFunctionSignature(existing_loc);
const new_ty = wasm.getFunctionSignature(location);
if (!existing_ty.eql(new_ty)) {
- var err = try wasm.addErrorWithNotes(3);
- try err.addMsg(wasm, "symbol '{s}' mismatching function signatures.", .{sym_name});
- try err.addNote(wasm, "expected signature {}, but found signature {}", .{ existing_ty, new_ty });
- try err.addNote(wasm, "first definition in '{s}'", .{existing_file_path});
- try err.addNote(wasm, "next definition in '{s}'", .{obj_file.path()});
+ var err = try wasm.base.addErrorWithNotes(3);
+ try err.addMsg("symbol '{s}' mismatching function signatures.", .{sym_name});
+ try err.addNote("expected signature {}, but found signature {}", .{ existing_ty, new_ty });
+ try err.addNote("first definition in '{s}'", .{existing_file_path});
+ try err.addNote("next definition in '{s}'", .{obj_file.path()});
}
}
@@ -930,9 +930,9 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void {
// Parse object and and resolve symbols again before we check remaining
// undefined symbols.
var object = archive.parseObject(wasm, offset.items[0]) catch |e| {
- var err_note = try wasm.addErrorWithNotes(1);
- try err_note.addMsg(wasm, "Failed parsing object: {s}", .{@errorName(e)});
- try err_note.addNote(wasm, "while parsing object in archive {s}", .{archive.name});
+ var err_note = try wasm.base.addErrorWithNotes(1);
+ try err_note.addMsg("Failed parsing object: {s}", .{@errorName(e)});
+ try err_note.addNote("while parsing object in archive {s}", .{archive.name});
return error.FlushFailure;
};
object.index = @enumFromInt(wasm.files.len);
@@ -1237,9 +1237,9 @@ fn validateFeatures(
allowed[used_index] = is_enabled;
emit_features_count.* += @intFromBool(is_enabled);
} else if (is_enabled and !allowed[used_index]) {
- var err = try wasm.addErrorWithNotes(1);
- try err.addMsg(wasm, "feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
- try err.addNote(wasm, "defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
+ var err = try wasm.base.addErrorWithNotes(1);
+ try err.addMsg("feature '{}' not allowed, but used by linked object", .{@as(types.Feature.Tag, @enumFromInt(used_index))});
+ try err.addNote("defined in '{s}'", .{wasm.files.items(.data)[used_set >> 1].object.path});
valid_feature_set = false;
}
}
@@ -1251,7 +1251,8 @@ fn validateFeatures(
if (shared_memory) {
const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)];
if (@as(u1, @truncate(disallowed_feature)) != 0) {
- try wasm.addErrorWithoutNotes(
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg(
"shared-memory is disallowed by '{s}' because it wasn't compiled with 'atomics' and 'bulk-memory' features enabled",
.{wasm.files.items(.data)[disallowed_feature >> 1].object.path},
);
@@ -1260,7 +1261,8 @@ fn validateFeatures(
for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
if (!allowed[@intFromEnum(feature)]) {
- try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for shared-memory", .{feature});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("feature '{}' is not used but is required for shared-memory", .{feature});
}
}
}
@@ -1268,7 +1270,8 @@ fn validateFeatures(
if (has_tls) {
for ([_]types.Feature.Tag{ .atomics, .bulk_memory }) |feature| {
if (!allowed[@intFromEnum(feature)]) {
- try wasm.addErrorWithoutNotes("feature '{}' is not used but is required for thread-local storage", .{feature});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("feature '{}' is not used but is required for thread-local storage", .{feature});
}
}
}
@@ -1281,10 +1284,10 @@ fn validateFeatures(
// from here a feature is always used
const disallowed_feature = disallowed[@intFromEnum(feature.tag)];
if (@as(u1, @truncate(disallowed_feature)) != 0) {
- var err = try wasm.addErrorWithNotes(2);
- try err.addMsg(wasm, "feature '{}' is disallowed, but used by linked object", .{feature.tag});
- try err.addNote(wasm, "disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
- try err.addNote(wasm, "used in '{s}'", .{object.path});
+ var err = try wasm.base.addErrorWithNotes(2);
+ try err.addMsg("feature '{}' is disallowed, but used by linked object", .{feature.tag});
+ try err.addNote("disallowed by '{s}'", .{wasm.files.items(.data)[disallowed_feature >> 1].object.path});
+ try err.addNote("used in '{s}'", .{object.path});
valid_feature_set = false;
}
@@ -1295,10 +1298,10 @@ fn validateFeatures(
for (required, 0..) |required_feature, feature_index| {
const is_required = @as(u1, @truncate(required_feature)) != 0;
if (is_required and !object_used_features[feature_index]) {
- var err = try wasm.addErrorWithNotes(2);
- try err.addMsg(wasm, "feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
- try err.addNote(wasm, "required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
- try err.addNote(wasm, "missing in '{s}'", .{object.path});
+ var err = try wasm.base.addErrorWithNotes(2);
+ try err.addMsg("feature '{}' is required but not used in linked object", .{@as(types.Feature.Tag, @enumFromInt(feature_index))});
+ try err.addNote("required by '{s}'", .{wasm.files.items(.data)[required_feature >> 1].object.path});
+ try err.addNote("missing in '{s}'", .{object.path});
valid_feature_set = false;
}
}
@@ -1376,9 +1379,9 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void {
else
wasm.name;
const symbol_name = undef.getName(wasm);
- var err = try wasm.addErrorWithNotes(1);
- try err.addMsg(wasm, "could not resolve undefined symbol '{s}'", .{symbol_name});
- try err.addNote(wasm, "defined in '{s}'", .{file_name});
+ var err = try wasm.base.addErrorWithNotes(1);
+ try err.addMsg("could not resolve undefined symbol '{s}'", .{symbol_name});
+ try err.addNote("defined in '{s}'", .{file_name});
}
}
if (found_undefined_symbols) {
@@ -1757,7 +1760,8 @@ fn setupInitFunctions(wasm: *Wasm) !void {
break :ty object.func_types[func.type_index];
};
if (ty.params.len != 0) {
- try wasm.addErrorWithoutNotes("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("constructor functions cannot take arguments: '{s}'", .{object.string_table.get(symbol.name)});
}
log.debug("appended init func '{s}'\n", .{object.string_table.get(symbol.name)});
wasm.init_funcs.appendAssumeCapacity(.{
@@ -2140,7 +2144,8 @@ fn checkExportNames(wasm: *Wasm) !void {
for (force_exp_names) |exp_name| {
const loc = wasm.findGlobalSymbol(exp_name) orelse {
- try wasm.addErrorWithoutNotes("could not export '{s}', symbol not found", .{exp_name});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("could not export '{s}', symbol not found", .{exp_name});
failed_exports = true;
continue;
};
@@ -2203,13 +2208,15 @@ fn setupStart(wasm: *Wasm) !void {
const entry_name = wasm.entry_name orelse return;
const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse {
- try wasm.addErrorWithoutNotes("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name});
return error.FlushFailure;
};
const symbol = symbol_loc.getSymbol(wasm);
if (symbol.tag != .function) {
- try wasm.addErrorWithoutNotes("Entry symbol '{s}' is not a function", .{entry_name});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("Entry symbol '{s}' is not a function", .{entry_name});
return error.FlushFailure;
}
@@ -2314,13 +2321,16 @@ fn setupMemory(wasm: *Wasm) !void {
if (wasm.initial_memory) |initial_memory| {
if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) {
- try wasm.addErrorWithoutNotes("Initial memory must be {d}-byte aligned", .{page_size});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("Initial memory must be {d}-byte aligned", .{page_size});
}
if (memory_ptr > initial_memory) {
- try wasm.addErrorWithoutNotes("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("Initial memory too small, must be at least {d} bytes", .{memory_ptr});
}
if (initial_memory > max_memory_allowed) {
- try wasm.addErrorWithoutNotes("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("Initial memory exceeds maximum memory {d}", .{max_memory_allowed});
}
memory_ptr = initial_memory;
}
@@ -2337,13 +2347,16 @@ fn setupMemory(wasm: *Wasm) !void {
if (wasm.max_memory) |max_memory| {
if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) {
- try wasm.addErrorWithoutNotes("Maximum memory must be {d}-byte aligned", .{page_size});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("Maximum memory must be {d}-byte aligned", .{page_size});
}
if (memory_ptr > max_memory) {
- try wasm.addErrorWithoutNotes("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("Maximum memory too small, must be at least {d} bytes", .{memory_ptr});
}
if (max_memory > max_memory_allowed) {
- try wasm.addErrorWithoutNotes("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("Maximum memory exceeds maximum amount {d}", .{max_memory_allowed});
}
wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size));
wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX);
@@ -2446,9 +2459,9 @@ pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Sym
break :blk index;
};
} else {
- var err = try wasm.addErrorWithNotes(1);
- try err.addMsg(wasm, "found unknown section '{s}'", .{section_name});
- try err.addNote(wasm, "defined in '{s}'", .{obj_file.path()});
+ var err = try wasm.base.addErrorWithNotes(1);
+ try err.addMsg("found unknown section '{s}'", .{section_name});
+ try err.addNote("defined in '{s}'", .{obj_file.path()});
return error.UnexpectedValue;
}
},
@@ -2564,23 +2577,23 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
if (wasm.zig_object_index != .null) {
try wasm.resolveSymbolsInObject(wasm.zig_object_index);
}
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (wasm.base.hasErrors()) return error.FlushFailure;
for (wasm.objects.items) |object_index| {
try wasm.resolveSymbolsInObject(object_index);
}
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (wasm.base.hasErrors()) return error.FlushFailure;
var emit_features_count: u32 = 0;
var enabled_features: [@typeInfo(types.Feature.Tag).Enum.fields.len]bool = undefined;
try wasm.validateFeatures(&enabled_features, &emit_features_count);
try wasm.resolveSymbolsInArchives();
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (wasm.base.hasErrors()) return error.FlushFailure;
try wasm.resolveLazySymbols();
try wasm.checkUndefinedSymbols();
try wasm.checkExportNames();
try wasm.setupInitFunctions();
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (wasm.base.hasErrors()) return error.FlushFailure;
try wasm.setupStart();
try wasm.markReferences();
@@ -2589,7 +2602,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
try wasm.mergeTypes();
try wasm.allocateAtoms();
try wasm.setupMemory();
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (wasm.base.hasErrors()) return error.FlushFailure;
wasm.allocateVirtualAddresses();
wasm.mapFunctionTable();
try wasm.initializeCallCtorsFunction();
@@ -2599,7 +2612,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no
try wasm.setupStartSection();
try wasm.setupExports();
try wasm.writeToFile(enabled_features, emit_features_count, arena);
- if (comp.link_errors.items.len > 0) return error.FlushFailure;
+ if (wasm.base.hasErrors()) return error.FlushFailure;
}
/// Writes the WebAssembly in-memory module to the file
@@ -2997,7 +3010,10 @@ fn writeToFile(
}) catch unreachable;
try emitBuildIdSection(&binary_bytes, str);
},
- else => |mode| try wasm.addErrorWithoutNotes("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)}),
+ else => |mode| {
+ var err = try wasm.base.addErrorWithNotes(0);
+ try err.addMsg("build-id '{s}' is not supported for WebAssembly", .{@tagName(mode)});
+ },
}
var debug_bytes = std.ArrayList(u8).init(gpa);
@@ -4086,57 +4102,3 @@ fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8
.command => "_start",
};
}
-
-const ErrorWithNotes = struct {
- /// Allocated index in comp.link_errors array.
- index: usize,
-
- /// Next available note slot.
- note_slot: usize = 0,
-
- pub fn addMsg(
- err: ErrorWithNotes,
- wasm_file: *const Wasm,
- comptime format: []const u8,
- args: anytype,
- ) error{OutOfMemory}!void {
- const comp = wasm_file.base.comp;
- const gpa = comp.gpa;
- const err_msg = &comp.link_errors.items[err.index];
- err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
- }
-
- pub fn addNote(
- err: *ErrorWithNotes,
- wasm_file: *const Wasm,
- comptime format: []const u8,
- args: anytype,
- ) error{OutOfMemory}!void {
- const comp = wasm_file.base.comp;
- const gpa = comp.gpa;
- const err_msg = &comp.link_errors.items[err.index];
- err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
- err.note_slot += 1;
- }
-};
-
-pub fn addErrorWithNotes(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
- const comp = wasm.base.comp;
- const gpa = comp.gpa;
- try comp.link_errors.ensureUnusedCapacity(gpa, 1);
- return wasm.addErrorWithNotesAssumeCapacity(note_count);
-}
-
-pub fn addErrorWithoutNotes(wasm: *const Wasm, comptime fmt: []const u8, args: anytype) !void {
- const err = try wasm.addErrorWithNotes(0);
- try err.addMsg(wasm, fmt, args);
-}
-
-fn addErrorWithNotesAssumeCapacity(wasm: *const Wasm, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
- const comp = wasm.base.comp;
- const gpa = comp.gpa;
- const index = comp.link_errors.items.len;
- const err = comp.link_errors.addOneAssumeCapacity();
- err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) };
- return .{ .index = index };
-}
src/Compilation.zig
@@ -105,8 +105,9 @@ win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMa
pub fn deinit(_: @This(), _: Allocator) void {}
} = .{},
-link_error_flags: link.File.ErrorFlags = .{},
link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{},
+link_errors_mutex: std.Thread.Mutex = .{},
+link_error_flags: link.File.ErrorFlags = .{},
lld_errors: std.ArrayListUnmanaged(LldError) = .{},
work_queues: [
@@ -3067,7 +3068,6 @@ pub fn totalErrorCount(comp: *Compilation) u32 {
total += @intFromBool(comp.link_error_flags.no_entry_point_found);
}
total += @intFromBool(comp.link_error_flags.missing_libc);
-
total += comp.link_errors.items.len;
// Compile log errors only count if there are no other errors.
src/link.zig
@@ -439,6 +439,58 @@ pub const File = struct {
}
}
+ pub const ErrorWithNotes = struct {
+ base: *const File,
+
+ /// Allocated index in base.errors array.
+ index: usize,
+
+ /// Next available note slot.
+ note_slot: usize = 0,
+
+ pub fn addMsg(
+ err: ErrorWithNotes,
+ comptime format: []const u8,
+ args: anytype,
+ ) error{OutOfMemory}!void {
+ const gpa = err.base.comp.gpa;
+ const err_msg = &err.base.comp.link_errors.items[err.index];
+ err_msg.msg = try std.fmt.allocPrint(gpa, format, args);
+ }
+
+ pub fn addNote(
+ err: *ErrorWithNotes,
+ comptime format: []const u8,
+ args: anytype,
+ ) error{OutOfMemory}!void {
+ const gpa = err.base.comp.gpa;
+ const err_msg = &err.base.comp.link_errors.items[err.index];
+ assert(err.note_slot < err_msg.notes.len);
+ err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) };
+ err.note_slot += 1;
+ }
+ };
+
+ pub fn addErrorWithNotes(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
+ base.comp.link_errors_mutex.lock();
+ defer base.comp.link_errors_mutex.unlock();
+ const gpa = base.comp.gpa;
+ try base.comp.link_errors.ensureUnusedCapacity(gpa, 1);
+ return base.addErrorWithNotesAssumeCapacity(note_count);
+ }
+
+ pub fn addErrorWithNotesAssumeCapacity(base: *const File, note_count: usize) error{OutOfMemory}!ErrorWithNotes {
+ const gpa = base.comp.gpa;
+ const index = base.comp.link_errors.items.len;
+ const err = base.comp.link_errors.addOneAssumeCapacity();
+ err.* = .{ .msg = undefined, .notes = try gpa.alloc(ErrorMsg, note_count) };
+ return .{ .base = base, .index = index };
+ }
+
+ pub fn hasErrors(base: *const File) bool {
+ return base.comp.link_errors.items.len > 0 or base.comp.link_error_flags.isSet();
+ }
+
pub fn releaseLock(self: *File) void {
if (self.lock) |*lock| {
lock.release();
@@ -874,9 +926,23 @@ pub const File = struct {
}
};
- pub const ErrorFlags = struct {
+ pub const ErrorFlags = packed struct {
no_entry_point_found: bool = false,
missing_libc: bool = false,
+
+ const Int = blk: {
+ const bits = @typeInfo(@This()).Struct.fields.len;
+ break :blk @Type(.{
+ .Int = .{
+ .signedness = .unsigned,
+ .bits = bits,
+ },
+ });
+ };
+
+ fn isSet(ef: ErrorFlags) bool {
+ return @as(Int, @bitCast(ef)) > 0;
+ }
};
pub const ErrorMsg = struct {