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.

load

ElfFile.load
pub fn load(
    gpa: Allocator,
    io: Io,
    elf_file: Io.File,
    opt_build_id: ?[]const u8,
    di_search_paths: *const DebugInfoSearchPaths,
) LoadError!ElfFile

File

lib/std/debug/ElfFile.zig:126

Code

pub fn load(
    gpa: Allocator,
    io: Io,
    elf_file: Io.File,
    opt_build_id: ?[]const u8,
    di_search_paths: *const DebugInfoSearchPaths,
) LoadError!ElfFile {
    var arena_instance: std.heap.ArenaAllocator = .init(gpa);
    errdefer arena_instance.deinit();
    const arena = arena_instance.allocator();

    var result = loadInner(arena, io, elf_file, null) catch |err| switch (err) {
        error.CrcMismatch => unreachable, // we passed crc as null
        else => |e| return e,
    };
    errdefer std.posix.munmap(result.mapped_mem);

    // `loadInner` did most of the work, but we might need to load an external debug info file

    const di_mapped_mem: ?[]align(std.heap.page_size_min) const u8 = load_di: {
        if (result.sections.get(.debug_info) != null and
            result.sections.get(.debug_abbrev) != null and
            result.sections.get(.debug_str) != null and
            result.sections.get(.debug_line) != null)
        {
            // The info is already loaded from this file alone!
            break :load_di null;
        }

        // We're missing some debug info---let's try and load it from a separate file.

        build_id: {
            const build_id = opt_build_id orelse break :build_id;
            if (build_id.len < 3) break :build_id;

            for (di_search_paths.global_debug) |global_debug| {
                if (try loadSeparateDebugFile(arena, io, &result, null, "{s}/.build-id/{x}/{x}.debug", .{
                    global_debug,
                    build_id[0..1],
                    build_id[1..],
                })) |mapped| break :load_di mapped;
            }

            if (di_search_paths.debuginfod_client) |components| {
                if (try loadSeparateDebugFile(arena, io, &result, null, "{s}{s}/{x}/debuginfo", .{
                    components[0],
                    components[1],
                    build_id,
                })) |mapped| break :load_di mapped;
            }
        }

        debug_link: {
            const section = result.sections.get(.gnu_debuglink) orelse break :debug_link;
            const debug_filename = std.mem.sliceTo(section.bytes, 0);
            const crc_offset = std.mem.alignForward(usize, debug_filename.len + 1, 4);
            if (section.bytes.len < crc_offset + 4) break :debug_link;
            const debug_crc = std.mem.readInt(u32, section.bytes[crc_offset..][0..4], result.endian);

            const exe_dir = di_search_paths.exe_dir orelse break :debug_link;

            if (try loadSeparateDebugFile(arena, io, &result, debug_crc, "{s}/{s}", .{
                exe_dir,
                debug_filename,
            })) |mapped| break :load_di mapped;
            if (try loadSeparateDebugFile(arena, io, &result, debug_crc, "{s}/.debug/{s}", .{
                exe_dir,
                debug_filename,
            })) |mapped| break :load_di mapped;
            for (di_search_paths.global_debug) |global_debug| {
                // This looks like a bug; it isn't. They really do embed the absolute path to the
                // exe's dirname, *under* the global debug path.
                if (try loadSeparateDebugFile(arena, io, &result, debug_crc, "{s}/{s}/{s}", .{
                    global_debug,
                    exe_dir,
                    debug_filename,
                })) |mapped| break :load_di mapped;
            }
        }

        break :load_di null;
    };
    errdefer comptime unreachable;

    return .{
        .is_64 = result.is_64,
        .endian = result.endian,
        .dwarf = dwarf: {
            if (result.sections.get(.debug_info) == null or
                result.sections.get(.debug_abbrev) == null or
                result.sections.get(.debug_str) == null or
                result.sections.get(.debug_line) == null)
            {
                break :dwarf null; // debug info not present
            }
            var sections: Dwarf.SectionArray = @splat(null);
            const info = @typeInfo(Dwarf.Section.Id).@"enum";
            inline for (info.field_names, info.field_values) |f_name, f_value| {
                if (result.sections.get(@field(Section.Id, f_name))) |s| {
                    sections[f_value] = .{ .data = s.bytes, .owned = false };
                }
            }
            break :dwarf .{ .sections = sections };
        },
        .eh_frame = if (result.sections.get(.eh_frame)) |s| .{
            .vaddr = s.header.sh_addr,
            .bytes = s.bytes,
        } else null,
        .debug_frame = if (result.sections.get(.debug_frame)) |s| .{
            .vaddr = s.header.sh_addr,
            .bytes = s.bytes,
        } else null,
        .strtab = if (result.sections.get(.strtab)) |s| s.bytes else null,
        .symtab = if (result.sections.get(.symtab)) |s| .{
            .entry_size = s.header.sh_entsize,
            .bytes = s.bytes,
        } else null,
        .symbol_search_table = null,
        .mapped_file = result.mapped_mem,
        .mapped_debug_file = di_mapped_mem,
        .arena = arena_instance.state,
    };
}