* feat(domain): parse and validate GameCube/Wii DOL headers Add a pure parser for the 0x100-byte DOL header (7 text and 11 data slots, BSS, entry point). Used slots must lie after the header and inside the file and the 0x80000000..0x81800000 load window; text slots are 4-byte aligned; sections may not overlap in memory or in the file; the entry point must be inside a text section. BSS may overlap data sections, as .sdata/.sdata2 do in shipped DOLs. Refs #1741 * feat(binary): admit caller-selected GameCube/Wii DOL targets * feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script * test(ghidra): cover DOL launcher arguments, attestation and raw profile digests * test(ghidra): add the real verify:ghidra:dol lane and DOL docs * fix(ghidra): read only the DOL header and section ranges instead of the whole file * test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog * fix(ghidra): attest DOL block permissions, space, overlay and load state * fix(ghidra): keep load-image permissions optional for older inspections * fix(contracts): accept DOL targets in binary_session output * fix(ghidra): adapt the DOL lane to the current upstream address schema Validate DOL load-image coordinates with the upstream observed-address schema, drop a duplicate import left by the rebase, and re-pin the raw profile digests to the unmodified upstream resolver. * fix(ghidra): preserve bridge inventory symbol provenance * fix(ghidra): align DOL verification with shared workflow contracts * feat(domain): parse and validate GameCube/Wii DOL headers Add a pure parser for the 0x100-byte DOL header (7 text and 11 data slots, BSS, entry point). Used slots must lie after the header and inside the file and the 0x80000000..0x81800000 load window; text slots are 4-byte aligned; sections may not overlap in memory or in the file; the entry point must be inside a text section. BSS may overlap data sections, as .sdata/.sdata2 do in shipped DOLs. Refs #1741 * feat(binary): admit caller-selected GameCube/Wii DOL targets * feat(ghidra): import GameCube/Wii DOL targets through a section-map pre-script * test(ghidra): cover DOL launcher arguments, attestation and raw profile digests * test(ghidra): add the real verify:ghidra:dol lane and DOL docs * fix(ghidra): read only the DOL header and section ranges instead of the whole file * test(ghidra): derive DOL lane addresses from readelf and admit Gekko from the install catalog * fix(ghidra): attest DOL block permissions, space, overlay and load state * fix(ghidra): keep load-image permissions optional for older inspections * fix(contracts): accept DOL targets in binary_session output * fix(ghidra): adapt the DOL lane to the current upstream address schema Validate DOL load-image coordinates with the upstream observed-address schema, drop a duplicate import left by the rebase, and re-pin the raw profile digests to the unmodified upstream resolver. * docs(ghidra): qualify the DOL memory window * refactor(ghidra): own DOL BSS ranges in the parsed header * docs(ghidra): record real DOL workflows and task costs * docs(ghidra): record final DOL integration verification * fix(mcp): share the native procedure search row contract * docs(ghidra): record public procedure search verification --------- Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
280 lines
9.5 KiB
TypeScript
280 lines
9.5 KiB
TypeScript
import type { DolHeader } from "./gamecubeDol.js";
|
|
import type { ZipPackageFormat } from "./zipPackageFormat.js";
|
|
|
|
/** Provider-neutral CPU families detected from headers or explicitly selected raw loads. */
|
|
export const BINARY_ARCHITECTURES = [
|
|
"x86",
|
|
"x86_64",
|
|
"arm",
|
|
"arm64",
|
|
"arm64_32",
|
|
"mips",
|
|
"6502",
|
|
"riscv",
|
|
"powerpc",
|
|
"m68k",
|
|
"hexagon",
|
|
] as const;
|
|
export type BinaryArchitecture = (typeof BINARY_ARCHITECTURES)[number];
|
|
|
|
/**
|
|
* Families admitted only by explicit raw-binary selection. Header detection
|
|
* never yields them, so detected targets and Evidence subjects exclude them.
|
|
*/
|
|
export const RAW_ONLY_ARCHITECTURES = [
|
|
"6502",
|
|
"m68k",
|
|
] as const satisfies readonly BinaryArchitecture[];
|
|
export type RawOnlyArchitecture = (typeof RAW_ONLY_ARCHITECTURES)[number];
|
|
|
|
/**
|
|
* ELF header facts for a family whose ISA and ABI are not established by the
|
|
* family name alone. Consumers admit a lane from these declarations rather than
|
|
* re-reading the header.
|
|
*/
|
|
export interface ElfFamilyMetadata {
|
|
readonly elfClass: 32 | 64;
|
|
readonly byteOrder: "little" | "big";
|
|
readonly type: number;
|
|
readonly flags: number;
|
|
}
|
|
|
|
/** Referenced CodeView debug symbols from a PE debug directory. */
|
|
export interface PeCodeViewRecord {
|
|
readonly pdbPath: string;
|
|
readonly pdbFileName: string;
|
|
readonly guid: string | null;
|
|
readonly age: number;
|
|
}
|
|
|
|
/** Families whose parsed target variant carries `ElfFamilyMetadata`. */
|
|
export const ELF_METADATA_ARCHITECTURES = [
|
|
"mips",
|
|
"riscv",
|
|
"powerpc",
|
|
] as const satisfies readonly BinaryArchitecture[];
|
|
export type ElfMetadataArchitecture =
|
|
(typeof ELF_METADATA_ARCHITECTURES)[number];
|
|
|
|
/** Families whose ISA and ABI need a separate ELF declaration to be admitted. */
|
|
export const isElfMetadataArchitecture = (
|
|
architecture: BinaryArchitecture,
|
|
): architecture is ElfMetadataArchitecture =>
|
|
(ELF_METADATA_ARCHITECTURES as readonly string[]).includes(architecture);
|
|
|
|
/** Recognized family with no ELF metadata member; the header alone is the fact. */
|
|
export type HeaderArchitecture = Exclude<
|
|
BinaryArchitecture,
|
|
ElfMetadataArchitecture | RawOnlyArchitecture | "hexagon" | "arm64_32"
|
|
>;
|
|
|
|
/** Raw ELF facts; family recognition does not establish ISA or ABI support. */
|
|
export interface MipsElfMetadata extends ElfFamilyMetadata {
|
|
/** Undefined until inspected; null means no ABI record was observed. */
|
|
readonly abiFlags?: MipsAbiFlags | null;
|
|
/** Undefined until inspected; null means no GNU attribute section was observed. */
|
|
readonly gnuAttributes?: MipsGnuAttributes | null;
|
|
/** Unavailable declaration sources; missing records there remain unknown. */
|
|
readonly limitations?: readonly string[];
|
|
/** Complete dynamic REL declarations, or the reason they could not be inspected. */
|
|
readonly sharedLoad?:
|
|
| {
|
|
readonly status: "complete";
|
|
readonly relocationCount: number;
|
|
readonly relocationTypes: readonly number[];
|
|
readonly symbolRelativeCount: number;
|
|
}
|
|
| { readonly status: "unavailable"; readonly reason: string };
|
|
}
|
|
|
|
/** Declared fields of the 24-byte MIPS ABI flags record, not inferred semantics. */
|
|
export interface MipsAbiFlags {
|
|
readonly version: number;
|
|
readonly isaLevel: number;
|
|
readonly isaRevision: number;
|
|
readonly gprSize: number;
|
|
readonly cpr1Size: number;
|
|
readonly cpr2Size: number;
|
|
readonly fpAbi: number;
|
|
readonly isaExtension: number;
|
|
readonly ases: number;
|
|
readonly flags1: number;
|
|
readonly flags2: number;
|
|
}
|
|
|
|
/** File-scope GNU object attributes parsed from `SHT_GNU_ATTRIBUTES`. */
|
|
export interface MipsGnuAttributes {
|
|
/** Tag_GNU_MIPS_ABI_FP; null means this file did not declare the tag. */
|
|
readonly fpAbi: number | null;
|
|
/** Tag_GNU_MIPS_ABI_MSA vector-argument ABI; null means undeclared. */
|
|
readonly msa: number | null;
|
|
}
|
|
|
|
/**
|
|
* ELF class and flags retain the width and ABI declarations used by this lane.
|
|
* ISA extensions in `.riscv.attributes` are not inspected by this header parser.
|
|
*/
|
|
export type RiscvElfMetadata = ElfFamilyMetadata;
|
|
|
|
/** Raw PowerPC declarations; family identity does not select a processor variant. */
|
|
export type PowerPcElfMetadata = ElfFamilyMetadata;
|
|
|
|
/** Byte range of the selected slice inside its universal Mach-O container. */
|
|
export interface UniversalMachOSlice {
|
|
readonly offset: number;
|
|
readonly size: number;
|
|
}
|
|
|
|
interface BinaryTargetIdentity {
|
|
readonly path: string;
|
|
readonly sourcePath?: string;
|
|
/** Info.plist of the app bundle the target was resolved from. */
|
|
readonly bundleInfoPlist?: string;
|
|
readonly sha256: string;
|
|
}
|
|
|
|
type NonExecutableMetadata = {
|
|
readonly architecture?: never;
|
|
readonly availableArchitectures?: never;
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
};
|
|
|
|
type ExecutableTarget = BinaryTargetIdentity & {
|
|
readonly kind: "executable";
|
|
readonly architecture: HeaderArchitecture;
|
|
readonly availableArchitectures: readonly HeaderArchitecture[];
|
|
};
|
|
|
|
/**
|
|
* Canonical local target identity and provider-neutral file classification.
|
|
* Each variant carries only metadata established for that exact file kind.
|
|
*/
|
|
export type BinaryTarget =
|
|
| (BinaryTargetIdentity & {
|
|
readonly kind: "executable";
|
|
readonly format: "elf";
|
|
readonly architecture: "mips";
|
|
readonly availableArchitectures: readonly "mips"[];
|
|
readonly mips: MipsElfMetadata;
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
})
|
|
| (BinaryTargetIdentity & {
|
|
readonly kind: "executable";
|
|
readonly format: "elf";
|
|
readonly architecture: "riscv";
|
|
readonly availableArchitectures: readonly "riscv"[];
|
|
readonly riscv: RiscvElfMetadata;
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
})
|
|
| (BinaryTargetIdentity & {
|
|
readonly kind: "executable";
|
|
readonly format: "elf";
|
|
readonly architecture: "powerpc";
|
|
readonly availableArchitectures: readonly "powerpc"[];
|
|
readonly powerpc: PowerPcElfMetadata;
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
})
|
|
| (BinaryTargetIdentity & {
|
|
readonly kind: "executable";
|
|
readonly format: "elf";
|
|
readonly architecture: "hexagon";
|
|
readonly availableArchitectures: readonly "hexagon"[];
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
})
|
|
| (BinaryTargetIdentity & {
|
|
readonly kind: "executable";
|
|
readonly format: "mach-o";
|
|
readonly architecture: "arm64_32";
|
|
readonly availableArchitectures: readonly "arm64_32"[];
|
|
readonly universalSlice?: never;
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
})
|
|
| (ExecutableTarget & {
|
|
readonly format: "mach-o" | "elf" | "dos-mz" | "dos-com";
|
|
readonly universalSlice?: never;
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
})
|
|
| (ExecutableTarget & {
|
|
readonly format: "mach-o-universal";
|
|
/** Selected slice range a provider must analyze instead of the container. */
|
|
readonly universalSlice: UniversalMachOSlice;
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
})
|
|
| (BinaryTargetIdentity & {
|
|
readonly kind: "executable";
|
|
readonly format: "raw-binary";
|
|
readonly architecture: HeaderArchitecture | RawOnlyArchitecture;
|
|
readonly availableArchitectures: readonly (
|
|
| HeaderArchitecture
|
|
| RawOnlyArchitecture
|
|
)[];
|
|
readonly entryPoint: string;
|
|
readonly entryPointSource: string;
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
})
|
|
| (BinaryTargetIdentity & {
|
|
readonly kind: "executable";
|
|
readonly format: "gamecube-dol";
|
|
/** Fixed by the DOL selection; the header does not carry a CPU family. */
|
|
readonly architecture: "powerpc";
|
|
readonly availableArchitectures: readonly ["powerpc"];
|
|
readonly byteOrder: "big";
|
|
readonly bits: 32;
|
|
/** Canonical hex entry taken from the DOL header. */
|
|
readonly entryPoint: string;
|
|
readonly entryPointSource: "dol-header";
|
|
/** Validated header: used sections, BSS, and BSS-overlapping data slots. */
|
|
readonly dol: DolHeader;
|
|
readonly executableRole?: never;
|
|
readonly managed?: never;
|
|
})
|
|
| (ExecutableTarget & {
|
|
readonly format: "pe";
|
|
/** Original COFF machine; undefined means this header fact is unavailable. */
|
|
readonly peMachine?: number;
|
|
/** Provider-neutral PE image role observed from COFF characteristics. */
|
|
readonly executableRole:
|
|
| "application"
|
|
| "shared-library"
|
|
| "non-executable";
|
|
/** Whether the PE declares a non-empty CLI header data-directory entry. */
|
|
readonly managed: boolean;
|
|
/** Referenced CodeView debug symbols from the PE debug directory, if present. */
|
|
readonly codeView?: PeCodeViewRecord;
|
|
})
|
|
| (BinaryTargetIdentity &
|
|
NonExecutableMetadata & {
|
|
readonly kind: "database";
|
|
readonly format: "analysis-database";
|
|
})
|
|
| (BinaryTargetIdentity &
|
|
NonExecutableMetadata & {
|
|
readonly kind: "archive";
|
|
readonly format:
|
|
| ZipPackageFormat
|
|
| "asar"
|
|
| "godot-pck"
|
|
| "dmg"
|
|
| "pkg";
|
|
})
|
|
| (BinaryTargetIdentity &
|
|
NonExecutableMetadata & {
|
|
readonly kind: "artifact";
|
|
readonly format: "plist" | "javascript" | "source-map";
|
|
});
|
|
|
|
/** Whether an executable target contains a thin or universal Mach-O image. */
|
|
export const isMachOFormat = (
|
|
format: unknown,
|
|
): format is "mach-o" | "mach-o-universal" =>
|
|
format === "mach-o" || format === "mach-o-universal";
|