2929import ghidra .app .plugin .core .analysis .AutoAnalysisManager ;
3030import ghidra .app .services .ProgramManager ;
3131import ghidra .app .util .importer .AutoImporter ;
32+ import ghidra .app .util .opinion .LoadException ;
3233import ghidra .app .util .importer .MessageLog ;
3334import ghidra .app .util .opinion .LoadResults ;
3435import ghidra .app .util .opinion .Loaded ;
4748import ghidra .framework .plugintool .PluginTool ;
4849import ghidra .program .model .lang .CompilerSpec ;
4950import ghidra .program .model .lang .CompilerSpecID ;
51+ import ghidra .util .Msg ;
5052import ghidra .program .model .lang .Language ;
5153import ghidra .program .model .lang .LanguageCompilerSpecPair ;
5254import ghidra .program .model .lang .LanguageDescription ;
@@ -694,7 +696,10 @@ private void registerImportProgramTool() throws McpError {
694696 "Where to save the programs in the project (default: /)" , "/"
695697 ));
696698 properties .put ("processorSpec" , SchemaUtil .stringProperty (
697- "Optional processor/compiler spec (e.g., 'x86:LE:64:default', 'golang:BE:64:default'). If not specified, Ghidra will auto-detect."
699+ "Optional processor/compiler spec hint (e.g., 'x86:LE:64:default'). " +
700+ "If not specified, Ghidra will automatically detect the best match. " +
701+ "Only use if auto-detection chooses incorrectly. " +
702+ "The actual loadspec used will be returned in the response."
698703 ));
699704 properties .put ("runAnalysis" , SchemaUtil .booleanPropertyWithDefault (
700705 "Whether to run auto-analysis after loading" , true
@@ -1111,29 +1116,25 @@ private McpSchema.CallToolResult importMultipleFiles(List<File> files, String pr
11111116 int failureCount = 0 ;
11121117
11131118 for (File file : files ) {
1114- try {
1115- Map <String , Object > result = importSingleFile (file , folder , processor , runAnalysis , openProgram );
1116- results .add (result );
1117- if (result .get ("success" ).equals (true )) {
1118- successCount ++;
1119- } else {
1120- failureCount ++;
1121- }
1122- } catch (Exception e ) {
1123- Map <String , Object > errorResult = new HashMap <>();
1124- errorResult .put ("success" , false );
1125- errorResult .put ("filePath" , file .getAbsolutePath ());
1126- errorResult .put ("error" , e .getMessage ());
1127- results .add (errorResult );
1119+ Map <String , Object > result = importSingleFile (file , folder , processor , runAnalysis , openProgram );
1120+ results .add (result );
1121+
1122+ if (Boolean .TRUE .equals (result .get ("success" ))) {
1123+ successCount ++;
1124+ } else {
11281125 failureCount ++;
1126+ // Log the failure but continue with other files
1127+ String error = (String ) result .get ("error" );
1128+ Msg .warn (this , "Failed to import " + file .getName () + ": " + error );
11291129 }
11301130 }
11311131
11321132 Map <String , Object > response = new HashMap <>();
1133- response .put ("success" , true );
1133+ response .put ("success" , successCount > 0 ); // Success if at least one file imported
11341134 response .put ("sourceType" , sourceType );
11351135 response .put ("sourcePath" , sourcePath );
11361136 response .put ("isListing" , false );
1137+ response .put ("projectPath" , projectPath );
11371138 response .put ("totalFiles" , files .size ());
11381139 response .put ("successCount" , successCount );
11391140 response .put ("failureCount" , failureCount );
@@ -1160,29 +1161,25 @@ private McpSchema.CallToolResult importMultipleFilesFromArchive(List<FSRL> files
11601161 int failureCount = 0 ;
11611162
11621163 for (FSRL fileFsrl : files ) {
1163- try {
1164- Map <String , Object > result = importSingleFileFromArchive (fileFsrl , folder , processor , runAnalysis , openProgram );
1165- results .add (result );
1166- if (result .get ("success" ).equals (true )) {
1167- successCount ++;
1168- } else {
1169- failureCount ++;
1170- }
1171- } catch (Exception e ) {
1172- Map <String , Object > errorResult = new HashMap <>();
1173- errorResult .put ("success" , false );
1174- errorResult .put ("filePath" , fileFsrl .toString ());
1175- errorResult .put ("error" , e .getMessage ());
1176- results .add (errorResult );
1164+ Map <String , Object > result = importSingleFileFromArchive (fileFsrl , folder , processor , runAnalysis , openProgram );
1165+ results .add (result );
1166+
1167+ if (Boolean .TRUE .equals (result .get ("success" ))) {
1168+ successCount ++;
1169+ } else {
11771170 failureCount ++;
1171+ // Log the failure but continue with other files
1172+ String error = (String ) result .get ("error" );
1173+ Msg .warn (this , "Failed to import " + fileFsrl .getName () + ": " + error );
11781174 }
11791175 }
11801176
11811177 Map <String , Object > response = new HashMap <>();
1182- response .put ("success" , true );
1178+ response .put ("success" , successCount > 0 ); // Success if at least one file imported
11831179 response .put ("sourceType" , "archive" );
11841180 response .put ("sourcePath" , sourcePath );
11851181 response .put ("isListing" , false );
1182+ response .put ("projectPath" , projectPath );
11861183 response .put ("totalFiles" , files .size ());
11871184 response .put ("successCount" , successCount );
11881185 response .put ("failureCount" , failureCount );
@@ -1201,16 +1198,35 @@ private Map<String, Object> importSingleFile(File file, DomainFolder folder, Lan
12011198 String baseName = file .getName ();
12021199 String programName = createUniqueFileName (folder , baseName );
12031200
1204- // Use AutoImporter to import the file
12051201 Project project = AppInfo .getActiveProject ();
12061202 MessageLog messageLog = new MessageLog ();
1207- LoadResults <Program > loadResults = AutoImporter .importByUsingBestGuess (
1208- file , project , folder .getPathname () + "/" + programName , this , messageLog , TaskMonitor .DUMMY );
1203+ LoadResults <Program > loadResults ;
1204+
1205+ // Use the processor spec if provided, otherwise auto-detect
1206+ if (processor != null ) {
1207+ try {
1208+ Language language = processor .getLanguage ();
1209+ CompilerSpec compilerSpec = processor .getCompilerSpec ();
1210+ loadResults = AutoImporter .importByLookingForLcs (
1211+ file , project , folder .getPathname () + "/" + programName ,
1212+ language , compilerSpec , this , messageLog , TaskMonitor .DUMMY );
1213+ } catch (Exception e ) {
1214+ // If specific processor spec fails, fall back to auto-detect
1215+ Msg .warn (this , "Processor spec " + processor + " failed for " + file .getName () +
1216+ ", falling back to auto-detection: " + e .getMessage ());
1217+ loadResults = AutoImporter .importByUsingBestGuess (
1218+ file , project , folder .getPathname () + "/" + programName , this , messageLog , TaskMonitor .DUMMY );
1219+ }
1220+ } else {
1221+ loadResults = AutoImporter .importByUsingBestGuess (
1222+ file , project , folder .getPathname () + "/" + programName , this , messageLog , TaskMonitor .DUMMY );
1223+ }
12091224
12101225 if (loadResults == null || loadResults .size () == 0 ) {
12111226 result .put ("success" , false );
12121227 result .put ("filePath" , file .getAbsolutePath ());
12131228 result .put ("error" , "No programs were imported from file" );
1229+ result .put ("errorType" , "NO_PROGRAMS_IMPORTED" );
12141230 return result ;
12151231 }
12161232
@@ -1221,6 +1237,11 @@ private Map<String, Object> importSingleFile(File file, DomainFolder folder, Lan
12211237 Program program = loadResults .getPrimaryDomainObject ();
12221238 String programPath = program .getDomainFile ().getPathname ();
12231239
1240+ // Extract the actual loadspec used
1241+ String languageId = program .getLanguage ().getLanguageID ().getIdAsString ();
1242+ String compilerSpecId = program .getCompilerSpec ().getCompilerSpecID ().getIdAsString ();
1243+ String loadSpec = languageId + ":" + compilerSpecId ;
1244+
12241245 // Run analysis on all loaded programs if requested
12251246 if (runAnalysis ) {
12261247 for (Loaded <Program > loaded : loadResults ) {
@@ -1232,6 +1253,7 @@ private Map<String, Object> importSingleFile(File file, DomainFolder folder, Lan
12321253 } catch (Exception e ) {
12331254 loadedProgram .endTransaction (analysisTransactionID , false );
12341255 // Log but don't fail the import due to analysis issues
1256+ Msg .warn (this , "Analysis failed for " + loadedProgram .getName () + ": " + e .getMessage ());
12351257 }
12361258 }
12371259 }
@@ -1247,15 +1269,26 @@ private Map<String, Object> importSingleFile(File file, DomainFolder folder, Lan
12471269 result .put ("filePath" , file .getAbsolutePath ());
12481270 result .put ("programPath" , programPath );
12491271 result .put ("programName" , programName );
1272+ result .put ("languageId" , languageId );
1273+ result .put ("compilerSpecId" , compilerSpecId );
1274+ result .put ("loadSpec" , loadSpec );
12501275 result .put ("totalProgramsImported" , loadResults .size ());
12511276
1252- // Release the LoadResults (this will release all loaded programs)
1253- loadResults .release (this );
1277+ // Don't release the LoadResults immediately - let programs stay open for use
1278+ // This fixes the "program not found after import" bug
1279+ // loadResults.release(this); // REMOVED
12541280
1281+ } catch (LoadException e ) {
1282+ // Specific handling for files that can't be loaded
1283+ result .put ("success" , false );
1284+ result .put ("filePath" , file .getAbsolutePath ());
1285+ result .put ("error" , "Could not find a suitable loader for this file: " + e .getMessage ());
1286+ result .put ("errorType" , "NO_LOADER" );
12551287 } catch (Exception e ) {
12561288 result .put ("success" , false );
12571289 result .put ("filePath" , file .getAbsolutePath ());
12581290 result .put ("error" , e .getMessage ());
1291+ result .put ("errorType" , e .getClass ().getSimpleName ());
12591292 }
12601293
12611294 return result ;
@@ -1271,16 +1304,35 @@ private Map<String, Object> importSingleFileFromArchive(FSRL fileFsrl, DomainFol
12711304 String fileName = fileFsrl .getName ();
12721305 String programName = createUniqueFileName (folder , fileName );
12731306
1274- // Use AutoImporter to import from archive
12751307 Project project = AppInfo .getActiveProject ();
12761308 MessageLog messageLog = new MessageLog ();
1277- LoadResults <Program > loadResults = AutoImporter .importByUsingBestGuess (
1278- fileFsrl , project , folder .getPathname () + "/" + programName , this , messageLog , TaskMonitor .DUMMY );
1309+ LoadResults <Program > loadResults ;
1310+
1311+ // Use the processor spec if provided, otherwise auto-detect
1312+ if (processor != null ) {
1313+ try {
1314+ Language language = processor .getLanguage ();
1315+ CompilerSpec compilerSpec = processor .getCompilerSpec ();
1316+ loadResults = AutoImporter .importByLookingForLcs (
1317+ fileFsrl , project , folder .getPathname () + "/" + programName ,
1318+ language , compilerSpec , this , messageLog , TaskMonitor .DUMMY );
1319+ } catch (Exception e ) {
1320+ // If specific processor spec fails, fall back to auto-detect
1321+ Msg .warn (this , "Processor spec " + processor + " failed for " + fileName +
1322+ ", falling back to auto-detection: " + e .getMessage ());
1323+ loadResults = AutoImporter .importByUsingBestGuess (
1324+ fileFsrl , project , folder .getPathname () + "/" + programName , this , messageLog , TaskMonitor .DUMMY );
1325+ }
1326+ } else {
1327+ loadResults = AutoImporter .importByUsingBestGuess (
1328+ fileFsrl , project , folder .getPathname () + "/" + programName , this , messageLog , TaskMonitor .DUMMY );
1329+ }
12791330
12801331 if (loadResults == null || loadResults .size () == 0 ) {
12811332 result .put ("success" , false );
12821333 result .put ("filePath" , fileFsrl .toString ());
12831334 result .put ("error" , "No programs were imported from archive file" );
1335+ result .put ("errorType" , "NO_PROGRAMS_IMPORTED" );
12841336 return result ;
12851337 }
12861338
@@ -1291,6 +1343,11 @@ private Map<String, Object> importSingleFileFromArchive(FSRL fileFsrl, DomainFol
12911343 Program program = loadResults .getPrimaryDomainObject ();
12921344 String programPath = program .getDomainFile ().getPathname ();
12931345
1346+ // Extract the actual loadspec used
1347+ String languageId = program .getLanguage ().getLanguageID ().getIdAsString ();
1348+ String compilerSpecId = program .getCompilerSpec ().getCompilerSpecID ().getIdAsString ();
1349+ String loadSpec = languageId + ":" + compilerSpecId ;
1350+
12941351 // Run analysis on all loaded programs if requested
12951352 if (runAnalysis ) {
12961353 for (Loaded <Program > loaded : loadResults ) {
@@ -1302,6 +1359,7 @@ private Map<String, Object> importSingleFileFromArchive(FSRL fileFsrl, DomainFol
13021359 } catch (Exception e ) {
13031360 loadedProgram .endTransaction (analysisTransactionID , false );
13041361 // Log but don't fail the import due to analysis issues
1362+ Msg .warn (this , "Analysis failed for " + loadedProgram .getName () + ": " + e .getMessage ());
13051363 }
13061364 }
13071365 }
@@ -1317,15 +1375,26 @@ private Map<String, Object> importSingleFileFromArchive(FSRL fileFsrl, DomainFol
13171375 result .put ("filePath" , fileFsrl .toString ());
13181376 result .put ("programPath" , programPath );
13191377 result .put ("programName" , programName );
1378+ result .put ("languageId" , languageId );
1379+ result .put ("compilerSpecId" , compilerSpecId );
1380+ result .put ("loadSpec" , loadSpec );
13201381 result .put ("totalProgramsImported" , loadResults .size ());
13211382
1322- // Release the LoadResults (this will release all loaded programs)
1323- loadResults .release (this );
1383+ // Don't release the LoadResults immediately - let programs stay open for use
1384+ // This fixes the "program not found after import" bug
1385+ // loadResults.release(this); // REMOVED
13241386
1387+ } catch (LoadException e ) {
1388+ // Specific handling for files that can't be loaded
1389+ result .put ("success" , false );
1390+ result .put ("filePath" , fileFsrl .toString ());
1391+ result .put ("error" , "Could not find a suitable loader for this file: " + e .getMessage ());
1392+ result .put ("errorType" , "NO_LOADER" );
13251393 } catch (Exception e ) {
13261394 result .put ("success" , false );
13271395 result .put ("filePath" , fileFsrl .toString ());
13281396 result .put ("error" , e .getMessage ());
1397+ result .put ("errorType" , e .getClass ().getSimpleName ());
13291398 }
13301399
13311400 return result ;
0 commit comments