mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Elf2: load archives
This commit is contained in:
parent
7542c3260f
commit
c4478e078b
4 changed files with 421 additions and 271 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,256 +1261,370 @@ 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| {
|
|
||||||
const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len);
|
log.debug("loadArchive({f})", .{path.fmtEscapeString()});
|
||||||
log.debug("loadInput(.{{ .object = {f} }}) = {d}", .{ object.path, ii });
|
if (!std.mem.eql(u8, try r.take(std.elf.ARMAG.len), std.elf.ARMAG))
|
||||||
{
|
return diags.failParse(path, "bad magic", .{});
|
||||||
try elf.symtab.ensureUnusedCapacity(gpa, 1);
|
var strtab: std.Io.Writer.Allocating = .init(gpa);
|
||||||
try elf.inputs.ensureUnusedCapacity(gpa, 1);
|
defer strtab.deinit();
|
||||||
const si = try elf.initSymbolAssumeCapacity(.{
|
while (r.takeStruct(std.elf.ar_hdr, native_endian)) |header| {
|
||||||
.name = std.fs.path.stem(object.path.sub_path),
|
if (!std.mem.eql(u8, &header.ar_fmag, std.elf.ARFMAG))
|
||||||
.type = .FILE,
|
return diags.failParse(path, "bad file magic", .{});
|
||||||
.shndx = std.elf.SHN_ABS,
|
const size = header.size() catch
|
||||||
});
|
return diags.failParse(path, "bad member size", .{});
|
||||||
elf.inputs.addOneAssumeCapacity().* = .{
|
if (std.mem.eql(u8, &header.ar_name, std.elf.STRNAME)) {
|
||||||
.path = object.path,
|
strtab.clearRetainingCapacity();
|
||||||
.si = si,
|
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;
|
var buf: [4096]u8 = undefined;
|
||||||
var fr = object.file.reader(comp.io, &buf);
|
fsr.reset(size, &buf);
|
||||||
const r = &fr.interface;
|
try elf.loadObject(path, member, fsr);
|
||||||
const ident = try r.peek(std.elf.EI.NIDENT);
|
try fsr.seekTo(size);
|
||||||
if (!std.mem.eql(u8, elf.mf.contents[0..std.elf.EI.NIDENT], ident))
|
continue;
|
||||||
return diags.failParse(object.path, "wrong ident", .{});
|
}
|
||||||
const target_endian = elf.targetEndian();
|
try r.discardAll(size);
|
||||||
switch (elf.identClass()) {
|
} else |err| switch (err) {
|
||||||
.NONE, _ => unreachable,
|
error.EndOfStream => if (!fsr.file.atEnd()) return error.EndOfStream,
|
||||||
inline else => |class| {
|
else => |e| return e,
|
||||||
const ElfN = class.ElfN();
|
}
|
||||||
const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
|
}
|
||||||
if (ehdr.type != .REL)
|
fn fmtMemberString(member: ?[]const u8) std.fmt.Alt(?[]const u8, memberStringEscape) {
|
||||||
return diags.failParse(object.path, "unsupported object type", .{});
|
return .{ .data = member };
|
||||||
if (ehdr.machine != elf.ehdrField(.machine))
|
}
|
||||||
return diags.failParse(object.path, "wrong machine", .{});
|
fn memberStringEscape(member: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {
|
||||||
if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
|
try w.print("({f})", .{std.zig.fmtString(member orelse return)});
|
||||||
if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
|
}
|
||||||
return diags.failParse(object.path, "unsupported shentsize", .{});
|
fn loadObject(elf: *Elf, path: std.Build.Cache.Path, member: ?[]const u8, fsr: *FileSliceReader) !void {
|
||||||
const sections = try gpa.alloc(struct {
|
const comp = elf.base.comp;
|
||||||
shdr: ElfN.Shdr,
|
const gpa = comp.gpa;
|
||||||
si: Symbol.Index,
|
const diags = &comp.link_diags;
|
||||||
}, ehdr.shnum);
|
const r = &fsr.reader.interface;
|
||||||
defer gpa.free(sections);
|
|
||||||
try fr.seekTo(ehdr.shoff);
|
const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len);
|
||||||
for (sections) |*section| {
|
log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });
|
||||||
section.* = .{
|
const ident = try r.peek(std.elf.EI.NIDENT);
|
||||||
.shdr = try r.peekStruct(ElfN.Shdr, target_endian),
|
if (!std.mem.eql(u8, ident, elf.mf.contents[0..std.elf.EI.NIDENT]))
|
||||||
.si = .null,
|
return diags.failParse(path, "bad ident", .{});
|
||||||
};
|
try elf.symtab.ensureUnusedCapacity(gpa, 1);
|
||||||
try fr.seekBy(ehdr.shentsize);
|
try elf.inputs.ensureUnusedCapacity(gpa, 1);
|
||||||
}
|
elf.inputs.addOneAssumeCapacity().* = .{
|
||||||
const shstrtab = shstrtab: {
|
.path = path,
|
||||||
if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum)
|
.member = if (member) |m| try gpa.dupe(u8, m) else null,
|
||||||
return diags.failParse(object.path, "missing section names", .{});
|
.si = try elf.initSymbolAssumeCapacity(.{
|
||||||
const shdr = §ions[ehdr.shstrndx].shdr;
|
.name = std.fs.path.stem(member orelse path.sub_path),
|
||||||
if (shdr.type != std.elf.SHT_STRTAB)
|
.type = .FILE,
|
||||||
return diags.failParse(object.path, "invalid shstrtab type", .{});
|
.shndx = std.elf.SHN_ABS,
|
||||||
const shstrtab = try gpa.alloc(u8, @intCast(shdr.size));
|
}),
|
||||||
errdefer gpa.free(shstrtab);
|
};
|
||||||
try fr.seekTo(shdr.offset);
|
const target_endian = elf.targetEndian();
|
||||||
try r.readSliceAll(shstrtab);
|
switch (elf.identClass()) {
|
||||||
break :shstrtab shstrtab;
|
.NONE, _ => unreachable,
|
||||||
};
|
inline else => |class| {
|
||||||
defer gpa.free(shstrtab);
|
const ElfN = class.ElfN();
|
||||||
try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
|
const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
|
||||||
try elf.symtab.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
|
if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{});
|
||||||
try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
|
if (ehdr.machine != elf.ehdrField(.machine))
|
||||||
for (sections[1..]) |*section| switch (section.shdr.type) {
|
return diags.failParse(path, "bad machine", .{});
|
||||||
else => {},
|
if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
|
||||||
std.elf.SHT_PROGBITS, std.elf.SHT_NOBITS => {
|
if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
|
||||||
if (section.shdr.name >= shstrtab.len) continue;
|
return diags.failParse(path, "unsupported shentsize", .{});
|
||||||
const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
|
const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, si: Symbol.Index }, ehdr.shnum);
|
||||||
const parent_si = namedSection(name) orelse continue;
|
defer gpa.free(sections);
|
||||||
const ni = try elf.mf.addLastChildNode(gpa, parent_si.node(elf), .{
|
try fsr.seekTo(ehdr.shoff);
|
||||||
.size = section.shdr.size,
|
for (sections) |*section| {
|
||||||
.alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
|
section.* = .{
|
||||||
usize,
|
.shdr = try r.peekStruct(ElfN.Shdr, target_endian),
|
||||||
@intCast(@max(section.shdr.addralign, 1)),
|
.si = .null,
|
||||||
)),
|
};
|
||||||
.moved = true,
|
try r.discardAll(ehdr.shentsize);
|
||||||
});
|
switch (section.shdr.type) {
|
||||||
elf.nodes.appendAssumeCapacity(.{
|
std.elf.SHT_NULL, std.elf.SHT_NOBITS => {},
|
||||||
.input_section = @enumFromInt(elf.input_sections.items.len),
|
else => if (section.shdr.offset + section.shdr.size > fsr.file_location.size)
|
||||||
});
|
return diags.failParse(path, "bad section offset/size", .{}),
|
||||||
section.si = try elf.initSymbolAssumeCapacity(.{
|
}
|
||||||
.type = .SECTION,
|
}
|
||||||
.shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx),
|
const shstrtab = shstrtab: {
|
||||||
});
|
if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum)
|
||||||
section.si.get(elf).ni = ni;
|
return diags.failParse(path, "missing section names", .{});
|
||||||
elf.input_sections.addOneAssumeCapacity().* = .{
|
const shdr = §ions[ehdr.shstrndx].shdr;
|
||||||
.ii = ii,
|
if (shdr.type != std.elf.SHT_STRTAB)
|
||||||
.si = section.si,
|
return diags.failParse(path, "invalid shstrtab type", .{});
|
||||||
.file_location = .{
|
const shstrtab = try gpa.alloc(u8, @intCast(shdr.size));
|
||||||
.offset = section.shdr.offset,
|
errdefer gpa.free(shstrtab);
|
||||||
.size = section.shdr.size,
|
try fsr.seekTo(shdr.offset);
|
||||||
},
|
try r.readSliceAll(shstrtab);
|
||||||
};
|
break :shstrtab shstrtab;
|
||||||
elf.synth_prog_node.increaseEstimatedTotalItems(1);
|
};
|
||||||
|
defer gpa.free(shstrtab);
|
||||||
|
try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
|
||||||
|
try elf.symtab.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
|
||||||
|
try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
|
||||||
|
for (sections[1..]) |*section| switch (section.shdr.type) {
|
||||||
|
else => {},
|
||||||
|
std.elf.SHT_PROGBITS, std.elf.SHT_NOBITS => {
|
||||||
|
if (section.shdr.name >= shstrtab.len) continue;
|
||||||
|
const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
|
||||||
|
const parent_si = namedSection(name) orelse continue;
|
||||||
|
const ni = try elf.mf.addLastChildNode(gpa, parent_si.node(elf), .{
|
||||||
|
.size = section.shdr.size,
|
||||||
|
.alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
|
||||||
|
usize,
|
||||||
|
@intCast(@max(section.shdr.addralign, 1)),
|
||||||
|
)),
|
||||||
|
.moved = true,
|
||||||
|
});
|
||||||
|
elf.nodes.appendAssumeCapacity(.{
|
||||||
|
.input_section = @enumFromInt(elf.input_sections.items.len),
|
||||||
|
});
|
||||||
|
section.si = try elf.initSymbolAssumeCapacity(.{
|
||||||
|
.type = .SECTION,
|
||||||
|
.shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx),
|
||||||
|
});
|
||||||
|
section.si.get(elf).ni = ni;
|
||||||
|
elf.input_sections.addOneAssumeCapacity().* = .{
|
||||||
|
.ii = ii,
|
||||||
|
.si = section.si,
|
||||||
|
.file_location = .{
|
||||||
|
.offset = fsr.file_location.offset + section.shdr.offset,
|
||||||
|
.size = section.shdr.size,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
var symmap: std.ArrayList(Symbol.Index) = .empty;
|
elf.synth_prog_node.increaseEstimatedTotalItems(1);
|
||||||
defer symmap.deinit(gpa);
|
},
|
||||||
for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
|
};
|
||||||
|
var symmap: std.ArrayList(Symbol.Index) = .empty;
|
||||||
|
defer symmap.deinit(gpa);
|
||||||
|
for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
|
||||||
|
else => {},
|
||||||
|
std.elf.SHT_SYMTAB => {
|
||||||
|
if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
|
||||||
|
return diags.failParse(path, "unsupported symtab entsize", .{});
|
||||||
|
const strtab = strtab: {
|
||||||
|
if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
|
||||||
|
return diags.failParse(path, "missing symbol names", .{});
|
||||||
|
const shdr = §ions[symtab.shdr.link].shdr;
|
||||||
|
if (shdr.type != std.elf.SHT_STRTAB)
|
||||||
|
return diags.failParse(path, "invalid strtab type", .{});
|
||||||
|
const strtab = try gpa.alloc(u8, @intCast(shdr.size));
|
||||||
|
errdefer gpa.free(strtab);
|
||||||
|
try fsr.seekTo(shdr.offset);
|
||||||
|
try r.readSliceAll(strtab);
|
||||||
|
break :strtab strtab;
|
||||||
|
};
|
||||||
|
defer gpa.free(strtab);
|
||||||
|
const symnum = std.math.divExact(
|
||||||
|
u32,
|
||||||
|
@intCast(symtab.shdr.size),
|
||||||
|
@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();
|
||||||
|
try symmap.resize(gpa, symnum);
|
||||||
|
try elf.symtab.ensureUnusedCapacity(gpa, symnum);
|
||||||
|
try elf.globals.ensureUnusedCapacity(gpa, symnum);
|
||||||
|
try fsr.seekTo(symtab.shdr.offset + symtab.shdr.entsize);
|
||||||
|
symmap.items[0] = .null;
|
||||||
|
for (symmap.items[1..]) |*si| {
|
||||||
|
si.* = .null;
|
||||||
|
const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
|
||||||
|
try r.discardAll64(symtab.shdr.entsize);
|
||||||
|
if (input_sym.name >= strtab.len or input_sym.shndx == std.elf.SHN_UNDEF or
|
||||||
|
input_sym.shndx >= ehdr.shnum) continue;
|
||||||
|
switch (input_sym.info.type) {
|
||||||
|
else => continue,
|
||||||
|
.SECTION => {
|
||||||
|
const section = §ions[input_sym.shndx];
|
||||||
|
if (input_sym.value == section.shdr.addr) si.* = section.si;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
.OBJECT, .FUNC => {},
|
||||||
|
}
|
||||||
|
const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
|
||||||
|
const parent_si = sections[input_sym.shndx].si;
|
||||||
|
si.* = try elf.initSymbolAssumeCapacity(.{
|
||||||
|
.name = name,
|
||||||
|
.value = input_sym.value,
|
||||||
|
.size = input_sym.size,
|
||||||
|
.type = input_sym.info.type,
|
||||||
|
.bind = input_sym.info.bind,
|
||||||
|
.visibility = input_sym.other.visibility,
|
||||||
|
.shndx = elf.targetLoad(switch (elf.symPtr(parent_si)) {
|
||||||
|
inline else => |parent_sym| &parent_sym.shndx,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
si.get(elf).ni = parent_si.get(elf).ni;
|
||||||
|
switch (input_sym.info.bind) {
|
||||||
|
else => {},
|
||||||
|
.GLOBAL => {
|
||||||
|
const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
|
||||||
|
&@field(elf.symPtr(si.*), @tagName(class)).name,
|
||||||
|
));
|
||||||
|
if (gop.found_existing) switch (elf.targetLoad(
|
||||||
|
switch (elf.symPtr(gop.value_ptr.*)) {
|
||||||
|
inline else => |sym| &sym.info,
|
||||||
|
},
|
||||||
|
).bind) {
|
||||||
|
else => unreachable,
|
||||||
|
.GLOBAL => return diags.failParse(
|
||||||
|
path,
|
||||||
|
"multiple definitions of '{s}'",
|
||||||
|
.{name},
|
||||||
|
),
|
||||||
|
.WEAK => {},
|
||||||
|
};
|
||||||
|
gop.value_ptr.* = si.*;
|
||||||
|
},
|
||||||
|
.WEAK => {
|
||||||
|
const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
|
||||||
|
&@field(elf.symPtr(si.*), @tagName(class)).name,
|
||||||
|
));
|
||||||
|
if (!gop.found_existing) gop.value_ptr.* = si.*;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (sections[1..]) |*rels| switch (rels.shdr.type) {
|
||||||
else => {},
|
else => {},
|
||||||
std.elf.SHT_SYMTAB => {
|
inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| {
|
||||||
if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
|
if (rels.shdr.link != symtab_shndx or rels.shdr.info == std.elf.SHN_UNDEF or
|
||||||
return diags.failParse(object.path, "unsupported symtab entsize", .{});
|
rels.shdr.info >= ehdr.shnum) continue;
|
||||||
const strtab = strtab: {
|
const Rel = switch (sht) {
|
||||||
if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
|
else => comptime unreachable,
|
||||||
return diags.failParse(object.path, "missing symbol names", .{});
|
std.elf.SHT_REL => ElfN.Rel,
|
||||||
const shdr = §ions[symtab.shdr.link].shdr;
|
std.elf.SHT_RELA => ElfN.Rela,
|
||||||
if (shdr.type != std.elf.SHT_STRTAB)
|
|
||||||
return diags.failParse(object.path, "invalid strtab type", .{});
|
|
||||||
const strtab = try gpa.alloc(u8, @intCast(shdr.size));
|
|
||||||
errdefer gpa.free(strtab);
|
|
||||||
try fr.seekTo(shdr.offset);
|
|
||||||
try r.readSliceAll(strtab);
|
|
||||||
break :strtab strtab;
|
|
||||||
};
|
};
|
||||||
defer gpa.free(strtab);
|
if (rels.shdr.entsize < @sizeOf(Rel))
|
||||||
const symnum = try std.math.divExact(
|
return diags.failParse(path, "unsupported rel entsize", .{});
|
||||||
|
const loc_sec = §ions[rels.shdr.info];
|
||||||
|
if (loc_sec.si == .null) continue;
|
||||||
|
const relnum = std.math.divExact(
|
||||||
u32,
|
u32,
|
||||||
@intCast(symtab.shdr.size),
|
@intCast(rels.shdr.size),
|
||||||
@intCast(symtab.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 },
|
||||||
);
|
);
|
||||||
symmap.clearRetainingCapacity();
|
try elf.relocs.ensureUnusedCapacity(gpa, relnum);
|
||||||
try symmap.resize(gpa, symnum);
|
try fsr.seekTo(rels.shdr.offset);
|
||||||
try elf.symtab.ensureUnusedCapacity(gpa, symnum);
|
for (0..relnum) |_| {
|
||||||
try elf.globals.ensureUnusedCapacity(gpa, symnum);
|
const rel = try r.peekStruct(Rel, target_endian);
|
||||||
try fr.seekTo(symtab.shdr.offset + symtab.shdr.entsize);
|
try r.discardAll64(rels.shdr.entsize);
|
||||||
symmap.items[0] = .null;
|
if (rel.info.sym >= symnum) continue;
|
||||||
for (symmap.items[1..]) |*si| {
|
const target_si = symmap.items[rel.info.sym];
|
||||||
si.* = .null;
|
if (target_si == .null) continue;
|
||||||
const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
|
elf.addRelocAssumeCapacity(
|
||||||
try fr.seekBy(@intCast(symtab.shdr.entsize));
|
loc_sec.si,
|
||||||
if (input_sym.name >= strtab.len or
|
rel.offset - loc_sec.shdr.addr,
|
||||||
input_sym.shndx == std.elf.SHN_UNDEF or
|
target_si,
|
||||||
input_sym.shndx >= ehdr.shnum) continue;
|
rel.addend,
|
||||||
switch (input_sym.info.type) {
|
switch (elf.ehdrField(.machine)) {
|
||||||
else => continue,
|
else => unreachable,
|
||||||
.SECTION => {
|
inline .AARCH64,
|
||||||
const section = §ions[input_sym.shndx];
|
.PPC64,
|
||||||
if (input_sym.value == section.shdr.addr) si.* = section.si;
|
.RISCV,
|
||||||
continue;
|
.X86_64,
|
||||||
|
=> |machine| @unionInit(
|
||||||
|
Reloc.Type,
|
||||||
|
@tagName(machine),
|
||||||
|
@enumFromInt(rel.info.type),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
.OBJECT, .FUNC => {},
|
);
|
||||||
}
|
|
||||||
const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
|
|
||||||
const parent_si = sections[input_sym.shndx].si;
|
|
||||||
si.* = try elf.initSymbolAssumeCapacity(.{
|
|
||||||
.name = name,
|
|
||||||
.value = input_sym.value,
|
|
||||||
.size = input_sym.size,
|
|
||||||
.type = input_sym.info.type,
|
|
||||||
.bind = input_sym.info.bind,
|
|
||||||
.visibility = input_sym.other.visibility,
|
|
||||||
.shndx = elf.targetLoad(switch (elf.symPtr(parent_si)) {
|
|
||||||
inline else => |parent_sym| &parent_sym.shndx,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
si.get(elf).ni = parent_si.get(elf).ni;
|
|
||||||
switch (input_sym.info.bind) {
|
|
||||||
else => {},
|
|
||||||
.GLOBAL => {
|
|
||||||
const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
|
|
||||||
&@field(elf.symPtr(si.*), @tagName(class)).name,
|
|
||||||
));
|
|
||||||
if (gop.found_existing) switch (elf.targetLoad(
|
|
||||||
switch (elf.symPtr(gop.value_ptr.*)) {
|
|
||||||
inline else => |sym| &sym.info,
|
|
||||||
},
|
|
||||||
).bind) {
|
|
||||||
else => unreachable,
|
|
||||||
.GLOBAL => return diags.failParse(
|
|
||||||
object.path,
|
|
||||||
"multiple definitions of '{s}'",
|
|
||||||
.{name},
|
|
||||||
),
|
|
||||||
.WEAK => {},
|
|
||||||
};
|
|
||||||
gop.value_ptr.* = si.*;
|
|
||||||
},
|
|
||||||
.WEAK => {
|
|
||||||
const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
|
|
||||||
&@field(elf.symPtr(si.*), @tagName(class)).name,
|
|
||||||
));
|
|
||||||
if (!gop.found_existing) gop.value_ptr.* = si.*;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (sections[1..]) |*rels| switch (rels.shdr.type) {
|
|
||||||
else => {},
|
|
||||||
inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| {
|
|
||||||
if (rels.shdr.link != symtab_shndx or
|
|
||||||
rels.shdr.info == std.elf.SHN_UNDEF or
|
|
||||||
rels.shdr.info >= ehdr.shnum) continue;
|
|
||||||
const Rel = switch (sht) {
|
|
||||||
else => comptime unreachable,
|
|
||||||
std.elf.SHT_REL => ElfN.Rel,
|
|
||||||
std.elf.SHT_RELA => ElfN.Rela,
|
|
||||||
};
|
|
||||||
if (rels.shdr.entsize < @sizeOf(Rel)) return diags.failParse(
|
|
||||||
object.path,
|
|
||||||
"unsupported rel entsize",
|
|
||||||
.{},
|
|
||||||
);
|
|
||||||
const loc_sec = §ions[rels.shdr.info];
|
|
||||||
if (loc_sec.si == .null) continue;
|
|
||||||
const relnum = try std.math.divExact(
|
|
||||||
u32,
|
|
||||||
@intCast(rels.shdr.size),
|
|
||||||
@intCast(rels.shdr.entsize),
|
|
||||||
);
|
|
||||||
try elf.relocs.ensureUnusedCapacity(gpa, relnum);
|
|
||||||
try fr.seekTo(rels.shdr.offset);
|
|
||||||
for (0..relnum) |_| {
|
|
||||||
const rel = try r.peekStruct(Rel, target_endian);
|
|
||||||
try fr.seekBy(@intCast(rels.shdr.entsize));
|
|
||||||
if (rel.info.sym >= symnum) continue;
|
|
||||||
const target_si = symmap.items[rel.info.sym];
|
|
||||||
if (target_si == .null) continue;
|
|
||||||
elf.addReloc(
|
|
||||||
loc_sec.si,
|
|
||||||
rel.offset - loc_sec.shdr.addr,
|
|
||||||
target_si,
|
|
||||||
rel.addend,
|
|
||||||
switch (elf.ehdrField(.machine)) {
|
|
||||||
else => unreachable,
|
|
||||||
inline .AARCH64,
|
|
||||||
.PPC64,
|
|
||||||
.RISCV,
|
|
||||||
.X86_64,
|
|
||||||
=> |machine| @unionInit(
|
|
||||||
Reloc.Type,
|
|
||||||
@tagName(machine),
|
|
||||||
@enumFromInt(rel.info.type),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
) catch unreachable;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
else => {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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(
|
||||||
e,
|
"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,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
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);
|
||||||
elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),
|
break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
|
||||||
}) catch &name,
|
ii.path(elf).fmtEscapeString(),
|
||||||
|
fmtMemberString(ii.member(elf)),
|
||||||
|
elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),
|
||||||
|
}) 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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue