Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

loadInner

ElfFile.loadInner
fn loadInner(
    arena: Allocator,
    io: Io,
    elf_file: Io.File,
    opt_crc: ?u32,
) (LoadError || error

File

lib/std/debug/ElfFile.zig:428

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, // not asking for PROT_EXEC
                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, // not using FIXED_NOREPLACE
            error.PermissionDenied => unreachable, // not asking for PROT_EXEC
            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) {
                // The compression algorithm is unsupported, but don't make that a hard error; the
                // 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(&section_reader, .zlib, &.{});
            const n = decompress.reader.streamRemaining(&fw) catch |err| switch (err) {
                // If a write failed, then `buf` filled up, so `ch_size` was incorrect
                error.WriteFailed => return error.InvalidCompressedSection,
                // If a read failed, flate expected the section to have more data
                error.ReadFailed => return error.InvalidCompressedSection,
            };
            // It's also an error if the data is shorter than expected.
            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,
    };
}