Merge remote-tracking branch

'origin/GP-3808-emteere-SPARC_delaySlotReturn' (Closes #5646,
Closes #6300)
This commit is contained in:
Ryan Kurtz 2024-09-09 05:59:32 -04:00
commit f390125c1f
15 changed files with 906 additions and 113 deletions

View File

@ -614,6 +614,7 @@ public class SymbolicPropogator {
// FLOW end will probably work correctly, but.... // FLOW end will probably work correctly, but....
// //
vContext.flowStart(minInstrAddress, maxAddr); vContext.flowStart(minInstrAddress, maxAddr);
retAddr = null;
} }
} }

View File

@ -11,5 +11,5 @@ data/languages/SparcV9_64.cspec||GHIDRA||||END|
data/languages/SparcV9_64.slaspec||GHIDRA||||END| data/languages/SparcV9_64.slaspec||GHIDRA||||END|
data/languages/SparcVIS.sinc||GHIDRA||||END| data/languages/SparcVIS.sinc||GHIDRA||||END|
data/manuals/Sparc.idx||GHIDRA||reviewed||END| data/manuals/Sparc.idx||GHIDRA||reviewed||END|
data/patterns/SPARC_patterns.xml||GHIDRA||reviewed||END| data/patterns/SPARC_patterns.xml||GHIDRA||||END|
data/patterns/patternconstraints.xml||GHIDRA||||END| data/patterns/patternconstraints.xml||GHIDRA||||END|

View File

@ -5,7 +5,7 @@
endian="big" endian="big"
size="32" size="32"
variant="default" variant="default"
version="1.4" version="1.5"
slafile="SparcV9_32.sla" slafile="SparcV9_32.sla"
processorspec="SparcV9.pspec" processorspec="SparcV9.pspec"
manualindexfile="../manuals/Sparc.idx" manualindexfile="../manuals/Sparc.idx"
@ -20,7 +20,7 @@
endian="big" endian="big"
size="64" size="64"
variant="default" variant="default"
version="1.4" version="1.5"
slafile="SparcV9_64.sla" slafile="SparcV9_64.sla"
processorspec="SparcV9.pspec" processorspec="SparcV9.pspec"
manualindexfile="../manuals/Sparc.idx" manualindexfile="../manuals/Sparc.idx"

View File

