@@ -20,6 +20,55 @@ using namespace std;
2020#define snprintf _snprintf
2121#endif
2222
23+ static bool IsConditionalBranch (const decomp_result& decomp)
24+ {
25+ return (decomp.mnem == ARMV7_B ) && (decomp.format ->operationFlags & INSTR_FORMAT_FLAG_CONDITIONAL )
26+ && (decomp.fields [FIELD_cond] != COND_AL );
27+ }
28+
29+ static bool IsSameOrInvertedCondition (uint32_t lhs, uint32_t rhs)
30+ {
31+ return (lhs == rhs) || ((lhs < COND_AL ) && (rhs < COND_AL ) && ((lhs ^ 1 ) == rhs));
32+ }
33+
34+ static bool ThumbITInstructionWritesAPSR (const decomp_result& decomp)
35+ {
36+ switch (decomp.mnem )
37+ {
38+ case ARMV7_CMN :
39+ case ARMV7_CMP :
40+ case ARMV7_TEQ :
41+ case ARMV7_TST :
42+ return true ;
43+ default :
44+ return false ;
45+ }
46+ }
47+
48+ static uint32_t GetConditionalBranchTarget (const decomp_result& decomp)
49+ {
50+ return decomp.pc + decomp.fields [decomp.format ->operands [0 ].field0 ];
51+ }
52+
53+ static void EmitDirectThumbJump (Architecture* arch, LowLevelILFunction& il, uint32_t target)
54+ {
55+ BNLowLevelILLabel* label = il.GetLabelForAddress (arch, target);
56+ if (label)
57+ il.AddInstruction (il.Goto (*label));
58+ else
59+ il.AddInstruction (il.Jump (il.ConstPointer (4 , target)));
60+ }
61+
62+ struct ThumbITSlot
63+ {
64+ uint32_t addr = 0 ;
65+ decomp_result decomp = {};
66+ bool thenSlot = false ;
67+ bool lift = false ;
68+ bool directBranch = false ;
69+ uint32_t branchTarget = 0 ;
70+ };
71+
2372static Ref<Enumeration> get_msr_op_enum ()
2473{
2574 EnumerationBuilder builder;
@@ -167,7 +216,9 @@ class Thumb2Architecture: public ArmCommonArchitecture
167216
168217 virtual size_t GetMaxInstructionLength () const override
169218 {
170- return 18 ; // IT blocks can have up to four following associated instructions
219+ // IT blocks can have up to four following associated instructions, and
220+ // may be coalesced with a following conditional branch.
221+ return 22 ;
171222 }
172223
173224 virtual size_t GetInstructionAlignment () const override
@@ -232,6 +283,8 @@ class Thumb2Architecture: public ArmCommonArchitecture
232283 bool falseBranched = false ;
233284 bool trueReturned = false ;
234285 bool falseReturned = false ;
286+ bool trueWroteFlags = false ;
287+ bool falseWroteFlags = false ;
235288
236289 uint64_t trueBranchTargetAddr = 0 ;
237290 uint64_t falseBranchTargetAddr = 0 ;
@@ -240,56 +293,95 @@ class Thumb2Architecture: public ArmCommonArchitecture
240293 {
241294 bool isTrue = (i == 0 ) || (((mask >> (4 - i)) & 1 ) == (cond & 1 ));
242295
243- InstructionInfo innerResult;
244- if (!GetInstructionInfo (data + offset, addr + offset, maxLen - offset, innerResult))
296+ if (offset >= maxLen || (maxLen - offset) < 2 )
297+ break ;
298+
299+ decomp_result innerDecomp;
300+ size_t remainingLen = maxLen - offset;
301+ bool decoded = populateDecomposeRequest (&request, data + offset, remainingLen, addr + offset,
302+ IFTHEN_YES , ((i + 1 ) >= instrCount) ? IFTHENLAST_YES : IFTHENLAST_NO )
303+ && (thumb_decompose (&request, &innerDecomp) == STATUS_OK )
304+ && !(innerDecomp.status & STATUS_UNDEFINED ) && innerDecomp.format ;
305+ if (!decoded)
245306 break ;
246- if ((offset + innerResult.length ) > maxLen)
307+ size_t innerLen = innerDecomp.instrSize / 8 ;
308+ if ((innerLen == 0 ) || (innerLen > remainingLen))
247309 break ;
248310
249311 bool & terminated = isTrue ? trueTerminated : falseTerminated;
250312 bool & branched = isTrue ? trueBranched : falseBranched;
251313 bool & returned = isTrue ? trueReturned : falseReturned;
314+ bool & wroteFlags = isTrue ? trueWroteFlags : falseWroteFlags;
252315 uint64_t & branchTarget = isTrue ? trueBranchTargetAddr : falseBranchTargetAddr;
253316
254317 // Only process if the conditional branch we're following isn't terminated
255318 // Otherwise, just track if the arch is switching and the offset
256319 if (!terminated)
257320 {
258- for (size_t j = 0 ; j < innerResult.branchCount ; j++)
321+ wroteFlags |= ThumbITInstructionWritesAPSR (innerDecomp);
322+
323+ InstructionInfo innerResult;
324+ if (GetInstructionInfo (data + offset, addr + offset, remainingLen, innerResult))
259325 {
260- switch ( innerResult.branchType [j] )
326+ for ( size_t j = 0 ; j < innerResult.branchCount ; j++ )
261327 {
262- case UnconditionalBranch:
263- case TrueBranch:
264- case FalseBranch:
265- branched = true ;
266- terminated = true ;
267- branchTarget = innerResult.branchTarget [j];
268- break ;
269- case FunctionReturn:
270- returned = true ;
271- terminated = true ;
272- break ;
273- case CallDestination:
274- result.AddBranch (CallDestination, innerResult.branchTarget [j],
275- innerResult.branchArch [j] ? m_armArch : this );
276- break ;
277- case UnresolvedBranch:
278- case IndirectBranch:
279- case ExceptionBranch:
280- // We don't know the branch target so just set terminated
281- terminated = true ;
282- break ;
283- default :
284- break ;
328+ switch (innerResult.branchType [j])
329+ {
330+ case UnconditionalBranch:
331+ case TrueBranch:
332+ case FalseBranch:
333+ branched = true ;
334+ terminated = true ;
335+ branchTarget = innerResult.branchTarget [j];
336+ break ;
337+ case FunctionReturn:
338+ returned = true ;
339+ terminated = true ;
340+ break ;
341+ case CallDestination:
342+ result.AddBranch (CallDestination, innerResult.branchTarget [j],
343+ innerResult.branchArch [j] ? m_armArch : this );
344+ break ;
345+ case UnresolvedBranch:
346+ case IndirectBranch:
347+ case ExceptionBranch:
348+ // We don't know the branch target so just set terminated
349+ terminated = true ;
350+ break ;
351+ default :
352+ break ;
353+ }
285354 }
355+
356+ if (innerResult.archTransitionByTargetAddr )
357+ result.archTransitionByTargetAddr = true ;
286358 }
287359 }
288360
289- if (innerResult.archTransitionByTargetAddr )
290- result.archTransitionByTargetAddr = true ;
361+ offset += innerLen;
362+ }
363+
364+ decomp_result branchDecomp;
365+ if ((offset < maxLen) && ((maxLen - offset) >= 2 )
366+ && populateDecomposeRequest (&request, data + offset, maxLen - offset, addr + offset, IFTHEN_NO , IFTHENLAST_NO )
367+ && (thumb_decompose (&request, &branchDecomp) == STATUS_OK )
368+ && ((offset + (branchDecomp.instrSize / 8 )) <= maxLen)
369+ && !(branchDecomp.status & STATUS_UNDEFINED ) && branchDecomp.format && IsConditionalBranch (branchDecomp)
370+ && IsSameOrInvertedCondition (branchDecomp.fields [FIELD_cond], cond))
371+ {
372+ bool branchOnTrue = branchDecomp.fields [FIELD_cond] == cond;
373+ bool & terminated = branchOnTrue ? trueTerminated : falseTerminated;
374+ bool & branched = branchOnTrue ? trueBranched : falseBranched;
375+ bool & wroteFlags = branchOnTrue ? trueWroteFlags : falseWroteFlags;
376+ uint64_t & branchTarget = branchOnTrue ? trueBranchTargetAddr : falseBranchTargetAddr;
291377
292- offset += innerResult.length ;
378+ if (!terminated && !wroteFlags)
379+ {
380+ branched = true ;
381+ terminated = true ;
382+ branchTarget = GetConditionalBranchTarget (branchDecomp);
383+ offset += branchDecomp.instrSize / 8 ;
384+ }
293385 }
294386
295387 result.length = offset;
@@ -2626,82 +2718,137 @@ class Thumb2Architecture: public ArmCommonArchitecture
26262718 uint32_t mask = decomp.fields [FIELD_mask];
26272719 uint32_t cond = decomp.fields [FIELD_firstcond];
26282720
2629- // Calculate number of instructions
26302721 size_t instrCount;
2631- if (decomp. fields [FIELD_mask] & 1 )
2722+ if (mask & 1 )
26322723 instrCount = 4 ;
2633- else if (decomp. fields [FIELD_mask] & 2 )
2724+ else if (mask & 2 )
26342725 instrCount = 3 ;
2635- else if (decomp. fields [FIELD_mask] & 4 )
2726+ else if (mask & 4 )
26362727 instrCount = 2 ;
26372728 else
26382729 instrCount = 1 ;
26392730
2640- // decompose all instructions in the if-then block
2641- vector<uint32_t > addrsTrue, addrsFalse;
2642- vector<decomp_result> decompsTrue, decompsFalse;
2731+ // Decompose all instructions in the IT block and keep their original
2732+ // mask slot. A path may skip later slots once it has branched/returned.
2733+ vector<ThumbITSlot> slots;
2734+ bool pathTerminated[2 ] = {false , false };
2735+ bool pathHasBody[2 ] = {false , false };
2736+ bool pathWroteFlags[2 ] = {false , false };
26432737
26442738 for (size_t i = 0 ; i < instrCount; i++)
26452739 {
26462740 if (offset >= len || (len - offset) < 2 )
26472741 return false ;
26482742
2649- bool isTrue = (i == 0 ) || (((mask >> (4 - i)) & 1 ) == (cond & 1 ));
2743+ bool thenSlot = (i == 0 ) || (((mask >> (4 - i)) & 1 ) == (cond & 1 ));
2744+ size_t stateIdx = thenSlot ? 0 : 1 ;
26502745 size_t remainingLen = len - offset;
26512746
2652- if (! populateDecomposeRequest (&request, data+ offset, remainingLen, addr+ offset,
2653- IFTHEN_YES , ((i + 1 ) >= instrCount) ? IFTHENLAST_YES : IFTHENLAST_NO ))
2654- return false ;
2655-
2656- if (thumb_decompose (&request, &decomp) != STATUS_OK )
2747+ bool decoded = populateDecomposeRequest (&request, data + offset, remainingLen, addr + offset,
2748+ IFTHEN_YES , ((i + 1 ) >= instrCount) ? IFTHENLAST_YES : IFTHENLAST_NO )
2749+ && ( thumb_decompose (&request, &decomp) == STATUS_OK )
2750+ && !(decomp. status & STATUS_UNDEFINED ) && decomp. format ;
2751+ if (!decoded )
26572752 return false ;
26582753 if ((decomp.instrSize / 8 ) > remainingLen)
26592754 return false ;
2660- if ((decomp.status & STATUS_UNDEFINED ) || (!decomp.format ))
2661- return false ;
26622755
2663- if (isTrue) {
2664- addrsTrue.push_back (request.addr );
2665- decompsTrue.push_back (decomp);
2666- }
2667- else {
2668- addrsFalse.push_back (request.addr );
2669- decompsFalse.push_back (decomp);
2756+ ThumbITSlot slot;
2757+ slot.addr = request.addr ;
2758+ slot.decomp = decomp;
2759+ slot.thenSlot = thenSlot;
2760+ slot.lift = !pathTerminated[stateIdx];
2761+ slots.push_back (slot);
2762+ pathHasBody[stateIdx] |= slot.lift ;
2763+
2764+ if (slot.lift )
2765+ {
2766+ pathWroteFlags[stateIdx] |= ThumbITInstructionWritesAPSR (decomp);
2767+
2768+ InstructionInfo innerResult;
2769+ if (GetInstructionInfo (data + offset, addr + offset, remainingLen, innerResult))
2770+ {
2771+ for (size_t j = 0 ; j < innerResult.branchCount ; j++)
2772+ {
2773+ switch (innerResult.branchType [j])
2774+ {
2775+ case UnconditionalBranch:
2776+ case TrueBranch:
2777+ case FalseBranch:
2778+ case FunctionReturn:
2779+ case UnresolvedBranch:
2780+ case IndirectBranch:
2781+ case ExceptionBranch:
2782+ pathTerminated[stateIdx] = true ;
2783+ break ;
2784+ default :
2785+ break ;
2786+ }
2787+ }
2788+ }
26702789 }
26712790
26722791 offset += decomp.instrSize / 8 ;
26732792 }
26742793
2794+ decomp_result branchDecomp;
2795+
2796+ if ((offset < len) && ((len - offset) >= 2 )
2797+ && populateDecomposeRequest (&request, data + offset, len - offset, addr + offset, IFTHEN_NO , IFTHENLAST_NO )
2798+ && (thumb_decompose (&request, &branchDecomp) == STATUS_OK )
2799+ && ((offset + (branchDecomp.instrSize / 8 )) <= len)
2800+ && !(branchDecomp.status & STATUS_UNDEFINED ) && branchDecomp.format && IsConditionalBranch (branchDecomp)
2801+ && IsSameOrInvertedCondition (branchDecomp.fields [FIELD_cond], cond))
2802+ {
2803+ bool branchOnTrue = branchDecomp.fields [FIELD_cond] == cond;
2804+ size_t stateIdx = branchOnTrue ? 0 : 1 ;
2805+ if (!pathTerminated[stateIdx] && !pathWroteFlags[stateIdx])
2806+ {
2807+ ThumbITSlot slot;
2808+ slot.addr = request.addr ;
2809+ slot.thenSlot = branchOnTrue;
2810+ slot.lift = true ;
2811+ slot.directBranch = true ;
2812+ slot.branchTarget = GetConditionalBranchTarget (branchDecomp);
2813+ slots.push_back (slot);
2814+ pathHasBody[stateIdx] = true ;
2815+ pathTerminated[stateIdx] = true ;
2816+ offset += branchDecomp.instrSize / 8 ;
2817+ }
2818+ }
2819+
26752820 // generate IL
26762821 LowLevelILLabel labelTrue, labelFalse, labelDone;
26772822
26782823 il.AddInstruction (il.If (GetCondition (il, cond), labelTrue, labelFalse));
26792824
2680- // generate IL for "true" if-else members
2681- il.MarkLabel (labelTrue);
2682-
2683- for (size_t i = 0 ; i < decompsTrue.size (); i++)
2825+ auto liftPath = [&](bool thenPath, LowLevelILLabel& label)
26842826 {
2685- il.SetCurrentAddress (this , addrsTrue[i]);
2686- GetLowLevelILForThumbInstruction (this , il, &(decompsTrue[i]), true );
2687- }
2827+ size_t stateIdx = thenPath ? 0 : 1 ;
26882828
2689- if (decompsFalse.empty ()) {
2690- il.MarkLabel (labelFalse);
2691- }
2692- else {
2693- il.AddInstruction (il.Goto (labelDone));
2694- il.MarkLabel (labelFalse);
2829+ il.MarkLabel (label);
26952830
2696- // generate IL for "false" if-else members
2697- for (int i = 0 ; i < decompsFalse.size (); i++)
2831+ for (auto & slot : slots)
26982832 {
2699- il.SetCurrentAddress (this , addrsFalse[i]);
2700- GetLowLevelILForThumbInstruction (this , il, &(decompsFalse[i]), true );
2833+ if (!slot.lift || (slot.thenSlot != thenPath))
2834+ continue ;
2835+
2836+ il.SetCurrentAddress (this , slot.addr );
2837+ if (slot.directBranch )
2838+ EmitDirectThumbJump (this , il, slot.branchTarget );
2839+ else
2840+ GetLowLevelILForThumbInstruction (this , il, &slot.decomp , true );
27012841 }
27022842
2843+ if (thenPath && pathHasBody[1 ] && !pathTerminated[stateIdx])
2844+ il.AddInstruction (il.Goto (labelDone));
2845+ };
2846+
2847+ liftPath (true , labelTrue);
2848+ liftPath (false , labelFalse);
2849+
2850+ if (pathHasBody[1 ])
27032851 il.MarkLabel (labelDone);
2704- }
27052852
27062853 len = offset;
27072854 return true ;
0 commit comments