Elf2: load archives

This commit is contained in:
Jacob Young 2025-10-14 22:24:26 -04:00
parent 7542c3260f
commit c4478e078b
4 changed files with 421 additions and 271 deletions

View file

@ -434,8 +434,7 @@ pub const Reader = struct {
return err; return err;
}; };
} }
r.interface.seek = 0; r.interface.tossBuffered();
r.interface.end = 0;
}, },
.failure => return r.seek_err.?, .failure => return r.seek_err.?,
} }
@ -467,15 +466,11 @@ pub const Reader = struct {
} }
fn setLogicalPos(r: *Reader, offset: u64) void { fn setLogicalPos(r: *Reader, offset: u64) void {
const logical_pos = logicalPos(r); const logical_pos = r.logicalPos();
if (offset < logical_pos or offset >= r.pos) { if (offset < logical_pos or offset >= r.pos) {
r.interface.seek = 0; r.interface.tossBuffered();
r.interface.end = 0;
r.pos = offset; r.pos = offset;
} else { } else r.interface.toss(@intCast(offset - logical_pos));
const logical_delta: usize = @intCast(offset - logical_pos);
r.interface.seek += logical_delta;
}
} }
/// Number of slices to store on the stack, when trying to send as many byte /// Number of slices to store on the stack, when trying to send as many byte

View file

@ -1989,7 +1989,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu; break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu;
}, },
} }
if (options.config.use_new_linker) break :s .obj;
} }
if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm
if (is_exe_or_dyn_lib) break :s .lib; if (is_exe_or_dyn_lib) break :s .lib;

View file

