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....
//
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/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|

View File

@ -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"

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.
#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]; }

View File

@ -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"/>

View File

@ -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>

View File

@ -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>

View File

@ -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);
}
}

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");
* 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.
@ -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,14 +50,14 @@ 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)
// Handle relative relocations that do not require symbolAddr or symbolValue
@ -64,7 +65,7 @@ public class SPARC_ElfRelocationHandler
case R_SPARC_RELATIVE:
newValue = (int) elfRelocationContext.getElfHeader().getImageBase() + (int) addend;
memory.setInt(relocationAddress, newValue);
memory.setInt(relocationAddress, (int) newValue);
return new RelocationResult(Status.APPLIED, byteLength);
case R_SPARC_COPY:
@ -80,39 +81,300 @@ public class SPARC_ElfRelocationHandler
if (handleUnresolvedSymbol(elfRelocationContext, relocation, relocationAddress)) {
return RelocationResult.FAILURE;
}
// 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 = 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());
return RelocationResult.UNSUPPORTED;
default:
markAsUnhandled(program, relocationAddress, type, symbolIndex, symbolName,
elfRelocationContext.getLog());

View File

@ -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

View File

@ -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();
}
}

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");
}
}