diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/DecompilerSwitchAnalyzer.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/DecompilerSwitchAnalyzer.java index cd1d252e2f..142233d89f 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/DecompilerSwitchAnalyzer.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/DecompilerSwitchAnalyzer.java @@ -18,8 +18,6 @@ package ghidra.app.plugin.core.analysis; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; -import java.math.BigInteger; - import generic.concurrent.*; import ghidra.app.cmd.function.CreateFunctionCmd; import ghidra.app.cmd.function.DecompilerSwitchAnalysisCmd; @@ -32,8 +30,11 @@ import ghidra.framework.options.Options; import ghidra.program.model.address.*; import ghidra.program.model.block.*; import ghidra.program.model.data.DataType; -import ghidra.program.model.lang.Register; +import ghidra.program.model.lang.InjectPayload; +import ghidra.program.model.lang.PcodeInjectLibrary; import ghidra.program.model.listing.*; +import ghidra.program.model.pcode.PcodeOp; +import ghidra.program.model.pcode.Varnode; import ghidra.program.model.symbol.*; import ghidra.program.util.*; import ghidra.util.Msg; @@ -53,10 +54,11 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer { public static final int OPTION_DEFAULT_DECOMPILER_TIMEOUT_SECS = 60; private int decompilerTimeoutSecondsOption = OPTION_DEFAULT_DECOMPILER_TIMEOUT_SECS; + // cache for pcode callother injection payloads + private HashMap injectPayloadCache = new HashMap<>(); + private boolean hitNonReturningFunction = false; - private Register isaModeSwitchRegister = null; - private Register isaModeRegister = null; //================================================================================================== // Interface Methods @@ -95,9 +97,6 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer { public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException { - isaModeSwitchRegister = program.getRegister("ISAModeSwitch"); - isaModeRegister = program.getRegister("ISA_MODE"); - try { ArrayList
locations = findLocations(program, set, monitor); if (locations.isEmpty()) { @@ -224,6 +223,16 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer { } } + /** + * Find locations that could be an unrecovered switches + * + * @param program program + * @param set area of program to check + * @param monitor monitor + * @return list of addresses that could be a switch + * + * @throws CancelledException if monitor cancels + */ private ArrayList
findLocations(Program program, AddressSetView set, TaskMonitor monitor) throws CancelledException { @@ -244,6 +253,12 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer { continue; } } + + // check for break type construct + if (hasUnrecoverableCallOther(program, instruction)) { + continue; + } + Address address = instruction.getMinAddress(); locations.add(address); @@ -254,6 +269,110 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer { return locations; } + /** + * Check an instruction for an unrecoverable computed destination due + * to calling a callOther pcode op that has no associated injection. + * If there is an associated injection, then it might yet be recoverable. + * + * @param program program + * @param instr branching instruction to check + * @return true if there is a callOther that will block switch recovery + */ + private boolean hasUnrecoverableCallOther(Program program, Instruction instr) { + HashSet callOtherOutputs = new HashSet(); + + PcodeOp[] pcode = instr.getPcode(true); + for (PcodeOp op : pcode) { + if (op.getOpcode() == PcodeOp.CALLOTHER) { + // if callother has defined pcode inject replacement + // then could recover + if (hasPcodeInject(program, op)) { + continue; + } + // save callother output varnode + Varnode dest = op.getOutput(); + if (dest != null) { + callOtherOutputs.add(dest); + } + continue; + } + if (!callOtherOutputs.isEmpty() && op.getOpcode()==PcodeOp.BRANCHIND) { + // check if branching to an output varnode of callother + if (callOtherOutputs.contains(op.getInput(0))) { + // target is computed from a callother output + return true; + } + continue; + } + + // if have a callother destinations, check for it as an input + if (!callOtherOutputs.isEmpty()) { + Varnode[] inputs = op.getInputs(); + for (Varnode in : inputs) { + if (callOtherOutputs.contains(in)) { + Varnode dest = op.getOutput(); + if (dest != null) { + callOtherOutputs.add(dest); + } + } + } + } + } + + return false; + } + + /** + * Check if the callOther Pcode op has an associated injection + * + * @param program program + * @param op callother pcode op + * @return true if there is a pcode injection, false otherwise + */ + private boolean hasPcodeInject(Program program, PcodeOp op) { + long callOtherIndex = op.getInput(0).getOffset(); + InjectPayload payload = findPcodeInjection(program, callOtherIndex); + + return payload != null; + } + + /** + * Find out if a callother pcode op has a pcodeInjection attached to it + * + * @param program program + * @param callOtherIndex callOther ID index + * @return injection payload or null if no register injections + */ + private InjectPayload findPcodeInjection(Program program, long callOtherIndex) { + InjectPayload payload = injectPayloadCache.get(callOtherIndex); + + // has a payload value for the pcode callother index + if (payload != null) { + return payload; + } + + // value null, if contains the key, then already looked up + if (injectPayloadCache.containsKey(callOtherIndex)) { + return null; + } + PcodeInjectLibrary snippetLibrary = program.getCompilerSpec().getPcodeInjectLibrary(); + + String opName = program.getLanguage().getUserDefinedOpName((int) callOtherIndex); + + // segment is special named injection + if ("segment".equals(opName)) { + payload = + snippetLibrary.getPayload(InjectPayload.EXECUTABLEPCODE_TYPE, "segment_pcode"); + } + else { + payload = snippetLibrary.getPayload(InjectPayload.CALLOTHERFIXUP_TYPE, opName); + } + + // save payload in cache for next lookup + injectPayloadCache.put(callOtherIndex, payload); + return payload; + } + private boolean isCallFixup(Program program, Instruction instr, FlowType flowType) { if (!flowType.isCall()) { return false; @@ -370,31 +489,9 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer { if (refType.isComputed() && refType.isFlow() && program.getMemory().contains(address)) { foundCount.incrementAndGet(); - // don't propagate low code mode, let something else do that - // propogateCodeMode(context, address); - // don't make references, let other analysis do this - //return true; } return false; } - - private void propogateCodeMode(VarnodeContext context, Address addr) { - // get CodeModeRegister and flow it to destination, if it is set here - - if (isaModeSwitchRegister == null) { - return; - } - BigInteger value = context.getValue(isaModeSwitchRegister, false); - if (value != null && program.getListing().getInstructionAt(addr) == null) { - try { - program.getProgramContext() - .setValue(isaModeRegister, addr, addr, value); - } - catch (ContextChangeException e) { - // ignore - } - } - } }, false, monitor); // only found one reference