GT-3041_emteere Added emulation tests and minor changes to calling

convention.  Assigning correct return storage will require a separate
change to core Ghidra.

Pulled-from: mumbel <22204578+mumbel@users.noreply.github.com>
This commit is contained in:
emteere 2019-08-02 10:40:38 -04:00
parent 56d427ce50
commit 0a517e6864
10 changed files with 432 additions and 63 deletions

View File

@ -4,16 +4,16 @@
Module.manifest||GHIDRA||||END|
build.gradle||GHIDRA||||END|
data/build.xml||GHIDRA||||END|
data/languages/tricore.cspec||GHIDRA||||END|
data/languages/tricore.ldefs||GHIDRA||||END|
data/languages/tricore.pspec||GHIDRA||||END|
data/languages/tc29x.pspec||GHIDRA||||END|
data/languages/tc172x.pspec||GHIDRA||||END|
data/languages/tc176x.pspec||GHIDRA||||END|
data/languages/tricore.slaspec||GHIDRA||||END|
data/languages/tricore.sinc||GHIDRA||||END|
data/languages/tricore.pcp.sinc||GHIDRA||||END|
data/languages/tc29x.pspec||GHIDRA||||END|
data/languages/tricore.cspec||GHIDRA||||END|
data/languages/tricore.dwarf||GHIDRA||||END|
data/languages/tricore.ldefs||GHIDRA||||END|
data/languages/tricore.opinion||GHIDRA||||END|
data/languages/tricore.pcp.sinc||GHIDRA||||END|
data/languages/tricore.pspec||GHIDRA||||END|
data/languages/tricore.sinc||GHIDRA||||END|
data/languages/tricore.slaspec||GHIDRA||||END|
data/manuals/tricore.idx||GHIDRA||||END|
data/manuals/tricore2.idx||GHIDRA||||END|

View File

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<processor_spec>
<properties>
<property key="emulateInstructionStateModifierClass"
value="ghidra.program.emulation.TRICOREEmulateInstructionStateModifier"/>
</properties>
<programcounter register="PC"/>
<data_space space="ram"/>

View File

@ -1,10 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<processor_spec>
<properties>
<property key="emulateInstructionStateModifierClass"
value="ghidra.program.emulation.TRICOREEmulateInstructionStateModifier"/>
</properties>
<programcounter register="PC"/>
<data_space space="ram"/>
<default_memory_blocks>
<memory_block name="LDRAM.13" start_address="0xd0000000" length="0x1e000" mode="rwv" initialized="false"/>
<memory_block name="SPRAM.13" start_address="0xd4000000" length="0x6000" mode="rwv" initialized="false"/>

View File

@ -1,10 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<processor_spec>
<properties>
<property key="emulateInstructionStateModifierClass"
value="ghidra.program.emulation.TRICOREEmulateInstructionStateModifier"/>
</properties>
<programcounter register="PC"/>
<data_space space="ram"/>
<default_memory_blocks>
<memory_block name="CPU2_DSPR" start_address="0x50000000" length="0x1E000" mode="rwv" initialized="false"/>
<memory_block name="CPU2_PSPR" start_address="0x50100000" length="0x8000" mode="rwv" initialized="false"/>

View File

