Files
reverse-engineering-anything/src/domain/binaryTargetTypes.ts
T
Lewis WangandClaude Opus 5.5 861e17a3a7 fix(native): admit thin arm64_32 Mach-O targets for static inspection (#5788)
* fix(native): admit thin arm64_32 Mach-O targets for static inspection

Target admission mapped Mach-O CPU types only to x86, x86_64, arm and
arm64, so a thin arm64_32 object was rejected as "unsupported Mach-O
architecture" before list-architectures, inspect-macho or
inspect-signature could run, although the same slice inside a FAT file
was inventoried.

Recognize CPU_TYPE_ARM64_32 as its own Mach-O-only architecture with a
dedicated thin target variant, so ELF, PE and raw-binary paths cannot
carry it. Add its slice policy so 32-bit dispatch metadata refuses it
like other 32-bit Mach-O images instead of reporting a CPU mismatch.
Ghidra, Hopper and IDA have no verified arm64_32 lane and decline it
explicitly; Hopper's declined families now share one predicate.

Fixes #5713

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci(apple): run the thin arm64_32 CLI regression on macOS

The regression compiles an arm64_32 object with Apple clang, so it skips
on the Ubuntu jobs. List it in the macOS Apple artifact job and its
change scope so the job runs it and changes to it select that job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 05:14:42 +08:00

263 lines
8.8 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",
"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;
})
| (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";