Merge remote-tracking branch 'origin/GP-5838_ghidragander_calling-convention-junit--SQUASHED'

This commit is contained in:
Ryan Kurtz
2026-04-22 10:32:00 -04:00
21 changed files with 3892 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -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,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;
}
}
+2
View File
@@ -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.
+27
View File
@@ -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)
}
+1
View File
@@ -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) {