Files
reverse-engineering-anything/src/domain/binaryTargetTypes.ts
T
zhugehyukandClaude Opus 5.5 d62d3082c2 feat(ghidra): support raw big-endian m68k analysis (#4018)
* feat(ghidra): support raw big-endian m68k analysis

Admit `m68k` for headerless raw-binary targets. Admission requires a
2-byte aligned entry and a mapping within 0x0..0xffffffff, and Ghidra
imports with BinaryLoader using 68000:BE:32:default, the default compiler
specification, and the `ram` default address space.

`6502` and `m68k` now share one RAW_ONLY_ARCHITECTURES owner, so detected
targets, Evidence subjects, snapshots, and lifecycle output contracts
exclude both instead of repeating a 6502 literal.

Ghidra's default 68000 language decodes the 68040 superset; separate
68020/68030/CPU32/ColdFire variants, Amiga/Atari containers, and reset
vector discovery remain unsupported. Windows Ghidra P0 still admits raw
6502 only.

Verified with Ghidra 12.1.4 on macOS against an 8-byte self-built fixture
(nop; nop; moveq #1,d0; rts at 0x1000): load image verified in `ram`,
entry decompiles to `return 1`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(ghidra): verify raw m68k analysis with real Ghidra

Add npm run verify:ghidra:m68k. The verifier loads a source-owned 22-byte
68000 fixture (annotated hex, no assembler) at 0x1000 and checks, through
the compiled CLI and stdio MCP server: the 68000:BE:32:default BinaryLoader
import, load-image parity, exact mapped bytes, both functions, the bsr.b
call reference, decompilation by address and file offset, rejection of a
misaligned entry and of a mapping past 0xffffffff without replacing the
active session, unchanged source bytes, and runtime cleanup.

Run with Ghidra 12.1.4 and JDK 21 on macOS arm64. Linux has not been
exercised; Windows Ghidra P0 does not admit m68k.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 23:57:34 +08:00

242 lines
8.1 KiB
TypeScript

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",
"mips",
"6502",
"riscv",
"powerpc",
"m68k",
] 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
>;
/** 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;
})
| (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: Exclude<
BinaryArchitecture,
ElfMetadataArchitecture
>;
readonly availableArchitectures: readonly Exclude<
BinaryArchitecture,
ElfMetadataArchitecture
>[];
readonly entryPoint: string;
readonly entryPointSource: string;
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" | "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";