diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java index 894068140e..68be22b86a 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateDefaultGDTArchivesScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -31,12 +31,9 @@ import java.io.IOException; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; -import ghidra.app.util.cparser.C.CParserUtils.CParseResults; import ghidra.app.util.cparser.C.ParseException; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.FileDataTypeManager; -import ghidra.util.Msg; -import ghidra.util.task.TaskMonitor; public class CreateDefaultGDTArchivesScript extends GhidraScript { @@ -69,16 +66,12 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript { throws ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { String dataTypeFile = outputDir + File.separator + gdtName + ".gdt"; - - File f = getArchiveFile(dataTypeFile); - - FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f); - - CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, includePaths, args, dtMgr, languageID, compiler, monitor); - - Msg.info(this, results.getFormattedParseMessage(null)); - dtMgr.save(); + File f = getArchiveFile(dataTypeFile); + + FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames, + includePaths, args, f.getAbsolutePath(), languageID, compiler, monitor); + dtMgr.close(); } diff --git a/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java b/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java index 41f8ab3043..5aed3943a3 100644 --- a/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/CreateExampleGDTArchiveScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -31,11 +31,9 @@ import java.io.IOException; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; -import ghidra.app.util.cparser.C.CParserUtils.CParseResults; import ghidra.app.util.cparser.C.ParseException; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.FileDataTypeManager; -import ghidra.util.Msg; public class CreateExampleGDTArchiveScript extends GhidraScript { @@ -69,13 +67,9 @@ public class CreateExampleGDTArchiveScript extends GhidraScript { File f = getArchiveFile(dataTypeFile); - FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f); - - CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, includePaths, args, dtMgr, languageID, compiler, monitor); - - Msg.info(this, results.getFormattedParseMessage(null)); + FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames, + includePaths, args, f.getAbsolutePath(), languageID, compiler, monitor); - dtMgr.save(); dtMgr.close(); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java index e485c51c7a..ac3ba4e266 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserPlugin.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,18 +16,14 @@ package ghidra.app.plugin.core.cparser; import java.io.File; -import java.io.IOException; import java.util.ArrayList; import java.util.StringTokenizer; -import javax.swing.SwingUtilities; - import docking.ActionContext; import docking.action.DockingAction; import docking.action.MenuData; import docking.tool.ToolConstants; import docking.widgets.OptionDialog; -import docking.widgets.dialogs.MultiLineMessageDialog; import ghidra.app.CorePluginPackage; import ghidra.app.plugin.PluginCategoryNames; import ghidra.app.plugin.ProgramPlugin; @@ -40,7 +36,8 @@ import ghidra.framework.plugintool.PluginInfo; import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.util.PluginStatus; import ghidra.program.database.data.ProgramDataTypeManager; -import ghidra.program.model.data.*; +import ghidra.program.model.data.BuiltInDataTypeManager; +import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.lang.LanguageCompilerSpecPair; import ghidra.program.model.listing.Program; import ghidra.util.*; @@ -168,9 +165,9 @@ public class CParserPlugin extends ProgramPlugin { /* * Parse C-source into a data type manager */ - protected void parse(String[] filenames, String includePaths[], String options, - String languageIDString, String compilerSpecID, DataTypeManager dtMgr, - TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + protected CParseResults parse(String[] filenames, String includePaths[], String options, + DataTypeManager dtMgr, TaskMonitor monitor) + throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException { results = null; @@ -181,43 +178,26 @@ public class CParserPlugin extends ProgramPlugin { try { openDTmanagers = getOpenDTMgrs(); } catch (CancelledException exc) { - return; // parse canceled + return null; // parse canceled } - try { - results = CParserUtils.parseHeaderFiles(openDTmanagers, filenames, includePaths, - args, dtMgr, languageIDString, compilerSpecID, monitor); - - final boolean isProgramDtMgr = (dtMgr instanceof ProgramDataTypeManager); - - SwingUtilities.invokeLater(() -> { - // CParserTask will show any errors - if (!results.successful()) { - return; - } - if (isProgramDtMgr) { - MultiLineMessageDialog.showModalMessageDialog(parseDialog.getComponent(), - "C-Parse of Header Files Complete", - "Successfully parsed header file(s) to Program.", - getFormattedParseMessage("Check the Manage Data Types window for added data types."), - MultiLineMessageDialog.INFORMATION_MESSAGE); - } - else { - String archiveName = dtMgr.getName(); - if (dtMgr instanceof FileDataTypeManager) { - archiveName = ((FileDataTypeManager) dtMgr).getFilename(); - } - MultiLineMessageDialog.showModalMessageDialog(parseDialog.getComponent(), - "C-Parse of Header Files Complete", - "Successfully parsed header file(s) to Archive File: " + archiveName, - getFormattedParseMessage(null), - MultiLineMessageDialog.INFORMATION_MESSAGE); - } - }); + results = CParserUtils.parseHeaderFiles(openDTmanagers, filenames, includePaths, + args, dtMgr, monitor); + + return results; + } + + boolean isOpenInTool(DataTypeManager dtm) { + DataTypeManagerService dtService = tool.getService(DataTypeManagerService.class); + if (dtService == null) { + return false; } - catch (IOException e) { - // ignore + for (DataTypeManager openDtm : dtService.getDataTypeManagers()) { + if (dtm == openDtm) { + return true; + } } + return false; } /** @@ -327,11 +307,9 @@ public class CParserPlugin extends ProgramPlugin { CParserTask parseTask = new CParserTask(this, currentProgram.getDataTypeManager()) - .setFileNames(filenames) - .setIncludePaths(includePaths) - .setOptions(options) - .setLanguageID(procID) - .setCompilerID(compilerID); + .setFileNames(filenames) + .setIncludePaths(includePaths) + .setOptions(options); tool.execute(parseTask); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java index 68ae94864a..50fda6c116 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/cparser/CParserTask.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,11 +16,16 @@ package ghidra.app.plugin.core.cparser; import java.io.File; +import java.io.IOException; +import javax.help.UnsupportedOperationException; import javax.swing.SwingUtilities; import docking.widgets.dialogs.MultiLineMessageDialog; -import ghidra.program.model.data.*; +import ghidra.app.util.cparser.C.CParserUtils.CParseResults; +import ghidra.program.database.data.ProgramDataTypeManager; +import ghidra.program.model.data.DataTypeManager; +import ghidra.program.model.data.FileDataTypeManager; import ghidra.util.Msg; import ghidra.util.exception.DuplicateFileException; import ghidra.util.task.Task; @@ -34,18 +39,19 @@ import ghidra.util.task.TaskMonitor; class CParserTask extends Task { private CParserPlugin plugin; - private String dataFileName; private String[] filenames; private String[] includePaths; private String options; - private String languageString; - private String compilerString; + // Language and Compiler Spec IDs valid only for new dataFileName use + private String languageId; + private String compilerSpecId; - private DataTypeManager dtMgr; - + // Either dataTypeManager or dataFileName must be set, but not both + private final DataTypeManager dataTypeManager; // specified for an existing DataTypeManager + private final File dataFile; // specified for a new file /** * Create task to parse to a dataFile @@ -55,44 +61,64 @@ class CParserTask extends Task { */ CParserTask(CParserPlugin plugin, String dataFileName) { super("Parsing C Files", true, false, false); - + dataTypeManager = null; this.plugin = plugin; - this.dataFileName = dataFileName; + this.dataFile = new File(dataFileName); } /** * Create task to parse to a dataTypeManager * - * @param plugin - * @param dataTypeManager + * NOTE: The Language ID and Compiler Spec ID must not be set since the dataTypeManager's + * current architecture will be used. + * + * @param plugin CParserPlugin that will do the work + * @param dataTypeManager target data type manager */ public CParserTask(CParserPlugin plugin, DataTypeManager dataTypeManager) { super("Parsing C Files", true, false, false); - + dataFile = null; this.plugin = plugin; - this.dtMgr = dataTypeManager; + this.dataTypeManager = dataTypeManager; } /** - * Create task to parse to a ProgramDataTypeManager + * Set the language ID to be used. * - * @param plugin - * @param dataTypeManager + * NOTE: The compiler spec ID must also be set, see {@code #setCompilerID(String)}. + * See language *.ldefs file for defined compiler spec IDs or existing Program info. + * + * @param languageId language ID + * @return this task + * @throws UnsupportedOperationException if task was constructed with a DataTypeManager whose + * existing architecture will be used. */ - public CParserTask(CParserPlugin plugin, ProgramBasedDataTypeManager dataTypeManager) { - super("Parsing C Files", true, false, false); - - this.plugin = plugin; - this.dtMgr = dataTypeManager; - } - - public CParserTask setLanguageID(String languageID) { - this.languageString = languageID; + public CParserTask setLanguageID(String languageId) { + if (dataTypeManager != null) { + throw new UnsupportedOperationException( + "setLanguageID not supported when constructed with DataTypeManager"); + } + this.languageId = languageId; return this; } - public CParserTask setCompilerID(String compilerID) { - this.compilerString = compilerID; + /** + * Set the compiler spec ID to be used. This ID must be defined for the specified language. + * + * NOTE: The language ID must also be set, see {@code #setLanguageID(String)}. + * See language *.ldefs file for defined compiler spec IDs or existing Program info. + * + * @param compilerSpecId compiler spec ID + * @return this task + * @throws UnsupportedOperationException if task was constructed with a DataTypeManager whose + * existing architecture will be used. + */ + public CParserTask setCompilerID(String compilerSpecId) { + if (dataTypeManager != null) { + throw new UnsupportedOperationException( + "setLanguageID not supported when constructed with DataTypeManager"); + } + this.compilerSpecId = compilerSpecId; return this; } @@ -120,96 +146,142 @@ class CParserTask extends Task { return msg; } + private String getResultMessage(DataTypeManager dtMgr, int initialDtCount) { + + int finalDtCount = dtMgr.getDataTypeCount(true) - initialDtCount; + + String msg = (finalDtCount == 0 ? "No" : Integer.toString(finalDtCount)) + + " Data Types added."; + if (finalDtCount != 0 && plugin.isOpenInTool(dtMgr)) { + msg += "\nCheck the Data Type Manager window for added data types."; + } + return msg; + } + + private String getParseDestination(DataTypeManager dtMgr) { + String parseDest = ""; + if (dtMgr instanceof ProgramDataTypeManager) { + parseDest = "Program " + dtMgr.getName(); + } + else if (dtMgr instanceof FileDataTypeManager fileDtm) { + parseDest = "Archive File: " + fileDtm.getFilename(); + } + else { + parseDest = dtMgr.getName(); + } + return parseDest; + } + @Override public void run(TaskMonitor monitor) { - DataTypeManager fileDtMgr = null; - try { - if (dtMgr == null) { - File file = new File(dataFileName); - dtMgr = FileDataTypeManager.createFileArchive(file); - fileDtMgr = dtMgr; - } - plugin.parse(filenames, includePaths, options, languageString, compilerString, dtMgr, monitor); - if (dataFileName != null) { - // TODO: does not consider existing datatypes - if (dtMgr.getDataTypeCount(true) != 0) { - try { - ((FileDataTypeManager) dtMgr).save(); - dtMgr.close(); - } - catch (DuplicateFileException e) { - Msg.showError(this, plugin.getDialog().getComponent(), "Error During Save", - e.getMessage()); - } - catch (Exception e) { - Msg.showError(this, plugin.getDialog().getComponent(), "Error During Save", - "Could not save to file " + dataFileName, e); - } - finally { - if (dtMgr instanceof FileDataTypeManager) { - dtMgr.close(); - } - } - } - else { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - // no results, was canceled - if (plugin.getParseResults() == null) { - return; - } - MultiLineMessageDialog.showModalMessageDialog( - plugin.getDialog().getComponent(), "Parse Errors", - "File was not created due to parse errors: " + - ((FileDataTypeManager) dtMgr).getFilename(), - plugin.getFormattedParseMessage(null), - MultiLineMessageDialog.INFORMATION_MESSAGE); - } - }); + FileDataTypeManager fileDtMgr = null; + if (dataFile != null) { + try { + if ((languageId != null) != (compilerSpecId != null)) { + Msg.showError(this, plugin.getDialog().getComponent(), "Archive Failure", + "Language/CompilerSpec improperly specified: " + languageId + "/" + + compilerSpecId); + return; } + fileDtMgr = + FileDataTypeManager.createFileArchive(dataFile, languageId, compilerSpecId); + } + catch (IOException e) { + Msg.showError(this, plugin.getDialog().getComponent(), "Archive Failure", + "Failed to create archive datatype manager: " + e.getMessage()); + return; } } - catch (ghidra.app.util.cparser.C.ParseException e) { - final String errMsg = e.getMessage(); - System.err.println(errMsg); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - String msg = getFirstMessageLine(errMsg); - MultiLineMessageDialog.showModalMessageDialog(plugin.getDialog().getComponent(), - "Parse Errors", msg, plugin.getFormattedParseMessage(errMsg), - MultiLineMessageDialog.ERROR_MESSAGE); + + DataTypeManager dtMgr = fileDtMgr != null ? fileDtMgr : dataTypeManager; + + int initialDtCount = dtMgr.getDataTypeCount(true); + + try { + + CParseResults results = plugin.parse(filenames, includePaths, options, dtMgr, monitor); + if (results == null) { + return; // cancelled + } + + if (fileDtMgr != null && dtMgr.getDataTypeCount(true) != 0) { + // If archive created - save to file + try { + fileDtMgr.save(); } + catch (DuplicateFileException e) { + Msg.showError(this, plugin.getDialog().getComponent(), + "C-Parse Error During Save", + e.getMessage()); + } + catch (Exception e) { + Msg.showError(this, plugin.getDialog().getComponent(), + "C-Parse Error During Save", + "Could not save to file " + dataFile.getPath(), e); + } + } + + String msg = getResultMessage(dtMgr, initialDtCount); + + SwingUtilities.invokeLater(() -> { + if (!results.successful()) { + MultiLineMessageDialog.showModalMessageDialog( + plugin.getDialog().getComponent(), "C-Parse Failed", + "Failed to parse header file(s) to " + getParseDestination(dtMgr), + plugin.getFormattedParseMessage(msg), + MultiLineMessageDialog.INFORMATION_MESSAGE); + } + else { + MultiLineMessageDialog.showModalMessageDialog( + plugin.getDialog().getComponent(), + "C-Parse Completed", + "Successfully parsed header file(s) to " + getParseDestination(dtMgr), + plugin.getFormattedParseMessage(msg), + MultiLineMessageDialog.INFORMATION_MESSAGE); + } + }); + } + catch (ghidra.app.util.cparser.C.ParseException e) { + final String errMsg = getResultMessage(dtMgr, initialDtCount) + "\n\n" + e.getMessage(); + SwingUtilities.invokeLater(() -> { + MultiLineMessageDialog.showMessageDialog(plugin.getDialog().getComponent(), + "C-Parse Failed", + "Failed to parse header file(s) to " + getParseDestination(dtMgr), + plugin.getFormattedParseMessage(errMsg), + MultiLineMessageDialog.ERROR_MESSAGE); }); } catch (ghidra.app.util.cparser.CPP.ParseException e) { - final String errMsg = e.getMessage(); - System.err.println(errMsg); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - String msg = getFirstMessageLine(errMsg); - MultiLineMessageDialog.showModalMessageDialog(plugin.getDialog().getComponent(), - "PreProcessor Parse Errors", msg, plugin.getFormattedParseMessage(errMsg), - MultiLineMessageDialog.ERROR_MESSAGE); - } + final String errMsg = getResultMessage(dtMgr, initialDtCount) + "\n\n" + e.getMessage(); + SwingUtilities.invokeLater(() -> { + MultiLineMessageDialog.showMessageDialog(plugin.getDialog().getComponent(), + "C-PreProcessor Parse Failed", + "Failed to parse header file(s) to " + getParseDestination(dtMgr), + plugin.getFormattedParseMessage(errMsg), + MultiLineMessageDialog.ERROR_MESSAGE); }); } catch (Exception e) { - final String errMsg = e.getMessage(); - String msg = getFirstMessageLine(errMsg); - Msg.showError(this, plugin.getDialog().getComponent(), "Error During Parse", + final String errMsg = getResultMessage(dtMgr, initialDtCount) + "\n\n" + e.getMessage(); + Msg.showError(this, plugin.getDialog().getComponent(), "Error During C-Parse", "Parse header files failed" + "\n\nParser Messages:\n" + plugin.getParseMessage(), e); - MultiLineMessageDialog.showModalMessageDialog(plugin.getDialog().getComponent(), - "Error During Parse", msg, plugin.getFormattedParseMessage(errMsg), - MultiLineMessageDialog.ERROR_MESSAGE); + SwingUtilities.invokeLater(() -> { + MultiLineMessageDialog.showMessageDialog(plugin.getDialog().getComponent(), + "Error During C-Parse", + "Failed to parse header file(s) to " + getParseDestination(dtMgr), + plugin.getFormattedParseMessage(errMsg), + MultiLineMessageDialog.ERROR_MESSAGE); + }); } finally { if (fileDtMgr != null) { + boolean deleteFile = fileDtMgr.getDataTypeCount(true) == 0; fileDtMgr.close(); + if (deleteFile) { + dataFile.delete(); + } } } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java index 657a1ae83c..8a4fb7af95 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/cparser/C/CParserUtils.java @@ -18,32 +18,26 @@ package ghidra.app.util.cparser.C; import java.io.*; import java.util.Arrays; -import javax.help.UnsupportedOperationException; - import generic.theme.GThemeDefaults.Colors; import generic.theme.GThemeDefaults.Colors.Messages; import ghidra.app.services.DataTypeManagerService; import ghidra.app.util.cparser.CPP.PreProcessor; import ghidra.framework.Application; import ghidra.framework.plugintool.ServiceProvider; -import ghidra.framework.store.LockException; import ghidra.program.model.data.*; -import ghidra.program.model.lang.*; -import ghidra.program.model.listing.IncompatibleLanguageException; import ghidra.program.model.listing.Program; -import ghidra.program.util.DefaultLanguageService; import ghidra.util.*; -import ghidra.util.exception.CancelledException; import ghidra.util.exception.DuplicateNameException; import ghidra.util.task.TaskMonitor; public class CParserUtils { - + private CParserUtils() { // utils class } - - public record CParseResults(PreProcessor preProcessor, String cppParseMessages, String cParseMessages, boolean successful) { + + public record CParseResults(PreProcessor preProcessor, String cppParseMessages, + String cParseMessages, boolean successful) { public String getFormattedParseMessage(String errMsg) { String message = ""; @@ -62,7 +56,7 @@ public class CParserUtils { } return message; - } + } } /** @@ -245,8 +239,10 @@ public class CParserUtils { } /** - * Parse a set of C Header files and associated parsing arguments, returning a new File Data TypeManager - * with in the provided dataFileName. + * Parse a set of C Header files and associated parsing arguments, returning a new File + * Data TypeManager with in the provided dataFileName. The resulting archive + * will not be associated with a specific language/compiler and will use the default + * data organization. * * Note: Using another open archive while parsing will cause: * - a dependence on the other archive @@ -273,17 +269,20 @@ public class CParserUtils { * @throws IOException if there io are errors saving the archive * */ - - public static FileDataTypeManager parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String args[], String dataFileName, + + public static FileDataTypeManager parseHeaderFiles(DataTypeManager[] openDTMgrs, + String[] filenames, String[] args, String dataFileName, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { - + return parseHeaderFiles(openDTMgrs, filenames, null, args, dataFileName, monitor); } - + /** - * Parse a set of C Header files and associated parsing arguments, returning a new File Data TypeManager - * with in the provided dataFileName. + * Parse a set of C Header files and associated parsing arguments, returning a new + * File Data TypeManager with in the provided dataFileName. The resulting archive + * will not be associated with a specific language/compiler and will use the default + * data organization. * * Note: Using another open archive while parsing will cause: * - a dependence on the other archive @@ -312,19 +311,22 @@ public class CParserUtils { * @throws IOException if there io are errors saving the archive * */ - - public static FileDataTypeManager parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String includePaths[], String args[], String dataFileName, + + public static FileDataTypeManager parseHeaderFiles(DataTypeManager[] openDTMgrs, + String[] filenames, String[] includePaths, String[] args, String dataFileName, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException, IOException { - - return parseHeaderFiles(openDTMgrs, filenames, includePaths, args, dataFileName, null, null, monitor); + + return parseHeaderFiles(openDTMgrs, filenames, includePaths, args, dataFileName, null, null, + monitor); } - /** * Parse a set of C Header files and associated parsing arguments, returning a new File Data TypeManager * with in the provided dataFileName. * + * When parsing is complete any parser messages will be logged. + * * Note: Using another open archive while parsing will cause: * - a dependence on the other archive * - any missing data types while parsing are supplied if present from an openDTMgr @@ -333,7 +335,7 @@ public class CParserUtils { * * NOTE: This will only occur if the data type from the openDTMgr's is equivalent. * - * NOTE: Providing the correct languageID and compilerSpec is very important for header files that might use sizeof() + * NOTE: Providing the correct languageId and compileSpecId is very important for header files that might use sizeof() * * @param openDTMgrs array of datatypes managers to use for undefined data types * @@ -349,32 +351,43 @@ public class CParserUtils { * * @param monitor used to cancel or provide results * - * @return the data types in the ghidra .gdt archive file + * @return the FileDataTypeManager corresponding to the Ghidra .gdt archive file. + * The caller is responsible for closing the instance. * * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing * @throws IOException if there io are errors saving the archive * */ - public static FileDataTypeManager parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String includePaths[], String args[], String dataFileName, - String languageId, String compileSpecId, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, - ghidra.app.util.cparser.CPP.ParseException, IOException { + public static FileDataTypeManager parseHeaderFiles(DataTypeManager[] openDTMgrs, + String[] filenames, String[] includePaths, String[] args, String dataFileName, + String languageId, String compileSpecId, TaskMonitor monitor) + throws ghidra.app.util.cparser.C.ParseException, + ghidra.app.util.cparser.CPP.ParseException, IOException { - File file = new File(dataFileName); - FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(file); - - CParseResults results; - results = parseHeaderFiles(openDTMgrs, filenames, includePaths, args, dtMgr, languageId, compileSpecId, monitor); - - String messages = results.getFormattedParseMessage(null); - Msg.info(CParserUtils.class, messages); - - dtMgr.save(); - - return dtMgr; - } + File file = new File(dataFileName); + FileDataTypeManager dtMgr = + FileDataTypeManager.createFileArchive(file, languageId, compileSpecId); + boolean success = false; + try { + CParseResults results = + parseHeaderFiles(openDTMgrs, filenames, includePaths, args, dtMgr, monitor); + + String messages = results.getFormattedParseMessage(null); + Msg.info(CParserUtils.class, messages); + + dtMgr.save(); + + success = true; + return dtMgr; + } + finally { + if (!success) { + dtMgr.close(); + } + } + } - /** * Parse a set of C Header files and associated parsing arguments, data types are added to the provided * DTMgr. @@ -387,7 +400,6 @@ public class CParserUtils { * * NOTE: This will only occur if the data type from the openDTMgr's is equivalent. * - * NOTE: Providing the correct languageID and compilerSpec is very important for header files that might use sizeof() * @param openDTMgrs array of datatypes managers to use for undefined data types * * @param filenames names of files in order to parse, could include strings with @@ -395,10 +407,7 @@ public class CParserUtils { * @param args arguments for parsing, {@code -D=}, ({@code -I} use * includePaths parm instead) * - * @param existingDTMgr datatypes will be populated into this provided DTMgr, can pass Program or File DTMgr - * - * @param languageId language identification to use for data type organization definitions (int, long, ptr size) - * @param compileSpecId compiler specification to use for parsing + * @param dtMgr datatypes will be populated into this provided DTMgr, can pass Program or File DTMgr * * @param monitor used to cancel or provide results * @@ -409,86 +418,14 @@ public class CParserUtils { * @throws IOException if there io are errors saving the archive * */ - public static CParseResults parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String args[], DataTypeManager existingDTMgr, - String languageId, String compileSpecId, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, - ghidra.app.util.cparser.CPP.ParseException, IOException { - - return parseHeaderFiles(openDTMgrs, filenames, null, args, existingDTMgr, languageId, compileSpecId, monitor); - } - - /** - * Parse a set of C Header files and associated parsing arguments, data types are added to the provided - * DTMgr. - * - * Note: Using another open archive while parsing will cause: - * - a dependence on the other archive - * - any missing data types while parsing are supplied if present from an openDTMgr - * - after parsing all data types parsed with an equivalent data type in any openDTMgr - * replaced by the data type from the openDTMgr - * - * NOTE: This will only occur if the data type from the openDTMgr's is equivalent. - * - * NOTE: Providing the correct languageID and compilerSpec is very important for header files that might use sizeof() - * @param openDTMgrs array of datatypes managers to use for undefined data types - * - * @param filenames names of files in order to parse, could include strings with - * "#" at start, which are ignored as comments - * @param includePaths paths to include files, instead of using {@code -I} in args - * @param args arguments for parsing, {@code -D=}, ( {@code -I} use includePaths parm instead) - * - * @param existingDTMgr datatypes will be populated into this provided DTMgr, can pass Program or File DTMgr - * - * @param languageId language identification to use for data type organization definitions (int, long, ptr size) - * @param compileSpecId compiler specification to use for parsing - * - * @param monitor used to cancel or provide results - * - * @return a formatted string of any output from pre processor parsing or C parsing - * - * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing - * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing - * @throws IOException if there io are errors saving the archive - * - */ - public static CParseResults parseHeaderFiles(DataTypeManager openDTMgrs[], String[] filenames, String includePaths[], String args[], DataTypeManager existingDTMgr, - String languageId, String compileSpecId, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, - ghidra.app.util.cparser.CPP.ParseException, IOException { - - Language language = DefaultLanguageService.getLanguageService().getLanguage(new LanguageID(languageId)); - CompilerSpec compilerSpec = language.getCompilerSpecByID(new CompilerSpecID(compileSpecId)); + public static CParseResults parseHeaderFiles(DataTypeManager[] openDTMgrs, String[] filenames, + String[] args, DataTypeManager dtMgr, + TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, + ghidra.app.util.cparser.CPP.ParseException, IOException { + + return parseHeaderFiles(openDTMgrs, filenames, null, args, dtMgr, monitor); + } - if (existingDTMgr instanceof StandAloneDataTypeManager) { - try { - ((StandAloneDataTypeManager) existingDTMgr).setProgramArchitecture(language, compilerSpec.getCompilerSpecID(), - StandAloneDataTypeManager.LanguageUpdateOption.UNCHANGED, monitor); - } - catch (CompilerSpecNotFoundException e) { - e.printStackTrace(); - } - catch (LanguageNotFoundException e) { - e.printStackTrace(); - } - catch (CancelledException e) { - // ignore - } - catch (LockException e) { - e.printStackTrace(); - } - catch (UnsupportedOperationException e) { - e.printStackTrace(); - } - catch (IOException e) { - e.printStackTrace(); - } - catch (IncompatibleLanguageException e) { - // Shouldn't happen, unless already had a language - e.printStackTrace(); - } - } - - return parseHeaderFiles(openDTMgrs, filenames, includePaths, args, existingDTMgr, monitor); - } - /** * Parse a set of C Header files and associated parsing arguments, data types are added to the provided * DTMgr. @@ -522,8 +459,9 @@ public class CParserUtils { * @throws ghidra.app.util.cparser.C.ParseException for catastrophic errors in C parsing * @throws ghidra.app.util.cparser.CPP.ParseException for catastrophic errors in Preprocessor macro parsing */ - public static CParseResults parseHeaderFiles(DataTypeManager[] openDTmanagers, String[] filenames, String[] includePaths, - String args[], DataTypeManager dtMgr, TaskMonitor monitor) + public static CParseResults parseHeaderFiles(DataTypeManager[] openDTmanagers, + String[] filenames, String[] includePaths, + String[] args, DataTypeManager dtMgr, TaskMonitor monitor) throws ghidra.app.util.cparser.C.ParseException, ghidra.app.util.cparser.CPP.ParseException { @@ -534,9 +472,9 @@ public class CParserUtils { cpp.addIncludePaths(includePaths); PrintStream os = System.out; - + String fName = dtMgr.getName(); - + // make a path to tmpdir with name of data type manager String path = new File(Application.getUserTempDirectory(), fName).getAbsolutePath(); // if file data type manager, use path to .gdt file @@ -544,10 +482,11 @@ public class CParserUtils { path = ((FileDataTypeManager) dtMgr).getPath(); } path = path + "_CParser.out"; - + try { os = new PrintStream(new FileOutputStream(path)); - } catch (FileNotFoundException e2) { + } + catch (FileNotFoundException e2) { Msg.error(CParserUtils.class, "Unexpected Exception: " + e2.getMessage(), e2); } // cpp.setOutputStream(os); @@ -580,26 +519,29 @@ public class CParserUtils { parseFile(child.getAbsolutePath(), monitor, cpp); } } - } else { + } + else { parseFile(filename, monitor, cpp); } } parseSucceeded = true; - } catch (Throwable e) { + } + catch (Throwable e) { Msg.info(cpp, cpp.getParseMessages()); - } finally { + } + finally { System.out.println(bos); os.flush(); os.close(); System.setOut(old); - + } - + cppMessages = cpp.getParseMessages(); if (!parseSucceeded) { return new CParseResults(cpp, "", cppMessages, false); } - + // process all the defines and add any that are integer values into // the Equates table cpp.getDefinitions().populateDefineEquates(openDTmanagers, dtMgr); @@ -608,7 +550,7 @@ public class CParserUtils { boolean cparseSucceeded = false; if (!monitor.isCancelled()) { monitor.setMessage("Parsing C"); - + CParser cParser = new CParser(dtMgr, true, openDTmanagers); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); try { @@ -617,16 +559,18 @@ public class CParserUtils { cParser.setMonitor(monitor); cParser.parse(bis); cparseSucceeded = cParser.didParseSucceed(); - } catch (RuntimeException re) { + } + catch (RuntimeException re) { Msg.info(cpp, cpp.getParseMessages()); - } finally { + } + finally { parserMessages = cParser.getParseMessages(); } } - + return new CParseResults(cpp, parserMessages, cppMessages, cparseSucceeded); } - + private static String parseFile(String filename, TaskMonitor monitor, PreProcessor cpp) throws ghidra.app.util.cparser.CPP.ParseException { monitor.setMessage("PreProcessing " + filename); @@ -640,10 +584,10 @@ public class CParserUtils { throw new ghidra.app.util.cparser.CPP.ParseException(e.getMessage()); } - + return cpp.getParseMessages(); } - + private static DataTypeManager[] getDataTypeManagers(DataTypeManagerService service) { if (service == null) { @@ -782,54 +726,52 @@ public class CParserUtils { errorIndex + "
" + successFailureBuffer; } + public static File getFile(String parent, String filename) { + File file = findFile(parent, filename); + if (file != null) { + return file; + } + // filename lower + file = findFile(parent, filename.toLowerCase()); + if (file != null) { + return file; + } + // parent and filename lower + file = findFile(parent.toLowerCase(), filename.toLowerCase()); + if (file != null) { + return file; + } + // parent and filename upper + file = findFile(parent.toUpperCase(), filename.toUpperCase()); + return file; + } - public static File getFile(String parent, String filename) { - File file = findFile(parent, filename); - if (file != null) { - return file; - } - // filename lower - file = findFile(parent, filename.toLowerCase()); - if (file != null) { - return file; - } - // parent and filename lower - file = findFile(parent.toLowerCase(), filename.toLowerCase()); - if (file != null) { - return file; - } - // parent and filename upper - file = findFile(parent.toUpperCase(), filename.toUpperCase()); - return file; - } + private static File findFile(String parent, String filename) { + File iFile = null; - private static File findFile(String parent, String filename) { - File iFile = null; + iFile = new File(parent + File.separator + filename); + if (iFile.exists()) + return iFile; - iFile = new File(parent + File.separator + filename); - if (iFile.exists()) - return iFile; + // try just in this directory + File sameiFile = new File(parent + File.separator + (new File(filename)).getName()); + if (sameiFile.exists()) + return sameiFile; - // try just in this directory - File sameiFile = new File(parent + File.separator - + (new File(filename)).getName()); - if (sameiFile.exists()) - return sameiFile; + // try all files in this directory doing to-lower on both input file and output file + // if match return it + File folder = new File(parent); + if (folder.isDirectory()) { + File[] listOfFiles = folder.listFiles(); - // try all files in this directory doing to-lower on both input file and output file - // if match return it - File folder = new File(parent); - if (folder.isDirectory()) { - File[] listOfFiles = folder.listFiles(); - - if (listOfFiles != null) { - for (File file : listOfFiles) { - if (file.isFile() && filename.compareToIgnoreCase(file.getName()) == 0) { - return file; - } - } - } - } - return null; - } + if (listOfFiles != null) { + for (File file : listOfFiles) { + if (file.isFile() && filename.compareToIgnoreCase(file.getName()) == 0) { + return file; + } + } + } + } + return null; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java index 3cb3f27158..2dcb3adc31 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/FileDataTypeManager.java @@ -17,13 +17,16 @@ package ghidra.program.model.data; import java.io.File; import java.io.IOException; +import java.util.Objects; import generic.jar.ResourceFile; import ghidra.framework.data.OpenMode; +import ghidra.framework.store.LockException; import ghidra.framework.store.db.PackedDBHandle; import ghidra.framework.store.db.PackedDatabase; -import ghidra.program.model.lang.CompilerSpec; -import ghidra.program.model.lang.Language; +import ghidra.program.model.lang.*; +import ghidra.program.model.listing.IncompatibleLanguageException; +import ghidra.program.util.DefaultLanguageService; import ghidra.util.InvalidNameException; import ghidra.util.UniversalID; import ghidra.util.exception.*; @@ -101,6 +104,77 @@ public class FileDataTypeManager extends StandAloneDataTypeManager } } + /** + * Create a new data-type file archive using the default data organization. + * @param packedDbFile archive file (filename must end with DataTypeFileManager.SUFFIX) + * @param languageId valid language ID (see appropriate *.ldefs file for defined IDs) + * @param compilerSpecId valid compiler spec ID which corresponds to the language ID. + * @return data-type manager backed by the specified packedDbFile + * @returns DuplicateFileException if {@code packedDbFile} already exists + * @throws LanguageNotFoundException if specified {@code languageId} not defined. + * @throws CompilerSpecNotFoundException if specified {@code compilerSpecId} is not defined + * for the specified language. + * @throws IOException if an IO error occurs + */ + public static FileDataTypeManager createFileArchive(File packedDbFile, LanguageID languageId, + CompilerSpecID compilerSpecId) + throws LanguageNotFoundException, CompilerSpecNotFoundException, IOException { + Objects.requireNonNull(languageId, "missing required languageId"); + Objects.requireNonNull(compilerSpecId, "missing required compilerSpecId"); + try { + if (packedDbFile.exists()) { + throw new DuplicateFileException("File already exists: " + packedDbFile); + } + + // Verify that the specified language and compiler spec are valid + LanguageService languageService = DefaultLanguageService.getLanguageService(); + Language language = languageService.getLanguage(languageId); + language.getCompilerSpecByID(compilerSpecId); + + FileDataTypeManager dtm = + new FileDataTypeManager(new ResourceFile(packedDbFile), OpenMode.CREATE, + TaskMonitor.DUMMY); + + dtm.setProgramArchitecture(language, compilerSpecId, LanguageUpdateOption.CLEAR, + TaskMonitor.DUMMY); + + return dtm; + } + catch (CancelledException e) { + throw new AssertException(e); // unexpected without task monitor use + } + catch (LockException | IncompatibleLanguageException | UnsupportedOperationException e) { + throw new RuntimeException(e); // unexpected for new archive + } + } + + /** + * Create a new data-type file archive using the default data organization. + * @param packedDbfile archive file (filename must end with DataTypeFileManager.SUFFIX) + * @param languageId valid language ID (see appropriate *.ldefs file for defined IDs). If null + * invocation will be deferred to {@link #createFileArchive(File)}. + * @param compilerSpecId valid compiler spec ID which corresponds to the language ID. + * @return data-type manager backed by the specified packedDbFile + * @throws LanguageNotFoundException if specified {@code languageId} not defined. + * @throws CompilerSpecNotFoundException if specified {@code compilerSpecId} is not defined + * for the specified language. + * @throws IOException if an IO error occurs + */ + public static FileDataTypeManager createFileArchive(File packedDbfile, String languageId, + String compilerSpecId) throws IOException { + if (languageId == null) { + if (compilerSpecId != null) { + throw new IllegalArgumentException("compilerSpecId specified without languageId"); + } + return createFileArchive(packedDbfile); + } + if (compilerSpecId == null) { + throw new IllegalArgumentException("languageId specified without compilerSpecId"); + } + return createFileArchive(packedDbfile, new LanguageID(languageId), + new CompilerSpecID(compilerSpecId)); + } + /** * Open an existing data-type file archive using the default data organization. *

@@ -161,7 +235,6 @@ public class FileDataTypeManager extends StandAloneDataTypeManager public void saveAs(File saveFile, UniversalID newUniversalId) throws DuplicateFileException, IOException { ResourceFile resourceSaveFile = new ResourceFile(saveFile); -// TODO: this should really be a package method and not public! validateFilename(resourceSaveFile); try { universalID = newUniversalId; @@ -333,3 +406,4 @@ public class FileDataTypeManager extends StandAloneDataTypeManager return getClass().getSimpleName() + " - " + getName(); } } + diff --git a/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java b/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java index a6e58f445b..22f1645005 100644 --- a/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java +++ b/Ghidra/Processors/Atmel/ghidra_scripts/CreateAVR8GDTArchiveScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -32,9 +32,7 @@ import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; import ghidra.app.util.cparser.C.CParserUtils.CParseResults; -import ghidra.app.util.cparser.CPP.DefineTable; -import ghidra.app.util.cparser.CPP.ParseException; -import ghidra.app.util.cparser.CPP.PreProcessor; +import ghidra.app.util.cparser.CPP.*; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.FileDataTypeManager; import ghidra.program.util.AddressEvaluator; @@ -127,7 +125,8 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript { File f = getArchiveFile(dataTypeFile); - FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f); + FileDataTypeManager dtMgr = + FileDataTypeManager.createFileArchive(f, "avr8:LE:16:atmega256", "gcc"); // Parse each processor variant as an individual parse that gets added to the data // type manager. If all header files were parsed at once, there are conflicting @@ -178,7 +177,7 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript { String args[] = Arrays.append(orig_args, "-D__AVR_" + procName + "__"); CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, - "avr8:LE:16:atmega256", "gcc", monitor); + monitor); Msg.info(this, results.getFormattedParseMessage(null)); diff --git a/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java b/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java index 4091798a8f..902e89c12a 100644 --- a/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java +++ b/Ghidra/Processors/JVM/ghidra_scripts/CreateJNIGDTArchivesScript.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -33,11 +33,9 @@ import generic.jar.ResourceFile; import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility; import ghidra.app.script.GhidraScript; import ghidra.app.util.cparser.C.CParserUtils; -import ghidra.app.util.cparser.C.CParserUtils.CParseResults; import ghidra.app.util.cparser.C.ParseException; import ghidra.program.model.data.DataTypeManager; import ghidra.program.model.data.FileDataTypeManager; -import ghidra.util.Msg; public class CreateJNIGDTArchivesScript extends GhidraScript { @@ -63,13 +61,9 @@ public class CreateJNIGDTArchivesScript extends GhidraScript { File f = getArchiveFile(dataTypeFile); - FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f); - - CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, languageID, compiler, monitor); + FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames, null, args, + f.getAbsolutePath(), languageID, compiler, monitor); - Msg.info(this, results.getFormattedParseMessage(null)); - - dtMgr.save(); dtMgr.close(); }