feature. See also
. The project being documented here (as the example) is the Zig library itself.
ElfFile.loadInner
fn loadInner(
arena: Allocator,
io: Io,
elf_file: Io.File,
opt_crc: ?u32,
) (LoadError || error
File
Code
fn loadInner(
arena: Allocator,
io: Io,
elf_file: Io.File,
opt_crc: ?u32,
) (LoadError || error{ CrcMismatch, Streaming, Canceled })!LoadInnerResult {
const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: {
const file_len = std.math.cast(
usize,
elf_file.length(io) catch |err| switch (err) {
error.PermissionDenied => unreachable,
else => |e| return e,
},
) orelse return error.Overflow;
break :mapped std.posix.mmap(
null,
file_len,
.{ .READ = true },
.{ .TYPE = .SHARED },
elf_file.handle,
0,
) catch |err| switch (err) {
error.MappingAlreadyExists => unreachable,
error.PermissionDenied => unreachable,
else => |e| return e,
};
};
if (opt_crc) |crc| {
if (std.hash.Crc32.hash(mapped_mem) != crc) {
return error.CrcMismatch;
}
}
errdefer std.posix.munmap(mapped_mem);
var fr: std.Io.Reader = .fixed(mapped_mem);
const header = elf.Header.read(&fr) catch |err| switch (err) {
error.ReadFailed => unreachable,
error.EndOfStream => return error.TruncatedElfFile,
error.InvalidElfMagic,
error.InvalidElfVersion,
error.InvalidElfClass,
error.InvalidElfEndian,
=> |e| return e,
};
const endian = header.endian;
const shstrtab_shdr_off = try std.math.add(
u64,
header.shoff,
try std.math.mul(u64, header.shstrndx, header.shentsize),
);
fr.seek = std.math.cast(usize, shstrtab_shdr_off) orelse return error.Overflow;
const shstrtab: []const u8 = if (header.is_64) shstrtab: {
const shdr = fr.takeStruct(elf.Elf64_Shdr, endian) catch return error.TruncatedElfFile;
if (shdr.sh_offset + shdr.sh_size > mapped_mem.len) return error.TruncatedElfFile;
break :shstrtab mapped_mem[@intCast(shdr.sh_offset)..][0..@intCast(shdr.sh_size)];
} else shstrtab: {
const shdr = fr.takeStruct(elf.Elf32_Shdr, endian) catch return error.TruncatedElfFile;
if (shdr.sh_offset + shdr.sh_size > mapped_mem.len) return error.TruncatedElfFile;
break :shstrtab mapped_mem[@intCast(shdr.sh_offset)..][0..@intCast(shdr.sh_size)];
};
var sections: Section.Array = .initFill(null);
var it = header.iterateSectionHeadersBuffer(mapped_mem);
while (it.next() catch return error.TruncatedElfFile) |shdr| {
if (shdr.sh_type == elf.SHT_NULL or shdr.sh_type == elf.SHT_NOBITS) continue;
if (shdr.sh_name > shstrtab.len) return error.TruncatedElfFile;
const name = std.mem.sliceTo(shstrtab[@intCast(shdr.sh_name)..], 0);
const section_id: Section.Id = inline for (
@typeInfo(Section.Id).@"enum".field_names,
@typeInfo(Section.Id).@"enum".field_values,
) |s_name, s_value| {
if (std.mem.eql(u8, "." ++ s_name, name)) {
break @fromBackingInt(@intCast(s_value));
}
} else continue;
if (sections.get(section_id) != null) continue;
if (shdr.sh_offset + shdr.sh_size > mapped_mem.len) return error.TruncatedElfFile;
const raw_section_bytes = mapped_mem[@intCast(shdr.sh_offset)..][0..@intCast(shdr.sh_size)];
const section_bytes: []const u8 = bytes: {
if ((shdr.sh_flags & elf.SHF_COMPRESSED) == 0) break :bytes raw_section_bytes;
var section_reader: std.Io.Reader = .fixed(raw_section_bytes);
const ch_type: elf.COMPRESS, const ch_size: u64 = if (header.is_64) ch: {
const chdr = section_reader.takeStruct(elf.Elf64_Chdr, endian) catch return error.InvalidCompressedSection;
break :ch .{ chdr.ch_type, chdr.ch_size };
} else ch: {
const chdr = section_reader.takeStruct(elf.Elf32_Chdr, endian) catch return error.InvalidCompressedSection;
break :ch .{ chdr.ch_type, chdr.ch_size };
};
if (ch_type != .ZLIB) {
// file might still be valid, and we might still be okay without this section.
continue;
}
const buf = try arena.alloc(u8, std.math.cast(usize, ch_size) orelse return error.Overflow);
var fw: std.Io.Writer = .fixed(buf);
var decompress: std.compress.flate.Decompress = .init(§ion_reader, .zlib, &.{});
const n = decompress.reader.streamRemaining(&fw) catch |err| switch (err) {
error.WriteFailed => return error.InvalidCompressedSection,
error.ReadFailed => return error.InvalidCompressedSection,
};
if (n != buf.len) return error.InvalidCompressedSection;
break :bytes buf;
};
sections.set(section_id, .{ .header = shdr, .bytes = section_bytes });
}
return .{
.is_64 = header.is_64,
.endian = endian,
.sections = sections,
.mapped_mem = mapped_mem,
};
}