Merge remote-tracking branch 'origin/patch'

This commit is contained in:
Ryan Kurtz 2024-12-03 10:15:21 -05:00
commit 16388dc261
11 changed files with 502 additions and 427 deletions

View file

@ -26,8 +26,7 @@ import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
import ghidra.framework.model.*;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressRangeImpl;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.trace.model.modules.TraceModule;
@ -73,8 +72,7 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
// TODO: Note language and prefer those from the same processor?
// Will get difficult with new OBTR, since I'd need a platform
// There's also the WoW64 issue....
protected record IndexEntry(String name, String dfID, NameSource source) {
}
protected record IndexEntry(String name, String dfID, NameSource source) {}
protected class ModuleChangeListener
implements DomainObjectListener, DomainObjectClosedListener {
@ -383,7 +381,11 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
public DomainFile getBestMatch(TraceModule module, Program currentProgram,
Collection<IndexEntry> entries) {
return getBestMatch(module.getBase().getAddressSpace(), module, currentProgram, entries);
Address base = module.getBase();
AddressSpace space = base == null
? module.getTrace().getBaseAddressFactory().getDefaultAddressSpace()
: base.getAddressSpace();
return getBestMatch(space, module, currentProgram, entries);
}
public List<IndexEntry> getBestEntries(TraceModule module) {

View file

@ -69,32 +69,7 @@ import ghidra.util.table.GhidraTable;
@Category(NightlyCategory.class)
public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerTest {
DebuggerModulesProvider provider;
protected TraceObjectModule modExe;
protected TraceObjectSection secExeText;
protected TraceObjectSection secExeData;
protected TraceObjectModule modLib;
protected TraceObjectSection secLibText;
protected TraceObjectSection secLibData;
protected SchemaContext ctx;
@Override
protected void createTrace(String langID) throws IOException {
super.createTrace(langID);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
public void activateObjectsMode() throws Exception {
// NOTE the use of index='1' allowing object-based managers to ID unique path
ctx = XmlSchemaContext.deserialize("""
public static final String CTX_XML = """
<context>
<schema name='Session' elementResync='NEVER' attributeResync='ONCE'>
<attribute name='Processes' schema='ProcessContainer' />
@ -129,7 +104,34 @@ public class DebuggerModulesProviderTest extends AbstractGhidraHeadedDebuggerTes
<schema name='Section' elementResync='NEVER' attributeResync='NEVER'>
<interface name='Section' />
</schema>
</context>""");
</context>""";
DebuggerModulesProvider provider;
protected TraceObjectModule modExe;
protected TraceObjectSection secExeText;
protected TraceObjectSection secExeData;
protected TraceObjectModule modLib;
protected TraceObjectSection secLibText;
protected TraceObjectSection secLibData;
protected SchemaContext ctx;
@Override
protected void createTrace(String langID) throws IOException {
super.createTrace(langID);
try {
activateObjectsMode();
}
catch (Exception e) {
throw new AssertionError(e);
}
}
public void activateObjectsMode() throws Exception {
// NOTE the use of index='1' allowing object-based managers to ID unique path
ctx = XmlSchemaContext.deserialize(CTX_XML);
try (Transaction tx = tb.startTransaction()) {
tb.trace.getObjectManager().createRootObject(ctx.getSchema(new SchemaName("Session")));

View file

@ -25,18 +25,26 @@ import org.junit.Test;
import db.Transaction;
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest;
import ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesProviderTest;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerStaticMappingService.MappedAddressRange;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.framework.model.DomainFile;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.memory.DBTraceMemoryManager;
import ghidra.trace.database.target.DBTraceObject;
import ghidra.trace.database.target.DBTraceObjectManager;
import ghidra.trace.model.*;
import ghidra.trace.model.memory.TraceMemoryFlag;
import ghidra.trace.model.memory.TraceMemoryRegion;
import ghidra.trace.model.modules.*;
import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.target.TraceObjectKeyPath;
import ghidra.util.Msg;
// Not technically a GUI test, but must be carried out in the context of a plugin tool
@ -667,4 +675,21 @@ public class DebuggerStaticMappingServiceTest extends AbstractGhidraHeadedDebugg
assertMapsTwoWay(Long.MAX_VALUE, Long.MAX_VALUE);
assertMapsTwoWay(Long.MIN_VALUE, Long.MIN_VALUE);
}
@Test
public void testProposeModuleMappingNullBase() throws Throwable {
DBTraceObject objModBash;
try (Transaction tx = tb.startTransaction()) {
SchemaContext ctx = XmlSchemaContext.deserialize(DebuggerModulesProviderTest.CTX_XML);
DBTraceObjectManager objects = tb.trace.getObjectManager();
objects.createRootObject(ctx.getSchema(new SchemaName("Session")));
objModBash =
objects.createObject(TraceObjectKeyPath.parse("Processes[1].Modules[/bin/bash]"));
objModBash.insert(Lifespan.nowOn(0), ConflictResolution.DENY);
}
TraceModule modBash = objModBash.queryInterface(TraceObjectModule.class);
assertEquals(Map.of(),
mappingService.proposeModuleMaps(List.of(modBash), List.of(program)));
}
}

View file

@ -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 {
@ -72,13 +69,9 @@ public class CreateDefaultGDTArchivesScript extends GhidraScript {
File f = getArchiveFile(dataTypeFile);
FileDataTypeManager dtMgr = FileDataTypeManager.createFileArchive(f);
FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames,
includePaths, args, f.getAbsolutePath(), languageID, compiler, monitor);
CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, includePaths, args, dtMgr, languageID, compiler, monitor);
Msg.info(this, results.getFormattedParseMessage(null));
dtMgr.save();
dtMgr.close();
}

View file

@ -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);
FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames,
includePaths, args, f.getAbsolutePath(), languageID, compiler, monitor);
CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, includePaths, args, dtMgr, languageID, compiler, monitor);
Msg.info(this, results.getFormattedParseMessage(null));
dtMgr.save();
dtMgr.close();
}

View file

@ -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);
args, dtMgr, monitor);
final boolean isProgramDtMgr = (dtMgr instanceof ProgramDataTypeManager);
return results;
}
SwingUtilities.invokeLater(() -> {
// CParserTask will show any errors
if (!results.successful()) {
return;
boolean isOpenInTool(DataTypeManager dtm) {
DataTypeManagerService dtService = tool.getService(DataTypeManagerService.class);
if (dtService == null) {
return false;
}
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);
for (DataTypeManager openDtm : dtService.getDataTypeManagers()) {
if (dtm == openDtm) {
return true;
}
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);
}
});
}
catch (IOException e) {
// ignore
}
return false;
}
/**
@ -329,9 +309,7 @@ public class CParserPlugin extends ProgramPlugin {
new CParserTask(this, currentProgram.getDataTypeManager())
.setFileNames(filenames)
.setIncludePaths(includePaths)
.setOptions(options)
.setLanguageID(procID)
.setCompilerID(compilerID);
.setOptions(options);
tool.execute(parseTask);
}

View file

@ -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;
private DataTypeManager dtMgr;
// Language and Compiler Spec IDs valid only for new dataFileName use
private String languageId;
private String compilerSpecId;
// 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) {
if (dataTypeManager != null) {
throw new UnsupportedOperationException(
"setLanguageID not supported when constructed with DataTypeManager");
}
public CParserTask setLanguageID(String languageID) {
this.languageString = languageID;
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;
}
@Override
public void run(TaskMonitor monitor) {
DataTypeManager fileDtMgr = null;
try {
if (dtMgr == null) {
File file = new File(dataFileName);
dtMgr = FileDataTypeManager.createFileArchive(file);
fileDtMgr = dtMgr;
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;
}
plugin.parse(filenames, includePaths, options, languageString, compilerString, dtMgr, monitor);
if (dataFileName != null) {
// TODO: does not consider existing datatypes
if (dtMgr.getDataTypeCount(true) != 0) {
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) {
FileDataTypeManager fileDtMgr = null;
if (dataFile != null) {
try {
((FileDataTypeManager) dtMgr).save();
dtMgr.close();
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;
}
}
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(), "Error During Save",
Msg.showError(this, plugin.getDialog().getComponent(),
"C-Parse 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();
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 {
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),
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 = 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),
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),
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),
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();
}
}
}
}

View file

@ -18,22 +18,15 @@ 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;
@ -43,7 +36,8 @@ public class 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 = "";
@ -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
@ -274,7 +270,8 @@ public class CParserUtils {
*
*/
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 {
@ -282,8 +279,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
@ -313,18 +312,21 @@ public class CParserUtils {
*
*/
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,31 +351,42 @@ 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,
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);
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
@ -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<defn>=}, ({@code -I<includepath>} 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,84 +418,12 @@ 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,
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, 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<includepath>} in args
* @param args arguments for parsing, {@code -D<defn>=}, ( {@code -I<includepath>} 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));
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);
return parseHeaderFiles(openDTMgrs, filenames, null, args, dtMgr, monitor);
}
/**
@ -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 {
@ -547,7 +485,8 @@ public class CParserUtils {
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,14 +519,17 @@ 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();
@ -617,9 +559,11 @@ 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();
}
}
@ -782,7 +726,6 @@ public class CParserUtils {
errorIndex + "<br>" + successFailureBuffer;
}
public static File getFile(String parent, String filename) {
File file = findFile(parent, filename);
if (file != null) {
@ -811,8 +754,7 @@ public class CParserUtils {
return iFile;
// try just in this directory
File sameiFile = new File(parent + File.separator
+ (new File(filename)).getName());
File sameiFile = new File(parent + File.separator + (new File(filename)).getName());
if (sameiFile.exists())
return sameiFile;

View file

@ -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.
* <p>
@ -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();
}
}

View file

@ -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));

View file

@ -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);
FileDataTypeManager dtMgr = CParserUtils.parseHeaderFiles(openTypes, filenames, null, args,
f.getAbsolutePath(), languageID, compiler, monitor);
CParseResults results = CParserUtils.parseHeaderFiles(openTypes, filenames, args, dtMgr, languageID, compiler, monitor);
Msg.info(this, results.getFormattedParseMessage(null));
dtMgr.save();
dtMgr.close();
}