From c74d302cc40f255e0fefc6be375d1433b0561dd1 Mon Sep 17 00:00:00 2001 From: Adolanium <94890352+Adolanium@users.noreply.github.com> Date: Sun, 25 Oct 2026 08:19:14 +0200 Subject: [PATCH] fix(ghidra): report a DOL language override conflict as a configuration error (#7081) * fix(ghidra): report a DOL language override conflict as a configuration error A REA_GHIDRA_LANGUAGE_ID outside PowerPC:BE:32:* made an otherwise admitted GameCube DOL an unsupported target. docs/ghidra-provider.md and the other fixed-import lanes treat such an override as a configuration error naming the conflicting setting. The DOL lane now uses the same languageOverrideConflict owner and names only REA_GHIDRA_LANGUAGE_ID, since DOL accepts a compiler-spec override. * test(ghidra): keep a valid compiler spec out of the DOL language conflict --- docs/ghidra-provider.md | 2 +- src/ghidra/GhidraAnalysisProfile.ts | 15 ++++++----- src/ghidra/GhidraDolProfile.test.ts | 42 +++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/docs/ghidra-provider.md b/docs/ghidra-provider.md index 8c9013fd0d..fe8a0fd589 100644 --- a/docs/ghidra-provider.md +++ b/docs/ghidra-provider.md @@ -15,7 +15,7 @@ snapshot parser and a separate optional annotation extension. Check the [release boundary](installation.md#released-package-and-main) when using npm. See [import language and analysis seeds](installation.md#ghidra-import-language-and-analysis-seeds) -to override the header-detected language or seed analysis. An override that conflicts with a fixed raw, DOS, PSP, MIPS or RISC-V import +to override the header-detected language or seed analysis. An override that conflicts with a fixed raw, DOS, PSP, MIPS, RISC-V or GameCube DOL import is a configuration error naming the conflicting settings. It does not make an otherwise admitted target unsupported; REA preserves the requested settings. An inaccessible language diff --git a/src/ghidra/GhidraAnalysisProfile.ts b/src/ghidra/GhidraAnalysisProfile.ts index 6de60f9e27..1ea8726ef6 100644 --- a/src/ghidra/GhidraAnalysisProfile.ts +++ b/src/ghidra/GhidraAnalysisProfile.ts @@ -33,6 +33,7 @@ import { } from "./GhidraPowerPcProfile.js"; import { ghidraRawBinaryLanguage } from "./GhidraRawBinaryLanguage.js"; import { + GHIDRA_GAMECUBE_DOL_LANGUAGE, ghidraDolLanguage, ghidraDolLanguageUnsupportedReason, ghidraDolProfileParameters, @@ -180,14 +181,16 @@ export const resolveGhidraAnalysisProfile = async ( if (target.format === "gamecube-dol") { // The DOL admission owns the processor family; only a BE32 PowerPC variant may replace the stock language. const reason = ghidraDolLanguageUnsupportedReason(languageOverride); - if (reason !== null) + // The DOL is admitted; only the selected language conflicts with it. + if (reason !== null && languageOverride !== undefined) return Promise.resolve( err( - new AnalysisUnsupportedTargetError( - "resolve_analysis_profile", - target.path, - reason, - ), + languageOverrideConflict(languageOverride, reason, { + languageId: GHIDRA_GAMECUBE_DOL_LANGUAGE.languageId, + ...(languageOverride.compilerSpecId === undefined + ? {} + : { compilerSpecId: languageOverride.compilerSpecId }), + }), ), ); const language = ghidraDolLanguage(languageOverride); diff --git a/src/ghidra/GhidraDolProfile.test.ts b/src/ghidra/GhidraDolProfile.test.ts index 236beb52b1..b7b0dd5bd4 100644 --- a/src/ghidra/GhidraDolProfile.test.ts +++ b/src/ghidra/GhidraDolProfile.test.ts @@ -4,6 +4,7 @@ import { describe, expect, it } from "vitest"; import { parseBinaryTarget } from "../application/BinaryTargetResolver.js"; import { createTestTempDirectory } from "../../tests/fixtures/temporaryDirectory.js"; import { parseConfig } from "../config/parseConfig.js"; +import { projectAnalysisError } from "../domain/analysisErrorProjection.js"; import type { BinaryTarget } from "../domain/binaryTargetTypes.js"; import { buildGamecubeDol } from "../domain/gamecubeDol.fixture.js"; import { parseDolHeader } from "../domain/gamecubeDol.js"; @@ -160,11 +161,42 @@ describe("GameCube DOL Ghidra profile", () => { host, ); const resolved = await ghidra.resolveAnalysisProfile(await dolTarget()); - expect(resolved.ok).toBe(false); - if (!resolved.ok) - expect(resolved.error.message).toContain( - "requires a PowerPC:BE:32:* language", - ); + if (resolved.ok) throw new Error("expected an override conflict"); + expect(resolved.error.message).toContain( + "requires a PowerPC:BE:32:* language", + ); + // The setting conflicts with the admitted DOL; the target stays supported. + expect(projectAnalysisError(resolved.error)).toMatchObject({ + code: "configuration_invalid", + details: { + settings: [ + expect.objectContaining({ setting: "REA_GHIDRA_LANGUAGE_ID" }), + ], + }, + }); + }); + + it("names only the language when a compiler spec accompanies a conflicting language", async () => { + const ghidra = new GhidraProvider( + await installedConfig({ + REA_GHIDRA_LANGUAGE_ID: "x86:LE:32:default", + REA_GHIDRA_COMPILER_SPEC_ID: "gcc", + }), + silentLogger, + {}, + host, + ); + const resolved = await ghidra.resolveAnalysisProfile(await dolTarget()); + if (resolved.ok) throw new Error("expected an override conflict"); + // DOL accepts a compiler-spec override, so only the language is blamed. + expect(projectAnalysisError(resolved.error)).toMatchObject({ + code: "configuration_invalid", + details: { + settings: [ + expect.objectContaining({ setting: "REA_GHIDRA_LANGUAGE_ID" }), + ], + }, + }); }); it("does not admit DOL through Windows Ghidra P0", async () => {