@ -20,78 +20,146 @@
<entry size="8" alignment="4" />
</size_alignment_map>
</data_organization>
<spacebase name="a0_" register="a0" space="ram"/>
<spacebase name="a1_" register="a1" space="ram"/>
<spacebase name="a8_" register="a8" space="ram"/>
<spacebase name="a9_" register="a9" space="ram"/>
<global>
<register name="a8"/>
<register name="a9"/>
<register name="p8"/>
<register name="a0"/>
<register name="a1"/>
<register name="p0"/>
<range space="ram"/>
</global>
<returnaddress>
<register name="a11"/>
</returnaddress>
<stackpointer register="a10" space="ram"/>
<default_proto>
<prototype name="__stdcall" extrapop="0" stackshift="0" strategy="register">
<input>
<pentry minsize="1" maxsize="4">
<!-- This is the first non pointer -->
<register name="a4"/>
</pentry>
<pentry minsize="1" maxsize="4">
<!-- This is the first non pointer -->
<register name="d4"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a5"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d5"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a6"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d6"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a7"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d7"/>
</pentry>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="16" space="ram"/>
</pentry>
<pentry minsize="1" maxsize="4"> <!-- This is the first non pointer -->
<register name="a4"/>
</pentry>
<pentry minsize="1" maxsize="4"> <!-- This is the first non pointer -->
<register name="d4"/>
</pentry>
<pentry minsize="5" maxsize="8"> <!-- This is the first >4 byte non pointer -->
<register name="e4"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a5"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d5"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a6"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d6"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a7"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d7"/>
</pentry>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="16" space="ram"/>
</pentry>
</input>
<!-- There are issues with locking in function signatures with multiple possible
return locations. When the signature is committed/locked, Ghidra will apply the
first available location, which is incorrect. The decompiler can figure
out between two variable locations but doesn't currently pass back the
return storage location. A fix is needed, or use custom storage. -->
<output>
<pentry minsize="1" maxsize="4">
<pentry minsize="1" maxsize="4" metatype="ptr">
<register name="a2"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d2"/>
<pentry minsize="1" maxsize="8">
<register name="e2"/>
</pentry>
</output>
<unaffected>
<register name="d8"/>
<register name="d9"/>
<register name="d10"/>
<register name="d11"/>
<register name="d12"/>
<register name="d13"/>
<register name="d14"/>
<register name="d15"/>
<register name="a10"/>
<register name="a11"/>
<register name="a12"/>
<register name="a13"/>
<register name="a14"/>
<register name="a15"/>
<register name="d8"/>
<register name="d9"/>
<register name="d10"/>
<register name="d11"/>
<register name="d12"/>
<register name="d13"/>
<register name="d14"/>
<register name="d15"/>
<register name="a10"/>
<register name="a11"/>
<register name="a12"/>
<register name="a13"/>
<register name="a14"/>
<register name="a15"/>
</unaffected>
</prototype>
</default_proto>
<!-- This will no longer be necessary once fixes are made to support the
correct choice of storage location when there are multiple return types -->
<prototype name="__stdcall_data" extrapop="0" stackshift="0" strategy="register">
<input>
<pentry minsize="1" maxsize="4"> <!-- This is the first non pointer -->
<register name="a4"/>
</pentry>
<pentry minsize="1" maxsize="4"> <!-- This is the first non pointer -->
<register name="d4"/>
</pentry>
<pentry minsize="5" maxsize="8"> <!-- This is the first >4 byte non pointer -->
<register name="e4"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a5"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d5"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a6"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d6"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="a7"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="d7"/>
</pentry>
<pentry minsize="1" maxsize="500" align="4">
<addr offset="16" space="ram"/>
</pentry>
</input>
<output>
<pentry minsize="1" maxsize="8">
<register name="e2"/>
</pentry>
</output>
<unaffected>
<register name="d8"/>
<register name="d9"/>
<register name="d10"/>
<register name="d11"/>
<register name="d12"/>
<register name="d13"/>
<register name="d14"/>
<register name="d15"/>
<register name="a10"/>
<register name="a11"/>
<register name="a12"/>
<register name="a13"/>
<register name="a14"/>
<register name="a15"/>
</unaffected>
</prototype>
<callotherfixup targetop="saveCallerState">
<pcode>
<input name="fcx"/>

View File

@ -0,0 +1,5 @@
<opinions>
<constraint loader="Executable and Linking Format (ELF)" compilerSpecID="gcc">
<constraint primary="44" processor="tricore" endian="little" size="32" />
</constraint>
</opinions>

View File

@ -1,6 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<processor_spec>
<properties>
<property key="emulateInstructionStateModifierClass"
value="ghidra.program.emulation.TRICOREEmulateInstructionStateModifier"/>
</properties>
<programcounter register="PC"/>
<volatile outputop="write" inputop="read">
<range space="ram" first="0x0" last="0x20"/>

View File

