DoxigAlpha

make

Function parameters

Parameters

#
step:*Step
make_options:Step.MakeOptions

Functions in this namespace

Functions

#
checkExact
Adds an exact match phrase to the latest created Check.
checkExactPath
Like `checkExact()` but takes an additional argument `LazyPath` which will be
checkContains
Adds a fuzzy match phrase to the latest created Check.
checkContainsPath
Like `checkContains()` but takes an additional argument `lazy_path` which will be
checkExtract
Adds an exact match phrase with variable extractor to the latest created Check.
checkExtractLazyPath
Like `checkExtract()` but takes an additional argument `LazyPath` which will be
checkNotPresent
Adds another searched phrase to the latest created Check
checkNotPresentLazyPath
Like `checkExtract()` but takes an additional argument `LazyPath` which will be
checkInHeaders
Creates a new check checking in the file headers (section, program headers, etc.).
checkInSymtab
Creates a new check checking specifically symbol table parsed and dumped from the object
checkInDyldRebase
Creates a new check checking specifically dyld rebase opcodes contents parsed and dumped
checkInDyldBind
Creates a new check checking specifically dyld bind opcodes contents parsed and dumped
checkInDyldWeakBind
Creates a new check checking specifically dyld weak bind opcodes contents parsed and dumped
checkInDyldLazyBind
Creates a new check checking specifically dyld lazy bind opcodes contents parsed and dumped
checkInExports
Creates a new check checking specifically exports info contents parsed and dumped
checkInIndirectSymtab
Creates a new check checking specifically indirect symbol table parsed and dumped
checkInDynamicSymtab
Creates a new check checking specifically dynamic symbol table parsed and dumped from the object
checkInDynamicSection
Creates a new check checking specifically dynamic section parsed and dumped from the object
checkInArchiveSymtab
Creates a new check checking specifically symbol table parsed and dumped from the archive
checkComputeCompare
Creates a new standalone, singular check which allows running simple binary operations

= .check_object

Values

#
base_id
= .check_object

Source

Implementation

#
fn make(step: *Step, make_options: Step.MakeOptions) !void {
    _ = make_options;
    const b = step.owner;
    const gpa = b.allocator;
    const check_object: *CheckObject = @fieldParentPtr("step", step);
    try step.singleUnchangingWatchInput(check_object.source);

    const src_path = check_object.source.getPath3(b, step);
    const contents = src_path.root_dir.handle.readFileAllocOptions(
        gpa,
        src_path.sub_path,
        check_object.max_bytes,
        null,
        .of(u64),
        null,
    ) catch |err| return step.fail("unable to read '{f}': {s}", .{
        std.fmt.alt(src_path, .formatEscapeChar), @errorName(err),
    });

    var vars: std.StringHashMap(u64) = .init(gpa);
    for (check_object.checks.items) |chk| {
        if (chk.kind == .compute_compare) {
            assert(chk.actions.items.len == 1);
            const act = chk.actions.items[0];
            assert(act.tag == .compute_cmp);
            const res = act.computeCmp(b, step, vars) catch |err| switch (err) {
                error.UnknownVariable => return step.fail("Unknown variable", .{}),
                else => |e| return e,
            };
            if (!res) {
                return step.fail(
                    \\
                    \\========= comparison failed for action: ===========
                    \\{s} {f}
                    \\===================================================
                , .{ act.phrase.resolve(b, step), act.expected.? });
            }
            continue;
        }

        const output = switch (check_object.obj_format) {
            .macho => try MachODumper.parseAndDump(step, chk, contents),
            .elf => try ElfDumper.parseAndDump(step, chk, contents),
            .coff => return step.fail("TODO coff parser", .{}),
            .wasm => try WasmDumper.parseAndDump(step, chk, contents),
            else => unreachable,
        };

        // Depending on whether we requested dumping section verbatim or not,
        // we either format message string with escaped codes, or not to aid debugging
        // the failed test.
        const fmtMessageString = struct {
            fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(Ctx, formatMessageString) {
                return .{ .data = .{
                    .kind = kind,
                    .msg = msg,
                } };
            }

            const Ctx = struct {
                kind: Check.Kind,
                msg: []const u8,
            };

            fn formatMessageString(ctx: Ctx, w: *Writer) !void {
                switch (ctx.kind) {
                    .dump_section => try w.print("{f}", .{std.ascii.hexEscape(ctx.msg, .lower)}),
                    else => try w.writeAll(ctx.msg),
                }
            }
        }.fmtMessageString;

        var it = mem.tokenizeAny(u8, output, "\r\n");
        for (chk.actions.items) |act| {
            switch (act.tag) {
                .exact => {
                    while (it.next()) |line| {
                        if (act.exact(b, step, line)) break;
                    } else {
                        return step.fail(
                            \\
                            \\========= expected to find: ==========================
                            \\{f}
                            \\========= but parsed file does not contain it: =======
                            \\{f}
                            \\========= file path: =================================
                            \\{f}
                        , .{
                            fmtMessageString(chk.kind, act.phrase.resolve(b, step)),
                            fmtMessageString(chk.kind, output),
                            src_path,
                        });
                    }
                },

                .contains => {
                    while (it.next()) |line| {
                        if (act.contains(b, step, line)) break;
                    } else {
                        return step.fail(
                            \\
                            \\========= expected to find: ==========================
                            \\*{f}*
                            \\========= but parsed file does not contain it: =======
                            \\{f}
                            \\========= file path: =================================
                            \\{f}
                        , .{
                            fmtMessageString(chk.kind, act.phrase.resolve(b, step)),
                            fmtMessageString(chk.kind, output),
                            src_path,
                        });
                    }
                },

                .not_present => {
                    while (it.next()) |line| {
                        if (act.notPresent(b, step, line)) continue;
                        return step.fail(
                            \\
                            \\========= expected not to find: ===================
                            \\{f}
                            \\========= but parsed file does contain it: ========
                            \\{f}
                            \\========= file path: ==============================
                            \\{f}
                        , .{
                            fmtMessageString(chk.kind, act.phrase.resolve(b, step)),
                            fmtMessageString(chk.kind, output),
                            src_path,
                        });
                    }
                },

                .extract => {
                    while (it.next()) |line| {
                        if (try act.extract(b, step, line, &vars)) break;
                    } else {
                        return step.fail(
                            \\
                            \\========= expected to find and extract: ==============
                            \\{f}
                            \\========= but parsed file does not contain it: =======
                            \\{f}
                            \\========= file path: ==============================
                            \\{f}
                        , .{
                            fmtMessageString(chk.kind, act.phrase.resolve(b, step)),
                            fmtMessageString(chk.kind, output),
                            src_path,
                        });
                    }
                },

                .compute_cmp => unreachable,
            }
        }
    }
}