From edb74d7b7d9c91c27b643af300379af05386a9c7 Mon Sep 17 00:00:00 2001 From: emteere <47253321+emteere@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:29:02 -0400 Subject: [PATCH] GP-3808 Fixed sparc 32/64 prototype model, added missing instructions, fixed 64/32 relocations, handle call/return checking o7 link register --- .../program/util/SymbolicPropogator.java | 1 + .../Processors/Sparc/certification.manifest | 2 +- .../Sparc/data/languages/SparcV9.ldefs | 4 +- .../Sparc/data/languages/SparcV9.sinc | 14 +- .../Sparc/data/languages/SparcV9_32.cspec | 57 +++- .../Sparc/data/languages/SparcV9_64.cspec | 121 +++++-- .../Sparc/data/patterns/SPARC_patterns.xml | 15 + .../plugin/core/analysis/SparcAnalyzer.java | 70 ++-- .../analysis/SparcEarlyAddressAnalyzer.java | 86 +++++ .../SPARC64_ElfRelocationHandler.java | 150 ++++++++ .../SPARC_ElfRelocationHandler.java | 320 ++++++++++++++++-- .../relocation/SPARC_ElfRelocationType.java | 2 +- .../model/lang/AbstractProtoModelTest.java | 42 ++- .../model/lang/Sparc32ProtoModelTest.java | 53 +++ .../model/lang/Sparc64ProtoModelTest.java | 72 ++++ 15 files changed, 896 insertions(+), 113 deletions(-) create mode 100644 Ghidra/Processors/Sparc/src/main/java/ghidra/app/plugin/core/analysis/SparcEarlyAddressAnalyzer.java create mode 100644 Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC64_ElfRelocationHandler.java create mode 100644 Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/Sparc32ProtoModelTest.java create mode 100644 Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/Sparc64ProtoModelTest.java diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java index cdb61dbb3c..59595e8411 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java @@ -594,6 +594,7 @@ public class SymbolicPropogator { // FLOW end will probably work correctly, but.... // vContext.flowStart(minInstrAddress, maxAddr); + retAddr = null; } } diff --git a/Ghidra/Processors/Sparc/certification.manifest b/Ghidra/Processors/Sparc/certification.manifest index 79299893ed..b27bbfd6cd 100644 --- a/Ghidra/Processors/Sparc/certification.manifest +++ b/Ghidra/Processors/Sparc/certification.manifest @@ -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| diff --git a/Ghidra/Processors/Sparc/data/languages/SparcV9.ldefs b/Ghidra/Processors/Sparc/data/languages/SparcV9.ldefs index 95b4e5dd03..97599ce7df 100644 --- a/Ghidra/Processors/Sparc/data/languages/SparcV9.ldefs +++ b/Ghidra/Processors/Sparc/data/languages/SparcV9.ldefs @@ -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" diff --git a/Ghidra/Processors/Sparc/data/languages/SparcV9.sinc b/Ghidra/Processors/Sparc/data/languages/SparcV9.sinc index b78bff554c..61288ccb3d 100644 --- a/Ghidra/Processors/Sparc/data/languages/SparcV9.sinc +++ b/Ghidra/Processors/Sparc/data/languages/SparcV9.sinc @@ -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 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]; } diff --git a/Ghidra/Processors/Sparc/data/languages/SparcV9_32.cspec b/Ghidra/Processors/Sparc/data/languages/SparcV9_32.cspec index 28f5f3006a..78fedadcbb 100644 --- a/Ghidra/Processors/Sparc/data/languages/SparcV9_32.cspec +++ b/Ghidra/Processors/Sparc/data/languages/SparcV9_32.cspec @@ -30,41 +30,72 @@ - + + + + - + - + - + - + - + + + + + + + + + + + + + - + - - + + - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Processors/Sparc/data/languages/SparcV9_64.cspec b/Ghidra/Processors/Sparc/data/languages/SparcV9_64.cspec index 67ccac7080..a176a8a4ae 100644 --- a/Ghidra/Processors/Sparc/data/languages/SparcV9_64.cspec +++ b/Ghidra/Processors/Sparc/data/languages/SparcV9_64.cspec @@ -14,7 +14,7 @@ - + @@ -30,6 +30,9 @@ + + + @@ -42,53 +45,72 @@ - - + + - - + + - - - - - - - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + - - - - + - - + + - + + + + + + + + + @@ -127,22 +149,22 @@ - + - + - + - + - + - + @@ -150,7 +172,16 @@ - + + + + + + + + + + @@ -187,4 +218,32 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ghidra/Processors/Sparc/data/patterns/SPARC_patterns.xml b/Ghidra/Processors/Sparc/data/patterns/SPARC_patterns.xml index 3d1976d23e..019bb2c388 100644 --- a/Ghidra/Processors/Sparc/data/patterns/SPARC_patterns.xml +++ b/Ghidra/Processors/Sparc/data/patterns/SPARC_patterns.xml @@ -32,4 +32,19 @@ + + + 0x81 0xc3 0xe0 0x08 0xae 0x03 0xc0 0x17 + + + + + 0x81 0xc3 0xe0 0x08 0x82 0x03 0xc0 0x01 + + + + + 0x81 0xc3 0xe0 0x08 0x90 0x02 0x00 0x0f + + diff --git a/Ghidra/Processors/Sparc/src/main/java/ghidra/app/plugin/core/analysis/SparcAnalyzer.java b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/plugin/core/analysis/SparcAnalyzer.java index bceb4b428b..8c145fbeb6 100644 --- a/Ghidra/Processors/Sparc/src/main/java/ghidra/app/plugin/core/analysis/SparcAnalyzer.java +++ b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/plugin/core/analysis/SparcAnalyzer.java @@ -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); + } } diff --git a/Ghidra/Processors/Sparc/src/main/java/ghidra/app/plugin/core/analysis/SparcEarlyAddressAnalyzer.java b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/plugin/core/analysis/SparcEarlyAddressAnalyzer.java new file mode 100644 index 0000000000..7f1fec895a --- /dev/null +++ b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/plugin/core/analysis/SparcEarlyAddressAnalyzer.java @@ -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; + } +} diff --git a/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC64_ElfRelocationHandler.java b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC64_ElfRelocationHandler.java new file mode 100644 index 0000000000..e8bd240b2d --- /dev/null +++ b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC64_ElfRelocationHandler.java @@ -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); + } +} diff --git a/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC_ElfRelocationHandler.java b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC_ElfRelocationHandler.java index 4ead1c2f40..29ded93582 100644 --- a/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC_ElfRelocationHandler.java +++ b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC_ElfRelocationHandler.java @@ -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()); diff --git a/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC_ElfRelocationType.java b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC_ElfRelocationType.java index 4344498743..7ef04f1357 100644 --- a/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC_ElfRelocationType.java +++ b/Ghidra/Processors/Sparc/src/main/java/ghidra/app/util/bin/format/elf/relocation/SPARC_ElfRelocationType.java @@ -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 diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/AbstractProtoModelTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/AbstractProtoModelTest.java index 37b3398d4f..413fc59a72 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/AbstractProtoModelTest.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/AbstractProtoModelTest.java @@ -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 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(); + } + } diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/Sparc32ProtoModelTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/Sparc32ProtoModelTest.java new file mode 100644 index 0000000000..46f8715fc9 --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/Sparc32ProtoModelTest.java @@ -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"); + } + +} diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/Sparc64ProtoModelTest.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/Sparc64ProtoModelTest.java new file mode 100644 index 0000000000..fa1ce24d17 --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/program/model/lang/Sparc64ProtoModelTest.java @@ -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"); + + } + +}