mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-04-25 17:25:17 +02:00
Merge remote-tracking branch 'origin/GP-5838_ghidragander_calling-convention-junit--SQUASHED'
This commit is contained in:
File diff suppressed because it is too large
Load Diff
178
Ghidra/Features/Base/ghidra_scripts/TestPrototypeScript.java
Normal file
178
Ghidra/Features/Base/ghidra_scripts/TestPrototypeScript.java
Normal file
@@ -0,0 +1,178 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// This script uses the emulator to test a prototype defined in a cspec file. It is intended to be
|
||||
// run on programs produced by compiling a source file produced by the script
|
||||
// GeneratePrototypeTestFileScript.java. The program must have the same name as the source file
|
||||
// except without the .c suffix (e.g., program = test_file, source = test_file.c) and the two files
|
||||
// must reside in the same directory. The first time you run this file on a program, it will parse
|
||||
// the c source file and apply the correct data types and function definitions. If you run the
|
||||
// script without a selection it will test all test functions and print out which ones have
|
||||
// errors. If the script is run with a selection, it will print out detailed information about each
|
||||
// test function overlapping the selection (whether or not it has an error).
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.pcode.emu.EmulatorUtilities;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.InterruptPcodeExecutionException;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceIterator;
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTestUtil;
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTestUtil.TestResult;
|
||||
import ghidra.test.compilers.support.CSpecTestPCodeEmulator;
|
||||
import ghidra.util.DataConverter;
|
||||
|
||||
public class TestPrototypeScript extends GhidraScript {
|
||||
// Whether to print extra diagnostic information, such as the emulator's disassembly
|
||||
private static final boolean ENABLE_DEBUG_PRINTING = false;
|
||||
private static final int DEBUG_PRINTING_LEVEL = 3;
|
||||
|
||||
private DataConverter dataConverter;
|
||||
private LanguageCompilerSpecPair langCompPair;
|
||||
private boolean manualSelection = false;
|
||||
private CSpecTestPCodeEmulator emulator;
|
||||
private Consumer<String> logger = (msg -> printf(" %s\n", msg));
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
langCompPair = getLangCompPair(currentProgram);
|
||||
PrototypeModel model =
|
||||
CSpecPrototypeTestUtil.getProtoModelToTest(currentProgram, langCompPair);
|
||||
dataConverter = DataConverter.getInstance(langCompPair.getLanguage().isBigEndian());
|
||||
FunctionManager fManager = currentProgram.getFunctionManager();
|
||||
|
||||
CSpecPrototypeTestUtil.applyInfoFromSourceIfNeeded(currentProgram, model);
|
||||
|
||||
// Load program into emulator
|
||||
emulator =
|
||||
new CSpecTestPCodeEmulator(currentProgram.getLanguage(), !ENABLE_DEBUG_PRINTING,
|
||||
DEBUG_PRINTING_LEVEL, logger);
|
||||
EmulatorUtilities.loadProgram(emulator, currentProgram);
|
||||
|
||||
Iterator<Function> fIter = currentSelection == null ? fManager.getFunctionsNoStubs(true)
|
||||
: fManager.getFunctionsOverlapping(currentSelection);
|
||||
manualSelection = currentSelection != null;
|
||||
|
||||
List<Function> errors = new ArrayList<>();
|
||||
while (fIter.hasNext()) {
|
||||
Function caller = fIter.next();
|
||||
if (!(caller.getName().startsWith("params") || caller.getName().startsWith("return"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Function callee = CSpecPrototypeTestUtil.getFirstCall(caller);
|
||||
ArrayList<ParameterPieces> pieces =
|
||||
CSpecPrototypeTestUtil.getParameterPieces(caller, callee, model);
|
||||
Address breakpoint = null;
|
||||
if (caller.getName().startsWith("params")) {
|
||||
breakpoint = callee.getEntryPoint();
|
||||
}
|
||||
else {
|
||||
// find the address of the call to producer
|
||||
ReferenceIterator refIter =
|
||||
currentProgram.getReferenceManager().getReferencesTo(callee.getEntryPoint());
|
||||
if (!refIter.hasNext()) {
|
||||
throw new AssertionError(
|
||||
"no references to " + callee.getName() + " in " + caller.getName());
|
||||
}
|
||||
Reference ref = null;
|
||||
while (refIter.hasNext()) {
|
||||
Reference r = refIter.next();
|
||||
if (!r.getReferenceType().isCall()) {
|
||||
continue;
|
||||
}
|
||||
if (caller.getBody().contains(r.getFromAddress())) {
|
||||
ref = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ref == null) {
|
||||
throw new AssertionError(
|
||||
"call to " + callee.getName() + " not found in " + caller.getName());
|
||||
}
|
||||
Instruction afterCall =
|
||||
currentProgram.getListing().getInstructionAfter(ref.getFromAddress());
|
||||
// For architectures with a delay slot, break on the actual aftercall instruction,
|
||||
// by stepping instructions until we are out of the delay slot.
|
||||
while (afterCall.isInDelaySlot()) {
|
||||
afterCall = afterCall.getNext();
|
||||
}
|
||||
breakpoint = afterCall.getAddress();
|
||||
|
||||
}
|
||||
|
||||
boolean error = testFunction(caller, callee, breakpoint, pieces);
|
||||
if (error) {
|
||||
errors.add(caller);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.size() == 0) {
|
||||
printf("No prototype errors found.\n");
|
||||
return;
|
||||
}
|
||||
printf("%d prototype error(s) found:\n", errors.size());
|
||||
for (Function errFunc : errors) {
|
||||
printf(" %s\n", errFunc.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean testFunction(Function caller, Function callee, Address breakPoint,
|
||||
ArrayList<ParameterPieces> pieces) throws Exception {
|
||||
|
||||
List<byte[]> groundTruth =
|
||||
CSpecPrototypeTestUtil.getPassedValues(callee, pieces, dataConverter, logger);
|
||||
|
||||
// breakpoint will be skipped if condition is false, so add condition that is always true
|
||||
emulator.addBreakpoint(breakPoint, "1:1");
|
||||
|
||||
PcodeThread<byte[]> emuThread = emulator.prepareFunction(caller);
|
||||
|
||||
Register stackReg = caller.getProgram().getCompilerSpec().getStackPointer();
|
||||
|
||||
try {
|
||||
emuThread.run();
|
||||
printerr("Emulator should have hit breakpoint");
|
||||
}
|
||||
catch (InterruptPcodeExecutionException e) {
|
||||
// this is the breakpoint, which is what we want to happen
|
||||
}
|
||||
|
||||
List<byte[]> fromEmulator = new ArrayList<>();
|
||||
for (ParameterPieces piece : pieces) {
|
||||
fromEmulator.add(CSpecPrototypeTestUtil.readParameterPieces(emuThread, piece,
|
||||
emulator.getLanguage().getDefaultDataSpace(), stackReg, langCompPair,
|
||||
dataConverter));
|
||||
}
|
||||
|
||||
TestResult result =
|
||||
CSpecPrototypeTestUtil.getTestResult(callee, caller, pieces, fromEmulator, groundTruth);
|
||||
|
||||
if (manualSelection) {
|
||||
printf("%s\n", result.message());
|
||||
}
|
||||
return result.hasError();
|
||||
|
||||
}
|
||||
|
||||
private LanguageCompilerSpecPair getLangCompPair(Program program) {
|
||||
return program.getLanguageCompilerSpecPair();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.compilers.support;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
|
||||
import ghidra.app.util.importer.ProgramLoader;
|
||||
import ghidra.app.util.opinion.LoadResults;
|
||||
import ghidra.base.project.GhidraProject;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.pcode.emu.EmulatorUtilities;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.InterruptPcodeExecutionException;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceIterator;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTestUtil.TestResult;
|
||||
import ghidra.util.DataConverter;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* <code>CSpecPrototypeTest</code> provides an abstract JUnit test implementation
|
||||
* for processor-specific and compiler-specific calling convention test cases.
|
||||
*
|
||||
* Tests which extend this class must implement abstract functions to specify LANGUAGE_ID,
|
||||
* COMPILER_SPEC_ID, and CALLING_CONVENTION.
|
||||
*
|
||||
* An optional list of function names that contain errors can be passed to the constructor to
|
||||
* designate those errors as expected. The test will pass as long as only expected errors are found.
|
||||
*
|
||||
* Source and binary files have a naming scheme.
|
||||
* (LANGUAGE_ID)_(COMPILER_SPEC_ID)_(CALLING_CONVENTION)
|
||||
*
|
||||
* Trace logging is disabled by default. Specific traceLevel and traceLog disabled controlled via
|
||||
* environment properties CSpecTestTraceLevel and EmuTestTraceDisable.
|
||||
*
|
||||
* To create a new CSpecPrototypeTest for a given Module (e.g. Processors x86) complete the
|
||||
* following steps:
|
||||
*
|
||||
* 1. Generate source code using Ghidra and the Ghidra script "GeneratePrototypeTestFileScript".
|
||||
* NOTE: Do not rename the generated file; the filename is required for the test suit.
|
||||
* 2. Compile the source code using the following recommended GCC flags:
|
||||
* gcc -O1 -c -fno-inline -fno-leading-underscore -o filename_without_extension filename.c
|
||||
* 3. Place the source code and compiled binary in the module's "data/cspectests" directory or the
|
||||
* ghidra.bin repository in the directory: "Ghidra/Test/TestResources/data/cspectests"
|
||||
* 4. Add a new package named "ghidra.test.processors.cspec" to the module if it does not exist and
|
||||
* place all new CSpecTest's in this package.
|
||||
* 5. New CSpecTests should extend this class and have a class name which ends in 'CSpecTest' and
|
||||
* starts with processor details that indicate what cspec prototype is being tested.
|
||||
* - Implement abstract methods for Language ID, Compiler Spec ID, and Calling Convention.
|
||||
* 6. Use Ghidra and the Ghidra script "TestPrototypeScript" to debug errors.
|
||||
* - Click function links in the Script Console to jump to the Listing View.
|
||||
* - To isolate a single function, highlight it in the Listing and re-run the script
|
||||
* for detailed debug output.
|
||||
*
|
||||
* */
|
||||
public abstract class CSpecPrototypeTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
private static final String EMULATOR_TRACE_DISABLE_PROPERTY = "CSpecTestTraceDisable";
|
||||
private static final String EMULATOR_TRACE_LEVEL_PROPERTY = "CSpecTestTraceLevel";
|
||||
|
||||
// If cspectests data directory can not be found for the module containing the junit test,
|
||||
// This default ProcessorTest module will be searched instead.
|
||||
private static final String DEFAULT_PROCESSOR_TEST_MODULE = "Test/TestResources"; // module path relative to the Ghidra directory
|
||||
|
||||
private static final String TEST_RESOURCE_PATH = "data/cspectests/";
|
||||
|
||||
private TestEnv env;
|
||||
|
||||
private DataConverter dataConverter;
|
||||
private LanguageCompilerSpecPair langCompPair;
|
||||
private CSpecTestPCodeEmulator emulator;
|
||||
private Program currentProgram;
|
||||
|
||||
private final String languageId;
|
||||
private final String compilerSpecId;
|
||||
private final String testExecutableFileName;
|
||||
|
||||
private Collection<ResourceFile> applicationRootDirectories;
|
||||
private File resourcesTestDataDir;
|
||||
|
||||
private final String[] EXPECTED_PROTOTYPE_ERRORS;
|
||||
|
||||
private static boolean traceDisabled = true;
|
||||
private static int traceLevel = 3; // 0:disabled 1:Instruction 2:RegisterState 3:Reads-n-Writes
|
||||
|
||||
static {
|
||||
if (System.getProperty(EMULATOR_TRACE_DISABLE_PROPERTY) != null) {
|
||||
traceDisabled = Boolean.getBoolean(EMULATOR_TRACE_DISABLE_PROPERTY);
|
||||
}
|
||||
}
|
||||
|
||||
protected CSpecPrototypeTest() throws Exception {
|
||||
this(new String[] {});
|
||||
}
|
||||
|
||||
protected CSpecPrototypeTest(String[] expectedPrototypeErrors) throws Exception {
|
||||
languageId = getLanguageID();
|
||||
compilerSpecId = getCompilerSpecID();
|
||||
testExecutableFileName = this.languageId.toString().replace(":", "_") + "_" +
|
||||
this.compilerSpecId + "_" + getCallingConvention();
|
||||
EXPECTED_PROTOTYPE_ERRORS = expectedPrototypeErrors;
|
||||
|
||||
if (System.getProperty(EMULATOR_TRACE_DISABLE_PROPERTY) == null) {
|
||||
traceDisabled = true;
|
||||
}
|
||||
|
||||
String levelStr = System.getProperty(EMULATOR_TRACE_LEVEL_PROPERTY);
|
||||
if (levelStr != null) {
|
||||
traceLevel = Integer.parseInt(levelStr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ran before every test to prepare Ghidra for testing.
|
||||
* @throws Exception when the test environment fails to be created or the emulator fails to
|
||||
* load the program.
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
env = new TestEnv(10, "CSpec Prototype Tests");
|
||||
applicationRootDirectories = Application.getApplicationRootDirectories();
|
||||
|
||||
ResourceFile myModuleRootDirectory =
|
||||
Application.getModuleContainingClass(getClass());
|
||||
if (myModuleRootDirectory != null) {
|
||||
File myModuleRoot = myModuleRootDirectory.getFile(false);
|
||||
if (myModuleRoot != null) {
|
||||
resourcesTestDataDir = new File(myModuleRoot, TEST_RESOURCE_PATH);
|
||||
if (!resourcesTestDataDir.isDirectory()) {
|
||||
findTestResourceDirectory(getRelativeModulePath(myModuleRootDirectory));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Msg.warn(this,
|
||||
"Unable to identify pcodetest module directory! Project must contain Module.manifest file");
|
||||
}
|
||||
|
||||
if (resourcesTestDataDir == null || !resourcesTestDataDir.isDirectory()) {
|
||||
findTestResourceDirectory(DEFAULT_PROCESSOR_TEST_MODULE);
|
||||
}
|
||||
|
||||
Msg.info(this,
|
||||
"Locating " + testExecutableFileName + " C-Spec Prototype test binaries in: " +
|
||||
resourcesTestDataDir.getPath());
|
||||
|
||||
GhidraProject project = env.getGhidraProject();
|
||||
|
||||
File binaryFile = new File(resourcesTestDataDir + File.separator + testExecutableFileName);
|
||||
|
||||
LanguageService languageService = DefaultLanguageService.getLanguageService();
|
||||
Language language = languageService.getLanguage(new LanguageID(languageId));
|
||||
CompilerSpec compilerSpec =
|
||||
language.getCompilerSpecByID(new CompilerSpecID(compilerSpecId));
|
||||
|
||||
LoadResults<Program> loadResults = ProgramLoader.builder()
|
||||
.source(binaryFile)
|
||||
.project(project.getProject())
|
||||
.language(language)
|
||||
.compiler(compilerSpec)
|
||||
.monitor(TaskMonitor.DUMMY)
|
||||
.load();
|
||||
|
||||
currentProgram = loadResults.getPrimaryDomainObject(this);
|
||||
|
||||
currentProgram.startTransaction("Analysis");
|
||||
AutoAnalysisManager aam = AutoAnalysisManager.getAnalysisManager(currentProgram);
|
||||
aam.initializeOptions();
|
||||
aam.reAnalyzeAll(null);
|
||||
aam.startAnalysis(TaskMonitor.DUMMY);
|
||||
|
||||
langCompPair = currentProgram.getLanguageCompilerSpecPair();
|
||||
dataConverter = DataConverter.getInstance(langCompPair.getLanguage().isBigEndian());
|
||||
|
||||
// Load program into emulator
|
||||
emulator =
|
||||
new CSpecTestPCodeEmulator(currentProgram.getLanguage(), traceDisabled, traceLevel);
|
||||
EmulatorUtilities.loadProgram(emulator, currentProgram);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
Msg.info(this, "Disposing of testing environment.");
|
||||
|
||||
if (env != null) {
|
||||
env.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that for a given binary and source code all functions in the binary are
|
||||
* interpreted correctly by Ghidra using cspec files for the given calling convention.
|
||||
* @throws Exception when the Prototype cannot be established, the source code could not be
|
||||
* parsed correctly, or the test could not be completed.
|
||||
*/
|
||||
@Test
|
||||
public void prototypeTest() throws Exception {
|
||||
PrototypeModel model =
|
||||
CSpecPrototypeTestUtil.getProtoModelToTest(currentProgram, langCompPair);
|
||||
FunctionManager fManager = currentProgram.getFunctionManager();
|
||||
|
||||
Msg.info(this, "Locating C-Spec Prototype test source in: " +
|
||||
currentProgram.getExecutablePath());
|
||||
CSpecPrototypeTestUtil.applyInfoFromSourceIfNeeded(currentProgram, model);
|
||||
|
||||
Iterator<Function> fIter = fManager.getFunctionsNoStubs(true);
|
||||
|
||||
List<Function> errors = new ArrayList<>();
|
||||
while (fIter.hasNext()) {
|
||||
Function caller = fIter.next();
|
||||
if (!(caller.getName().startsWith("params") || caller.getName().startsWith("return"))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Function callee = CSpecPrototypeTestUtil.getFirstCall(caller);
|
||||
ArrayList<ParameterPieces> pieces =
|
||||
CSpecPrototypeTestUtil.getParameterPieces(caller, callee, model);
|
||||
Address breakpoint = null;
|
||||
if (caller.getName().startsWith("params")) {
|
||||
breakpoint = callee.getEntryPoint();
|
||||
}
|
||||
else {
|
||||
// find the address of the call to producer
|
||||
ReferenceIterator refIter =
|
||||
currentProgram.getReferenceManager().getReferencesTo(callee.getEntryPoint());
|
||||
if (!refIter.hasNext()) {
|
||||
throw new AssertionError(
|
||||
"no references to " + callee.getName() + " in " + caller.getName());
|
||||
}
|
||||
Reference ref = null;
|
||||
while (refIter.hasNext()) {
|
||||
Reference r = refIter.next();
|
||||
if (!r.getReferenceType().isCall()) {
|
||||
continue;
|
||||
}
|
||||
if (caller.getBody().contains(r.getFromAddress())) {
|
||||
ref = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ref == null) {
|
||||
throw new AssertionError(
|
||||
"call to " + callee.getName() + " not found in " + caller.getName());
|
||||
}
|
||||
Instruction afterCall =
|
||||
currentProgram.getListing().getInstructionAfter(ref.getFromAddress());
|
||||
// For architectures with a delay slot, break on the actual aftercall instruction,
|
||||
// by stepping instructions until we are out of the delay slot.
|
||||
while (afterCall.isInDelaySlot()) {
|
||||
afterCall = afterCall.getNext();
|
||||
}
|
||||
breakpoint = afterCall.getAddress();
|
||||
|
||||
}
|
||||
|
||||
boolean error = testFunction(caller, callee, breakpoint, pieces);
|
||||
|
||||
if (error) {
|
||||
errors.add(caller);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.size() == 0) {
|
||||
Msg.info(this, "No prototype errors found.");
|
||||
}
|
||||
else {
|
||||
Msg.info(this, errors.size() + " prototype error(s) found:");
|
||||
for (Function errFunc : errors) {
|
||||
Msg.info(this, "\t" + errFunc.getName());
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> actualErrors = errors.stream()
|
||||
.map(Function::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<String> expectedErrors = Set.of(EXPECTED_PROTOTYPE_ERRORS);
|
||||
|
||||
List<String> missingErrors = expectedErrors.stream()
|
||||
.filter(name -> !actualErrors.contains(name))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<String> unexpectedErrors = actualErrors.stream()
|
||||
.filter(name -> !expectedErrors.contains(name))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertTrue(
|
||||
"The following prototype errors were expected, but no corresponding error was found: " +
|
||||
missingErrors,
|
||||
missingErrors.isEmpty());
|
||||
|
||||
assertTrue(
|
||||
"The following prototype errors were found, but they were not in the expected list: " +
|
||||
unexpectedErrors,
|
||||
unexpectedErrors.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the 'expected' parameters to the 'from emulator' parameters of a function call
|
||||
* to determine if the binary was correctly interpreted by Ghidra using the cspec file for the
|
||||
* specified calling convention.
|
||||
* @param caller function calling the function to be tested
|
||||
* @param callee function that is being tested, called by the caller.
|
||||
* @param breakPoint Address to stop the emulator.
|
||||
* @param pieces ArrayList<ParameterPieces> parameter pieces gathered from parsing the binary's
|
||||
* source code.
|
||||
* @return boolean indicating the result of the test
|
||||
* @throws Exception when there's a problem establishing expected parameter or getting parameter
|
||||
* pieces from the emulator.
|
||||
*/
|
||||
private boolean testFunction(Function caller, Function callee, Address breakPoint,
|
||||
ArrayList<ParameterPieces> pieces) throws Exception {
|
||||
|
||||
List<byte[]> groundTruth =
|
||||
CSpecPrototypeTestUtil.getPassedValues(callee, pieces, dataConverter,
|
||||
(msg -> Msg.warn(this, msg)));
|
||||
|
||||
// breakpoint will be skipped if condition is false, so add condition that is always true
|
||||
emulator.addBreakpoint(breakPoint, "1:1");
|
||||
|
||||
PcodeThread<byte[]> emuThread = emulator.prepareFunction(caller);
|
||||
|
||||
Register stackReg = caller.getProgram().getCompilerSpec().getStackPointer();
|
||||
|
||||
try {
|
||||
emuThread.run();
|
||||
Msg.error(this, "Emulator should have hit breakpoint");
|
||||
}
|
||||
catch (InterruptPcodeExecutionException e) {
|
||||
// this is the breakpoint, which is what we want to happen
|
||||
}
|
||||
|
||||
List<byte[]> fromEmulator = new ArrayList<>();
|
||||
for (ParameterPieces piece : pieces) {
|
||||
fromEmulator.add(CSpecPrototypeTestUtil.readParameterPieces(emuThread, piece,
|
||||
emulator.getLanguage().getDefaultDataSpace(), stackReg, langCompPair,
|
||||
dataConverter));
|
||||
}
|
||||
|
||||
TestResult result =
|
||||
CSpecPrototypeTestUtil.getTestResult(callee, caller, pieces, fromEmulator, groundTruth);
|
||||
|
||||
if (result.hasError()) {
|
||||
Msg.info(this, result.message());
|
||||
}
|
||||
|
||||
return result.hasError();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource directory for the test, this is where a binary and it's source code
|
||||
* should be located.
|
||||
* @param relativeModulePath directory of the module that contains this class
|
||||
*/
|
||||
private void findTestResourceDirectory(String relativeModulePath) {
|
||||
if (relativeModulePath == null) {
|
||||
return;
|
||||
}
|
||||
for (ResourceFile appRoot : applicationRootDirectories) {
|
||||
File moduleRoot = new File(appRoot.getAbsolutePath(), relativeModulePath);
|
||||
File dir = new File(moduleRoot, TEST_RESOURCE_PATH);
|
||||
if (dir.isDirectory()) {
|
||||
resourcesTestDataDir = dir;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the path of the test module for the purposes of finding a binary and source code to use with
|
||||
* the test.
|
||||
* @param myModuleRootDirectory directory of the root of the module that contains this class
|
||||
* @return String
|
||||
*/
|
||||
private String getRelativeModulePath(ResourceFile myModuleRootDirectory) {
|
||||
String absolutePath = myModuleRootDirectory.getAbsolutePath();
|
||||
for (ResourceFile appRoot : applicationRootDirectories) {
|
||||
String rootPath = appRoot.getAbsolutePath();
|
||||
if (absolutePath.startsWith(rootPath)) {
|
||||
return absolutePath.substring(rootPath.length() + 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String Language ID
|
||||
*/
|
||||
public abstract String getLanguageID();
|
||||
|
||||
/**
|
||||
* @return String Compiler Spec ID
|
||||
*/
|
||||
public abstract String getCompilerSpecID();
|
||||
|
||||
/**
|
||||
* @return String Calling Convention
|
||||
*/
|
||||
public abstract String getCallingConvention();
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.compilers.support;
|
||||
|
||||
/**
|
||||
* Constants that are used by the CSpecPrototypeUtil to decode cspec test binary source code function
|
||||
* names.
|
||||
*/
|
||||
public class CSpecPrototypeTestConstants {
|
||||
public static final String FIELD_NAME_PREFIX = "fld";
|
||||
|
||||
public static final String STRUCT_CHAR_SINGLETON_NAME = "sc";
|
||||
public static final String STRUCT_SHORT_SINGLETON_NAME = "ss";
|
||||
public static final String STRUCT_INT_SINGLETON_NAME = "si";
|
||||
public static final String STRUCT_LONG_SINGLETON_NAME = "sl";
|
||||
public static final String STRUCT_LONG_LONG_SINGLETON_NAME = "sll";
|
||||
public static final String STRUCT_FLOAT_SINGLETON_NAME = "sf";
|
||||
public static final String STRUCT_DOUBLE_SINGLETON_NAME = "sd";
|
||||
|
||||
public static final String STRUCT_CHAR_PAIR_NAME = "prc";
|
||||
public static final String STRUCT_SHORT_PAIR_NAME = "prs";
|
||||
public static final String STRUCT_INT_PAIR_NAME = "pri";
|
||||
public static final String STRUCT_LONG_PAIR_NAME = "prl";
|
||||
public static final String STRUCT_LONG_LONG_PAIR_NAME = "prll";
|
||||
public static final String STRUCT_FLOAT_PAIR_NAME = "prf";
|
||||
public static final String STRUCT_DOUBLE_PAIR_NAME = "prd";
|
||||
|
||||
public static final String STRUCT_CHAR_TRIP_NAME = "trc";
|
||||
public static final String STRUCT_SHORT_TRIP_NAME = "trs";
|
||||
public static final String STRUCT_INT_TRIP_NAME = "tri";
|
||||
public static final String STRUCT_LONG_TRIP_NAME = "trl";
|
||||
public static final String STRUCT_LONG_LONG_TRIP_NAME = "trll";
|
||||
public static final String STRUCT_FLOAT_TRIP_NAME = "trf";
|
||||
public static final String STRUCT_DOUBLE_TRIP_NAME = "trd";
|
||||
|
||||
public static final String STRUCT_CHAR_QUAD_NAME = "qc";
|
||||
public static final String STRUCT_SHORT_QUAD_NAME = "qs";
|
||||
public static final String STRUCT_INT_QUAD_NAME = "qi";
|
||||
public static final String STRUCT_LONG_QUAD_NAME = "ql";
|
||||
public static final String STRUCT_LONG_LONG_QUAD_NAME = "qll";
|
||||
public static final String STRUCT_FLOAT_QUAD_NAME = "qf";
|
||||
public static final String STRUCT_DOUBLE_QUAD_NAME = "qd";
|
||||
|
||||
public static final String STRUCT_INT_LONG_INT = "stili";
|
||||
public static final String STRUCT_FLOAT_INT_FLOAT = "stfif";
|
||||
public static final String STRUCT_LONG_DOUBLE_LONG = "stldl";
|
||||
public static final String STRUCT_FLOAT_DOUBLE_FLOAT = "stfdf";
|
||||
|
||||
public static final String UNION_CHAR = "unsc";
|
||||
public static final String UNION_SHORT = "unss";
|
||||
public static final String UNION_INT = "unsi";
|
||||
public static final String UNION_LONG = "unsl";
|
||||
public static final String UNION_FLOAT = "unsf";
|
||||
public static final String UNION_DOUBLE = "unsd";
|
||||
public static final String UNION_LONG_LONG = "unsll";
|
||||
|
||||
public static final String UNION_INT_LONG = "unpil";
|
||||
public static final String UNION_FLOAT_DOUBLE = "unpfd";
|
||||
public static final String UNION_INT_FLOAT = "unpif";
|
||||
public static final String UNION_LONG_DOUBLE = "unpld";
|
||||
public static final String UNION_INT_DOUBLE = "unpid";
|
||||
public static final String UNION_LONG_FLOAT = "unplf";
|
||||
|
||||
public static final String UNION_STRUCT_INT = "unsti";
|
||||
public static final String UNION_STRUCT_FLOAT = "unstf";
|
||||
public static final String UNION_MIXED_STRUCT_INTEGRAL = "unmsti";
|
||||
public static final String UNION_MIXED_STRUCT_FLOATING = "unmstf";
|
||||
public static final String UNION_MIXED_STRUCT_ALL_SMALL = "unmstas";
|
||||
public static final String UNION_MIXED_STRUCT_ALL_LARGE = "unmstal";
|
||||
|
||||
public static final String UNION_STRUCT_TRIP_CHAR = "unsttc";
|
||||
public static final String UNION_STRUCT_TRIP_SHORT = "unstts";
|
||||
|
||||
public static final String PARAMS_PRIMITIVE_IDENTICAL = "paramsPrimitiveIdentical";
|
||||
public static final String PARAMS_PRIMITIVE_ALTERNATE = "paramsPrimitiveAlternate";
|
||||
public static final String PARAMS_MISC = "paramsMisc";
|
||||
public static final String PARAMS_VARIADIC = "paramsVariadic";
|
||||
public static final String PARAMS_SINGLETON_STRUCT = "paramsSingletonStruct";
|
||||
public static final String PARAMS_PAIR_STRUCT = "paramsPairStruct";
|
||||
public static final String PARAMS_TRIP_STRUCT = "paramsTripStruct";
|
||||
public static final String PARAMS_QUAD_STRUCT = "paramsQuadStruct";
|
||||
public static final String PARAMS_MIXED_STRUCT = "paramsMixedStruct";
|
||||
public static final String PARAMS_UNION = "paramsUnion";
|
||||
public static final String PRODUCER = "producer";
|
||||
public static final String EXTERNAL = "external";
|
||||
|
||||
public static final String RETURN_PRIMITIVE = "returnPrimitive";
|
||||
public static final String RETURN_SINGLETON = "returnSingleton";
|
||||
public static final String RETURN_PAIR = "returnPair";
|
||||
public static final String RETURN_TRIPLE = "returnTriple";
|
||||
public static final String RETURN_QUAD = "returnQuad";
|
||||
public static final String RETURN_MIXED = "returnMixed";
|
||||
public static final String RETURN_UNION = "returnUnion";
|
||||
}
|
||||
@@ -0,0 +1,817 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.compilers.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import com.google.common.primitives.*;
|
||||
|
||||
import ghidra.app.cmd.function.ApplyFunctionDataTypesCmd;
|
||||
import ghidra.app.util.cparser.C.CParserUtils;
|
||||
import ghidra.app.util.cparser.C.ParseException;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.floatformat.FloatFormat;
|
||||
import ghidra.pcode.floatformat.FloatFormatFactory;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.pcode.HighFunctionDBUtil;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.DataConverter;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* Utility for testing prototype models defined in cspec files.
|
||||
*/
|
||||
public class CSpecPrototypeTestUtil {
|
||||
|
||||
private static HexFormat hexFormat = HexFormat.of();
|
||||
|
||||
public static record TestResult(String message, boolean hasError) {}
|
||||
|
||||
/**
|
||||
* Returns a byte array that represents parameters from the emulator.
|
||||
* This data is used to compare against the representation of parameters constructed from
|
||||
* the binary's source code.
|
||||
* @param emulatorThread the active emulator thread to inspect
|
||||
* @param piece basic elements of a parameter
|
||||
* @param addrSpace the address space where the stack resides
|
||||
* @param stackReg the register acting as the stack pointer
|
||||
* @param langCompPair the language/compiler specification pair
|
||||
* @param dataConverter used to determine endianness and convert data to byte representation.
|
||||
* @return byte[] byte array representation of parameter pieces
|
||||
* @throws Exception if there is a problem reading the emulator stack or getting the correct
|
||||
* language.
|
||||
*/
|
||||
public static byte[] readParameterPieces(PcodeThread<byte[]> emulatorThread,
|
||||
ParameterPieces piece,
|
||||
AddressSpace addrSpace, Register stackReg, LanguageCompilerSpecPair langCompPair,
|
||||
DataConverter dataConverter)
|
||||
throws Exception {
|
||||
if (piece.type instanceof VoidDataType) {
|
||||
return new byte[0];
|
||||
}
|
||||
if (piece.joinPieces != null) {
|
||||
Varnode[] varnodes = piece.joinPieces.clone();
|
||||
if (!langCompPair.getLanguage().isBigEndian()) {
|
||||
ArrayUtils.reverse(varnodes); // probably correct...
|
||||
}
|
||||
byte[] bytes = null;
|
||||
for (Varnode vn : varnodes) {
|
||||
byte[] varnodeBytes = null;
|
||||
|
||||
if (vn.getAddress().isStackAddress()) {
|
||||
varnodeBytes =
|
||||
readEmulatorStack(emulatorThread, stackReg, addrSpace, (int) vn.getOffset(),
|
||||
vn.getSize(), dataConverter);
|
||||
}
|
||||
else {
|
||||
varnodeBytes =
|
||||
readEmulatorMemory(emulatorThread, vn.getAddress(), vn.getSize());
|
||||
}
|
||||
bytes = ArrayUtils.addAll(bytes, varnodeBytes);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
int dataTypeSize = piece.type.getLength();
|
||||
byte[] bytes = null;
|
||||
if (piece.address.isStackAddress()) {
|
||||
bytes =
|
||||
readEmulatorStack(emulatorThread, stackReg, addrSpace,
|
||||
(int) piece.address.getOffset(),
|
||||
dataTypeSize, dataConverter);
|
||||
}
|
||||
else {
|
||||
bytes = readEmulatorMemory(emulatorThread, piece.address, dataTypeSize);
|
||||
}
|
||||
if ((piece.hiddenReturnPtr || piece.isIndirect) && (piece.address != null)) {
|
||||
// value is an address, we need to verify the value stored there
|
||||
if (!(piece.type instanceof PointerDataType pointerType)) {
|
||||
return bytes;
|
||||
}
|
||||
if (!langCompPair.getLanguage().isBigEndian()) {
|
||||
ArrayUtils.reverse(bytes);
|
||||
}
|
||||
long offset = -1;
|
||||
switch (bytes.length) {
|
||||
case 2:
|
||||
offset = Shorts.fromByteArray(bytes);
|
||||
break;
|
||||
case 4:
|
||||
offset = Ints.fromByteArray(bytes);
|
||||
break;
|
||||
case 8:
|
||||
offset = Longs.fromByteArray(bytes);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("unsupported size: " + bytes.length);
|
||||
}
|
||||
Address addr = addrSpace.getAddress(offset);
|
||||
DataType base = pointerType.getDataType();
|
||||
bytes = readEmulatorMemory(emulatorThread, addr, base.getLength());
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from emulator's memory at the given Address and for the given size and returns as
|
||||
* a byte array.
|
||||
* @param emulatorThread the active emulator thread to inspect
|
||||
* @param address Address of memory to read
|
||||
* @param size Size of memory chunk to read
|
||||
* @return byte[] containing the data read from the emulator's memory
|
||||
*/
|
||||
public static byte[] readEmulatorMemory(PcodeThread<byte[]> emulatorThread, Address address,
|
||||
int size) {
|
||||
return emulatorThread.getState().getVar(address, size, false, Reason.INSPECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from the emulator's stack memory by resolving the current stack pointer
|
||||
* and applying a specified offset.
|
||||
* @param emulatorThread the active emulator thread to inspect
|
||||
* @param stackReg the register acting as the stack pointer
|
||||
* @param addrSpace the address space where the stack resides
|
||||
* @param offset the byte offset from the stack pointer
|
||||
* @param size the number of bytes to read from the stack
|
||||
* @param dataConverter the converter used to interpret the stack pointer's endianness
|
||||
* @return byte[] containing the data read from the emulator's memory
|
||||
* @throws Exception if the register cannot be read or the address is invalid within the state
|
||||
*/
|
||||
public static byte[] readEmulatorStack(PcodeThread<byte[]> emulatorThread, Register stackReg,
|
||||
AddressSpace addrSpace, int offset, int size, DataConverter dataConverter)
|
||||
throws Exception {
|
||||
byte[] stackPtr = emulatorThread.getState().getVar(stackReg, Reason.INSPECT);
|
||||
ByteBuffer stackPtrBuf = ByteBuffer.wrap(stackPtr);
|
||||
stackPtrBuf.order(
|
||||
dataConverter.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
|
||||
long stackPtrVal;
|
||||
if (stackPtr.length >= 8) {
|
||||
stackPtrVal = stackPtrBuf.getLong();
|
||||
}
|
||||
// Avoid sign-extending negative values
|
||||
else if (stackPtr.length >= 4) {
|
||||
stackPtrVal = Integer.toUnsignedLong(stackPtrBuf.getInt());
|
||||
}
|
||||
else {
|
||||
stackPtrVal = Short.toUnsignedLong(stackPtrBuf.getShort());
|
||||
}
|
||||
Address addr = addrSpace.getAddress(stackPtrVal + offset);
|
||||
return emulatorThread.getState().getVar(addr, size, false, Reason.INSPECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given function, and list of parameter pieces, return a list of bytes representing the
|
||||
* values of the parameters. This data is produced from the source code of the binary which is
|
||||
* used in cspec tests to compare against the list of bytes representing parameter values from
|
||||
* the emulator.
|
||||
* @param func Function to get the parameter values of.
|
||||
* @param pieces ParameterPieces representing basic elements of the parameters of the function.
|
||||
* @param dataConverter used to convert data to bytes and vice versa.
|
||||
* @param logger {@code Consumer<String>} lambda function to print logging information
|
||||
* @return {@code List<byte[]>} byte representation of function parameter values
|
||||
* @throws MemoryAccessException if there is a problem accessing the function's program memory.
|
||||
*/
|
||||
public static List<byte[]> getPassedValues(Function func, List<ParameterPieces> pieces,
|
||||
DataConverter dataConverter, Consumer<String> logger)
|
||||
throws MemoryAccessException {
|
||||
Program program = func.getProgram();
|
||||
List<byte[]> groundTruth = new ArrayList<>();
|
||||
for (int i = 0; i < pieces.size(); ++i) {
|
||||
ParameterPieces piece = pieces.get(i);
|
||||
DataType dt = piece.type;
|
||||
if (dt == null) {
|
||||
throw new AssertionError("null datatype for piece " + i + " in " + func.getName());
|
||||
}
|
||||
if (dt instanceof VoidDataType) {
|
||||
groundTruth.add(new byte[0]); // for testing return values
|
||||
continue;
|
||||
}
|
||||
boolean isPointer = false;
|
||||
DataType baseType = dt;
|
||||
if (dt instanceof Pointer pointer) {
|
||||
baseType = pointer.getDataType();
|
||||
isPointer = true;
|
||||
}
|
||||
int index = i;
|
||||
if (i >= 1) {
|
||||
Parameter param = func.getParameter(i - 1);
|
||||
if (param != null) {
|
||||
AutoParameterType autoType = param.getAutoParameterType();
|
||||
if (autoType != null &&
|
||||
param.getAutoParameterType().equals(AutoParameterType.RETURN_STORAGE_PTR)) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String symbolName = baseType.getName() + "_" + Integer.toString(index);
|
||||
Symbol symbol = program.getSymbolTable().getSymbols(symbolName).next();
|
||||
if (symbol == null) {
|
||||
// Sometimes compilers will prepend a leading underscore to symbol names
|
||||
// try -fno-leading-underscore
|
||||
symbolName = "_" + symbolName;
|
||||
symbol = program.getSymbolTable().getSymbols(symbolName).next();
|
||||
if (symbol == null) {
|
||||
throw new AssertionError("null Symbol for name " + symbolName + " in " +
|
||||
func.getName() + " piece " + i);
|
||||
}
|
||||
|
||||
}
|
||||
byte[] value = new byte[dt.getLength()];
|
||||
if (isPointer) {
|
||||
if ((piece.hiddenReturnPtr || piece.isIndirect) && (piece.address != null)) {
|
||||
value = new byte[baseType.getLength()];
|
||||
program.getMemory().getBytes(symbol.getAddress(), value);
|
||||
}
|
||||
else {
|
||||
long offset = symbol.getAddress().getAddressableWordOffset();
|
||||
dataConverter.getBytes(offset, dt.getLength(), value, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
program.getMemory().getBytes(symbol.getAddress(), value);
|
||||
// handle calling-convention enforced conversions, such as
|
||||
// converting floats to doubles
|
||||
if (piece.joinPieces != null && piece.joinPieces.length == 1 &&
|
||||
piece.joinPieces[0].getSize() != dt.getLength()) {
|
||||
value = getExtendedValue(value, dt, piece.joinPieces[0], dataConverter, logger);
|
||||
}
|
||||
}
|
||||
groundTruth.add(value);
|
||||
}
|
||||
return groundTruth;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the byte representation of values from their original DataType to the DataType that
|
||||
* is indicated by the joinPiece Varnode's size.
|
||||
* @param value the raw byte array of the original value
|
||||
* @param dt the source data type (e.g., FloatDataType, DoubleDataType, or Structure)
|
||||
* @param joinPiece representing the target storage, used to determine target size
|
||||
* @param dataConverter the converter used to determine endianness
|
||||
* @param logger {@code Consumer<String>} lambda function to print logging information
|
||||
* @return byte[] a byte array containing the extended value,
|
||||
*/
|
||||
public static byte[] getExtendedValue(byte[] value, DataType dt, Varnode joinPiece,
|
||||
DataConverter dataConverter, Consumer<String> logger) {
|
||||
byte[] extended = new byte[joinPiece.getSize()];
|
||||
// float -> double
|
||||
if (dt instanceof FloatDataType && dt.getLength() == 4 && joinPiece.getSize() == 8) {
|
||||
ByteOrder byteOrder =
|
||||
dataConverter.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
|
||||
float floatValue = ByteBuffer.wrap(value).order(byteOrder).getFloat();
|
||||
double doubleValue = floatValue;
|
||||
extended = ByteBuffer.allocate(8).order(byteOrder).putDouble(doubleValue).array();
|
||||
}
|
||||
// float -> double, but the float is a single-element HFA
|
||||
else if (dt instanceof Structure struct &&
|
||||
struct.getComponent(0).getDataType() instanceof FloatDataType && dt.getLength() == 4 &&
|
||||
joinPiece.getSize() == 8) {
|
||||
|
||||
ByteOrder byteOrder =
|
||||
dataConverter.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
|
||||
float floatValue = ByteBuffer.wrap(value).order(byteOrder).getFloat();
|
||||
double doubleValue = floatValue;
|
||||
extended = ByteBuffer.allocate(8).order(byteOrder).putDouble(doubleValue).array();
|
||||
}
|
||||
// float -> 80 bit floating point format
|
||||
else if (dt instanceof FloatDataType && dt.getLength() == 4 && joinPiece.getSize() == 10) {
|
||||
ByteOrder byteOrder =
|
||||
dataConverter.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
|
||||
float floatValue = ByteBuffer.wrap(value).order(byteOrder).getFloat();
|
||||
// See OpBehaviorFloatFloat2Float
|
||||
FloatFormat inFF = FloatFormatFactory.getFloatFormat(4);
|
||||
FloatFormat outFF = FloatFormatFactory.getFloatFormat(10);
|
||||
long inEncoded = inFF.getEncoding(floatValue);
|
||||
BigInteger inEncodedBig = BigInteger.valueOf(inEncoded);
|
||||
BigInteger outEncoded = inFF.opFloat2Float(inEncodedBig, outFF);
|
||||
|
||||
byte[] outBytes = outEncoded.toByteArray(); // Returned as Big-Endian
|
||||
// Pad to expected size and/or flip byte order
|
||||
if (byteOrder == ByteOrder.BIG_ENDIAN) {
|
||||
for (int i = 10 - outBytes.length; i < 10; i++) {
|
||||
extended[i] = outBytes[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < outBytes.length; i++) {
|
||||
extended[9 - i] = outBytes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// double -> 80 bit floating point format
|
||||
else if (dt instanceof DoubleDataType && dt.getLength() == 8 && joinPiece.getSize() == 10) {
|
||||
ByteOrder byteOrder =
|
||||
dataConverter.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
|
||||
double doubleValue = ByteBuffer.wrap(value).order(byteOrder).getDouble();
|
||||
// See OpBehaviorFloatFloat2Float
|
||||
FloatFormat inFF = FloatFormatFactory.getFloatFormat(8);
|
||||
FloatFormat outFF = FloatFormatFactory.getFloatFormat(10);
|
||||
long inEncoded = inFF.getEncoding(doubleValue);
|
||||
BigInteger inEncodedBig = BigInteger.valueOf(inEncoded);
|
||||
BigInteger outEncoded = inFF.opFloat2Float(inEncodedBig, outFF);
|
||||
|
||||
byte[] outBytes = outEncoded.toByteArray(); // Returned as Big-Endian
|
||||
// Pad to expected size and/or flip byte order
|
||||
if (byteOrder == ByteOrder.BIG_ENDIAN) {
|
||||
for (int i = 10 - outBytes.length; i < 10; i++) {
|
||||
extended[i] = outBytes[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < outBytes.length; i++) {
|
||||
extended[9 - i] = outBytes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.accept("Unhandled extension: dt=%s; size=%d, extended size=%d\n"
|
||||
.formatted(dt.getDisplayName(), dt.getLength(), joinPiece.getSize()));
|
||||
}
|
||||
return extended;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameters between a caller and callee and organizes them in a PrototypePieces
|
||||
* object by prototypeModel. ParameterPieces may be spread between them depending on the calling
|
||||
* convention being used.
|
||||
* @param caller The function that calls the callee.
|
||||
* @param callee The function being called by the caller.
|
||||
* @param model PrototypeModel corresponding to the calling convention.
|
||||
* @return {@code ArrayList<ParameterPieces>}
|
||||
*/
|
||||
public static ArrayList<ParameterPieces> getParameterPieces(Function caller, Function callee,
|
||||
PrototypeModel model) {
|
||||
Program program = callee.getProgram();
|
||||
PrototypePieces pieces = new PrototypePieces(model, null);
|
||||
FunctionSignature funcSig = callee.getSignature(true);
|
||||
pieces.outtype = funcSig.getReturnType();
|
||||
if (callee.hasVarArgs()) {
|
||||
// args for callsite encode in caller's name
|
||||
List<DataType> types = getVarArgsParamTypes(caller);
|
||||
for (DataType type : types) {
|
||||
pieces.intypes.add(type);
|
||||
}
|
||||
pieces.firstVarArgSlot = funcSig.getArguments().length;
|
||||
}
|
||||
else {
|
||||
for (ParameterDefinition def : funcSig.getArguments()) {
|
||||
pieces.intypes.add(def.getDataType());
|
||||
}
|
||||
}
|
||||
ArrayList<ParameterPieces> paramPieces = new ArrayList<>();
|
||||
model.assignParameterStorage(pieces, program.getDataTypeManager(), paramPieces, true);
|
||||
return paramPieces;
|
||||
}
|
||||
|
||||
/**
|
||||
* All functions in the source code for these Cspec tests are named in such a way that the
|
||||
* parameter types of the function are encoded in the name. This function decodes the
|
||||
* function names to retrieve the function parameter types as a list.
|
||||
* @param func The function to decode into it's parameter's datatypes
|
||||
* @return {@code List<DataType>} the datatypes of the parameters of the function.
|
||||
*/
|
||||
public static List<DataType> getVarArgsParamTypes(Function func) {
|
||||
List<DataType> types = new ArrayList<>();
|
||||
String name = func.getName();
|
||||
String[] parts = name.split("_");
|
||||
DataTypeManager dtManager = func.getProgram().getDataTypeManager();
|
||||
// name is paramsVariadic_(type list)_counter
|
||||
for (int i = 1; i < parts.length - 1; ++i) {
|
||||
DataType type = null;
|
||||
switch (parts[i]) {
|
||||
case "c":
|
||||
type = new CharDataType(dtManager);
|
||||
break;
|
||||
case "C":
|
||||
type = new UnsignedCharDataType(dtManager);
|
||||
break;
|
||||
case "s":
|
||||
type = new ShortDataType(dtManager);
|
||||
break;
|
||||
case "S":
|
||||
type = new UnsignedShortDataType(dtManager);
|
||||
break;
|
||||
case "i":
|
||||
type = new IntegerDataType(dtManager);
|
||||
break;
|
||||
case "I":
|
||||
type = new UnsignedIntegerDataType(dtManager);
|
||||
break;
|
||||
case "l":
|
||||
type = new LongDataType(dtManager);
|
||||
break;
|
||||
case "L":
|
||||
type = new UnsignedLongDataType(dtManager);
|
||||
break;
|
||||
case "f":
|
||||
type = new FloatDataType(dtManager);
|
||||
break;
|
||||
case "d":
|
||||
type = new DoubleDataType(dtManager);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unsupported data type: " + parts[i]);
|
||||
}
|
||||
types.add(type);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the function that calls the given function first.
|
||||
* @param function the callee which is searched for
|
||||
* @return Function the caller which is first
|
||||
*/
|
||||
public static Function getFirstCall(Function function) {
|
||||
String[] parts = function.getName().split("_");
|
||||
String count = parts[parts.length - 1];
|
||||
Set<Function> callees = function.getCalledFunctions(TaskMonitor.DUMMY);
|
||||
if (callees.size() == 0) {
|
||||
throw new AssertionError("no called functions found for " + function.getName());
|
||||
}
|
||||
for (Function callee : callees) {
|
||||
String calleeName = getAdjustedCalleeName(callee.getName());
|
||||
|
||||
if (calleeName.startsWith(CSpecPrototypeTestConstants.EXTERNAL) ||
|
||||
calleeName.startsWith(CSpecPrototypeTestConstants.PRODUCER)) {
|
||||
if (calleeName.endsWith(count)) {
|
||||
return callee;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new AssertionError("no appropriate functions called by " + function.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the name of a function to remove characters prepended by compilers.
|
||||
* @param calleeName the name of the function
|
||||
* @return String the name of the function after it has been adjusted
|
||||
*/
|
||||
public static String getAdjustedCalleeName(String calleeName) {
|
||||
// Hack for PowerPC 64-bit object files that prepend plt_call.<funcname> on the extern calls
|
||||
if (!calleeName.contains("plt_call.")) {
|
||||
return calleeName;
|
||||
}
|
||||
return calleeName.split("plt_call.")[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PrototypeModel from the compiler spec of the given LanguageCompilerSpecPair and
|
||||
* calling convention specified in the program's name.
|
||||
* @param program Program whose name contains the desired calling convention
|
||||
* @param langComp the language/compiler specification pair to query
|
||||
* @return PrototypeModel Model corresponding to the extracted calling convention
|
||||
* @throws CompilerSpecNotFoundException if the compiler specification cannot be found/loaded
|
||||
* @throws LanguageNotFoundException if the specified language is not available
|
||||
*/
|
||||
public static PrototypeModel getProtoModelToTest(Program program,
|
||||
LanguageCompilerSpecPair langComp)
|
||||
throws CompilerSpecNotFoundException, LanguageNotFoundException {
|
||||
String name = program.getName();
|
||||
// Expected program name format: <arch>_<endian>_<bits>_<machine>_<compiler>_<callingconv>
|
||||
|
||||
//Handle cases where the architecture name contains underscores
|
||||
String architecture = name.split("_LE_|_BE_")[0];
|
||||
if (architecture.contains("_")) {
|
||||
String cleanArchitecture = name.replace("_", "");
|
||||
name = name.replaceFirst(architecture, cleanArchitecture);
|
||||
}
|
||||
|
||||
String[] splitname = name.split("_");
|
||||
|
||||
// Handle cases where calling convention contains underscores
|
||||
String ccName = String.join("_", Arrays.copyOfRange(splitname, 5, splitname.length));
|
||||
|
||||
return langComp.getCompilerSpec().getCallingConvention(ccName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses function definitions and data types of global variables from the source code and
|
||||
* applies them to the binary. Also applies the correct signature overrides to calls of
|
||||
* variadic functions.
|
||||
* @param program Program produced from the source code being parsed.
|
||||
* @param model The PrototypeModel of the program.
|
||||
* @throws ParseException when the c source code cannot be parsed.
|
||||
* @throws IOException when the c source code cannot be parsed.
|
||||
* @throws ghidra.app.util.cparser.CPP.ParseException when the c source code cannot be parsed.
|
||||
* @throws CodeUnitInsertionException When code units cannot be created at the given address.
|
||||
*/
|
||||
public static void applyInfoFromSourceIfNeeded(Program program, PrototypeModel model)
|
||||
throws ParseException, IOException, ghidra.app.util.cparser.CPP.ParseException,
|
||||
CodeUnitInsertionException {
|
||||
|
||||
Category funcsFromSource = getFuncDefs(program);
|
||||
if (funcsFromSource != null) {
|
||||
return; // assume that this method has already been run on the test binary.
|
||||
}
|
||||
|
||||
DataTypeManager dtManager = program.getDataTypeManager();
|
||||
|
||||
// Parse the c source file. This assumes that the name of the source file is the
|
||||
// name of the test binary + ".c" and that the two files are in the same directory
|
||||
CParserUtils.parseHeaderFiles(null, new String[] { program.getExecutablePath() + ".c" },
|
||||
new String[0], dtManager, TaskMonitor.DUMMY);
|
||||
|
||||
funcsFromSource = getFuncDefs(program);
|
||||
if (funcsFromSource == null) {
|
||||
throw new AssertionError("Error parsing C file; datatypes not added");
|
||||
}
|
||||
FunctionIterator fIter = program.getFunctionManager().getExternalFunctions();
|
||||
AddressSet entryPoints = new AddressSet();
|
||||
while (fIter.hasNext()) {
|
||||
entryPoints.add(fIter.next().getEntryPoint());
|
||||
}
|
||||
entryPoints = entryPoints.union(program.getMemory().getExecuteSet());
|
||||
|
||||
ApplyFunctionDataTypesCmd cmd = new ApplyFunctionDataTypesCmd(funcsFromSource, entryPoints,
|
||||
SourceType.USER_DEFINED, true, true);
|
||||
cmd.applyTo(program, TaskMonitor.DUMMY);
|
||||
|
||||
// set the calling convention on the test functions
|
||||
// first the external test functions
|
||||
fIter = program.getFunctionManager().getExternalFunctions();
|
||||
while (fIter.hasNext()) {
|
||||
Function func = fIter.next();
|
||||
try {
|
||||
func.setCallingConvention(model.getName());
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
// shouldn't happen
|
||||
throw new AssertionError(
|
||||
"Bad calling convention name for prototype: " + model.getName());
|
||||
}
|
||||
}
|
||||
// now the "producer" functions used for testing returned values
|
||||
fIter = program.getFunctionManager().getFunctions(true);
|
||||
while (fIter.hasNext()) {
|
||||
Function func = fIter.next();
|
||||
if (!func.getName().startsWith(CSpecPrototypeTestConstants.PRODUCER)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
func.setCallingConvention(model.getName());
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
// shouldn't happen
|
||||
throw new AssertionError(
|
||||
"Bad calling convention name for prototype: " + model.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// apply the correct overrides to the varargs functions
|
||||
ReferenceManager refManager = program.getReferenceManager();
|
||||
fIter = program.getFunctionManager().getFunctions(true);
|
||||
while (fIter.hasNext()) {
|
||||
Function func = fIter.next();
|
||||
if (!func.getName().startsWith(CSpecPrototypeTestConstants.PARAMS_VARIADIC)) {
|
||||
continue;
|
||||
}
|
||||
Function varArgsFunc = CSpecPrototypeTestUtil.getFirstCall(func);
|
||||
if (!varArgsFunc.hasVarArgs()) {
|
||||
throw new AssertionError(varArgsFunc.getName() + " should be marked varargs");
|
||||
}
|
||||
Reference call = null;
|
||||
ReferenceIterator refIter = refManager.getReferencesTo(varArgsFunc.getEntryPoint());
|
||||
while (refIter.hasNext()) {
|
||||
Reference ref = refIter.next();
|
||||
if (!ref.getReferenceType().isCall()) {
|
||||
continue;
|
||||
}
|
||||
if (func.getBody().contains(ref.getFromAddress())) {
|
||||
call = ref; // assume the first one found is what we want
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (call == null) {
|
||||
throw new AssertionError(
|
||||
"no call references to " + varArgsFunc.getName() + " in " + func.getName());
|
||||
}
|
||||
List<DataType> types = CSpecPrototypeTestUtil.getVarArgsParamTypes(func);
|
||||
FunctionDefinitionDataType override =
|
||||
new FunctionDefinitionDataType(varArgsFunc.getName(), program.getDataTypeManager());
|
||||
try {
|
||||
override.setCallingConvention(model.getName());
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
// shouldn't happen
|
||||
throw new AssertionError("bad calling convention name: " + model.getName());
|
||||
}
|
||||
override.setReturnType(VoidDataType.dataType);
|
||||
ParameterDefinition[] paramDefs = new ParameterDefinition[types.size()];
|
||||
for (int i = 0; i < types.size(); ++i) {
|
||||
paramDefs[i] = new ParameterDefinitionImpl("param" + i, types.get(i), null);
|
||||
}
|
||||
override.setArguments(paramDefs);
|
||||
try {
|
||||
HighFunctionDBUtil.writeOverride(func, call.getFromAddress(), override);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
throw new AssertionError("bad overriding signature for variadic function");
|
||||
}
|
||||
}
|
||||
|
||||
// finally, apply datatypes to global variables
|
||||
SymbolIterator symbolIter = program.getSymbolTable().getDefinedSymbols();
|
||||
CategoryPath source =
|
||||
new CategoryPath(CategoryPath.ROOT, List.of(program.getName() + ".c"));
|
||||
while (symbolIter.hasNext()) {
|
||||
Symbol symbol = symbolIter.next();
|
||||
String name = symbol.getName();
|
||||
if (name == null) {
|
||||
continue;
|
||||
}
|
||||
int underScoreIndex = name.indexOf('_');
|
||||
if (underScoreIndex == -1) {
|
||||
continue;
|
||||
}
|
||||
String typeName = name.substring(0, underScoreIndex);
|
||||
DataType type = null;
|
||||
switch (typeName) {
|
||||
case "char":
|
||||
case "short":
|
||||
case "int":
|
||||
case "long":
|
||||
case "longlong":
|
||||
case "float":
|
||||
case "double":
|
||||
type = dtManager.getDataType(CategoryPath.ROOT, typeName);
|
||||
break;
|
||||
case CSpecPrototypeTestConstants.STRUCT_CHAR_SINGLETON_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_SHORT_SINGLETON_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_INT_SINGLETON_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_LONG_SINGLETON_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_LONG_LONG_SINGLETON_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_FLOAT_SINGLETON_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_DOUBLE_SINGLETON_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_CHAR_PAIR_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_SHORT_PAIR_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_INT_PAIR_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_LONG_PAIR_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_LONG_LONG_PAIR_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_FLOAT_PAIR_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_DOUBLE_PAIR_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_CHAR_TRIP_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_SHORT_TRIP_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_INT_TRIP_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_LONG_TRIP_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_LONG_LONG_TRIP_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_FLOAT_TRIP_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_DOUBLE_TRIP_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_CHAR_QUAD_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_SHORT_QUAD_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_INT_QUAD_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_LONG_QUAD_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_LONG_LONG_QUAD_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_FLOAT_QUAD_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_DOUBLE_QUAD_NAME:
|
||||
case CSpecPrototypeTestConstants.STRUCT_INT_LONG_INT:
|
||||
case CSpecPrototypeTestConstants.STRUCT_FLOAT_INT_FLOAT:
|
||||
case CSpecPrototypeTestConstants.STRUCT_LONG_DOUBLE_LONG:
|
||||
case CSpecPrototypeTestConstants.STRUCT_FLOAT_DOUBLE_FLOAT:
|
||||
case CSpecPrototypeTestConstants.UNION_CHAR:
|
||||
case CSpecPrototypeTestConstants.UNION_SHORT:
|
||||
case CSpecPrototypeTestConstants.UNION_INT:
|
||||
case CSpecPrototypeTestConstants.UNION_LONG:
|
||||
case CSpecPrototypeTestConstants.UNION_LONG_LONG:
|
||||
case CSpecPrototypeTestConstants.UNION_INT_LONG:
|
||||
case CSpecPrototypeTestConstants.UNION_FLOAT_DOUBLE:
|
||||
case CSpecPrototypeTestConstants.UNION_INT_FLOAT:
|
||||
case CSpecPrototypeTestConstants.UNION_LONG_DOUBLE:
|
||||
case CSpecPrototypeTestConstants.UNION_INT_DOUBLE:
|
||||
case CSpecPrototypeTestConstants.UNION_LONG_FLOAT:
|
||||
case CSpecPrototypeTestConstants.UNION_STRUCT_INT:
|
||||
case CSpecPrototypeTestConstants.UNION_STRUCT_FLOAT:
|
||||
case CSpecPrototypeTestConstants.UNION_MIXED_STRUCT_INTEGRAL:
|
||||
case CSpecPrototypeTestConstants.UNION_MIXED_STRUCT_FLOATING:
|
||||
case CSpecPrototypeTestConstants.UNION_MIXED_STRUCT_ALL_SMALL:
|
||||
case CSpecPrototypeTestConstants.UNION_MIXED_STRUCT_ALL_LARGE:
|
||||
case CSpecPrototypeTestConstants.UNION_STRUCT_TRIP_CHAR:
|
||||
case CSpecPrototypeTestConstants.UNION_STRUCT_TRIP_SHORT:
|
||||
type = dtManager.getDataType(source, typeName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (type == null) {
|
||||
continue;
|
||||
}
|
||||
// first clear any types that an analyzer may have laid down
|
||||
// for example, the ASCII string searcher
|
||||
program.getListing()
|
||||
.clearCodeUnits(symbol.getAddress(), symbol.getAddress().add(type.getLength()),
|
||||
false);
|
||||
program.getListing().createData(symbol.getAddress(), type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a TestResult record that includes a message detailing the differences between
|
||||
* parameter data in the c source code, and parameter data in the Ghidra emulator between a
|
||||
* specific caller function and callee function.
|
||||
* @param caller The function that calls the callee.
|
||||
* @param callee The function being called by the caller.
|
||||
* @param pieces {@code ArrayList<ParameterPieces>} representing basic elements of the parameters
|
||||
* between callers and callees.
|
||||
* @param fromEmulator Byte list representing parameter values from the emulator.
|
||||
* @param groundTruth Byte list representing parameter values from the c source code.
|
||||
* @return TestResult result record object that contains a message and a boolean hasError.
|
||||
*/
|
||||
public static TestResult getTestResult(Function callee, Function caller,
|
||||
ArrayList<ParameterPieces> pieces, List<byte[]> fromEmulator,
|
||||
List<byte[]> groundTruth) {
|
||||
boolean error = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Caller: ");
|
||||
sb.append(caller.getName());
|
||||
sb.append("\nCallee: ");
|
||||
sb.append(callee.getName());
|
||||
sb.append("\n\n");
|
||||
|
||||
boolean inputTest = pieces.get(0).type instanceof VoidDataType;
|
||||
int begin = inputTest ? 1 : 0;
|
||||
int end = inputTest ? fromEmulator.size() : 1;
|
||||
for (int i = begin; i < end; ++i) {
|
||||
if (!Arrays.equals(groundTruth.get(i), fromEmulator.get(i))) {
|
||||
error = true;
|
||||
sb.append("X ");
|
||||
}
|
||||
else {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(pieces.get(i).type.getDisplayName());
|
||||
if (i == 0) {
|
||||
sb.append(" return");
|
||||
}
|
||||
else {
|
||||
sb.append(" param");
|
||||
sb.append(Integer.toString(i));
|
||||
}
|
||||
sb.append("\n");
|
||||
sb.append(" location: ");
|
||||
String location = pieces.get(i).getVariableStorage(callee.getProgram()).toString();
|
||||
sb.append(location);
|
||||
sb.append("\n");
|
||||
ParameterPieces piece = pieces.get(i);
|
||||
if ((piece.hiddenReturnPtr || piece.isIndirect) && (piece.address != null)) {
|
||||
sb.append(" expected bytes points to: ");
|
||||
}
|
||||
else {
|
||||
sb.append(" expected bytes: ");
|
||||
}
|
||||
sb.append(hexFormat.formatHex(groundTruth.get(i)));
|
||||
sb.append("\n");
|
||||
|
||||
if ((piece.hiddenReturnPtr || piece.isIndirect) && (piece.address != null)) {
|
||||
sb.append(" emulator points to: ");
|
||||
}
|
||||
else {
|
||||
sb.append(" emulator: ");
|
||||
}
|
||||
sb.append(hexFormat.formatHex(fromEmulator.get(i)));
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
return new TestResult(sb.toString(), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the function definitions for the program as a {@link Category}.
|
||||
* @param program Program to get the function definitions from.
|
||||
* @return Category
|
||||
*/
|
||||
private static Category getFuncDefs(Program program) {
|
||||
String name = program.getName();
|
||||
CategoryPath path = new CategoryPath(CategoryPath.ROOT, List.of(name + ".c", "functions"));
|
||||
return program.getDataTypeManager().getCategory(path);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.compilers.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HexFormat;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import ghidra.app.util.PseudoInstruction;
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* An extension of {@link PcodeEmulator} that can load program memory and set up the emulator
|
||||
* to run at a specific function entry point.
|
||||
*/
|
||||
public class CSpecTestPCodeEmulator extends PcodeEmulator {
|
||||
private boolean traceDisabled = true;
|
||||
private int traceLevel = 3;
|
||||
private Consumer<String> logger = (msg -> Msg.debug(this, msg));
|
||||
|
||||
public CSpecTestPCodeEmulator(Language lang) {
|
||||
super(lang);
|
||||
}
|
||||
|
||||
public CSpecTestPCodeEmulator(Language lang, boolean traceDisabled, int traceLevel) {
|
||||
this(lang, traceDisabled, traceLevel, null);
|
||||
}
|
||||
|
||||
public CSpecTestPCodeEmulator(Language lang, boolean traceDisabled, int traceLevel,
|
||||
Consumer<String> logger) {
|
||||
super(lang);
|
||||
this.traceDisabled = traceDisabled;
|
||||
this.traceLevel = traceLevel;
|
||||
if (logger != null)
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create BytesPcodeThread object with an overwritten 'createInstructionDecoder' method.
|
||||
* @param name The name of the thread.
|
||||
*/
|
||||
@Override
|
||||
protected BytesPcodeThread createThread(String name) {
|
||||
return new BytesPcodeThread(name, this) {
|
||||
@Override
|
||||
protected SleighInstructionDecoder createInstructionDecoder(
|
||||
PcodeExecutorState<byte[]> sharedState) {
|
||||
return new SleighInstructionDecoder(language, sharedState) {
|
||||
@Override
|
||||
public PseudoInstruction decodeInstruction(Address address,
|
||||
RegisterValue context) {
|
||||
//Msg.debug(this, "Dissassembly at " + address + ": ");
|
||||
PseudoInstruction inst = super.decodeInstruction(address, context);
|
||||
//Msg.debug(this, inst.toString());
|
||||
|
||||
if (!traceDisabled && traceLevel > 0) {
|
||||
logger.accept(
|
||||
"Disassembly at " + address + ": " + inst.toString());
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the function entry point context registers into emulator, create stack space,
|
||||
* set program counter. Return a emulator thread ready for a run() call
|
||||
* @param func The function to prepare the emulator to run.
|
||||
* @return {@code PcodeThread<byte[]>}
|
||||
*/
|
||||
public PcodeThread<byte[]> prepareFunction(Function func) {
|
||||
PcodeThread<byte[]> emuThread = newThread();
|
||||
PcodeArithmetic<byte[]> emuArith = emuThread.getArithmetic();
|
||||
|
||||
long stackOffset =
|
||||
(func.getEntryPoint().getAddressSpace().getMaxAddress().getOffset() >>> 1) - 0x7ff;
|
||||
Register stackReg = func.getProgram().getCompilerSpec().getStackPointer();
|
||||
|
||||
emuThread.getState()
|
||||
.setVar(stackReg,
|
||||
emuArith.fromConst(stackOffset, stackReg.getMinimumByteSize()));
|
||||
|
||||
Instruction entry =
|
||||
func.getProgram().getListing().getInstructionAt(func.getEntryPoint());
|
||||
|
||||
for (Register reg : entry.getRegisters()) {
|
||||
RegisterValue val = entry.getRegisterValue(reg);
|
||||
if (reg.isBaseRegister() && val != null && val.hasAnyValue()) {
|
||||
//Msg.debug(this, "Adding register: " + reg + ", is BE? " + reg.isBigEndian() +
|
||||
// ", is context? " + reg.isProcessorContext());
|
||||
byte[] curVal = emuThread.getState().getVar(reg, Reason.INSPECT);
|
||||
byte[] bytes = val.toBytes();
|
||||
// bytes field of a RegisterValue is (mask : val) concatenated
|
||||
byte[] maskedVal = new byte[bytes.length / 2];
|
||||
for (int i = 0; i < maskedVal.length; i++) {
|
||||
// don't adjust endianness for context registers
|
||||
if (!reg.isBigEndian() && !reg.isProcessorContext()) {
|
||||
maskedVal[maskedVal.length - 1 - i] =
|
||||
(byte) (bytes[i] & bytes[i + maskedVal.length]);
|
||||
}
|
||||
else {
|
||||
maskedVal[i] = (byte) (bytes[i] & bytes[i + maskedVal.length]);
|
||||
}
|
||||
}
|
||||
emuThread.getState().setVar(reg, emuArith.fromConst(maskedVal));
|
||||
|
||||
if (!traceDisabled && traceLevel > 1) {
|
||||
logger.accept("Adding register: " + reg + ", is BE? " + reg.isBigEndian() +
|
||||
", is context? " + reg.isProcessorContext());
|
||||
logger.accept("\tRegister " + reg + " set to value: [" +
|
||||
HexFormat.ofDelimiter(", ").formatHex(maskedVal) + "]");
|
||||
logger.accept(
|
||||
"\tFrom context (mask : value): [" +
|
||||
HexFormat.ofDelimiter(", ")
|
||||
.formatHex(Arrays.copyOfRange(bytes, 0, curVal.length)) +
|
||||
" : " + HexFormat.ofDelimiter(", ")
|
||||
.formatHex(
|
||||
Arrays.copyOfRange(bytes, curVal.length, bytes.length)) +
|
||||
"]");
|
||||
logger.accept(
|
||||
"\tWas: [" + HexFormat.ofDelimiter(", ").formatHex(curVal) + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emuThread.reInitialize();
|
||||
|
||||
emuThread.overrideCounter(func.getEntryPoint());
|
||||
|
||||
return emuThread;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class AARCH64_CSpecTest extends CSpecPrototypeTest {
|
||||
private static final String LANGUAGE_ID = "AARCH64:LE:64:v8A";
|
||||
private static final String COMPILER_SPEC_ID = "default";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__cdecl";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"paramsUnion_65",
|
||||
"paramsUnion_68",
|
||||
"paramsUnion_70",
|
||||
"returnUnion_113"
|
||||
};
|
||||
|
||||
public AARCH64_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class AARCH64_ilp32_CSpecTest extends CSpecPrototypeTest {
|
||||
private static final String LANGUAGE_ID = "AARCH64:BE:32:ilp32";
|
||||
private static final String COMPILER_SPEC_ID = "default";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__cdecl";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"paramsPrimitiveIdentical_0",
|
||||
"paramsPrimitiveIdentical_1",
|
||||
"paramsPrimitiveIdentical_2",
|
||||
"paramsPrimitiveIdentical_3",
|
||||
"paramsPrimitiveIdentical_4",
|
||||
"paramsPrimitiveAlternate_5",
|
||||
"paramsPrimitiveAlternate_6",
|
||||
"paramsPrimitiveAlternate_8",
|
||||
"paramsPrimitiveAlternate_9",
|
||||
"paramsPrimitiveIdentical_10",
|
||||
"paramsPrimitiveIdentical_13",
|
||||
"paramsPrimitiveAlternate_16",
|
||||
"paramsPrimitiveAlternate_17",
|
||||
"paramsSingletonStruct_18",
|
||||
"paramsSingletonStruct_19",
|
||||
"paramsSingletonStruct_20",
|
||||
"paramsSingletonStruct_21",
|
||||
"paramsSingletonStruct_22",
|
||||
"paramsSingletonStruct_23",
|
||||
"paramsPairStruct_25",
|
||||
"paramsPairStruct_26",
|
||||
"paramsPairStruct_29",
|
||||
"paramsPairStruct_30",
|
||||
"paramsPairStruct_31",
|
||||
"paramsTripStruct_32",
|
||||
"paramsTripStruct_33",
|
||||
"paramsTripStruct_34",
|
||||
"paramsTripStruct_35",
|
||||
"paramsTripStruct_36",
|
||||
"paramsTripStruct_37",
|
||||
"paramsTripStruct_38",
|
||||
"paramsQuadStruct_39",
|
||||
"paramsQuadStruct_41",
|
||||
"paramsQuadStruct_42",
|
||||
"paramsQuadStruct_43",
|
||||
"paramsQuadStruct_44",
|
||||
"paramsQuadStruct_45",
|
||||
"paramsMixedStruct_46",
|
||||
"paramsMixedStruct_47",
|
||||
"paramsMixedStruct_48",
|
||||
"paramsMixedStruct_49",
|
||||
"paramsVariadic_I_I_C_C_S_S_I_I_L_L_50",
|
||||
"paramsVariadic_I_I_I_I_I_I_I_I_I_I_51",
|
||||
"paramsMisc_53",
|
||||
"paramsMisc_54",
|
||||
"paramsMisc_55",
|
||||
"paramsUnion_56",
|
||||
"paramsUnion_57",
|
||||
"paramsUnion_58",
|
||||
"paramsUnion_59",
|
||||
"paramsUnion_60",
|
||||
"paramsUnion_61",
|
||||
"paramsUnion_62",
|
||||
"paramsUnion_63",
|
||||
"paramsUnion_64",
|
||||
"paramsUnion_65",
|
||||
"paramsUnion_66",
|
||||
"paramsUnion_67",
|
||||
"paramsUnion_68",
|
||||
"paramsUnion_69",
|
||||
"paramsUnion_70",
|
||||
"paramsUnion_73",
|
||||
"paramsUnion_75",
|
||||
"returnSingleton_78",
|
||||
"returnPair_79",
|
||||
"returnTriple_80",
|
||||
"returnQuad_81",
|
||||
"returnUnion_82",
|
||||
"returnUnion_83",
|
||||
"returnSingleton_85",
|
||||
"returnPair_86",
|
||||
"returnTriple_87",
|
||||
"returnUnion_89",
|
||||
"returnUnion_90",
|
||||
"returnSingleton_92",
|
||||
"returnTriple_94",
|
||||
"returnQuad_95",
|
||||
"returnUnion_96",
|
||||
"returnSingleton_98",
|
||||
"returnTriple_100",
|
||||
"returnQuad_101",
|
||||
"returnUnion_102",
|
||||
"returnMixed_103",
|
||||
"returnUnion_104",
|
||||
"returnUnion_105",
|
||||
"returnSingleton_107",
|
||||
"returnPair_108",
|
||||
"returnTriple_109",
|
||||
"returnQuad_110",
|
||||
"returnMixed_111",
|
||||
"returnUnion_113",
|
||||
"returnUnion_114",
|
||||
"returnSingleton_116",
|
||||
"returnPair_117",
|
||||
"returnTriple_118",
|
||||
"returnQuad_119",
|
||||
"returnMixed_120",
|
||||
"returnUnion_122",
|
||||
"returnMixed_123",
|
||||
"returnUnion_124",
|
||||
"returnPair_127",
|
||||
"returnTriple_128",
|
||||
"returnQuad_129"
|
||||
};
|
||||
|
||||
public AARCH64_ilp32_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class ARM_CSpecTest extends CSpecPrototypeTest {
|
||||
//ARM_LE_32_v8_default___stdcall
|
||||
private static final String LANGUAGE_ID = "ARM:LE:32:v8";
|
||||
private static final String COMPILER_SPEC_ID = "default";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__stdcall";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"paramsUnion_65",
|
||||
"paramsUnion_68",
|
||||
"paramsUnion_70",
|
||||
"returnUnion_112",
|
||||
"returnUnion_113",
|
||||
"returnUnion_121"
|
||||
};
|
||||
|
||||
public ARM_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class ARM_apcs_CSpecTest extends CSpecPrototypeTest {
|
||||
//ARM_BE_32_v8-m_apcs___stdcall
|
||||
private static final String LANGUAGE_ID = "ARM:BE:32:v8-m";
|
||||
private static final String COMPILER_SPEC_ID = "apcs";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__stdcall";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"paramsSingletonStruct_7",
|
||||
"paramsSingletonStruct_8",
|
||||
"paramsPairStruct_11",
|
||||
"paramsTripStruct_15",
|
||||
"paramsTripStruct_16",
|
||||
"paramsUnion_26",
|
||||
"paramsUnion_27",
|
||||
"paramsUnion_33",
|
||||
"paramsUnion_34",
|
||||
"returnSingleton_36",
|
||||
"returnPair_37",
|
||||
"returnTriple_38",
|
||||
"returnUnion_40",
|
||||
"returnUnion_41",
|
||||
"returnSingleton_43",
|
||||
"returnUnion_47"
|
||||
};
|
||||
|
||||
public ARM_apcs_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class ARM_softfp_CSpecTest extends CSpecPrototypeTest {
|
||||
private static final String LANGUAGE_ID = "ARM:LE:32:v8";
|
||||
private static final String COMPILER_SPEC_ID = "default";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__stdcall_softfp";
|
||||
|
||||
public ARM_softfp_CSpecTest() throws Exception {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class ARM_v45_CSpecTest extends CSpecPrototypeTest {
|
||||
//ARM_LE_32_v8_default___stdcall
|
||||
private static final String LANGUAGE_ID = "ARM:LE:32:v6";
|
||||
private static final String COMPILER_SPEC_ID = "default";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__stdcall";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"paramsPrimitiveIdentical_7",
|
||||
"paramsPrimitiveAlternate_8",
|
||||
"paramsPrimitiveAlternate_9",
|
||||
"paramsPrimitiveIdentical_13",
|
||||
"paramsPrimitiveAlternate_14",
|
||||
"paramsPrimitiveAlternate_15",
|
||||
"paramsPrimitiveAlternate_16",
|
||||
"paramsPrimitiveAlternate_17",
|
||||
"paramsSingletonStruct_23",
|
||||
"paramsSingletonStruct_24",
|
||||
"paramsPairStruct_27",
|
||||
"paramsPairStruct_28",
|
||||
"paramsPairStruct_29",
|
||||
"paramsPairStruct_30",
|
||||
"paramsPairStruct_31",
|
||||
"paramsTripStruct_33",
|
||||
"paramsTripStruct_34",
|
||||
"paramsTripStruct_35",
|
||||
"paramsTripStruct_36",
|
||||
"paramsTripStruct_37",
|
||||
"paramsTripStruct_38",
|
||||
"paramsQuadStruct_40",
|
||||
"paramsQuadStruct_41",
|
||||
"paramsQuadStruct_42",
|
||||
"paramsQuadStruct_43",
|
||||
"paramsQuadStruct_44",
|
||||
"paramsQuadStruct_45",
|
||||
"paramsMixedStruct_46",
|
||||
"paramsMixedStruct_47",
|
||||
"paramsMixedStruct_48",
|
||||
"paramsMixedStruct_49",
|
||||
"paramsVariadic_L_d_L_d_L_d_L_d_L_d_52",
|
||||
"paramsMisc_54",
|
||||
"paramsMisc_55",
|
||||
"paramsUnion_61",
|
||||
"paramsUnion_62",
|
||||
"paramsUnion_64",
|
||||
"paramsUnion_68",
|
||||
"paramsUnion_69",
|
||||
"paramsUnion_70",
|
||||
"paramsUnion_71",
|
||||
"paramsUnion_72",
|
||||
"paramsUnion_73",
|
||||
"paramsUnion_74",
|
||||
"paramsUnion_75",
|
||||
"paramsUnion_76",
|
||||
"returnTriple_87",
|
||||
"returnQuad_88",
|
||||
"returnUnion_90",
|
||||
"returnPair_93",
|
||||
"returnPair_99",
|
||||
"returnPair_108",
|
||||
"returnSingleton_116",
|
||||
"returnUnion_121",
|
||||
"returnSingleton_126",
|
||||
"returnUnion_130",
|
||||
};
|
||||
|
||||
public ARM_v45_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class MSP430X_CSpecTest extends CSpecPrototypeTest {
|
||||
private static final String LANGUAGE_ID = "TI_MSP430X:LE:32:default";
|
||||
private static final String COMPILER_SPEC_ID = "default";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__stdcall";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"paramsPrimitiveIdentical_0",
|
||||
"paramsPrimitiveIdentical_3",
|
||||
"paramsPrimitiveIdentical_4",
|
||||
"paramsPrimitiveAlternate_5",
|
||||
"paramsPrimitiveAlternate_6",
|
||||
"paramsPrimitiveIdentical_7",
|
||||
"paramsPrimitiveAlternate_9",
|
||||
"paramsPrimitiveIdentical_10",
|
||||
"paramsPrimitiveAlternate_11",
|
||||
"paramsPrimitiveAlternate_12",
|
||||
"paramsPrimitiveIdentical_13",
|
||||
"paramsPrimitiveAlternate_14",
|
||||
"paramsPrimitiveAlternate_15",
|
||||
"paramsPrimitiveAlternate_16",
|
||||
"paramsPrimitiveAlternate_17",
|
||||
"paramsSingletonStruct_18",
|
||||
"paramsSingletonStruct_19",
|
||||
"paramsSingletonStruct_20",
|
||||
"paramsSingletonStruct_21",
|
||||
"paramsSingletonStruct_22",
|
||||
"paramsSingletonStruct_23",
|
||||
"paramsSingletonStruct_24",
|
||||
"paramsPairStruct_25",
|
||||
"paramsPairStruct_26",
|
||||
"paramsPairStruct_27",
|
||||
"paramsPairStruct_28",
|
||||
"paramsPairStruct_29",
|
||||
"paramsPairStruct_30",
|
||||
"paramsPairStruct_31",
|
||||
"paramsTripStruct_32",
|
||||
"paramsTripStruct_33",
|
||||
"paramsTripStruct_34",
|
||||
"paramsTripStruct_35",
|
||||
"paramsTripStruct_36",
|
||||
"paramsTripStruct_37",
|
||||
"paramsTripStruct_38",
|
||||
"paramsQuadStruct_39",
|
||||
"paramsQuadStruct_40",
|
||||
"paramsQuadStruct_41",
|
||||
"paramsQuadStruct_42",
|
||||
"paramsQuadStruct_43",
|
||||
"paramsQuadStruct_44",
|
||||
"paramsQuadStruct_45",
|
||||
"paramsMixedStruct_46",
|
||||
"paramsMixedStruct_47",
|
||||
"paramsMixedStruct_48",
|
||||
"paramsMixedStruct_49",
|
||||
"paramsVariadic_I_I_C_C_S_S_I_I_L_L_50",
|
||||
"paramsVariadic_I_I_I_I_I_I_I_I_I_I_51",
|
||||
"paramsVariadic_L_d_L_d_L_d_L_d_L_d_52",
|
||||
"paramsMisc_53",
|
||||
"paramsMisc_54",
|
||||
"paramsMisc_55",
|
||||
"paramsUnion_56",
|
||||
"paramsUnion_57",
|
||||
"paramsUnion_58",
|
||||
"paramsUnion_59",
|
||||
"paramsUnion_60",
|
||||
"paramsUnion_61",
|
||||
"paramsUnion_62",
|
||||
"paramsUnion_63",
|
||||
"paramsUnion_64",
|
||||
"paramsUnion_65",
|
||||
"paramsUnion_66",
|
||||
"paramsUnion_67",
|
||||
"paramsUnion_68",
|
||||
"paramsUnion_69",
|
||||
"paramsUnion_70",
|
||||
"paramsUnion_71",
|
||||
"paramsUnion_72",
|
||||
"paramsUnion_73",
|
||||
"paramsUnion_74",
|
||||
"paramsUnion_75",
|
||||
"paramsUnion_76",
|
||||
"returnSingleton_78",
|
||||
"returnPair_79",
|
||||
"returnTriple_80",
|
||||
"returnQuad_81",
|
||||
"returnUnion_82",
|
||||
"returnUnion_83",
|
||||
"returnSingleton_85",
|
||||
"returnPair_86",
|
||||
"returnUnion_89",
|
||||
"returnSingleton_92",
|
||||
"returnPair_93",
|
||||
"returnUnion_96",
|
||||
"returnPrimitive_97",
|
||||
"returnSingleton_98",
|
||||
"returnUnion_102",
|
||||
"returnPrimitive_106",
|
||||
"returnSingleton_107",
|
||||
"returnUnion_112",
|
||||
"returnPrimitive_115",
|
||||
"returnPrimitive_125",
|
||||
};
|
||||
|
||||
public MSP430X_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class MSP430_CSpecTest extends CSpecPrototypeTest {
|
||||
private static final String LANGUAGE_ID = "TI_MSP430:LE:16:default";
|
||||
private static final String COMPILER_SPEC_ID = "default";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__stdcall";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"paramsVariadic_I_I_C_C_S_S_I_I_L_L_50",
|
||||
"paramsUnion_56",
|
||||
"paramsUnion_57",
|
||||
"paramsUnion_58",
|
||||
"paramsUnion_59",
|
||||
"paramsUnion_60",
|
||||
"paramsUnion_61",
|
||||
"paramsUnion_62",
|
||||
"paramsUnion_63",
|
||||
"paramsUnion_64",
|
||||
"paramsUnion_65",
|
||||
"paramsUnion_66",
|
||||
"paramsUnion_67",
|
||||
"paramsUnion_68",
|
||||
"paramsUnion_69",
|
||||
"paramsUnion_70",
|
||||
"paramsUnion_71",
|
||||
"paramsUnion_72",
|
||||
"paramsUnion_73",
|
||||
"paramsUnion_74",
|
||||
"paramsUnion_75",
|
||||
"paramsUnion_76",
|
||||
"returnUnion_82",
|
||||
"returnUnion_83",
|
||||
"returnUnion_89",
|
||||
"returnUnion_90",
|
||||
"returnUnion_96",
|
||||
"returnUnion_102",
|
||||
"returnUnion_104",
|
||||
"returnUnion_112",
|
||||
"returnUnion_121",
|
||||
"returnUnion_130",
|
||||
};
|
||||
|
||||
public MSP430_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class X86_64_gcc_CSpecTest extends CSpecPrototypeTest {
|
||||
private static final String LANGUAGE_ID = "x86:LE:64:default";
|
||||
private static final String COMPILER_SPEC_ID = "gcc";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__stdcall";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"returnMixed_103",
|
||||
"returnMixed_123"
|
||||
};
|
||||
|
||||
public X86_64_gcc_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class X86_64_gcc_MSABI_CSpecTest extends CSpecPrototypeTest {
|
||||
|
||||
private static final String LANGUAGE_ID = "x86:LE:64:default";
|
||||
private static final String COMPILER_SPEC_ID = "gcc";
|
||||
|
||||
private static final String CALLING_CONVENTION = "MSABI";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"paramsSingletonStruct_22",
|
||||
"paramsSingletonStruct_23",
|
||||
"paramsMisc_53",
|
||||
"paramsUnion_63",
|
||||
"paramsUnion_64",
|
||||
"returnUnion_83",
|
||||
"returnUnion_90",
|
||||
"returnQuad_95",
|
||||
"returnPair_99",
|
||||
"returnMixed_103",
|
||||
"returnUnion_104",
|
||||
"returnQuad_110",
|
||||
"returnUnion_113",
|
||||
"returnPair_117",
|
||||
"returnMixed_123",
|
||||
"returnPair_127",
|
||||
};
|
||||
|
||||
public X86_64_gcc_MSABI_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class X86_64_windows_CSpecTest extends CSpecPrototypeTest {
|
||||
private static final String LANGUAGE_ID = "x86:LE:64:default";
|
||||
private static final String COMPILER_SPEC_ID = "windows";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__fastcall";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"paramsUnion_63",
|
||||
"paramsUnion_64",
|
||||
"returnUnion_83",
|
||||
"returnUnion_90"
|
||||
};
|
||||
|
||||
public X86_64_windows_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.test.processors.cspec;
|
||||
|
||||
import ghidra.test.compilers.support.CSpecPrototypeTest;
|
||||
|
||||
public class X86_gcc_CSpecTest extends CSpecPrototypeTest {
|
||||
private static final String LANGUAGE_ID = "x86:LE:32:default";
|
||||
private static final String COMPILER_SPEC_ID = "gcc";
|
||||
|
||||
private static final String CALLING_CONVENTION = "__cdecl";
|
||||
|
||||
private static final String[] EXPECTED_PROTOTYPE_ERRORS = {
|
||||
"returnUnion_82",
|
||||
"returnUnion_83",
|
||||
"returnUnion_89",
|
||||
"returnUnion_90",
|
||||
"returnUnion_96",
|
||||
"returnUnion_102",
|
||||
"returnUnion_112",
|
||||
"returnUnion_121",
|
||||
"returnUnion_130"
|
||||
};
|
||||
|
||||
public X86_gcc_CSpecTest() throws Exception {
|
||||
super(EXPECTED_PROTOTYPE_ERRORS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageID() {
|
||||
return LANGUAGE_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCompilerSpecID() {
|
||||
return COMPILER_SPEC_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallingConvention() {
|
||||
return CALLING_CONVENTION;
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,7 @@ configurations {
|
||||
integrationTestImplementation.extendsFrom testImplementation
|
||||
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly, integrationTestImplementation
|
||||
pcodeTestImplementation.extendsFrom implementation
|
||||
cspecTestImplementation.extendsFrom implementation
|
||||
scriptsImplementation.extendsFrom implementation
|
||||
testArtifacts.extendsFrom testRuntimeOnly
|
||||
integrationTestArtifacts.extendsFrom integrationTestRuntimeOnly
|
||||
@@ -163,6 +164,7 @@ dependencies {
|
||||
|
||||
testImplementation "junit:junit:4.13.2"
|
||||
pcodeTestImplementation "junit:junit:4.13.2"
|
||||
cspecTestImplementation "junit:junit:4.13.2"
|
||||
}
|
||||
|
||||
// For Java 9, we must explicitly export references to the internal classes we are using.
|
||||
|
||||
@@ -94,6 +94,33 @@ task pcodeTest (type: Test) { t ->
|
||||
}
|
||||
}
|
||||
|
||||
task cspecTest (type: Test) { t ->
|
||||
description = "Runs cspec tests."
|
||||
group = "cspecTest"
|
||||
testClassesDirs = files sourceSets.pcodeTest.output.classesDirs
|
||||
classpath = sourceSets.pcodeTest.runtimeClasspath
|
||||
|
||||
filter {
|
||||
setIncludePatterns() // Clear default Ghidra test filters
|
||||
// Tell JUnit to ONLY run tests located in the cspec package
|
||||
includeTestsMatching "ghidra.test.processors.cspec.*"
|
||||
|
||||
failOnNoMatchingTests = false
|
||||
}
|
||||
|
||||
// Enable if you want to force Gradle to launch a new JVM for each test.
|
||||
forkEvery = 1
|
||||
|
||||
initTestJVM(t, rootProject.ext.cspecTestRootDirName)
|
||||
|
||||
doFirst {
|
||||
startTestTimer(t)
|
||||
}
|
||||
doLast {
|
||||
endTestTimer(t)
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.unitTestReport {
|
||||
testResults.from(this.project.test)
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ if (!project.hasProperty('srcTreeName')) {
|
||||
|
||||
project.ext.testRootDirName = "JunitTest_" + srcTreeName
|
||||
project.ext.pcodeTestRootDirName = "PCodeTest_" + srcTreeName
|
||||
project.ext.cspecTestRootDirName = "CSpecTest_" + srcTreeName
|
||||
|
||||
project.ext.shareDir = System.properties.get('share.dir')
|
||||
if (project.ext.shareDir != null) {
|
||||
|
||||
Reference in New Issue
Block a user