mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-10 06:02:09 +00:00
GP-3808 Fixed sparc 32/64 prototype model, added missing instructions, fixed 64/32 relocations, handle call/return checking o7 link register
This commit is contained in:
parent
b78c975ee3
commit
edb74d7b7d
@ -594,6 +594,7 @@ public class SymbolicPropogator {
|
||||
// FLOW end will probably work correctly, but....
|
||||
//
|
||||
vContext.flowStart(minInstrAddress, maxAddr);
|
||||
retAddr = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,5 +11,5 @@ data/languages/SparcV9_64.cspec||GHIDRA||||END|
|
||||
data/languages/SparcV9_64.slaspec||GHIDRA||||END|
|
||||
data/languages/SparcVIS.sinc||GHIDRA||||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|
|
||||
|
@ -5,7 +5,7 @@
|
||||
endian="big"
|
||||
size="32"
|
||||
variant="default"
|
||||
version="1.4"
|
||||
version="1.5"
|
||||
slafile="SparcV9_32.sla"
|
||||
processorspec="SparcV9.pspec"
|
||||
manualindexfile="../manuals/Sparc.idx"
|
||||
@ -20,7 +20,7 @@
|
||||
endian="big"
|
||||
size="64"
|
||||
variant="default"
|
||||
version="1.4"
|
||||
version="1.5"
|
||||
slafile="SparcV9_64.sla"
|
||||
processorspec="SparcV9.pspec"
|
||||
manualindexfile="../manuals/Sparc.idx"
|
||||
|
@ -351,7 +351,13 @@ RS2: rs2 is rs2 { export rs2; }
|
||||
#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 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; }
|
||||
# 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; }
|
||||
|
||||
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
|
||||
: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]; }
|
||||
:retl is op=0x2 & rd=0 & rs1=15 & op3=0x38 & i=1 & simm13=8 & retea { build retea; delayslot(1); return [retea]; }
|
||||
|
||||
|
@ -30,41 +30,72 @@
|
||||
<default_proto>
|
||||
<prototype name="__stdcall" extrapop="0" stackshift="0">
|
||||
<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"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<pentry minsize="1" maxsize="4" extension="inttype">
|
||||
<register name="o1"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<pentry minsize="1" maxsize="4" extension="inttype">
|
||||
<register name="o2"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<pentry minsize="1" maxsize="4" extension="inttype">
|
||||
<register name="o3"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<pentry minsize="1" maxsize="4" extension="inttype">
|
||||
<register name="o4"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<pentry minsize="1" maxsize="4" extension="inttype">
|
||||
<register name="o5"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="500" align="4">
|
||||
<addr offset="0x5c" space="stack"/>
|
||||
</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>
|
||||
<output>
|
||||
<pentry minsize="4" maxsize="4" metatype="float">
|
||||
<pentry minsize="1" maxsize="4" metatype="float">
|
||||
<register name="fs0"/>
|
||||
</pentry>
|
||||
<pentry minsize="8" maxsize="8" metatype="float">
|
||||
<register name="fd0"/>
|
||||
<pentry minsize="1" maxsize="4" metatype="float">
|
||||
<register name="fs1"/>
|
||||
</pentry>
|
||||
<pentry minsize="16" maxsize="16" metatype="float">
|
||||
<register name="fq0"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="4">
|
||||
<pentry minsize="1" maxsize="4" extension="inttype">
|
||||
<register name="o0"/>
|
||||
</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>
|
||||
<unaffected>
|
||||
<register name="g0"/>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<long_long_size value="8" />
|
||||
<float_size value="4" />
|
||||
<double_size value="8" />
|
||||
<long_double_size value="12" />
|
||||
<long_double_size value="16" />
|
||||
<size_alignment_map>
|
||||
<entry size="1" alignment="1" />
|
||||
<entry size="2" alignment="2" />
|
||||
@ -30,6 +30,9 @@
|
||||
<default_proto>
|
||||
<prototype name="__stdcall" extrapop="0" stackshift="0">
|
||||
<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">
|
||||
<register name="fd0"/>
|
||||
</pentry>
|
||||
@ -42,53 +45,72 @@
|
||||
<pentry minsize="4" maxsize="8" metatype="float">
|
||||
<register name="fd6"/>
|
||||
</pentry>
|
||||
<pentry minsize="16" maxsize="16" metatype="float">
|
||||
<register name="fq0"/>
|
||||
<pentry minsize="4" maxsize="8" metatype="float">
|
||||
<register name="fd8"/>
|
||||
</pentry>
|
||||
<pentry minsize="16" maxsize="16" metatype="float">
|
||||
<register name="fq4"/>
|
||||
<pentry minsize="4" maxsize="8" metatype="float">
|
||||
<register name="fd10"/>
|
||||
</pentry>
|
||||
<pentry minsize="16" maxsize="16" metatype="float">
|
||||
<register name="fq8"/>
|
||||
</pentry>
|
||||
<pentry minsize="16" maxsize="16" metatype="float">
|
||||
<register name="fq12"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="o0"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="o1"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="o2"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="o3"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="o4"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="o5"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="500" align="8">
|
||||
<addr offset="0x8af" space="stack"/> <!-- Big offset is due to SPARC 64-bit "stack bias" -->
|
||||
</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>
|
||||
<output>
|
||||
<pentry minsize="4" maxsize="4" metatype="float">
|
||||
<register name="fs0"/>
|
||||
</pentry>
|
||||
<pentry minsize="8" maxsize="8" metatype="float">
|
||||
<pentry minsize="4" maxsize="8" metatype="float">
|
||||
<register name="fd0"/>
|
||||
</pentry>
|
||||
<pentry minsize="16" maxsize="16" metatype="float">
|
||||
<register name="fq0"/>
|
||||
<pentry minsize="4" maxsize="8" metatype="float">
|
||||
<register name="fd2"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="o0"/>
|
||||
</pentry>
|
||||
<rule>
|
||||
<datatype name="struct"/>
|
||||
<hidden_return/>
|
||||
</rule>
|
||||
<rule>
|
||||
<datatype name="float" minsize="8" maxsize="16"/>
|
||||
<join storage="float"/>
|
||||
</rule>
|
||||
</output>
|
||||
<unaffected>
|
||||
<register name="g0"/>
|
||||
@ -127,22 +149,22 @@
|
||||
|
||||
<prototype name="__nonwindowcall" extrapop="0" stackshift="0">
|
||||
<input>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="g1"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="g2"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="g3"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="g4"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="g5"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="8">
|
||||
<pentry minsize="1" maxsize="8" extension="inttype">
|
||||
<register name="g6"/>
|
||||
</pentry>
|
||||
<pentry minsize="1" maxsize="500" align="8">
|
||||
@ -150,7 +172,16 @@
|
||||
</pentry>
|
||||
</input>
|
||||
<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"/>
|
||||
</pentry>
|
||||
</output>
|
||||
@ -187,4 +218,32 @@
|
||||
<range space="stack" first="0x0" last="0x8ae"/> <!-- Stack bias of 7FF + 0xb0 window size -->
|
||||
</localrange>
|
||||
</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>
|
||||
|
@ -32,4 +32,19 @@
|
||||
<funcstart/>
|
||||
</postpatterns>
|
||||
</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>
|
||||
|
@ -17,13 +17,17 @@ package ghidra.app.plugin.core.analysis;
|
||||
|
||||
import ghidra.app.plugin.core.clear.ClearFlowAndRepairCmd;
|
||||
import ghidra.app.services.AnalysisPriority;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.Processor;
|
||||
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.program.model.symbol.FlowType;
|
||||
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.task.TaskMonitor;
|
||||
|
||||
@ -35,6 +39,14 @@ import ghidra.util.task.TaskMonitor;
|
||||
public class SparcAnalyzer extends ConstantPropagationAnalyzer {
|
||||
|
||||
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() {
|
||||
super(PROCESSOR_NAME);
|
||||
@ -43,36 +55,16 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
|
||||
|
||||
@Override
|
||||
public boolean canAnalyze(Program program) {
|
||||
return program.getLanguage().getProcessor().equals(
|
||||
Processor.findOrPossiblyCreateProcessor(PROCESSOR_NAME));
|
||||
Processor processor = program.getLanguage().getProcessor();
|
||||
|
||||
return processor.equals(Processor.findOrPossiblyCreateProcessor(PROCESSOR_NAME));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView flowConstants(final Program program, Address flowStart, AddressSetView flowSet, final SymbolicPropogator symEval, final TaskMonitor monitor)
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Register linkReg = program.getRegister("o7");
|
||||
|
||||
// follow all flows building up context
|
||||
// use context to fill out addresses on certain instructions
|
||||
@ -83,7 +75,7 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
|
||||
FlowType ftype = instr.getFlowType();
|
||||
// Check for a call with a restore in the delay slot
|
||||
// Then it is a non-returning call (share the called function return
|
||||
if (ftype.isCall()) {
|
||||
if (o7CallReturnAnalysis && ftype.isCall()) {
|
||||
Address fallAddr = instr.getFallThrough();
|
||||
if (fallAddr == null) {
|
||||
return false;
|
||||
@ -93,7 +85,16 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
|
||||
if (delayInstr == null) {
|
||||
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);
|
||||
Instruction fallInstr =
|
||||
instr.getProgram().getListing().getInstructionAt(fallAddr);
|
||||
@ -106,6 +107,8 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
|
||||
ClearFlowAndRepairCmd cmd =
|
||||
new ClearFlowAndRepairCmd(fallAddr, false, false, true);
|
||||
cmd.applyTo(instr.getProgram(), monitor);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -137,4 +140,17 @@ public class SparcAnalyzer extends ConstantPropagationAnalyzer {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -35,9 +35,10 @@ public class SPARC_ElfRelocationHandler
|
||||
|
||||
@Override
|
||||
public boolean canRelocate(ElfHeader elf) {
|
||||
return elf.e_machine() == ElfConstants.EM_SPARC ||
|
||||
elf.e_machine() == ElfConstants.EM_SPARC32PLUS ||
|
||||
elf.e_machine() == ElfConstants.EM_SPARCV9;
|
||||
boolean handleMachine = elf.e_machine() == ElfConstants.EM_SPARC ||
|
||||
elf.e_machine() == ElfConstants.EM_SPARC32PLUS ||
|
||||
elf.e_machine() == ElfConstants.EM_SPARCV9;
|
||||
return handleMachine && elf.is32Bit();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,52 +50,303 @@ public class SPARC_ElfRelocationHandler
|
||||
Program program = elfRelocationContext.getProgram();
|
||||
Memory memory = program.getMemory();
|
||||
|
||||
long addend = relocation.getAddend(); // will be 0 for REL case
|
||||
|
||||
// TODO: possible sign-extension seems wrong; there are both 32-bit and 64-bit variants
|
||||
long offset = (int) relocationAddress.getOffset();
|
||||
long addend = relocation.getAddend();
|
||||
|
||||
long pc = relocationAddress.getOffset();
|
||||
|
||||
int symbolIndex = relocation.getSymbolIndex();
|
||||
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)
|
||||
|
||||
|
||||
// Relocation docs: https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter6-24/index.html
|
||||
//
|
||||
switch (type) {
|
||||
case R_SPARC_DISP32:
|
||||
newValue = (int) (symbolValue + addend - offset);
|
||||
memory.setInt(relocationAddress, oldValue | newValue);
|
||||
case R_SPARC_8:
|
||||
newValue = symbolValue + addend;
|
||||
mask = 0x000000ff;
|
||||
oldValue &= ~(mask);
|
||||
newValue &= mask;
|
||||
memory.setInt(relocationAddress, oldValue | (int) newValue);
|
||||
break;
|
||||
case R_SPARC_WDISP30:
|
||||
newValue = (int) (symbolValue + addend - offset) >>> 2;
|
||||
memory.setInt(relocationAddress, oldValue | newValue);
|
||||
|
||||
case R_SPARC_16:
|
||||
newValue = symbolValue + addend;
|
||||
mask = 0x0000ffff;
|
||||
oldValue &= ~(mask);
|
||||
newValue &= mask;
|
||||
memory.setInt(relocationAddress, oldValue | (int) newValue);
|
||||
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:
|
||||
newValue = (int) symbolValue + (int) addend;
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
newValue = symbolValue + addend;
|
||||
memory.setInt(relocationAddress, (int) newValue);
|
||||
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:
|
||||
newValue = (int) symbolValue;
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
newValue = symbolValue;
|
||||
memory.setInt(relocationAddress, (int) newValue);
|
||||
break;
|
||||
|
||||
case R_SPARC_RELATIVE:
|
||||
newValue = (int) elfRelocationContext.getElfHeader().getImageBase() + (int) addend;
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
newValue = elfRelocationContext.getImageBaseWordAdjustmentOffset() + addend;
|
||||
memory.setInt(relocationAddress, (int) newValue);
|
||||
break;
|
||||
|
||||
case R_SPARC_UA32:
|
||||
newValue = (int) symbolValue + (int) addend;
|
||||
memory.setInt(relocationAddress, newValue);
|
||||
newValue = symbolValue + addend;
|
||||
memory.setInt(relocationAddress, (int) newValue);
|
||||
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());
|
||||
|
@ -54,7 +54,7 @@ public enum SPARC_ElfRelocationType implements ElfRelocationType {
|
||||
R_SPARC_HH22(34), // (S + A) >> 42
|
||||
R_SPARC_HM10(35), // ((S + A) >> 32) & 0x3ff
|
||||
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_LM22(39), // (S + A - P) >> 10
|
||||
R_SPARC_WDISP16(40), // (S + A - P) >> 2
|
||||
|
@ -87,6 +87,7 @@ public class AbstractProtoModelTest extends AbstractGenericTest {
|
||||
dtManager.addDataType(new CharDataType(), null);
|
||||
dtManager.addDataType(new UnsignedIntegerDataType(), null);
|
||||
dtManager.addDataType(new LongDataType(), null);
|
||||
dtManager.addDataType(new LongLongDataType(), null);
|
||||
dtManager.addDataType(new FloatDataType(), null);
|
||||
dtManager.addDataType(new DoubleDataType(), null);
|
||||
dtManager.addDataType(new Float16DataType(), null);
|
||||
@ -245,7 +246,9 @@ public class AbstractProtoModelTest extends AbstractGenericTest {
|
||||
parseStores(storeData, stores);
|
||||
Assert.assertEquals(storeData.size(), res.size());
|
||||
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;
|
||||
if (!compare) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
@ -253,10 +256,10 @@ public class AbstractProtoModelTest extends AbstractGenericTest {
|
||||
buffer.append(cspec.getCompilerSpecID());
|
||||
buffer.append(' ').append(model.getName()).append(' ');
|
||||
if (i == 0) {
|
||||
buffer.append("Output does not match for ");
|
||||
buffer.append("Output ").append("@"+toString(resPiece)).append(" does not match for ");
|
||||
}
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user