@ -0,0 +1,168 @@
/* ###
* 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.program.emulation;
import java.math.BigInteger;
import ghidra.pcode.emulate.Emulate;
import ghidra.pcode.emulate.EmulateInstructionStateModifier;
import ghidra.pcode.emulate.callother.OpBehaviorOther;
import ghidra.pcode.error.LowlevelError;
import ghidra.pcode.memstate.MemoryState;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.program.model.pcode.Varnode;
public class TRICOREEmulateInstructionStateModifier extends EmulateInstructionStateModifier {
Register FCX,PCXI,LCX,PSW,a10,a11,d8,a12,d12;
public TRICOREEmulateInstructionStateModifier(Emulate emu) {
super(emu);
registerPcodeOpBehavior("saveCallerState", new tricore_SaveCallerState());
registerPcodeOpBehavior("restoreCallerState", new tricore_RestoreCallerState());
cacheRegisters(emu);
}
// Save Caller State, could be done in Pcode
//
private class tricore_SaveCallerState implements OpBehaviorOther {
@Override
public void evaluate(Emulate emu, Varnode outputVarnode, Varnode[] inputs) {
int numArgs = inputs.length - 1;
if (numArgs != 3) throw new LowlevelError(this.getClass().getName() + ": requires 3 inputs (FCX, LCX, PCXI), got " + numArgs);
MemoryState memoryState = emu.getMemoryState();
// compute new EA
BigInteger FCXvalue = memoryState.getBigInteger(FCX);
// read the value at FCX, if get nothing, then assume just increment the FCX to get to new node.
// EA = {FCX.FCXS, 6'b0, FCX.FCXO, 6'b0};
long ea = FCXvalue.longValue();
ea = ((ea & 0xffff0000) << 12) | ((ea & 0xffff) << 6);
Address EA_addr = emu.getExecuteAddress().getNewAddress(ea);
AddressSpace addressSpace = emu.getExecuteAddress().getAddressSpace();
// new_FCX = M(EA, word);
BigInteger new_FCXvalue = memoryState.getBigInteger(addressSpace, ea, 4, false);
// if new_FCX == 0, or not-initialized, then just increment FCX again
if (new_FCXvalue.equals(BigInteger.ZERO)) {
new_FCXvalue = FCXvalue.add(BigInteger.ONE);
}
// M(EA,16 * word) = {PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12], A[13], A[14], A[15], D[12], D[13], D[14], D[15]};
byte[] outBytes = new byte[4*16];
int index = 0;
index += copyRegisterToArray(PCXI, PCXI.getBitLength()/8, memoryState, outBytes, index);
index += copyRegisterToArray(PSW, PSW.getBitLength()/8, memoryState, outBytes, index);
index += copyRegisterToArray(a10, 2 * a10.getBitLength()/8, memoryState, outBytes, index);
index += copyRegisterToArray(d8, 4 * d8.getBitLength()/8, memoryState, outBytes, index);
index += copyRegisterToArray(a12, 4 * a12.getBitLength()/8, memoryState, outBytes, index);
index += copyRegisterToArray(d12, 4 * d12.getBitLength()/8, memoryState, outBytes, index);
// write the bytes
memoryState.setChunk(outBytes, EA_addr.getAddressSpace(), EA_addr.getOffset(), 4*16);
BigInteger PCXIvalue = memoryState.getBigInteger(PCXI);
// PCXI[19:0] = FCX[19:0];
// PCXI.PCPN = ICR.CCPN;
// PCXI.PIE = ICR.IE;
// PCXI.UL = 1;
PCXIvalue = PCXIvalue.andNot(BigInteger.valueOf(0x000fffff)).or(FCXvalue.and(BigInteger.valueOf(0x000fffff)));
memoryState.setValue(PCXI, PCXIvalue);
// FCX[19:0] = new_FCX[19:0];
FCXvalue = FCXvalue.andNot(BigInteger.valueOf(0x000fffff)).or(new_FCXvalue.and(BigInteger.valueOf(0x000fffff)));
memoryState.setValue(FCX, FCXvalue);
// write to memory
BigInteger LCXvalue = memoryState.getBigInteger(LCX);
}
}
private class tricore_RestoreCallerState implements OpBehaviorOther {
@Override
public void evaluate(Emulate emu, Varnode outputVarnode, Varnode[] inputs) {
int numArgs = inputs.length - 1;
if (numArgs != 3) throw new LowlevelError(this.getClass().getName() + ": requires 3 inputs (FCX, LCX, PCXI), got " + numArgs);
MemoryState memoryState = emu.getMemoryState();
// compute new EA
BigInteger FCXvalue = memoryState.getBigInteger(FCX);
BigInteger PCXIvalue = memoryState.getBigInteger(PCXI);
// read the value at FCX, if get nothing, then assume just increment the FCX to get to new node.
// EA = {FCX.FCXS, 6'b0, FCX.FCXO, 6'b0};
long ea = PCXIvalue.longValue();
ea = ((ea & 0xffff0000) << 12) | ((ea & 0xffff) << 6);
Address EA_addr = emu.getExecuteAddress().getNewAddress(ea);
AddressSpace addressSpace = emu.getExecuteAddress().getAddressSpace();
// read the bytes
byte[] inBytes = new byte[4*16];
memoryState.getChunk(inBytes, addressSpace, EA_addr.getOffset(), 4*16, true);
// {PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12], A[13], A[14], A[15], D[12], D[13], D[14], D[15] = M(EA,16 * word)};
int index = 0;
index += copyArrayToRegister(PCXI, PCXI.getBitLength()/8, memoryState, inBytes, index);
index += copyArrayToRegister(PSW, PSW.getBitLength()/8, memoryState, inBytes, index);
index += copyArrayToRegister(a10, 2 * a10.getBitLength()/8, memoryState, inBytes, index);
index += copyArrayToRegister(d8, 4 * d8.getBitLength()/8, memoryState, inBytes, index);
index += copyArrayToRegister(a12, 4 * a12.getBitLength()/8, memoryState, inBytes, index);
index += copyArrayToRegister(d12, 4 * d12.getBitLength()/8, memoryState, inBytes, index);
// M(EA, word) = FCX;
memoryState.setValue(EA_addr.getAddressSpace(), EA_addr.getOffset(), 4, FCXvalue);
// FCX[19:0] = new_FCX[19:0];
FCXvalue = FCXvalue.andNot(BigInteger.valueOf(0x000fffff)).or(PCXIvalue.and(BigInteger.valueOf(0x000fffff)));
memoryState.setValue(FCX, FCXvalue);
}
}
// Helper functions
private int copyRegisterToArray(Register reg, int len, MemoryState memoryState, byte[] outBytes, int i) {
byte vBytes[] = new byte[len];
int nread = memoryState.getChunk(vBytes, reg.getAddressSpace(), reg.getOffset(), len, false);
System.arraycopy(vBytes, 0, outBytes, i, len);
return nread;
}
private int copyArrayToRegister(Register reg, int len, MemoryState memoryState, byte[] inBytes, int i) {
byte[] vBytes = new byte[len];
AddressSpace spc = reg.getAddressSpace();
System.arraycopy(inBytes, i, vBytes, 0, vBytes.length);
memoryState.setChunk(vBytes, spc, reg.getOffset(), vBytes.length);
return len;
}
private void cacheRegisters(Emulate emu) {
FCX = emu.getLanguage().getRegister("FCX");
LCX = emu.getLanguage().getRegister("LCX");
PCXI = emu.getLanguage().getRegister("PCXI");
PSW = emu.getLanguage().getRegister("PSW");
a10 = emu.getLanguage().getRegister("a10");
d8 = emu.getLanguage().getRegister("d8");
a12 = emu.getLanguage().getRegister("a12");
d12 = emu.getLanguage().getRegister("d12");
}
}

View File

@ -0,0 +1,50 @@
/* ###
* 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;
import ghidra.framework.options.Options;
import ghidra.program.model.listing.Program;
import ghidra.test.processors.support.EmulatorTestRunner;
import ghidra.test.processors.support.ProcessorEmulatorTestAdapter;
import junit.framework.Test;
public class TRICORE_BE_O0_EmulatorTest extends ProcessorEmulatorTestAdapter {
private static final String LANGUAGE_ID = "tricore:LE:32:default";
private static final String COMPILER_SPEC_ID = "default";
private static final String[] REG_DUMP_SET = new String[] {};
public TRICORE_BE_O0_EmulatorTest(String name) throws Exception {
super(name, LANGUAGE_ID, COMPILER_SPEC_ID, REG_DUMP_SET);
}
protected String getProcessorDesignator() { return "tricore_GCC_O0"; }
protected void initializeState(EmulatorTestRunner testRunner, Program program) throws Exception {
testRunner.setRegister("a10", 0x40000000L); // stack, unused location
testRunner.setRegister("FCX", 0x00020000L); // free context list start, unused location
testRunner.setRegister("LCX", 0x00030000L); // free context list max
testRunner.setRegister("PCXI", 0x0L); // current thread context list
}
public static Test suite() {
return ProcessorEmulatorTestAdapter.buildEmulatorTestSuite(TRICORE_BE_O0_EmulatorTest.class);
}
protected void setAnalysisOptions(Options analysisOptions) {
super.setAnalysisOptions(analysisOptions);
analysisOptions.setBoolean("Reference", false); // too many bad disassemblies
}
}

View File

@ -0,0 +1,61 @@
/* ###
* 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;
import ghidra.framework.options.Options;
import ghidra.program.model.listing.Program;
import ghidra.test.processors.support.EmulatorTestRunner;
import ghidra.test.processors.support.ProcessorEmulatorTestAdapter;
import junit.framework.Test;
public class TRICORE_BE_O3_EmulatorTest extends ProcessorEmulatorTestAdapter {
private static final String LANGUAGE_ID = "tricore:LE:32:default";
private static final String COMPILER_SPEC_ID = "default";
private static final String[] REG_DUMP_SET = new String[] {};
public TRICORE_BE_O3_EmulatorTest(String name) throws Exception {
super(name, LANGUAGE_ID, COMPILER_SPEC_ID, REG_DUMP_SET);
}
@Override
protected String getProcessorDesignator() {
return "tricore_GCC_O3";
}
@Override
protected void initializeState(EmulatorTestRunner testRunner, Program program) throws Exception {
testRunner.setRegister("a10", 0x40000000L); // stack, unused location
testRunner.setRegister("FCX", 0x00020000L); // free context list start, unused location
testRunner.setRegister("LCX", 0x00030000L); // free context list max
testRunner.setRegister("PCXI", 0x0L); // current thread context list
}
@Override
protected void setAnalysisOptions(Options analysisOptions) {
super.setAnalysisOptions(analysisOptions);
analysisOptions.setBoolean("Reference", false); // too many bad disassemblies
}
public static Test suite() {
return ProcessorEmulatorTestAdapter.buildEmulatorTestSuite(TRICORE_BE_O3_EmulatorTest.class);
}
}