@ -8,12 +8,13 @@ shstrtab: StringTable,
strtab: StringTable, strtab: StringTable,
inputs: std.ArrayList(struct { inputs: std.ArrayList(struct {
path: std.Build.Cache.Path, path: std.Build.Cache.Path,
member: ?[]const u8,
si: Symbol.Index, si: Symbol.Index,
}), }),
input_sections: std.ArrayList(struct { input_sections: std.ArrayList(struct {
ii: Node.InputIndex, ii: Node.InputIndex,
si: Symbol.Index,
file_location: MappedFile.Node.FileLocation, file_location: MappedFile.Node.FileLocation,
si: Symbol.Index,
}), }),
input_section_pending_index: u32, input_section_pending_index: u32,
globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index), globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index),
@ -53,6 +54,10 @@ pub const Node = union(enum) {
return elf.inputs.items[@intFromEnum(ii)].path; return elf.inputs.items[@intFromEnum(ii)].path;
} }
pub fn member(ii: InputIndex, elf: *const Elf) ?[]const u8 {
return elf.inputs.items[@intFromEnum(ii)].member;
}
pub fn symbol(ii: InputIndex, elf: *const Elf) Symbol.Index { pub fn symbol(ii: InputIndex, elf: *const Elf) Symbol.Index {
return elf.inputs.items[@intFromEnum(ii)].si; return elf.inputs.items[@intFromEnum(ii)].si;
} }
@ -73,13 +78,13 @@ pub const Node = union(enum) {
return elf.input_sections.items[@intFromEnum(isi)].ii; return elf.input_sections.items[@intFromEnum(isi)].ii;
} }
pub fn symbol(isi: InputSectionIndex, elf: *const Elf) Symbol.Index {
return elf.input_sections.items[@intFromEnum(isi)].si;
}
pub fn fileLocation(isi: InputSectionIndex, elf: *const Elf) MappedFile.Node.FileLocation { pub fn fileLocation(isi: InputSectionIndex, elf: *const Elf) MappedFile.Node.FileLocation {
return elf.input_sections.items[@intFromEnum(isi)].file_location; return elf.input_sections.items[@intFromEnum(isi)].file_location;
} }
pub fn symbol(isi: InputSectionIndex, elf: *const Elf) Symbol.Index {
return elf.input_sections.items[@intFromEnum(isi)].si;
}
}; };
pub const NavMapIndex = enum(u32) { pub const NavMapIndex = enum(u32) {
@ -571,6 +576,7 @@ pub fn deinit(elf: *Elf) void {
elf.symtab.deinit(gpa); elf.symtab.deinit(gpa);
elf.shstrtab.map.deinit(gpa); elf.shstrtab.map.deinit(gpa);
elf.strtab.map.deinit(gpa); elf.strtab.map.deinit(gpa);
for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
elf.inputs.deinit(gpa); elf.inputs.deinit(gpa);
elf.input_sections.deinit(gpa); elf.input_sections.deinit(gpa);
elf.globals.deinit(gpa); elf.globals.deinit(gpa);
@ -1255,68 +1261,182 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !Symbol.Index {
return lazy_gop.value_ptr.*; return lazy_gop.value_ptr.*;
} }
pub fn loadInput(elf: *Elf, input: link.Input) !void { pub fn loadInput(elf: *Elf, input: link.Input) (std.fs.File.Reader.SizeError ||
std.Io.File.Reader.Error || MappedFile.Error || error{ EndOfStream, LinkFailure })!void {
const io = elf.base.comp.io;
var buf: [4096]u8 = undefined;
switch (input) {
else => {},
.object => |object| {
var fsr: FileSliceReader = .init(object.file.reader(io, &.{}));
fsr.reset(try fsr.file.getSize(), &buf);
elf.loadObject(object.path, null, &fsr) catch |err| switch (err) {
error.ReadFailed => return fsr.file.err.?,
else => |e| return e,
};
},
.archive => |archive| {
var fsr: FileSliceReader = .init(archive.file.reader(io, &buf));
elf.loadArchive(archive.path, &fsr) catch |err| switch (err) {
error.ReadFailed => return fsr.file.err.?,
else => |e| return e,
};
},
}
}
const FileSliceReader = struct {
file: std.Io.File.Reader,
file_location: MappedFile.Node.FileLocation,
reader: std.Io.Reader.Limited,
pub fn init(file: std.Io.File.Reader) FileSliceReader {
return .{ .file = file, .file_location = undefined, .reader = undefined };
}
pub fn reset(fsr: *FileSliceReader, size: u64, buffer: []u8) void {
fsr.file_location = .{
.offset = fsr.file.logicalPos(),
.size = size,
};
fsr.reader = .init(&fsr.file.interface, .limited(@intCast(size)), buffer);
}
pub fn pos(fsr: *const FileSliceReader) u64 {
return fsr.file.logicalPos() - fsr.file_location.offset;
}
pub fn logicalPos(fsr: *const FileSliceReader) u64 {
return fsr.pos() - fsr.reader.interface.bufferedLen();
}
pub fn seekTo(fsr: *FileSliceReader, offset: u64) std.Io.File.Reader.SeekError!void {
if (offset > fsr.file_location.size) return error.EndOfStream;
const logical_pos = fsr.logicalPos();
if (offset < logical_pos or offset >= fsr.pos()) {
fsr.reader.interface.tossBuffered();
try fsr.file.seekTo(fsr.file_location.offset + offset);
fsr.reader.remaining = .limited(@intCast(fsr.file_location.size - offset));
} else fsr.reader.interface.toss(@intCast(offset - logical_pos));
}
};
fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fsr: *FileSliceReader) !void {
const comp = elf.base.comp; const comp = elf.base.comp;
const gpa = comp.gpa; const gpa = comp.gpa;
const diags = &comp.link_diags; const diags = &comp.link_diags;
switch (input) { const r = &fsr.file.interface;
.object => |object| {
log.debug("loadArchive({f})", .{path.fmtEscapeString()});
if (!std.mem.eql(u8, try r.take(std.elf.ARMAG.len), std.elf.ARMAG))
return diags.failParse(path, "bad magic", .{});
var strtab: std.Io.Writer.Allocating = .init(gpa);
defer strtab.deinit();
while (r.takeStruct(std.elf.ar_hdr, native_endian)) |header| {
if (!std.mem.eql(u8, &header.ar_fmag, std.elf.ARFMAG))
return diags.failParse(path, "bad file magic", .{});
const size = header.size() catch
return diags.failParse(path, "bad member size", .{});
if (std.mem.eql(u8, &header.ar_name, std.elf.STRNAME)) {
strtab.clearRetainingCapacity();
try strtab.ensureTotalCapacityPrecise(size);
r.streamExact(&strtab.writer, size) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
};
continue;
}
load_object: {
const member = header.name() orelse member: {
const strtab_offset = header.nameOffset() catch |err| switch (err) {
error.Overflow => break :member error.Overflow,
error.InvalidCharacter => break :load_object,
} orelse break :load_object;
const strtab_written = strtab.written();
if (strtab_offset > strtab_written.len) break :member error.Overflow;
const member = std.mem.sliceTo(strtab_written[strtab_offset..], '\n');
break :member if (std.mem.endsWith(u8, member, "/"))
member[0 .. member.len - "/".len]
else
member;
} catch |err| switch (err) {
error.Overflow => return diags.failParse(path, "bad member name offset", .{}),
};
if (!std.mem.endsWith(u8, member, ".o")) break :load_object;
var buf: [4096]u8 = undefined;
fsr.reset(size, &buf);
try elf.loadObject(path, member, fsr);
try fsr.seekTo(size);
continue;
}
try r.discardAll(size);
} else |err| switch (err) {
error.EndOfStream => if (!fsr.file.atEnd()) return error.EndOfStream,
else => |e| return e,
}
}
fn fmtMemberString(member: ?[]const u8) std.fmt.Alt(?[]const u8, memberStringEscape) {
return .{ .data = member };
}
fn memberStringEscape(member: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {
try w.print("({f})", .{std.zig.fmtString(member orelse return)});
}
fn loadObject(elf: *Elf, path: std.Build.Cache.Path, member: ?[]const u8, fsr: *FileSliceReader) !void {
const comp = elf.base.comp;
const gpa = comp.gpa;
const diags = &comp.link_diags;
const r = &fsr.reader.interface;
const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len); const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len);
log.debug("loadInput(.{{ .object = {f} }}) = {d}", .{ object.path, ii }); log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });
{ const ident = try r.peek(std.elf.EI.NIDENT);
if (!std.mem.eql(u8, ident, elf.mf.contents[0..std.elf.EI.NIDENT]))
return diags.failParse(path, "bad ident", .{});
try elf.symtab.ensureUnusedCapacity(gpa, 1); try elf.symtab.ensureUnusedCapacity(gpa, 1);
try elf.inputs.ensureUnusedCapacity(gpa, 1); try elf.inputs.ensureUnusedCapacity(gpa, 1);
const si = try elf.initSymbolAssumeCapacity(.{ elf.inputs.addOneAssumeCapacity().* = .{
.name = std.fs.path.stem(object.path.sub_path), .path = path,
.member = if (member) |m| try gpa.dupe(u8, m) else null,
.si = try elf.initSymbolAssumeCapacity(.{
.name = std.fs.path.stem(member orelse path.sub_path),
.type = .FILE, .type = .FILE,
.shndx = std.elf.SHN_ABS, .shndx = std.elf.SHN_ABS,
}); }),
elf.inputs.addOneAssumeCapacity().* = .{
.path = object.path,
.si = si,
}; };
}
var buf: [4096]u8 = undefined;
var fr = object.file.reader(comp.io, &buf);
const r = &fr.interface;
const ident = try r.peek(std.elf.EI.NIDENT);
if (!std.mem.eql(u8, elf.mf.contents[0..std.elf.EI.NIDENT], ident))
return diags.failParse(object.path, "wrong ident", .{});
const target_endian = elf.targetEndian(); const target_endian = elf.targetEndian();
switch (elf.identClass()) { switch (elf.identClass()) {
.NONE, _ => unreachable, .NONE, _ => unreachable,
inline else => |class| { inline else => |class| {
const ElfN = class.ElfN(); const ElfN = class.ElfN();
const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
if (ehdr.type != .REL) if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{});
return diags.failParse(object.path, "unsupported object type", .{});
if (ehdr.machine != elf.ehdrField(.machine)) if (ehdr.machine != elf.ehdrField(.machine))
return diags.failParse(object.path, "wrong machine", .{}); return diags.failParse(path, "bad machine", .{});
if (ehdr.shoff == 0 or ehdr.shnum <= 1) return; if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
if (ehdr.shentsize < @sizeOf(ElfN.Shdr)) if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
return diags.failParse(object.path, "unsupported shentsize", .{}); return diags.failParse(path, "unsupported shentsize", .{});
const sections = try gpa.alloc(struct { const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, si: Symbol.Index }, ehdr.shnum);
shdr: ElfN.Shdr,
si: Symbol.Index,
}, ehdr.shnum);
defer gpa.free(sections); defer gpa.free(sections);
try fr.seekTo(ehdr.shoff); try fsr.seekTo(ehdr.shoff);
for (sections) |*section| { for (sections) |*section| {
section.* = .{ section.* = .{
.shdr = try r.peekStruct(ElfN.Shdr, target_endian), .shdr = try r.peekStruct(ElfN.Shdr, target_endian),
.si = .null, .si = .null,
}; };
try fr.seekBy(ehdr.shentsize); try r.discardAll(ehdr.shentsize);
switch (section.shdr.type) {
std.elf.SHT_NULL, std.elf.SHT_NOBITS => {},
else => if (section.shdr.offset + section.shdr.size > fsr.file_location.size)
return diags.failParse(path, "bad section offset/size", .{}),
}
} }
const shstrtab = shstrtab: { const shstrtab = shstrtab: {
if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum) if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum)
return diags.failParse(object.path, "missing section names", .{}); return diags.failParse(path, "missing section names", .{});
const shdr = &sections[ehdr.shstrndx].shdr; const shdr = &sections[ehdr.shstrndx].shdr;
if (shdr.type != std.elf.SHT_STRTAB) if (shdr.type != std.elf.SHT_STRTAB)
return diags.failParse(object.path, "invalid shstrtab type", .{}); return diags.failParse(path, "invalid shstrtab type", .{});
const shstrtab = try gpa.alloc(u8, @intCast(shdr.size)); const shstrtab = try gpa.alloc(u8, @intCast(shdr.size));
errdefer gpa.free(shstrtab); errdefer gpa.free(shstrtab);
try fr.seekTo(shdr.offset); try fsr.seekTo(shdr.offset);
try r.readSliceAll(shstrtab); try r.readSliceAll(shstrtab);
break :shstrtab shstrtab; break :shstrtab shstrtab;
}; };
@ -1350,7 +1470,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) !void {
.ii = ii, .ii = ii,
.si = section.si, .si = section.si,
.file_location = .{ .file_location = .{
.offset = section.shdr.offset, .offset = fsr.file_location.offset + section.shdr.offset,
.size = section.shdr.size, .size = section.shdr.size,
}, },
}; };
@ -1363,37 +1483,40 @@ pub fn loadInput(elf: *Elf, input: link.Input) !void {
else => {}, else => {},
std.elf.SHT_SYMTAB => { std.elf.SHT_SYMTAB => {
if (symtab.shdr.entsize < @sizeOf(ElfN.Sym)) if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
return diags.failParse(object.path, "unsupported symtab entsize", .{}); return diags.failParse(path, "unsupported symtab entsize", .{});
const strtab = strtab: { const strtab = strtab: {
if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum) if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
return diags.failParse(object.path, "missing symbol names", .{}); return diags.failParse(path, "missing symbol names", .{});
const shdr = &sections[symtab.shdr.link].shdr; const shdr = &sections[symtab.shdr.link].shdr;
if (shdr.type != std.elf.SHT_STRTAB) if (shdr.type != std.elf.SHT_STRTAB)
return diags.failParse(object.path, "invalid strtab type", .{}); return diags.failParse(path, "invalid strtab type", .{});
const strtab = try gpa.alloc(u8, @intCast(shdr.size)); const strtab = try gpa.alloc(u8, @intCast(shdr.size));
errdefer gpa.free(strtab); errdefer gpa.free(strtab);
try fr.seekTo(shdr.offset); try fsr.seekTo(shdr.offset);
try r.readSliceAll(strtab); try r.readSliceAll(strtab);
break :strtab strtab; break :strtab strtab;
}; };
defer gpa.free(strtab); defer gpa.free(strtab);
const symnum = try std.math.divExact( const symnum = std.math.divExact(
u32, u32,
@intCast(symtab.shdr.size), @intCast(symtab.shdr.size),
@intCast(symtab.shdr.entsize), @intCast(symtab.shdr.entsize),
) catch return diags.failParse(
path,
"symtab section size (0x{x}) is not a multiple of entsize (0x{x})",
.{ symtab.shdr.size, symtab.shdr.entsize },
); );
symmap.clearRetainingCapacity(); symmap.clearRetainingCapacity();
try symmap.resize(gpa, symnum); try symmap.resize(gpa, symnum);
try elf.symtab.ensureUnusedCapacity(gpa, symnum); try elf.symtab.ensureUnusedCapacity(gpa, symnum);
try elf.globals.ensureUnusedCapacity(gpa, symnum); try elf.globals.ensureUnusedCapacity(gpa, symnum);
try fr.seekTo(symtab.shdr.offset + symtab.shdr.entsize); try fsr.seekTo(symtab.shdr.offset + symtab.shdr.entsize);
symmap.items[0] = .null; symmap.items[0] = .null;
for (symmap.items[1..]) |*si| { for (symmap.items[1..]) |*si| {
si.* = .null; si.* = .null;
const input_sym = try r.peekStruct(ElfN.Sym, target_endian); const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
try fr.seekBy(@intCast(symtab.shdr.entsize)); try r.discardAll64(symtab.shdr.entsize);
if (input_sym.name >= strtab.len or if (input_sym.name >= strtab.len or input_sym.shndx == std.elf.SHN_UNDEF or
input_sym.shndx == std.elf.SHN_UNDEF or
input_sym.shndx >= ehdr.shnum) continue; input_sym.shndx >= ehdr.shnum) continue;
switch (input_sym.info.type) { switch (input_sym.info.type) {
else => continue, else => continue,
@ -1431,7 +1554,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) !void {
).bind) { ).bind) {
else => unreachable, else => unreachable,
.GLOBAL => return diags.failParse( .GLOBAL => return diags.failParse(
object.path, path,
"multiple definitions of '{s}'", "multiple definitions of '{s}'",
.{name}, .{name},
), ),
@ -1450,35 +1573,35 @@ pub fn loadInput(elf: *Elf, input: link.Input) !void {
for (sections[1..]) |*rels| switch (rels.shdr.type) { for (sections[1..]) |*rels| switch (rels.shdr.type) {
else => {}, else => {},
inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| { inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| {
if (rels.shdr.link != symtab_shndx or if (rels.shdr.link != symtab_shndx or rels.shdr.info == std.elf.SHN_UNDEF or
rels.shdr.info == std.elf.SHN_UNDEF or
rels.shdr.info >= ehdr.shnum) continue; rels.shdr.info >= ehdr.shnum) continue;
const Rel = switch (sht) { const Rel = switch (sht) {
else => comptime unreachable, else => comptime unreachable,
std.elf.SHT_REL => ElfN.Rel, std.elf.SHT_REL => ElfN.Rel,
std.elf.SHT_RELA => ElfN.Rela, std.elf.SHT_RELA => ElfN.Rela,
}; };
if (rels.shdr.entsize < @sizeOf(Rel)) return diags.failParse( if (rels.shdr.entsize < @sizeOf(Rel))
object.path, return diags.failParse(path, "unsupported rel entsize", .{});
"unsupported rel entsize",
.{},
);
const loc_sec = &sections[rels.shdr.info]; const loc_sec = &sections[rels.shdr.info];
if (loc_sec.si == .null) continue; if (loc_sec.si == .null) continue;
const relnum = try std.math.divExact( const relnum = std.math.divExact(
u32, u32,
@intCast(rels.shdr.size), @intCast(rels.shdr.size),
@intCast(rels.shdr.entsize), @intCast(rels.shdr.entsize),
) catch return diags.failParse(
path,
"relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
.{ rels.shdr.size, rels.shdr.entsize },
); );
try elf.relocs.ensureUnusedCapacity(gpa, relnum); try elf.relocs.ensureUnusedCapacity(gpa, relnum);
try fr.seekTo(rels.shdr.offset); try fsr.seekTo(rels.shdr.offset);
for (0..relnum) |_| { for (0..relnum) |_| {
const rel = try r.peekStruct(Rel, target_endian); const rel = try r.peekStruct(Rel, target_endian);
try fr.seekBy(@intCast(rels.shdr.entsize)); try r.discardAll64(rels.shdr.entsize);
if (rel.info.sym >= symnum) continue; if (rel.info.sym >= symnum) continue;
const target_si = symmap.items[rel.info.sym]; const target_si = symmap.items[rel.info.sym];
if (target_si == .null) continue; if (target_si == .null) continue;
elf.addReloc( elf.addRelocAssumeCapacity(
loc_sec.si, loc_sec.si,
rel.offset - loc_sec.shdr.addr, rel.offset - loc_sec.shdr.addr,
target_si, target_si,
@ -1495,7 +1618,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) !void {
@enumFromInt(rel.info.type), @enumFromInt(rel.info.type),
), ),
}, },
) catch unreachable; );
} }
}, },
}; };
@ -1503,9 +1626,6 @@ pub fn loadInput(elf: *Elf, input: link.Input) !void {
}; };
}, },
} }
},
else => {},
}
} }
pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void { pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {
@ -1530,6 +1650,7 @@ fn prelinkInner(elf: *Elf) !void {
}); });
elf.inputs.addOneAssumeCapacity().* = .{ elf.inputs.addOneAssumeCapacity().* = .{
.path = elf.base.emit, .path = elf.base.emit,
.member = null,
.si = si, .si = si,
}; };
} }
@ -1667,10 +1788,20 @@ pub fn addReloc(
addend: i64, addend: i64,
@"type": Reloc.Type, @"type": Reloc.Type,
) !void { ) !void {
const gpa = elf.base.comp.gpa; try elf.relocs.ensureUnusedCapacity(elf.base.comp.gpa, 1);
elf.addRelocAssumeCapacity(loc_si, offset, target_si, addend, @"type");
}
pub fn addRelocAssumeCapacity(
elf: *Elf,
loc_si: Symbol.Index,
offset: u64,
target_si: Symbol.Index,
addend: i64,
@"type": Reloc.Type,
) void {
const target = target_si.get(elf); const target = target_si.get(elf);
const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len); const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);
(try elf.relocs.addOne(gpa)).* = .{ elf.relocs.addOneAssumeCapacity().* = .{
.type = @"type", .type = @"type",
.prev = .none, .prev = .none,
.next = target.target_relocs, .next = target.target_relocs,
@ -1961,11 +2092,20 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.symbol(elf).node(elf))); const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.symbol(elf).node(elf)));
defer sub_prog_node.end(); defer sub_prog_node.end();
elf.flushInputSection(isi) catch |err| switch (err) { elf.flushInputSection(isi) catch |err| switch (err) {
else => |e| return comp.link_diags.fail("linker failed to read input section '{s}' from \"{f}\": {t}", .{ else => |e| {
elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), const ii = isi.input(elf);
isi.input(elf).path(elf).fmtEscapeString(), return comp.link_diags.fail(
"linker failed to read input section '{s}' from \"{f}{f}\": {t}",
.{
elf.sectionName(
elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section,
),
ii.path(elf).fmtEscapeString(),
fmtMemberString(ii.member(elf)),
e, e,
}), },
);
},
}; };
break :task; break :task;
} }
@ -1998,10 +2138,14 @@ fn idleProgNode(
return prog_node.start(name: switch (node) { return prog_node.start(name: switch (node) {
else => |tag| @tagName(tag), else => |tag| @tagName(tag),
.section => |si| elf.sectionName(si), .section => |si| elf.sectionName(si),
.input_section => |isi| std.fmt.bufPrint(&name, "{f} {s}", .{ .input_section => |isi| {
isi.input(elf).path(elf), const ii = isi.input(elf);
break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
ii.path(elf).fmtEscapeString(),
fmtMemberString(ii.member(elf)),
elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),
}) catch &name, }) catch &name;
},
.nav => |nmi| { .nav => |nmi| {
const ip = &elf.base.comp.zcu.?.intern_pool; const ip = &elf.base.comp.zcu.?.intern_pool;
break :name ip.getNav(nmi.navIndex(elf)).fqn.toSlice(ip); break :name ip.getNav(nmi.navIndex(elf)).fqn.toSlice(ip);
@ -2128,16 +2272,17 @@ fn flushInputSection(elf: *Elf, isi: Node.InputSectionIndex) !void {
if (file_loc.size == 0) return; if (file_loc.size == 0) return;
const comp = elf.base.comp; const comp = elf.base.comp;
const gpa = comp.gpa; const gpa = comp.gpa;
const io = comp.io; const ii = isi.input(elf);
const path = isi.input(elf).path(elf); const path = ii.path(elf);
const file = try path.root_dir.handle.adaptToNewApi().openFile(io, path.sub_path, .{}); const file = try path.root_dir.handle.adaptToNewApi().openFile(comp.io, path.sub_path, .{});
defer file.close(io); defer file.close(comp.io);
var fr = file.reader(io, &.{}); var fr = file.reader(comp.io, &.{});
try fr.seekTo(file_loc.offset); try fr.seekTo(file_loc.offset);
var nw: MappedFile.Node.Writer = undefined; var nw: MappedFile.Node.Writer = undefined;
isi.symbol(elf).node(elf).writer(&elf.mf, gpa, &nw); isi.symbol(elf).node(elf).writer(&elf.mf, gpa, &nw);
defer nw.deinit(); defer nw.deinit();
if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size) return error.EndOfStream; if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size)
return error.EndOfStream;
} }
fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
@ -2396,10 +2541,14 @@ pub fn printNode(
switch (node) { switch (node) {
else => {}, else => {},
.section => |si| try w.print("({s})", .{elf.sectionName(si)}), .section => |si| try w.print("({s})", .{elf.sectionName(si)}),
.input_section => |isi| try w.print("({f}, {s})", .{ .input_section => |isi| {
isi.input(elf).path(elf), const ii = isi.input(elf);
elf.sectionName(elf.getNode(isi.node(elf).parent(&elf.mf)).section), try w.print("({f}{f}, {s})", .{
}), ii.path(elf).fmtEscapeString(),
fmtMemberString(ii.member(elf)),
elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),
});
},
.nav => |nmi| { .nav => |nmi| {
const zcu = elf.base.comp.zcu.?; const zcu = elf.base.comp.zcu.?;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;

View file

@ -144,7 +144,14 @@ pub const Node = extern struct {
} }
}; };
pub const FileLocation = struct { offset: u64, size: u64 }; pub const FileLocation = struct {
offset: u64,
size: u64,
pub fn end(fl: FileLocation) u64 {
return fl.offset + fl.size;
}
};
pub const Index = enum(u32) { pub const Index = enum(u32) {
none, none,