private void addBreakLabel(Edge exitEdge, BlockNode exit, InsnNode breakInsn) { BlockNode outBlock = BlockUtils.getNextBlock(exitEdge.getTarget()); if (outBlock == null) { return; } List<LoopInfo> exitLoop = mth.getAllLoopsForBlock(outBlock); if (!exitLoop.isEmpty()) { return; } List<LoopInfo> inLoops = mth.getAllLoopsForBlock(exitEdge.getSource()); if (inLoops.size() < 2) { return; } // search for parent loop LoopInfo parentLoop = null; for (LoopInfo loop : inLoops) { if (loop.getParentLoop() == null) { parentLoop = loop; break; } } if (parentLoop == null) { return; } if (parentLoop.getEnd() != exit && !parentLoop.getExitNodes().contains(exit)) { LoopLabelAttr labelAttr = new LoopLabelAttr(parentLoop); breakInsn.addAttr(labelAttr); parentLoop.getStart().addAttr(labelAttr); } }
private void processExcHandler(ExceptionHandler handler, Set<BlockNode> exits) { BlockNode start = handler.getHandlerBlock(); if (start == null) { return; } RegionStack stack = new RegionStack(mth); BlockNode dom; if (handler.isFinally()) { SplitterBlockAttr splitterAttr = start.get(AType.SPLITTER_BLOCK); if (splitterAttr == null) { return; } dom = splitterAttr.getBlock(); } else { dom = start; stack.addExits(exits); } BitSet domFrontier = dom.getDomFrontier(); List<BlockNode> handlerExits = BlockUtils.bitSetToBlocks(mth, domFrontier); boolean inLoop = mth.getLoopForBlock(start) != null; for (BlockNode exit : handlerExits) { if ((!inLoop || BlockUtils.isPathExists(start, exit)) && RegionUtils.isRegionContainsBlock(mth.getRegion(), exit)) { stack.addExit(exit); } } handler.setHandlerRegion(makeRegion(start, stack)); ExcHandlerAttr excHandlerAttr = start.get(AType.EXC_HANDLER); if (excHandlerAttr == null) { LOG.warn("Missing exception handler attribute for start block"); } else { handler.getHandlerRegion().addAttr(excHandlerAttr); } }
protected MethodNode getMethod(ClassNode cls, String method) { for (MethodNode mth : cls.getMethods()) { if (mth.getName().equals(method)) { return mth; } } fail("Method not found " + method + " in class " + cls); return null; }
public DebugInfoParser(MethodNode mth, int debugOffset, InsnNode[] insnByOffset) { this.mth = mth; this.dex = mth.dex(); this.section = dex.openSection(debugOffset); int regsCount = mth.getRegsCount(); this.locals = new LocalVar[regsCount]; this.activeRegisters = new InsnArg[regsCount]; this.insnByOffset = insnByOffset; }
private static void checkCode(ClassNode cls) { assertTrue( "Inconsistent cls: " + cls, !cls.contains(AFlag.INCONSISTENT_CODE) && !cls.contains(AType.JADX_ERROR)); for (MethodNode mthNode : cls.getMethods()) { assertTrue( "Inconsistent method: " + mthNode, !mthNode.contains(AFlag.INCONSISTENT_CODE) && !mthNode.contains(AType.JADX_ERROR)); } assertThat(cls.getCode().toString(), not(containsString("inconsistent"))); }
private boolean inExceptionHandlerBlocks(BlockNode loopEnd) { if (mth.getExceptionHandlersCount() == 0) { return false; } for (ExceptionHandler eh : mth.getExceptionHandlers()) { if (eh.getBlocks().contains(loopEnd)) { return true; } } return false; }
private static void renameVariables(MethodNode mth) { int regsCount = mth.getRegsCount(); SSAVar[] vars = new SSAVar[regsCount]; int[] versions = new int[regsCount]; // init method arguments for (RegisterArg arg : mth.getArguments(true)) { int regNum = arg.getRegNum(); vars[regNum] = mth.makeNewSVar(regNum, versions, arg); } renameVar(mth, vars, versions, mth.getEnterBlock()); }
/** Select loop exit and construct LoopRegion */ private LoopRegion makeLoopRegion(IRegion curRegion, LoopInfo loop, List<BlockNode> exitBlocks) { for (BlockNode block : exitBlocks) { if (block.contains(AType.EXC_HANDLER) || block.getInstructions().size() != 1 || block.getInstructions().get(0).getType() != InsnType.IF) { continue; } List<LoopInfo> loops = block.getAll(AType.LOOP); if (!loops.isEmpty() && loops.get(0) != loop) { // skip nested loop condition continue; } LoopRegion loopRegion = new LoopRegion(curRegion, loop, block, block == loop.getEnd()); boolean found; if (block == loop.getStart() || block == loop.getEnd() || BlockUtils.isEmptySimplePath(loop.getStart(), block)) { found = true; } else if (block.getPredecessors().contains(loop.getStart())) { loopRegion.setPreCondition(loop.getStart()); // if we can't merge pre-condition this is not correct header found = loopRegion.checkPreCondition(); } else { found = false; } if (found) { List<LoopInfo> list = mth.getAllLoopsForBlock(block); if (list.size() >= 2) { // bad condition if successors going out of all loops boolean allOuter = true; for (BlockNode outerBlock : block.getCleanSuccessors()) { List<LoopInfo> outLoopList = mth.getAllLoopsForBlock(outerBlock); outLoopList.remove(loop); if (!outLoopList.isEmpty()) { // goes to outer loop allOuter = false; break; } } if (allOuter) { found = false; } } } if (found) { return loopRegion; } } // no exit found => endless loop return null; }
private static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) { List<BlockNode> blocks = mth.getBasicBlocks(); int blocksCount = blocks.size(); BitSet hasPhi = new BitSet(blocksCount); BitSet processed = new BitSet(blocksCount); Deque<BlockNode> workList = new LinkedList<BlockNode>(); BitSet assignBlocks = la.getAssignBlocks(regNum); for (int id = assignBlocks.nextSetBit(0); id >= 0; id = assignBlocks.nextSetBit(id + 1)) { processed.set(id); workList.add(blocks.get(id)); } while (!workList.isEmpty()) { BlockNode block = workList.pop(); BitSet domFrontier = block.getDomFrontier(); for (int id = domFrontier.nextSetBit(0); id >= 0; id = domFrontier.nextSetBit(id + 1)) { if (!hasPhi.get(id) && la.isLive(id, regNum)) { BlockNode df = blocks.get(id); addPhi(df, regNum); hasPhi.set(id); if (!processed.get(id)) { processed.set(id); workList.add(df); } } } } }
@Override public void visit(MethodNode mth) throws JadxException { if (mth.isNoCode()) { return; } process(mth); }
private static boolean removePhiList(MethodNode mth, List<PhiInsn> insnToRemove) { if (insnToRemove.isEmpty()) { return false; } for (BlockNode block : mth.getBasicBlocks()) { PhiListAttr phiList = block.get(AType.PHI_LIST); if (phiList == null) { continue; } List<PhiInsn> list = phiList.getList(); for (PhiInsn phiInsn : insnToRemove) { if (list.remove(phiInsn)) { for (InsnArg arg : phiInsn.getArguments()) { SSAVar sVar = ((RegisterArg) arg).getSVar(); if (sVar != null) { sVar.setUsedInPhi(null); } } InstructionRemover.remove(mth, block, phiInsn); } } if (list.isEmpty()) { block.remove(AType.PHI_LIST); } } insnToRemove.clear(); return true; }
private static void renameVar(MethodNode mth, SSAVar[] vars, int[] vers, BlockNode block) { SSAVar[] inputVars = Arrays.copyOf(vars, vars.length); for (InsnNode insn : block.getInstructions()) { if (insn.getType() != InsnType.PHI) { for (InsnArg arg : insn.getArguments()) { if (!arg.isRegister()) { continue; } RegisterArg reg = (RegisterArg) arg; int regNum = reg.getRegNum(); SSAVar var = vars[regNum]; if (var == null) { throw new JadxRuntimeException( "Not initialized variable reg: " + regNum + ", insn: " + insn + ", block:" + block + ", method: " + mth); } var.use(reg); } } RegisterArg result = insn.getResult(); if (result != null) { int regNum = result.getRegNum(); vars[regNum] = mth.makeNewSVar(regNum, vers, result); } } for (BlockNode s : block.getSuccessors()) { PhiListAttr phiList = s.get(AType.PHI_LIST); if (phiList == null) { continue; } int j = s.getPredecessors().indexOf(block); if (j == -1) { throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s); } for (PhiInsn phiInsn : phiList.getList()) { if (j >= phiInsn.getArgsCount()) { continue; } int regNum = phiInsn.getResult().getRegNum(); SSAVar var = vars[regNum]; if (var == null) { continue; } var.use(phiInsn.getArg(j)); var.setUsedInPhi(phiInsn); } } for (BlockNode domOn : block.getDominatesOn()) { renameVar(mth, vars, vers, domOn); } System.arraycopy(inputVars, 0, vars, 0, vars.length); }
private static void process(MethodNode mth) { LiveVarAnalysis la = new LiveVarAnalysis(mth); la.runAnalysis(); int regsCount = mth.getRegsCount(); for (int i = 0; i < regsCount; i++) { placePhi(mth, i, la); } renameVariables(mth); fixLastTryCatchAssign(mth); if (removeUselessPhi(mth)) { renameVariables(mth); } }
private static boolean removeUselessPhi(MethodNode mth) { List<PhiInsn> insnToRemove = new ArrayList<PhiInsn>(); for (SSAVar var : mth.getSVars()) { // phi result not used if (var.getUseCount() == 0) { InsnNode assignInsn = var.getAssign().getParentInsn(); if (assignInsn != null && assignInsn.getType() == InsnType.PHI) { insnToRemove.add((PhiInsn) assignInsn); } } } for (BlockNode block : mth.getBasicBlocks()) { PhiListAttr phiList = block.get(AType.PHI_LIST); if (phiList == null) { continue; } for (PhiInsn phi : phiList.getList()) { removePhiWithSameArgs(phi, insnToRemove); } } return removePhiList(mth, insnToRemove); }
private static void fixLastTryCatchAssign(MethodNode mth) { for (BlockNode block : mth.getBasicBlocks()) { PhiListAttr phiList = block.get(AType.PHI_LIST); if (phiList == null || !block.contains(AType.EXC_HANDLER)) { continue; } for (PhiInsn phi : phiList.getList()) { for (int i = 0; i < phi.getArgsCount(); i++) { RegisterArg arg = phi.getArg(i); InsnNode parentInsn = arg.getAssignInsn(); if (parentInsn != null && parentInsn.getResult() != null && parentInsn.contains(AFlag.TRY_LEAVE)) { phi.removeArg(arg); } } } } }
private boolean canInsertBreak(BlockNode exit) { if (exit.contains(AFlag.RETURN) || BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) { return false; } List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit); if (!simplePath.isEmpty()) { BlockNode lastBlock = simplePath.get(simplePath.size() - 1); if (lastBlock.contains(AFlag.RETURN) || lastBlock.getSuccessors().isEmpty()) { return false; } } // check if there no outer switch (TODO: very expensive check) Set<BlockNode> paths = BlockUtils.getAllPathsBlocks(mth.getEnterBlock(), exit); for (BlockNode block : paths) { if (BlockUtils.checkLastInsnType(block, InsnType.SWITCH)) { return false; } } return true; }
public void processTryCatchBlocks(MethodNode mth) { Set<TryCatchBlock> tcs = new HashSet<TryCatchBlock>(); for (ExceptionHandler handler : mth.getExceptionHandlers()) { tcs.add(handler.getTryBlock()); } for (TryCatchBlock tc : tcs) { List<BlockNode> blocks = new ArrayList<BlockNode>(tc.getHandlersCount()); Set<BlockNode> splitters = new HashSet<BlockNode>(); for (ExceptionHandler handler : tc.getHandlers()) { BlockNode handlerBlock = handler.getHandlerBlock(); if (handlerBlock != null) { blocks.add(handlerBlock); splitters.addAll(handlerBlock.getPredecessors()); } else { LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No exception handler block: " + handler)); } } Set<BlockNode> exits = new HashSet<BlockNode>(); for (BlockNode splitter : splitters) { for (BlockNode handler : blocks) { List<BlockNode> s = splitter.getSuccessors(); if (s.isEmpty()) { LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No successors for splitter: " + splitter)); continue; } BlockNode ss = s.get(0); BlockNode cross = BlockUtils.getPathCross(mth, ss, handler); if (cross != null && cross != ss && cross != handler) { exits.add(cross); } } } for (ExceptionHandler handler : tc.getHandlers()) { processExcHandler(handler, exits); } } }
public RegionMaker(MethodNode mth) { this.mth = mth; if (Consts.DEBUG) { this.processedBlocks = new BitSet(mth.getBasicBlocks().size()); } }
private BlockNode processSwitch( IRegion currentRegion, BlockNode block, SwitchNode insn, RegionStack stack) { SwitchRegion sw = new SwitchRegion(currentRegion, block); currentRegion.getSubBlocks().add(sw); int len = insn.getTargets().length; // sort by target Map<Integer, List<Object>> casesMap = new LinkedHashMap<Integer, List<Object>>(len); for (int i = 0; i < len; i++) { Object key = insn.getKeys()[i]; int targ = insn.getTargets()[i]; List<Object> keys = casesMap.get(targ); if (keys == null) { keys = new ArrayList<Object>(2); casesMap.put(targ, keys); } keys.add(key); } Map<BlockNode, List<Object>> blocksMap = new LinkedHashMap<BlockNode, List<Object>>(len); for (Map.Entry<Integer, List<Object>> entry : casesMap.entrySet()) { BlockNode c = getBlockByOffset(entry.getKey(), block.getSuccessors()); assert c != null; blocksMap.put(c, entry.getValue()); } BlockNode defCase = getBlockByOffset(insn.getDefaultCaseOffset(), block.getSuccessors()); if (defCase != null) { blocksMap.remove(defCase); } LoopInfo loop = mth.getLoopForBlock(block); Map<BlockNode, BlockNode> fallThroughCases = new LinkedHashMap<BlockNode, BlockNode>(); List<BlockNode> basicBlocks = mth.getBasicBlocks(); BitSet outs = new BitSet(basicBlocks.size()); outs.or(block.getDomFrontier()); for (BlockNode s : block.getCleanSuccessors()) { BitSet df = s.getDomFrontier(); // fall through case block if (df.cardinality() > 1) { if (df.cardinality() > 2) { LOG.debug("Unexpected case pattern, block: {}, mth: {}", s, mth); } else { BlockNode first = basicBlocks.get(df.nextSetBit(0)); BlockNode second = basicBlocks.get(df.nextSetBit(first.getId() + 1)); if (second.getDomFrontier().get(first.getId())) { fallThroughCases.put(s, second); df = new BitSet(df.size()); df.set(first.getId()); } else if (first.getDomFrontier().get(second.getId())) { fallThroughCases.put(s, first); df = new BitSet(df.size()); df.set(second.getId()); } } } outs.or(df); } outs.clear(block.getId()); if (loop != null) { outs.clear(loop.getStart().getId()); } stack.push(sw); stack.addExits(BlockUtils.bitSetToBlocks(mth, outs)); // check cases order if fall through case exists if (!fallThroughCases.isEmpty()) { if (isBadCasesOrder(blocksMap, fallThroughCases)) { LOG.debug("Fixing incorrect switch cases order, method: {}", mth); blocksMap = reOrderSwitchCases(blocksMap, fallThroughCases); if (isBadCasesOrder(blocksMap, fallThroughCases)) { LOG.error("Can't fix incorrect switch cases order, method: {}", mth); mth.add(AFlag.INCONSISTENT_CODE); } } } // filter 'out' block if (outs.cardinality() > 1) { // remove exception handlers BlockUtils.cleanBitSet(mth, outs); } if (outs.cardinality() > 1) { // filter loop start and successors of other blocks for (int i = outs.nextSetBit(0); i >= 0; i = outs.nextSetBit(i + 1)) { BlockNode b = basicBlocks.get(i); outs.andNot(b.getDomFrontier()); if (b.contains(AFlag.LOOP_START)) { outs.clear(b.getId()); } else { for (BlockNode s : b.getCleanSuccessors()) { outs.clear(s.getId()); } } } } if (loop != null && outs.cardinality() > 1) { outs.clear(loop.getEnd().getId()); } if (outs.cardinality() == 0) { // one or several case blocks are empty, // run expensive algorithm for find 'out' block for (BlockNode maybeOut : block.getSuccessors()) { boolean allReached = true; for (BlockNode s : block.getSuccessors()) { if (!isPathExists(s, maybeOut)) { allReached = false; break; } } if (allReached) { outs.set(maybeOut.getId()); break; } } } BlockNode out = null; if (outs.cardinality() == 1) { out = basicBlocks.get(outs.nextSetBit(0)); stack.addExit(out); } else if (loop == null && outs.cardinality() > 1) { LOG.warn("Can't detect out node for switch block: {} in {}", block, mth); } if (loop != null) { // check if 'continue' must be inserted BlockNode end = loop.getEnd(); if (out != end && out != null) { insertContinueInSwitch(block, out, end); } } if (!stack.containsExit(defCase)) { sw.setDefaultCase(makeRegion(defCase, stack)); } for (Entry<BlockNode, List<Object>> entry : blocksMap.entrySet()) { BlockNode caseBlock = entry.getKey(); if (stack.containsExit(caseBlock)) { // empty case block sw.addCase(entry.getValue(), new Region(stack.peekRegion())); } else { BlockNode next = fallThroughCases.get(caseBlock); stack.addExit(next); Region caseRegion = makeRegion(caseBlock, stack); stack.removeExit(next); if (next != null) { next.add(AFlag.FALL_THROUGH); caseRegion.add(AFlag.FALL_THROUGH); } sw.addCase(entry.getValue(), caseRegion); // 'break' instruction will be inserted in RegionMakerVisitor.PostRegionVisitor } } stack.pop(); return out; }
public void process() throws DecodeException { int addr = 0; int line = section.readUleb128(); int paramsCount = section.readUleb128(); List<RegisterArg> mthArgs = mth.getArguments(false); assert paramsCount == mthArgs.size(); for (int i = 0; i < paramsCount; i++) { int id = section.readUleb128() - 1; if (id != DexNode.NO_INDEX) { String name = dex.getString(id); mthArgs.get(i).setName(name); } } for (RegisterArg arg : mthArgs) { int rn = arg.getRegNum(); locals[rn] = new LocalVar(arg); activeRegisters[rn] = arg; } // process '0' instruction addrChange(-1, 1, line); setLine(addr, line); int c = section.readByte() & 0xFF; while (c != DBG_END_SEQUENCE) { switch (c) { case DBG_ADVANCE_PC: { int addrInc = section.readUleb128(); addr = addrChange(addr, addrInc, line); setLine(addr, line); break; } case DBG_ADVANCE_LINE: { line += section.readSleb128(); break; } case DBG_START_LOCAL: { int regNum = section.readUleb128(); int nameId = section.readUleb128() - 1; int type = section.readUleb128() - 1; LocalVar var = new LocalVar(dex, regNum, nameId, type, DexNode.NO_INDEX); startVar(var, addr, line); break; } case DBG_START_LOCAL_EXTENDED: { int regNum = section.readUleb128(); int nameId = section.readUleb128() - 1; int type = section.readUleb128() - 1; int sign = section.readUleb128() - 1; LocalVar var = new LocalVar(dex, regNum, nameId, type, sign); startVar(var, addr, line); break; } case DBG_RESTART_LOCAL: { int regNum = section.readUleb128(); LocalVar var = locals[regNum]; if (var != null) { var.end(addr, line); setVar(var); var.start(addr, line); } break; } case DBG_END_LOCAL: { int regNum = section.readUleb128(); LocalVar var = locals[regNum]; if (var != null) { var.end(addr, line); setVar(var); } break; } case DBG_SET_PROLOGUE_END: case DBG_SET_EPILOGUE_BEGIN: // do nothing break; case DBG_SET_FILE: { int idx = section.readUleb128() - 1; if (idx != DexNode.NO_INDEX) { String sourceFile = dex.getString(idx); mth.addAttr(new SourceFileAttr(sourceFile)); } break; } default: { if (c >= DBG_FIRST_SPECIAL) { int adjustedOpcode = c - DBG_FIRST_SPECIAL; int addrInc = adjustedOpcode / DBG_LINE_RANGE; addr = addrChange(addr, addrInc, line); line += DBG_LINE_BASE + (adjustedOpcode % DBG_LINE_RANGE); setLine(addr, line); } else { throw new DecodeException("Unknown debug insn code: " + c); } break; } } c = section.readByte() & 0xFF; } for (LocalVar var : locals) { if (var != null && !var.isEnd()) { var.end(addr, line); setVar(var); } } setSourceLines(addr, insnByOffset.length, line); }