@ -351,7 +351,13 @@ RS2: rs2 is rs2 { export rs2; }
#For the destination operand RD, we export a temporary varnode with value 0. #For the destination operand RD, we export a temporary varnode with value 0.
#This is because writes to g0 are allowed, but they have no visible effect (see the Sparc manual). #This is because writes to g0 are allowed, but they have no visible effect (see the Sparc manual).
#This way the value of g0 won't appear to change when using the pcode emulator. #This way the value of g0 won't appear to change when using the pcode emulator.
#
RD: rd is rd & rd_zero=0 { local tmp:$(SIZE) = 0; export tmp; } RD: rd is rd & rd_zero=0 { local tmp:$(SIZE) = 0; export tmp; }
# didrestore is picked up by call instruction only
# this will cause any instruction that assigns to the o7 return address register
# in the delay slot of a call instruction to turn the call into a call/return
#
RD: rd is rd & rd_d=15 { didrestore = 1; export rd; }
RD: rd is rd { export rd; } RD: rd is rd { export rd; }
regorimm: RS2 is i=0 & RS2 { export RS2; } regorimm: RS2 is i=0 & RS2 { export RS2; }
@ -990,8 +996,14 @@ sethidisp: "%hi("^hi^")" is udisp22 [hi=udisp22<<10;] { export *[const]:$(SIZE)
# special case where link register is loaded with return address; functions as indirect call # special case where link register is loaded with return address; functions as indirect call
:jmpl retea,RD is op=0x2 & RD & prd=15 & op3=0x38 & retea { build retea; RD = inst_start; delayslot(1); call [retea]; } :jmpl retea,RD is op=0x2 & RD & prd=15 & op3=0x38 & retea { build retea; RD = inst_start; delayslot(1); call [retea]; }
:jmpl retea is op=0x2 & rd=0 & op3=0x38 & retea { build retea; delayslot(1); goto [retea]; }
:jmpl retea is op=0x2 & rd=0 & op3=0x38 & retea { build retea; delayslot(1); goto [retea]; } # special case: when returning a structure, some software inserts unimpl <struct size> instruction after every caller
# jumps to linkRegister(o7)+12, instead of normal linkregister(o7)+8
:jmpl retea is op=0x2 & rd=0 & rs1=31 & op3=0x38 & i=1 & simm13=12 & retea { build retea; delayslot(1); return [retea]; }
:jmpl retea is op=0x2 & rd=0 & rs1=15 & op3=0x38 & i=1 & simm13=12 & retea { build retea; delayslot(1); return [retea]; }
# really jmpl instruction using linkRegister(o7)+8
:ret is op=0x2 & rd=0 & rs1=31 & op3=0x38 & i=1 & simm13=8 & retea { build retea; delayslot(1); return [retea]; } :ret is op=0x2 & rd=0 & rs1=31 & op3=0x38 & i=1 & simm13=8 & retea { build retea; delayslot(1); return [retea]; }
:retl is op=0x2 & rd=0 & rs1=15 & op3=0x38 & i=1 & simm13=8 & retea { build retea; delayslot(1); return [retea]; } :retl is op=0x2 & rd=0 & rs1=15 & op3=0x38 & i=1 & simm13=8 & retea { build retea; delayslot(1); return [retea]; }

View File

@ -30,41 +30,72 @@
<default_proto> <default_proto>
<prototype name="__stdcall" extrapop="0" stackshift="0"> <prototype name="__stdcall" extrapop="0" stackshift="0">
<input> <input>
<pentry minsize="1" maxsize="4"> <pentry minsize="4" maxsize="4" storage="hiddenret">
<addr offset="64" space="stack"/>
</pentry>
<pentry minsize="1" maxsize="4" extension="inttype">
<register name="o0"/> <register name="o0"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="4"> <pentry minsize="1" maxsize="4" extension="inttype">
<register name="o1"/> <register name="o1"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="4"> <pentry minsize="1" maxsize="4" extension="inttype">
<register name="o2"/> <register name="o2"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="4"> <pentry minsize="1" maxsize="4" extension="inttype">
<register name="o3"/> <register name="o3"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="4"> <pentry minsize="1" maxsize="4" extension="inttype">
<register name="o4"/> <register name="o4"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="4"> <pentry minsize="1" maxsize="4" extension="inttype">
<register name="o5"/> <register name="o5"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="500" align="4"> <pentry minsize="1" maxsize="500" align="4">
<addr offset="0x5c" space="stack"/> <addr offset="0x5c" space="stack"/>
</pentry> </pentry>
<rule>
<datatype name="struct"/>
<convert_to_ptr/>
</rule>
<rule>
<datatype name="any" minsize="9"/>
<convert_to_ptr/>
</rule>
<rule>
<datatype name="any" maxsize="8"/>
<join storage="general"/> <!-- join is NOT aligned -->
</rule>
</input> </input>
<output> <output>
<pentry minsize="4" maxsize="4" metatype="float"> <pentry minsize="1" maxsize="4" metatype="float">
<register name="fs0"/> <register name="fs0"/>
</pentry> </pentry>
<pentry minsize="8" maxsize="8" metatype="float"> <pentry minsize="1" maxsize="4" metatype="float">
<register name="fd0"/> <register name="fs1"/>
</pentry> </pentry>
<pentry minsize="16" maxsize="16" metatype="float"> <pentry minsize="1" maxsize="4" extension="inttype">
<register name="fq0"/>
</pentry>
<pentry minsize="1" maxsize="4">
<register name="o0"/> <register name="o0"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="4">
<register name="o1"/>
</pentry>
<rule>
<datatype name="struct"/>
<hidden_return/>
</rule>
<rule>
<datatype name="any" minsize="9"/>
<hidden_return/>
</rule>
<rule>
<datatype name="float" maxsize="8"/>
<join storage="float"/>
</rule>
<rule>
<datatype name="any" maxsize="8"/>
<join storage="general"/>
</rule>
</output> </output>
<unaffected> <unaffected>
<register name="g0"/> <register name="g0"/>

View File

@ -14,7 +14,7 @@
<long_long_size value="8" /> <long_long_size value="8" />
<float_size value="4" /> <float_size value="4" />
<double_size value="8" /> <double_size value="8" />
<long_double_size value="12" /> <long_double_size value="16" />
<size_alignment_map> <size_alignment_map>
<entry size="1" alignment="1" /> <entry size="1" alignment="1" />
<entry size="2" alignment="2" /> <entry size="2" alignment="2" />
@ -30,6 +30,9 @@
<default_proto> <default_proto>
<prototype name="__stdcall" extrapop="0" stackshift="0"> <prototype name="__stdcall" extrapop="0" stackshift="0">
<input> <input>
<pentry minsize="8" maxsize="8" storage="hiddenret">
<addr offset="0x7ef" space="stack"/> <!-- hidden return storage is pointer in callers area of the stack -->
</pentry>
<pentry minsize="4" maxsize="8" metatype="float"> <pentry minsize="4" maxsize="8" metatype="float">
<register name="fd0"/> <register name="fd0"/>
</pentry> </pentry>
@ -42,53 +45,72 @@
<pentry minsize="4" maxsize="8" metatype="float"> <pentry minsize="4" maxsize="8" metatype="float">
<register name="fd6"/> <register name="fd6"/>
</pentry> </pentry>
<pentry minsize="16" maxsize="16" metatype="float"> <pentry minsize="4" maxsize="8" metatype="float">
<register name="fq0"/> <register name="fd8"/>
</pentry> </pentry>
<pentry minsize="16" maxsize="16" metatype="float"> <pentry minsize="4" maxsize="8" metatype="float">
<register name="fq4"/> <register name="fd10"/>
</pentry> </pentry>
<pentry minsize="16" maxsize="16" metatype="float"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="fq8"/>
</pentry>
<pentry minsize="16" maxsize="16" metatype="float">
<register name="fq12"/>
</pentry>
<pentry minsize="1" maxsize="8">
<register name="o0"/> <register name="o0"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="o1"/> <register name="o1"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="o2"/> <register name="o2"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="o3"/> <register name="o3"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="o4"/> <register name="o4"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="o5"/> <register name="o5"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="500" align="8"> <pentry minsize="1" maxsize="500" align="8">
<addr offset="0x8af" space="stack"/> <!-- Big offset is due to SPARC 64-bit "stack bias" --> <addr offset="0x8af" space="stack"/> <!-- Big offset is due to SPARC 64-bit "stack bias" -->
</pentry> </pentry>
<rule>
<datatype name="struct"/>
<convert_to_ptr/>
</rule>
<rule>
<datatype name="float" minsize="4" maxsize="8"/>
<consume storage="float"/>
<consume_extra storage="general"/> <!-- if consume a float slot, must consume an integer slot -->
</rule>
<rule>
<datatype name="float" minsize="16" maxsize="16"/>
<join storage="float"/>
<consume_extra storage="general"/> <!-- if join two float slots, must skip two integer slots -->
<consume_extra storage="general"/>
</rule>
<rule>
<datatype name="any"/>
<join storage="general"/>
<consume_extra storage="float"/> <!-- if consume an integer slot, must consume a float slot -->
</rule>
</input> </input>
<output> <output>
<pentry minsize="4" maxsize="4" metatype="float"> <pentry minsize="4" maxsize="8" metatype="float">
<register name="fs0"/>
</pentry>
<pentry minsize="8" maxsize="8" metatype="float">
<register name="fd0"/> <register name="fd0"/>
</pentry> </pentry>
<pentry minsize="16" maxsize="16" metatype="float"> <pentry minsize="4" maxsize="8" metatype="float">
<register name="fq0"/> <register name="fd2"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="o0"/> <register name="o0"/>
</pentry> </pentry>
<rule>
<datatype name="struct"/>
<hidden_return/>
</rule>
<rule>
<datatype name="float" minsize="8" maxsize="16"/>
<join storage="float"/>
</rule>
</output> </output>
<unaffected> <unaffected>
<register name="g0"/> <register name="g0"/>
@ -127,22 +149,22 @@
<prototype name="__nonwindowcall" extrapop="0" stackshift="0"> <prototype name="__nonwindowcall" extrapop="0" stackshift="0">
<input> <input>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="g1"/> <register name="g1"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="g2"/> <register name="g2"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="g3"/> <register name="g3"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="g4"/> <register name="g4"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="g5"/> <register name="g5"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="8"> <pentry minsize="1" maxsize="8" extension="inttype">
<register name="g6"/> <register name="g6"/>
</pentry> </pentry>
<pentry minsize="1" maxsize="500" align="8"> <pentry minsize="1" maxsize="500" align="8">
@ -150,7 +172,16 @@
</pentry> </pentry>
</input> </input>
<output> <output>
<pentry minsize="1" maxsize="8"> <pentry minsize="4" maxsize="4" metatype="float">
<register name="fs0"/>
</pentry>
<pentry minsize="8" maxsize="8" metatype="float">
<register name="fd0"/>
</pentry>
<pentry minsize="16" maxsize="16" metatype="float">
<register name="fq0"/>
</pentry>
<pentry minsize="1" maxsize="8" extension="inttype">
<register name="g0"/> <register name="g0"/>
</pentry> </pentry>
</output> </output>
@ -187,4 +218,32 @@
<range space="stack" first="0x0" last="0x8ae"/> <!-- Stack bias of 7FF + 0xb0 window size --> <range space="stack" first="0x0" last="0x8ae"/> <!-- Stack bias of 7FF + 0xb0 window size -->
</localrange> </localrange>
</prototype> </prototype>
<callfixup name="sparc_get_pc_thunk.l7">
<target name="__sparc_get_pc_thunk.l7"/>
<pcode>
<body><![CDATA[
l7 = o7 + l7;
]]></body>
</pcode>
</callfixup>
<callfixup name="sparc_get_pc_thunk.g1">
<target name="__sparc_get_pc_thunk.g1"/>
<pcode>
<body><![CDATA[
g1 = o7 + g1;
]]></body>
</pcode>
</callfixup>
<callfixup name="sparc_get_pc_thunk.o0">
<target name="__sparc_get_pc_thunk.o0"/>
<pcode>
<body><![CDATA[
o0 = o7 + o0;
]]></body>
</pcode>
</callfixup>
</compiler_spec> </compiler_spec>

View File

@ -32,4 +32,19 @@
<funcstart/> <funcstart/>
</postpatterns> </postpatterns>
</patternpairs> </patternpairs>
<pattern>
<data> 0x81 0xc3 0xe0 0x08 0xae 0x03 0xc0 0x17 </data> <!-- retl ; _add o7,l7,l7 -->
<funcstart label="__sparc_get_pc_thunk.l7" validcode="function"/>
</pattern>
<pattern>
<data> 0x81 0xc3 0xe0 0x08 0x82 0x03 0xc0 0x01 </data> <!-- retl ; _add o7,g1,g1 -->
<funcstart label="__sparc_get_pc_thunk.g1" validcode="function"/>
</pattern>
<pattern>
<data> 0x81 0xc3 0xe0 0x08 0x90 0x02 0x00 0x0f </data> <!-- retl ; _add o0,o7,o0 -->
<funcstart label="__sparc_get_pc_thunk.o0" validcode="function"/>
</pattern>
</patternlist> </patternlist>

View File

@ -17,13 +17,17 @@ package ghidra.app.plugin.core.analysis;
import ghidra.app.plugin.core.clear.ClearFlowAndRepairCmd; import ghidra.app.plugin.core.clear.ClearFlowAndRepairCmd;
import ghidra.app.services.AnalysisPriority; import ghidra.app.services.AnalysisPriority;
import ghidra.framework.options.Options;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.lang.Processor; import ghidra.program.model.lang.Processor;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.FlowType; import ghidra.program.model.symbol.FlowType;
import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.Reference;
import ghidra.program.util.*; import ghidra.program.util.SymbolicPropogator;
import ghidra.program.util.VarnodeContext;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -35,6 +39,14 @@ import ghidra.util.task.TaskMonitor;
public class SparcAnalyzer extends ConstantPropagationAnalyzer { public class SparcAnalyzer extends ConstantPropagationAnalyzer {
private final static String PROCESSOR_NAME = "Sparc"; private final static String PROCESSOR_NAME = "Sparc";
// option to turn off o7 call return analysis
protected static final String O7_CALLRETURN_NAME = "Call/Return o7 check";
protected static final String O7_CALLRETURN_DESCRIPTION =
"Turn on check for setting of o7 return link register in delay slot of all calls";
protected static final boolean EO7_CALLRETURN_DEFAULT_VALUE = true;
protected boolean o7CallReturnAnalysis = EO7_CALLRETURN_DEFAULT_VALUE;
public SparcAnalyzer() { public SparcAnalyzer() {
super(PROCESSOR_NAME); super(PROCESSOR_NAME);
@ -43,36 +55,16 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
@Override @Override
public boolean canAnalyze(Program program) { public boolean canAnalyze(Program program) {
return program.getLanguage().getProcessor().equals( Processor processor = program.getLanguage().getProcessor();
Processor.findOrPossiblyCreateProcessor(PROCESSOR_NAME));
return processor.equals(Processor.findOrPossiblyCreateProcessor(PROCESSOR_NAME));
} }
@Override @Override
public AddressSetView flowConstants(final Program program, Address flowStart, AddressSetView flowSet, final SymbolicPropogator symEval, final TaskMonitor monitor) public AddressSetView flowConstants(final Program program, Address flowStart, AddressSetView flowSet, final SymbolicPropogator symEval, final TaskMonitor monitor)
throws CancelledException { throws CancelledException {
// get the function body
Function func = program.getFunctionManager().getFunctionContaining(flowStart);
if (func != null) {
flowSet = func.getBody();
flowStart = func.getEntryPoint();
Instruction instr = program.getListing().getInstructionAt(flowStart); Register linkReg = program.getRegister("o7");
// special case for leaf PIC call
if (instr.getMnemonicString().equals("retl")) {
Instruction dInstr =
program.getListing().getInstructionAfter(instr.getMinAddress());
if (dInstr.getMnemonicString().equals("_add")) {
Register r0 = dInstr.getRegister(0);
Register r1 = dInstr.getRegister(1);
Register r2 = dInstr.getRegister(2);
// add some register to the o7 register. This is just getting offset of current location
if (r0 != null && r0.getName().equals("o7") && r1 != null && r1.equals(r2)) {
func.setInline(true);
}
}
}
}
// follow all flows building up context // follow all flows building up context
// use context to fill out addresses on certain instructions // use context to fill out addresses on certain instructions
@ -83,7 +75,7 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
FlowType ftype = instr.getFlowType(); FlowType ftype = instr.getFlowType();
// Check for a call with a restore in the delay slot // Check for a call with a restore in the delay slot
// Then it is a non-returning call (share the called function return // Then it is a non-returning call (share the called function return
if (ftype.isCall()) { if (o7CallReturnAnalysis && ftype.isCall()) {
Address fallAddr = instr.getFallThrough(); Address fallAddr = instr.getFallThrough();
if (fallAddr == null) { if (fallAddr == null) {
return false; return false;
@ -93,7 +85,16 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
if (delayInstr == null) { if (delayInstr == null) {
return false; return false;
} }
if (delayInstr.getMnemonicString().compareToIgnoreCase("_restore") == 0) { PcodeOp[] pcode = delayInstr.getPcode();
for (PcodeOp pcodeOp : pcode) {
Varnode output = pcodeOp.getOutput();
if (output == null || !output.equals(context.getRegisterVarnode(linkReg))) {
continue;
}
Varnode input = pcodeOp.getInput(0);
if (input.isConstant()) {
continue; // this is just assigning the return value after the call
}
instr.setFallThrough(null); instr.setFallThrough(null);
Instruction fallInstr = Instruction fallInstr =
instr.getProgram().getListing().getInstructionAt(fallAddr); instr.getProgram().getListing().getInstructionAt(fallAddr);
@ -106,6 +107,8 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
ClearFlowAndRepairCmd cmd = ClearFlowAndRepairCmd cmd =
new ClearFlowAndRepairCmd(fallAddr, false, false, true); new ClearFlowAndRepairCmd(fallAddr, false, false, true);
cmd.applyTo(instr.getProgram(), monitor); cmd.applyTo(instr.getProgram(), monitor);
break;
} }
} }
return false; return false;
@ -137,4 +140,17 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
return resultSet; return resultSet;
} }
@Override
public void registerOptions(Options options, Program program) {
super.registerOptions(options, program);
options.registerOption(O7_CALLRETURN_NAME, o7CallReturnAnalysis, null, O7_CALLRETURN_DESCRIPTION);
}
@Override
public void optionsChanged(Options options, Program program) {
super.optionsChanged(options, program);
o7CallReturnAnalysis = options.getBoolean(O7_CALLRETURN_NAME, o7CallReturnAnalysis);
}
} }

View File

@ -0,0 +1,86 @@
/* ###
* 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.app.plugin.core.analysis;
import ghidra.app.services.AnalysisPriority;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Analyze all call instructions with a delay slot to see if the o7 register is changed to something other than
* the normal return address after the call instruction.
*
* Note: This extends the SparcAnalyzer to use the same Analyzer name, this doesn't do constant analysis.
*/
public class SparcEarlyAddressAnalyzer extends SparcAnalyzer {
/**
* The early Sparc analyzer catches instructions with sets of the o7 link
* address register to a value other than right after the function
*/
public SparcEarlyAddressAnalyzer() {
super();
// analysis should happen right after disassembly
this.setPriority(AnalysisPriority.DISASSEMBLY);
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
if (!o7CallReturnAnalysis) {
return true;
}
AddressSet unanalyzedSet = new AddressSet(set);
Register linkReg = program.getLanguage().getRegister("o7");
InstructionIterator instructions = program.getListing().getInstructions(unanalyzedSet, true);
for (Instruction instr : instructions) {
if (!instr.getFlowType().isCall()) {
continue;
}
if (!instr.hasFallthrough()) {
continue;
}
PcodeOp[] pcode = instr.getPcode();
for (PcodeOp pcodeOp : pcode) {
Varnode output = pcodeOp.getOutput();
if (output == null || !output.getAddress().equals(linkReg.getAddress())) {
continue;
}
Varnode input = pcodeOp.getInput(0);
if (input.isConstant()) {
continue; // this is just assigning the return value after the call
}
//instr.setFallThrough(null);
instr.setFlowOverride(FlowOverride.CALL_RETURN);
break;
}
}
return true;
}
}

View File

@ -0,0 +1,150 @@
/* ###
* 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.app.util.bin.format.elf.relocation;
import ghidra.app.util.bin.format.elf.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.RelocationResult;
import ghidra.program.model.reloc.Relocation.Status;
public class SPARC64_ElfRelocationHandler extends SPARC_ElfRelocationHandler {
@Override
public boolean canRelocate(ElfHeader elf) {
boolean handleMachine = elf.e_machine() == ElfConstants.EM_SPARCV9;
return handleMachine && elf.is64Bit();
}
@Override
protected RelocationResult relocate(ElfRelocationContext<?> elfRelocationContext,
ElfRelocation relocation, SPARC_ElfRelocationType type, Address relocationAddress,
ElfSymbol sym, Address symbolAddr, long symbolValue, String symbolName)
throws MemoryAccessException {
Program program = elfRelocationContext.getProgram();
Memory memory = program.getMemory();
long addend = relocation.getAddend();
long pc = relocationAddress.getOffset();
int oldIntValue = memory.getInt(relocationAddress);
int newIntValue = 0;
long newLongValue = 0;
int mask = 0;
int byteLength = 8; // most relocations affect 8-bytes (change if different)
// Relocation docs: https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter6-24-1/index.html
//
switch (type) {
case R_SPARC_HI22:
newIntValue = (int) ((symbolValue + addend) >>> 10);
mask = 0x003fffff;
oldIntValue &= ~(mask);
newIntValue &= mask;
memory.setInt(relocationAddress, oldIntValue | newIntValue);
byteLength = 4;
break;
case R_SPARC_OLO10:
newIntValue = (int) ((symbolValue + addend) & 0x3FF) + (int)((relocation.getRelocationInfo()<<32)>>40);
mask = 0x00001fff;
oldIntValue &= ~(mask);
newIntValue &= mask;
memory.setInt(relocationAddress, oldIntValue | newIntValue);
byteLength = 4;
break;
case R_SPARC_GLOB_DAT:
newLongValue = symbolValue + addend;
memory.setLong(relocationAddress, newLongValue);
break;
case R_SPARC_RELATIVE:
newLongValue = elfRelocationContext.getImageBaseWordAdjustmentOffset() + addend;
memory.setLong(relocationAddress, newLongValue);
break;
case R_SPARC_64:
newLongValue = symbolValue + addend;
memory.setLong(relocationAddress, newLongValue);
break;
case R_SPARC_DISP64:
newLongValue = symbolValue + addend - pc;
memory.setLong(relocationAddress, newLongValue);
break;
case R_SPARC_UA64:
case R_SPARC_REGISTER:
newLongValue = symbolValue + addend;
memory.setLong(relocationAddress, newLongValue);
break;
case R_SPARC_SIZE64:
newLongValue = sym.getSize() + addend;
memory.setLong(relocationAddress, newLongValue);
break;
case R_SPARC_H34:
newIntValue = (int) ((symbolValue + addend) >>> 12);
mask = 0x003fffff;
oldIntValue &= ~(mask);
newIntValue &= mask;
memory.setInt(relocationAddress, oldIntValue | newIntValue);
byteLength = 4;
break;
case R_SPARC_JMP_SLOT:
final int sparc_sethi_g1 = 0x03000000; // sethi %hi(0x123),g1
final int sparc_sethi_g5 = 0x0b000000; // sethi %hi(0x123),g5
final int sparc_or_g5_immed_g5 = 0x8a116000; // or g5,0x123,g5
final int sparc_or_g1_immed_g1 = 0x82106000; // or g1,0x123,g1
final int sparc_sllx_g1_0x20 = 0x83287020; // sllx g1,32,g1
final int sparc_jmpl_g1_g5 = 0x81c04005; // jmpl g1+g5
final int sparc_nop = 0x01000000; // nop
// this is not the way JMP_SLOT is always done, but it should work in all cases of a large 64-bit address
// other variants are optimized to handle smaller address values
newLongValue = (symbolValue + addend);
long hh = newLongValue >> 42;
long hl = (newLongValue >> 32) & 0x3ff;
long lh = (newLongValue & 0xffffffff) >> 10;
long ll = newLongValue & 0x3ff;
memory.setInt(relocationAddress, (int) (sparc_sethi_g1 | hh));
memory.setInt(relocationAddress.add(4), (int) (sparc_sethi_g5 | lh));
memory.setInt(relocationAddress.add(8), (int) (sparc_or_g1_immed_g1 | hl));
memory.setInt(relocationAddress.add(12), (int) (sparc_or_g5_immed_g5 | ll));
memory.setInt(relocationAddress.add(16), (sparc_sllx_g1_0x20));
memory.setInt(relocationAddress.add(20), (sparc_jmpl_g1_g5));
memory.setInt(relocationAddress.add(24), sparc_nop);
break;
case R_SPARC_PLT64:
default:
// other relocations handled by base SPARC relocation handler, including marking unhandled relocations
return super.relocate(elfRelocationContext, relocation, type, relocationAddress, sym, symbolAddr, symbolValue, symbolName);
}
return new RelocationResult(Status.APPLIED, byteLength);
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -35,9 +35,10 @@ public class SPARC_ElfRelocationHandler
@Override @Override
public boolean canRelocate(ElfHeader elf) { public boolean canRelocate(ElfHeader elf) {
return elf.e_machine() == ElfConstants.EM_SPARC || boolean handleMachine = elf.e_machine() == ElfConstants.EM_SPARC ||
elf.e_machine() == ElfConstants.EM_SPARC32PLUS || elf.e_machine() == ElfConstants.EM_SPARC32PLUS ||
elf.e_machine() == ElfConstants.EM_SPARCV9; elf.e_machine() == ElfConstants.EM_SPARCV9;
return handleMachine && elf.is32Bit();
} }
@Override @Override
@ -49,14 +50,14 @@ public class SPARC_ElfRelocationHandler
Program program = elfRelocationContext.getProgram(); Program program = elfRelocationContext.getProgram();
Memory memory = program.getMemory(); Memory memory = program.getMemory();
long addend = relocation.getAddend(); // will be 0 for REL case long addend = relocation.getAddend();
// TODO: possible sign-extension seems wrong; there are both 32-bit and 64-bit variants long pc = relocationAddress.getOffset();
long offset = (int) relocationAddress.getOffset();
int symbolIndex = relocation.getSymbolIndex(); int symbolIndex = relocation.getSymbolIndex();
int oldValue = memory.getInt(relocationAddress); int oldValue = memory.getInt(relocationAddress);
int newValue = 0; long newValue = 0;
int mask = 0;
int byteLength = 4; // most relocations affect 4-bytes (change if different) int byteLength = 4; // most relocations affect 4-bytes (change if different)
// Handle relative relocations that do not require symbolAddr or symbolValue // Handle relative relocations that do not require symbolAddr or symbolValue
@ -64,7 +65,7 @@ public class SPARC_ElfRelocationHandler
case R_SPARC_RELATIVE: case R_SPARC_RELATIVE:
newValue = (int) elfRelocationContext.getElfHeader().getImageBase() + (int) addend; newValue = (int) elfRelocationContext.getElfHeader().getImageBase() + (int) addend;
memory.setInt(relocationAddress, newValue); memory.setInt(relocationAddress, (int) newValue);
return new RelocationResult(Status.APPLIED, byteLength); return new RelocationResult(Status.APPLIED, byteLength);
case R_SPARC_COPY: case R_SPARC_COPY:
@ -80,39 +81,300 @@ public class SPARC_ElfRelocationHandler
if (handleUnresolvedSymbol(elfRelocationContext, relocation, relocationAddress)) { if (handleUnresolvedSymbol(elfRelocationContext, relocation, relocationAddress)) {
return RelocationResult.FAILURE; return RelocationResult.FAILURE;
} }
// Relocation docs: https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter6-24/index.html
//
switch (type) { switch (type) {
case R_SPARC_DISP32: case R_SPARC_8:
newValue = (int) (symbolValue + addend - offset); newValue = symbolValue + addend;
memory.setInt(relocationAddress, oldValue | newValue); mask = 0x000000ff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break; break;
case R_SPARC_WDISP30:
newValue = (int) (symbolValue + addend - offset) >>> 2; case R_SPARC_16:
memory.setInt(relocationAddress, oldValue | newValue); newValue = symbolValue + addend;
mask = 0x0000ffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break; break;
case R_SPARC_HI22:
newValue = ((int) symbolValue + (int) addend) >>> 10;
memory.setInt(relocationAddress, oldValue | newValue);
break;
case R_SPARC_LO10:
newValue = ((int) symbolValue + (int) addend) & 0x3FF;
memory.setInt(relocationAddress, oldValue | newValue);
break;
case R_SPARC_JMP_SLOT:
// should copy address of symbol in EXTERNAL block
case R_SPARC_32: case R_SPARC_32:
newValue = (int) symbolValue + (int) addend; newValue = symbolValue + addend;
memory.setInt(relocationAddress, newValue); memory.setInt(relocationAddress, (int) newValue);
break; break;
// we punt on this because it's not linked yet!
case R_SPARC_DISP8:
newValue = (symbolValue + addend - pc);
mask = 0x000000ff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_DISP16:
newValue = (symbolValue + addend - pc);
mask = 0x0000ffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_DISP32:
newValue = symbolValue + addend - pc;
memory.setInt(relocationAddress, (int) newValue);
break;
case R_SPARC_WDISP30:
newValue = (symbolValue + addend - pc) >>> 2;
mask = 0x3fffffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_WDISP22:
newValue = (symbolValue + addend - pc) >>> 2;
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_HI22:
newValue = (symbolValue + addend) >>> 10;
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_22:
newValue = (symbolValue + addend);
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_13:
newValue = (symbolValue + addend);
mask = 0x001fff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_LO10:
newValue = (symbolValue + addend);
mask = 0x000003ff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_PC10:
newValue = (symbolValue + addend - pc);
mask = 0x00003ff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_PC22:
newValue = (symbolValue + addend - pc) >> 10;
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_JMP_SLOT:
final int sparc_sethi_g1 = 0x03000000; // sethi %hi(0x123),g1
final int sparc_jmpl_g1_immed_o1 = 0x81c06000; // jmpl g1+0x123
final int sparc_nop = 0x01000000; // nop
// this is not the way JMP_SLOT is always done, but it should work in all cases
newValue = (symbolValue + addend);
memory.setInt(relocationAddress, (int) (sparc_sethi_g1 | (newValue >> 10)));
memory.setInt(relocationAddress.add(4), (int) (sparc_jmpl_g1_immed_o1 | (newValue & 0x3ff)));
memory.setInt(relocationAddress.add(8), sparc_nop);
break;
case R_SPARC_GLOB_DAT: case R_SPARC_GLOB_DAT:
newValue = (int) symbolValue; newValue = symbolValue;
memory.setInt(relocationAddress, newValue); memory.setInt(relocationAddress, (int) newValue);
break; break;
case R_SPARC_RELATIVE:
newValue = elfRelocationContext.getImageBaseWordAdjustmentOffset() + addend;
memory.setInt(relocationAddress, (int) newValue);
break;
case R_SPARC_UA32: case R_SPARC_UA32:
newValue = (int) symbolValue + (int) addend; newValue = symbolValue + addend;
memory.setInt(relocationAddress, newValue); memory.setInt(relocationAddress, (int) newValue);
break; break;
case R_SPARC_10:
newValue = (symbolValue + addend);
mask = 0x0000003ff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_11:
newValue = (symbolValue + addend);
mask = 0x0000007ff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_HH22:
newValue = (symbolValue + addend) >> 42;
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_HM10:
newValue = (symbolValue + addend) >> 32;
mask = 0x000003ff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_LM22:
newValue = (symbolValue + addend) >>> 10;
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_PC_HH22:
newValue = (symbolValue + addend - pc) >> 42;
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_PC_HM10:
newValue = (symbolValue + addend - pc) >> 32;
mask = 0x000003ff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_PC_LM22:
newValue = (symbolValue + addend - pc) >> 10;
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_WDISP16:
newValue = (symbolValue + addend - pc) >>> 2;
oldValue &= 0x303fff;
newValue = ((newValue & 0xc000) << 6) | (newValue & 0x3fff);
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_WDISP19:
newValue = (symbolValue + addend - pc) >>> 2;
mask = 0x0007ffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_7:
newValue = (symbolValue + addend);
mask = 0x0000007f;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_5:
newValue = (symbolValue + addend);
mask = 0x0000001f;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_6:
newValue = (symbolValue + addend);
mask = 0x0000003f;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_HIX22:
newValue = ( (symbolValue + addend) ^ (-1) ) >> 10;
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_LOX10:
newValue = ( (symbolValue + addend) & 0x3ff ) | 0x1c00;
mask = 0x001fff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_H44:
newValue = (symbolValue + addend) >> 22;
mask = 0x003fffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_M44:
newValue = (symbolValue + addend) >> 12;
mask = 0x000003ff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_L44:
newValue = (symbolValue + addend);
mask = 0x00000fff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_UA16:
newValue = (symbolValue + addend);
mask = 0x0000ffff;
oldValue &= ~(mask);
newValue &= mask;
memory.setInt(relocationAddress, oldValue | (int) newValue);
break;
case R_SPARC_COPY:
markAsUnsupportedCopy(program, relocationAddress, type, symbolName, symbolIndex,
sym.getSize(), elfRelocationContext.getLog());
return RelocationResult.UNSUPPORTED;
default: default:
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName, markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
elfRelocationContext.getLog()); elfRelocationContext.getLog());

View File

@ -54,7 +54,7 @@ public enum SPARC_ElfRelocationType implements ElfRelocationType {
R_SPARC_HH22(34), // (S + A) >> 42 R_SPARC_HH22(34), // (S + A) >> 42
R_SPARC_HM10(35), // ((S + A) >> 32) & 0x3ff R_SPARC_HM10(35), // ((S + A) >> 32) & 0x3ff
R_SPARC_LM22(36), // (S + A) >> 10 R_SPARC_LM22(36), // (S + A) >> 10
R_SPARC_PC_H22(37), // (S + A - P) >> 42 R_SPARC_PC_HH22(37), // (S + A - P) >> 42
R_SPARC_PC_HM10(38), // ((S + A - P) >> 32) & 0x3ff R_SPARC_PC_HM10(38), // ((S + A - P) >> 32) & 0x3ff
R_SPARC_PC_LM22(39), // (S + A - P) >> 10 R_SPARC_PC_LM22(39), // (S + A - P) >> 10
R_SPARC_WDISP16(40), // (S + A - P) >> 2 R_SPARC_WDISP16(40), // (S + A - P) >> 2

View File

@ -87,6 +87,7 @@ public class AbstractProtoModelTest extends AbstractGenericTest {
dtManager.addDataType(new CharDataType(), null); dtManager.addDataType(new CharDataType(), null);
dtManager.addDataType(new UnsignedIntegerDataType(), null); dtManager.addDataType(new UnsignedIntegerDataType(), null);
dtManager.addDataType(new LongDataType(), null); dtManager.addDataType(new LongDataType(), null);
dtManager.addDataType(new LongLongDataType(), null);
dtManager.addDataType(new FloatDataType(), null); dtManager.addDataType(new FloatDataType(), null);
dtManager.addDataType(new DoubleDataType(), null); dtManager.addDataType(new DoubleDataType(), null);
dtManager.addDataType(new Float16DataType(), null); dtManager.addDataType(new Float16DataType(), null);
@ -245,7 +246,9 @@ public class AbstractProtoModelTest extends AbstractGenericTest {
parseStores(storeData, stores); parseStores(storeData, stores);
Assert.assertEquals(storeData.size(), res.size()); Assert.assertEquals(storeData.size(), res.size());
for (int i = 0; i < res.size(); ++i) { for (int i = 0; i < res.size(); ++i) {
boolean compare = comparePiece(storeData.get(i), res.get(i)); ParameterPieces resPiece = res.get(i);
ArrayList<Varnode> storePiece = storeData.get(i);
boolean compare = comparePiece(storePiece, resPiece);
String message = null; String message = null;
if (!compare) { if (!compare) {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
@ -253,10 +256,10 @@ public class AbstractProtoModelTest extends AbstractGenericTest {
buffer.append(cspec.getCompilerSpecID()); buffer.append(cspec.getCompilerSpecID());
buffer.append(' ').append(model.getName()).append(' '); buffer.append(' ').append(model.getName()).append(' ');
if (i == 0) { if (i == 0) {
buffer.append("Output does not match for "); buffer.append("Output ").append("@"+toString(resPiece)).append(" does not match for ");
} }
else { else {
buffer.append("Parameter ").append(i - 1).append(" does not match for: "); buffer.append("Parameter ").append(i - 1).append(" @"+toString(resPiece)+" ").append(" does not match for: ");
} }
buffer.append(signature); buffer.append(signature);
message = buffer.toString(); message = buffer.toString();
@ -265,4 +268,37 @@ public class AbstractProtoModelTest extends AbstractGenericTest {
} }
} }
private String toString(ParameterPieces resPiece) {
Varnode[] joinPieces = resPiece.joinPieces;
if (joinPieces != null) {
StringBuilder buffer = new StringBuilder("join ");
for (Varnode varnode : joinPieces) {
buffer.append(toString(varnode)).append(" ");
}
return buffer.toString();
}
Address addr = resPiece.address;
resPiece.type.getLength();
if (addr != null) {
if (addr.isRegisterAddress()) {
return language.getRegister(addr, resPiece.type.getLength()).getName();
}
return addr.toString();
}
return "UNKNOWN";
}
private String toString(Varnode varnode) {
if (varnode == null) {
return "@null";
}
if (varnode.isRegister()) {
return language.getRegister(varnode.getAddress(), varnode.getSize()).getName();
}
return varnode.toString();
}
} }

View File

@ -0,0 +1,53 @@
/* ###
* 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.model.lang;
import org.junit.Before;
import org.junit.Test;
public class Sparc32ProtoModelTest extends AbstractProtoModelTest {
@Before
public void setUp() throws Exception {
buildArchitecture("sparc:BE:32:default:default");
}
@Test
public void testStdCall() throws Exception {
PrototypeModel model = cspec.getCallingConvention("__stdcall");
test(model, "void func(int,long long,int,long long,int,int)",
"void,o0,join o1 o2,o3,join o4 o5,stack5c:4,stack60:4");
test(model, "void func(float,double,float,double,float,float)",
"void,o0,join o1 o2,o3,join o4 o5,stack5c:4,stack60:4");
test(model, "void func(int,double,long long,float,float)",
"void,o0,join o1 o2,join o3 o4,o5,stack5c:4");
test(model, "void func(int,long double,float)", "void,o0,o1,o2");
parseStructure("intpair", "int,int");
test(model, "void func(int,intpair,double)", "void,o0,o1,join o2 o3");
test(model, "int func()", "o0");
test(model, "long long func()", "join o0 o1");
test(model, "float func()", "fs0");
test(model, "double func()", "join fs0 fs1");
test(model, "long double func(int,int)", "o0,stack40:4,o0,o1");
test(model, "intpair func(long long)", "o0,stack40:4,join o0 o1");
}
}

View File

@ -0,0 +1,72 @@
/* ###
* 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.model.lang;
import org.junit.Before;
import org.junit.Test;
public class Sparc64ProtoModelTest extends AbstractProtoModelTest {
@Before
public void setUp() throws Exception {
buildArchitecture("sparc:BE:64:default:default");
}
@Test
public void testStdCall() throws Exception {
PrototypeModel model = cspec.getCallingConvention("__stdcall");
test(model, "void func(int,long long,int,long long,int,int,int)",
"void,o0:4,o1,o2:4,o3,o4:4,o5:4,stack8b3:4");
// extra checks of floating point mixed with integer types
// is to ensure that floating point and integer types with consume the
// space for the opposite type
test(model, "void func(float,float,char)", "void,fs1,fs3,o2:1");
test(model, "void func(double,double)", "void,fd0,fd2");
test(model, "void func(long double,int)", "void,join fd0 fd2,o4:4");
test(model, "void func(int,double,float,long long)", "void,o0:4,fd2,fs5,o3");
test(model, "void func(float,double,float,double,float,float)",
"void,fs1,fd2,fs5,fd6,fs9,fs11");
test(model, "void func(int,double,long long,float,float)",
"void,o0:4,fd2,o2,fd6:4,fs9");
test(model, "void func(int,double,long long,float,float)",
"void,o0:4,fd2,o2,fs7,fs9");
test(model, "char func()", "o0:1");
test(model, "int func()", "o0:4");
test(model, "long long func()", "o0");
test(model, "float func()", "fs1");
test(model, "double func()", "fd0");
// structures passed as pointer arguments
parseStructure("intpair", "int,int");
test(model, "void func(int,intpair,double)", "void,o0:4,o1,fd4");
// hidden return of structure
test(model, "intpair func(long long)", "o0,stack7ef:8,o0");
test(model, "long double func(int,int)", "join fd0 fd2,o0:4,o1:4");
}
}