@@ -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;
@@ -2748,82 +2840,137 @@ class Thumb2Architecture: public ArmCommonArchitecture
27482840 uint32_t mask = decomp.fields [FIELD_mask];
27492841 uint32_t cond = decomp.fields [FIELD_firstcond];
27502842
2751- // Calculate number of instructions
27522843 size_t instrCount;
2753- if (decomp. fields [FIELD_mask] & 1 )
2844+ if (mask & 1 )
27542845 instrCount = 4 ;
2755- else if (decomp. fields [FIELD_mask] & 2 )
2846+ else if (mask & 2 )
27562847 instrCount = 3 ;
2757- else if (decomp. fields [FIELD_mask] & 4 )
2848+ else if (mask & 4 )
27582849 instrCount = 2 ;
27592850 else
27602851 instrCount = 1 ;
27612852
2762- // decompose all instructions in the if-then block
2763- vector<uint32_t > addrsTrue, addrsFalse;
2764- vector<decomp_result> decompsTrue, decompsFalse;
2853+ // Decompose all instructions in the IT block and keep their original
2854+ // mask slot. A path may skip later slots once it has branched/returned.
2855+ vector<ThumbITSlot> slots;
2856+ bool pathTerminated[2 ] = {false , false };
2857+ bool pathHasBody[2 ] = {false , false };
2858+ bool pathWroteFlags[2 ] = {false , false };
27652859
27662860 for (size_t i = 0 ; i < instrCount; i++)
27672861 {
27682862 if (offset >= len || (len - offset) < 2 )
27692863 return false ;
27702864
2771- bool isTrue = (i == 0 ) || (((mask >> (4 - i)) & 1 ) == (cond & 1 ));
2865+ bool thenSlot = (i == 0 ) || (((mask >> (4 - i)) & 1 ) == (cond & 1 ));
2866+ size_t stateIdx = thenSlot ? 0 : 1 ;
27722867 size_t remainingLen = len - offset;
27732868
2774- if (! populateDecomposeRequest (&request, data+ offset, remainingLen, addr+ offset,
2775- IFTHEN_YES , ((i + 1 ) >= instrCount) ? IFTHENLAST_YES : IFTHENLAST_NO ))
2776- return false ;
2777-
2778- if (thumb_decompose (&request, &decomp) != STATUS_OK )
2869+ bool decoded = populateDecomposeRequest (&request, data + offset, remainingLen, addr + offset,
2870+ IFTHEN_YES , ((i + 1 ) >= instrCount) ? IFTHENLAST_YES : IFTHENLAST_NO )
2871+ && ( thumb_decompose (&request, &decomp) == STATUS_OK )
2872+ && !(decomp. status & STATUS_UNDEFINED ) && decomp. format ;
2873+ if (!decoded )
27792874 return false ;
27802875 if ((decomp.instrSize / 8 ) > remainingLen)
27812876 return false ;
2782- if ((decomp.status & STATUS_UNDEFINED ) || (!decomp.format ))
2783- return false ;
27842877
2785- if (isTrue) {
2786- addrsTrue.push_back (request.addr );
2787- decompsTrue.push_back (decomp);
2788- }
2789- else {
2790- addrsFalse.push_back (request.addr );
2791- decompsFalse.push_back (decomp);
2878+ ThumbITSlot slot;
2879+ slot.addr = request.addr ;
2880+ slot.decomp = decomp;
2881+ slot.thenSlot = thenSlot;
2882+ slot.lift = !pathTerminated[stateIdx];
2883+ slots.push_back (slot);
2884+ pathHasBody[stateIdx] |= slot.lift ;
2885+
2886+ if (slot.lift )
2887+ {
2888+ pathWroteFlags[stateIdx] |= ThumbITInstructionWritesAPSR (decomp);
2889+
2890+ InstructionInfo innerResult;
2891+ if (GetInstructionInfo (data + offset, addr + offset, remainingLen, innerResult))
2892+ {
2893+ for (size_t j = 0 ; j < innerResult.branchCount ; j++)
2894+ {
2895+ switch (innerResult.branchType [j])
2896+ {
2897+ case UnconditionalBranch:
2898+ case TrueBranch:
2899+ case FalseBranch:
2900+ case FunctionReturn:
2901+ case UnresolvedBranch:
2902+ case IndirectBranch:
2903+ case ExceptionBranch:
2904+ pathTerminated[stateIdx] = true ;
2905+ break ;
2906+ default :
2907+ break ;
2908+ }
2909+ }
2910+ }
27922911 }
27932912
27942913 offset += decomp.instrSize / 8 ;
27952914 }
27962915
2916+ decomp_result branchDecomp;
2917+
2918+ if ((offset < len) && ((len - offset) >= 2 )
2919+ && populateDecomposeRequest (&request, data + offset, len - offset, addr + offset, IFTHEN_NO , IFTHENLAST_NO )
2920+ && (thumb_decompose (&request, &branchDecomp) == STATUS_OK )
2921+ && ((offset + (branchDecomp.instrSize / 8 )) <= len)
2922+ && !(branchDecomp.status & STATUS_UNDEFINED ) && branchDecomp.format && IsConditionalBranch (branchDecomp)
2923+ && IsSameOrInvertedCondition (branchDecomp.fields [FIELD_cond], cond))
2924+ {
2925+ bool branchOnTrue = branchDecomp.fields [FIELD_cond] == cond;
2926+ size_t stateIdx = branchOnTrue ? 0 : 1 ;
2927+ if (!pathTerminated[stateIdx] && !pathWroteFlags[stateIdx])
2928+ {
2929+ ThumbITSlot slot;
2930+ slot.addr = request.addr ;
2931+ slot.thenSlot = branchOnTrue;
2932+ slot.lift = true ;
2933+ slot.directBranch = true ;
2934+ slot.branchTarget = GetConditionalBranchTarget (branchDecomp);
2935+ slots.push_back (slot);
2936+ pathHasBody[stateIdx] = true ;
2937+ pathTerminated[stateIdx] = true ;
2938+ offset += branchDecomp.instrSize / 8 ;
2939+ }
2940+ }
2941+
27972942 // generate IL
27982943 LowLevelILLabel labelTrue, labelFalse, labelDone;
27992944
28002945 il.AddInstruction (il.If (GetCondition (il, cond), labelTrue, labelFalse));
28012946
2802- // generate IL for "true" if-else members
2803- il.MarkLabel (labelTrue);
2804-
2805- for (size_t i = 0 ; i < decompsTrue.size (); i++)
2947+ auto liftPath = [&](bool thenPath, LowLevelILLabel& label)
28062948 {
2807- il.SetCurrentAddress (this , addrsTrue[i]);
2808- GetLowLevelILForThumbInstruction (this , il, &(decompsTrue[i]), true );
2809- }
2949+ size_t stateIdx = thenPath ? 0 : 1 ;
28102950
2811- if (decompsFalse.empty ()) {
2812- il.MarkLabel (labelFalse);
2813- }
2814- else {
2815- il.AddInstruction (il.Goto (labelDone));
2816- il.MarkLabel (labelFalse);
2951+ il.MarkLabel (label);
28172952
2818- // generate IL for "false" if-else members
2819- for (int i = 0 ; i < decompsFalse.size (); i++)
2953+ for (auto & slot : slots)
28202954 {
2821- il.SetCurrentAddress (this , addrsFalse[i]);
2822- GetLowLevelILForThumbInstruction (this , il, &(decompsFalse[i]), true );
2955+ if (!slot.lift || (slot.thenSlot != thenPath))
2956+ continue ;
2957+
2958+ il.SetCurrentAddress (this , slot.addr );
2959+ if (slot.directBranch )
2960+ EmitDirectThumbJump (this , il, slot.branchTarget );
2961+ else
2962+ GetLowLevelILForThumbInstruction (this , il, &slot.decomp , true );
28232963 }
28242964
2965+ if (thenPath && pathHasBody[1 ] && !pathTerminated[stateIdx])
2966+ il.AddInstruction (il.Goto (labelDone));
2967+ };
2968+
2969+ liftPath (true , labelTrue);
2970+ liftPath (false , labelFalse);
2971+
2972+ if (pathHasBody[1 ])
28252973 il.MarkLabel (labelDone);
2826- }
28272974
28282975 len = offset;
28292976 return true ;
0 commit comments