-
Notifications
You must be signed in to change notification settings - Fork 50
/
condexe.cc
931 lines (852 loc) · 31.4 KB
/
condexe.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
/* ###
* 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.
*/
#include "condexe.hh"
namespace ghidra {
const int4 BooleanExpressionMatch::maxDepth = 1;
/// \brief Test if two operations with same opcode produce complementary boolean values
///
/// This only tests for cases where the opcode is INT_LESS or INT_SLESS and one of the
/// inputs is constant.
/// \param bin1op is the first p-code op to compare
/// \param bin2op is the second p-code op to compare
/// \return \b true if the two operations always produce complementary values
bool BooleanExpressionMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op)
{
OpCode opcode = bin1op->code();
if ((opcode == CPUI_INT_SLESS)||(opcode==CPUI_INT_LESS)) {
// Basically we test for the scenario like: x < 9 8 < x
int4 constslot = 0;
if (bin1op->getIn(1)->isConstant())
constslot = 1;
if (!bin1op->getIn(constslot)->isConstant()) return false;
if (!bin2op->getIn(1-constslot)->isConstant()) return false;
if (!varnodeSame(bin1op->getIn(1-constslot),bin2op->getIn(constslot))) return false;
uintb val1 = bin1op->getIn(constslot)->getOffset();
uintb val2 = bin2op->getIn(1-constslot)->getOffset();
if (constslot!=0) {
uintb tmp = val2;
val2 = val1;
val1 = tmp;
}
if (val1 + 1 != val2) return false;
if ((val2 == 0)&&(opcode==CPUI_INT_LESS)) return false; // Corner case for unsigned
if (opcode==CPUI_INT_SLESS) { // Corner case for signed
int4 sz = bin1op->getIn(constslot)->getSize();
if (signbit_negative(val2,sz) && (!signbit_negative(val1,sz)))
return false;
}
return true;
}
return false;
}
/// \brief Do the given Varnodes hold the same value, possibly as constants
///
/// \param a is the first Varnode to compare
/// \param b is the second Varnode
/// \return \b true if the Varnodes (always) hold the same value
bool BooleanExpressionMatch::varnodeSame(Varnode *a,Varnode *b)
{
if (a == b) return true;
if (a->isConstant() && b->isConstant())
return (a->getOffset() == b->getOffset());
return false;
}
/// \brief Determine if two boolean Varnodes hold related values
///
/// The values may be the \e same, or opposite of each other (\e complementary).
/// Otherwise the values are \e uncorrelated. The trees constructing each Varnode
/// are examined up to a maximum \b depth. If this is exceeded \e uncorrelated is returned.
/// \param vn1 is the first boolean Varnode
/// \param vn2 is the second boolean Varnode
/// \param depth is the maximum depth to traverse in the evaluation
/// \return the correlation class
int4 BooleanExpressionMatch::evaluate(Varnode *vn1,Varnode *vn2,int4 depth)
{
if (vn1 == vn2) return same;
PcodeOp *op1,*op2;
OpCode opc1,opc2;
if (vn1->isWritten()) {
op1 = vn1->getDef();
opc1 = op1->code();
if (opc1 == CPUI_BOOL_NEGATE) {
int res = evaluate(op1->getIn(0),vn2,depth);
if (res == same) // Flip same <-> complementary result
res = complementary;
else if (res == complementary)
res = same;
return res;
}
}
else {
op1 = (PcodeOp *)0; // Don't give up before checking if op2 is BOOL_NEGATE
opc1 = CPUI_MAX;
}
if (vn2->isWritten()) {
op2 = vn2->getDef();
opc2 = op2->code();
if (opc2 == CPUI_BOOL_NEGATE) {
int4 res = evaluate(vn1,op2->getIn(0),depth);
if (res == same) // Flip same <-> complementary result
res = complementary;
else if (res == complementary)
res = same;
return res;
}
}
else
return uncorrelated;
if (op1 == (PcodeOp *)0)
return uncorrelated;
if (!op1->isBoolOutput() || !op2->isBoolOutput())
return uncorrelated;
if (depth != 0 && (opc1 == CPUI_BOOL_AND || opc1 == CPUI_BOOL_OR || opc1 == CPUI_BOOL_XOR)) {
if (opc2 == CPUI_BOOL_AND || opc2 == CPUI_BOOL_OR || opc2 == CPUI_BOOL_XOR) {
if (opc1 == opc2 || (opc1 == CPUI_BOOL_AND && opc2 == CPUI_BOOL_OR) || (opc1 == CPUI_BOOL_OR && opc2 == CPUI_BOOL_AND)) {
int4 pair1 = evaluate(op1->getIn(0),op2->getIn(0),depth-1);
int4 pair2;
if (pair1 == uncorrelated) {
pair1 = evaluate(op1->getIn(0),op2->getIn(1),depth-1); // Try other possible pairing (commutative op)
if (pair1 == uncorrelated)
return uncorrelated;
pair2 = evaluate(op1->getIn(1),op2->getIn(0),depth-1);
}
else {
pair2 = evaluate(op1->getIn(1),op2->getIn(1),depth-1);
}
if (pair2 == uncorrelated)
return uncorrelated;
if (opc1 == opc2) {
if (pair1 == same && pair2 == same)
return same;
else if (opc1 == CPUI_BOOL_XOR) {
if (pair1 == complementary && pair2 == complementary)
return same;
return complementary;
}
}
else { // Must be CPUI_BOOL_AND and CPUI_BOOL_OR
if (pair1 == complementary && pair2 == complementary)
return complementary; // De Morgan's Law
}
}
}
}
else {
// Two boolean output ops, compare them directly
if (opc1 == opc2) {
if (varnodeSame(op1->getIn(0),op2->getIn(0)) && varnodeSame(op1->getIn(1),op2->getIn(1)))
return same;
if (sameOpComplement(op1,op2)) {
return complementary;
}
return uncorrelated;
}
// Check if the binary ops are complements of one another
int4 slot1 = 0;
int4 slot2 = 0;
bool reorder;
if (opc1 != get_booleanflip(opc2,reorder))
return uncorrelated;
if (reorder) slot2 = 1;
if (!varnodeSame(op1->getIn(slot1),op2->getIn(slot2)))
return uncorrelated;
if (!varnodeSame(op1->getIn(1-slot1),op2->getIn(1-slot2)))
return uncorrelated;
return complementary;
}
return uncorrelated;
}
bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop)
{
int4 res = evaluate(op->getIn(1), iop->getIn(1), maxDepth);
if (res == uncorrelated)
return false;
matchflip = (res == complementary);
if (op->isBooleanFlip())
matchflip = !matchflip;
if (iop->isBooleanFlip())
matchflip = !matchflip;
return true;
}
/// \brief Calculate boolean array of all address spaces that have had a heritage pass run.
///
/// Used to test if all the links out of the iblock have been calculated.
void ConditionalExecution::buildHeritageArray(void)
{
heritageyes.clear();
Architecture *glb = fd->getArch();
heritageyes.resize(glb->numSpaces(),false);
for(int4 i=0;i<glb->numSpaces();++i) {
AddrSpace *spc = glb->getSpace(i);
if (spc == (AddrSpace *)0) continue;
int4 index = spc->getIndex();
if (!spc->isHeritaged()) continue;
if (fd->numHeritagePasses(spc) > 0)
heritageyes[index] = true; // At least one pass has been performed on the space
}
}
/// \brief Test the most basic requirements on \b iblock
///
/// The block must have 2 \b in edges and 2 \b out edges and a final CBRANCH op.
/// \return \b true if \b iblock matches basic requirements
bool ConditionalExecution::testIBlock(void)
{
if (iblock->sizeIn() != 2) return false;
if (iblock->sizeOut() != 2) return false;
cbranch = iblock->lastOp();
if (cbranch == (PcodeOp *)0) return false;
if (cbranch->code() != CPUI_CBRANCH) return false;
return true;
}
/// \return \b true if configuration between \b initblock and \b iblock is correct
bool ConditionalExecution::findInitPre(void)
{
FlowBlock *tmp = iblock->getIn(prea_inslot);
FlowBlock *last = iblock;
while((tmp->sizeOut()==1)&&(tmp->sizeIn()==1)) {
last = tmp;
tmp = tmp->getIn(0);
}
if (tmp->sizeOut() != 2) return false;
initblock = (BlockBasic *)tmp;
tmp = iblock->getIn(1-prea_inslot);
while((tmp->sizeOut()==1)&&(tmp->sizeIn()==1))
tmp = tmp->getIn(0);
if (tmp != initblock) return false;
if (initblock == iblock) return false;
init2a_true = (initblock->getTrueOut() == last);
return true;
}
/// The conditions must always have the same value or always have
/// complementary values.
/// \return \b true if the conditions are correlated
bool ConditionalExecution::verifySameCondition(void)
{
PcodeOp *init_cbranch = initblock->lastOp();
if (init_cbranch == (PcodeOp *)0) return false;
if (init_cbranch->code() != CPUI_CBRANCH) return false;
BooleanExpressionMatch tester;
if (!tester.verifyCondition(cbranch,init_cbranch))
return false;
if (tester.getFlip())
init2a_true = !init2a_true;
int4 multislot = tester.getMultiSlot();
if (multislot != -1) {
// This is a direct split
directsplit = true;
posta_outslot = (multislot == prea_inslot) ? 0 : 1;
if (init2a_true)
posta_outslot = 1 - posta_outslot;
}
return true;
}
/// The given Varnode is defined by a MULTIEQUAL in \b iblock which must be removed.
/// Test if this is possible/advisable given a specific p-code op that reads the Varnode
/// \param vn is the given Varnode
/// \param op is the given PcodeOp reading the Varnode
/// \return \b false if it is not possible to move the defining op (because of the given op)
bool ConditionalExecution::testMultiRead(Varnode *vn,PcodeOp *op)
{
if (op->getParent() == iblock) {
if (!directsplit) {
if (op->code() == CPUI_COPY) // The COPY is tested separately
return true; // If the COPY's output reads can be altered, then -vn- can be altered
return false;
}
}
if (op->code() == CPUI_RETURN) {
if ((op->numInput() < 2)||(op->getIn(1) != vn)) return false; // Only test for flow thru to return value
returnop.push_back(op); // mark that CPUI_RETURN needs special handling
}
return true;
}
/// The given Varnode is defined by an operation in \b iblock which must be removed.
/// Test if this is possible/advisable given a specific p-code op that reads the Varnode
/// \param vn is the given Varnode
/// \param op is the given PcodeOp reading the Varnode
/// \return \b false if it is not possible to move the defining op (because of the given op)
bool ConditionalExecution::testOpRead(Varnode *vn,PcodeOp *op)
{
if (op->getParent() == iblock) return true;
if ((op->code() == CPUI_RETURN)&&(!directsplit)) {
if ((op->numInput() < 2)||(op->getIn(1) != vn)) return false; // Only test for flow thru to return value
PcodeOp *copyop = vn->getDef();
if (copyop->code() == CPUI_COPY) {
// Ordinarily, if -vn- is produced by a COPY we want to return false here because the propagation
// hasn't had time to happen here. But if the flow is into a RETURN this can't propagate, so
// we allow this as a read that can be altered. (We have to move the COPY)
Varnode *invn = copyop->getIn(0);
if (!invn->isWritten()) return false;
PcodeOp *upop = invn->getDef();
if ((upop->getParent() == iblock)&&(upop->code() != CPUI_MULTIEQUAL))
return false;
returnop.push_back(op);
return true;
}
}
return false;
}
/// \brief Prebuild a replacement MULTIEQUAL for output Varnode of the given PcodeOp in \b posta_block
///
/// The new op will hold the same data-flow as the original Varnode once a new
/// edge into \b posta_block is created.
/// \param op is the given PcodeOp
void ConditionalExecution::predefineDirectMulti(PcodeOp *op)
{
PcodeOp *newop = fd->newOp(posta_block->sizeIn()+1,posta_block->getStart());
Varnode *outvn = op->getOut();
Varnode *newoutvn;
newoutvn = fd->newVarnodeOut(outvn->getSize(),outvn->getAddr(),newop);
fd->opSetOpcode(newop,CPUI_MULTIEQUAL);
Varnode *vn;
int4 inslot = iblock->getOutRevIndex(posta_outslot);
for(int4 i=0;i<posta_block->sizeIn();++i) {
if (i==inslot)
vn = op->getIn(1-camethruposta_slot);
else
vn = newoutvn;
fd->opSetInput(newop,vn,i);
}
fd->opSetInput(newop,op->getIn(camethruposta_slot),posta_block->sizeIn());
fd->opInsertBegin(newop,posta_block);
// Cache this new data flow holder
replacement[posta_block->getIndex()] = newoutvn;
}
/// In the \e direct \e split case, MULTIEQUALs in the body block (\b posta_block)
/// must update their flow to account for \b iblock being removed and a new
/// block flowing into the body block.
void ConditionalExecution::adjustDirectMulti(void)
{
list<PcodeOp *>::const_iterator iter;
PcodeOp *op;
iter = posta_block->beginOp();
int4 inslot = iblock->getOutRevIndex(posta_outslot);
while(iter != posta_block->endOp()) {
op = *iter++;
if (op->code() != CPUI_MULTIEQUAL) continue;
Varnode *vn = op->getIn(inslot);
if (vn->isWritten()&&(vn->getDef()->getParent() == iblock)) {
if (vn->getDef()->code() != CPUI_MULTIEQUAL)
throw LowlevelError("Cannot push non-trivial operation");
// Flow that stays in iblock, comes from modified side
fd->opSetInput(op,vn->getDef()->getIn(1-camethruposta_slot),inslot);
// Flow from unmodified side, forms new branch
vn = vn->getDef()->getIn(camethruposta_slot);
}
fd->opInsertInput(op,vn,op->numInput());
}
}
/// \brief Create a MULTIEQUAL in the given block that will hold data-flow from the given PcodeOp
///
/// A new MULTIEQUAL is created whose inputs are the output of the given PcodeOp
/// \param op is the PcodeOp whose output will get held
/// \param bl is the block that will contain the new MULTIEQUAL
/// \return the output Varnode of the new MULTIEQUAL
Varnode *ConditionalExecution::getNewMulti(PcodeOp *op,BlockBasic *bl)
{
PcodeOp *newop = fd->newOp(bl->sizeIn(),bl->getStart());
Varnode *outvn = op->getOut();
Varnode *newoutvn;
// Using the original outvn address may cause merge conflicts
// newoutvn = fd->newVarnodeOut(outvn->getSize(),outvn->getAddr(),newop);
newoutvn = fd->newUniqueOut(outvn->getSize(),newop);
fd->opSetOpcode(newop,CPUI_MULTIEQUAL);
// We create NEW references to outvn, these refs will get put
// at the end of the dependency list and will get handled in
// due course
for(int4 i=0;i<bl->sizeIn();++i)
fd->opSetInput(newop,outvn,i);
fd->opInsertBegin(newop,bl);
return newoutvn;
}
/// \brief Find a replacement Varnode for the output of the given PcodeOp that is read in the given block
///
/// The replacement Varnode must be valid for everything below (dominated) by the block.
/// If we can't find a replacement, create one (as a MULTIEQUAL) in the given
/// block (creating recursion through input blocks). Any new Varnode created is
/// cached in the \b replacement array so it can get picked up by other calls to this function
/// for different blocks.
/// \param op is the given PcodeOp whose output we must replace
/// \param bl is the given basic block (containing a read of the Varnode)
/// \return the replacement Varnode
Varnode *ConditionalExecution::getReplacementRead(PcodeOp *op,BlockBasic *bl)
{
map<int4,Varnode *>::const_iterator iter;
iter = replacement.find(bl->getIndex());
if (iter != replacement.end())
return (*iter).second;
BlockBasic *curbl = bl;
// Flow must eventually come through iblock
while(curbl->getImmedDom() != iblock) {
curbl = (BlockBasic *)curbl->getImmedDom(); // Get immediate dominator
if (curbl == (FlowBlock *)0)
throw LowlevelError("Conditional execution: Could not find dominator");
}
iter = replacement.find(curbl->getIndex());
if (iter != replacement.end()) {
replacement[bl->getIndex()] = (*iter).second;
return (*iter).second;
}
Varnode *res;
if (curbl->sizeIn()==1) {
// Since dominator is iblock, In(0) must be iblock
// Figure what side of -iblock- we came through
int4 slot = (curbl->getInRevIndex(0) == posta_outslot) ? camethruposta_slot : 1-camethruposta_slot;
res = op->getIn(slot);
}
else
res = getNewMulti(op,curbl);
replacement[curbl->getIndex()] = res;
if (curbl != bl)
replacement[bl->getIndex()] = res;
return res;
}
/// The data-flow for the given op is reproduced in the new control-flow configuration.
/// After completion of this method, the op can be removed.
/// \param op is the given PcodeOp
void ConditionalExecution::doReplacement(PcodeOp *op)
{
if (op->code() == CPUI_COPY) {
if (op->getOut()->hasNoDescend()) // Verify that this has been dealt with by fixReturnOp
return;
// It could be a COPY internal to iblock, we need to remove it like any other op
}
replacement.clear();
if (directsplit)
predefineDirectMulti(op);
Varnode *vn = op->getOut();
list<PcodeOp *>::const_iterator iter = vn->beginDescend();
while(iter != vn->endDescend()) {
PcodeOp *readop = *iter;
int4 slot = readop->getSlot(vn);
BlockBasic *bl = readop->getParent();
Varnode *rvn;
if (bl == iblock) {
if (directsplit)
fd->opSetInput(readop,op->getIn(1-camethruposta_slot),slot); // We know op is MULTIEQUAL
else
fd->opUnsetInput(readop,slot);
}
else {
if (readop->code() == CPUI_MULTIEQUAL) {
BlockBasic *inbl = (BlockBasic *)bl->getIn(slot);
if (inbl == iblock) {
int4 s = (bl->getInRevIndex(slot) == posta_outslot) ? camethruposta_slot : 1-camethruposta_slot;
rvn = op->getIn(s);
}
else
rvn = getReplacementRead(op,inbl);
}
else
rvn = getReplacementRead(op,bl);
fd->opSetInput(readop,rvn,slot);
}
// The last descendant is now gone
iter = vn->beginDescend();
}
}
/// \brief Reproduce COPY data-flow into RETURN ops affected by the removal of \b iblock
void ConditionalExecution::fixReturnOp(void)
{
for(int4 i=0;i<returnop.size();++i) {
PcodeOp *retop = returnop[i];
Varnode *retvn = retop->getIn(1);
PcodeOp *iblockop = retvn->getDef();
Varnode *invn;
if (iblockop->code() == CPUI_COPY)
invn = iblockop->getIn(0); // This must either be from MULTIEQUAL or something written outside of iblock
else
invn = retvn;
PcodeOp *newcopyop = fd->newOp(1,retop->getAddr());
fd->opSetOpcode(newcopyop,CPUI_COPY);
Varnode *outvn = fd->newVarnodeOut(retvn->getSize(),retvn->getAddr(),newcopyop); // Preserve the CPUI_RETURN storage address
fd->opSetInput(newcopyop,invn,0);
fd->opSetInput(retop,outvn,1);
fd->opInsertBefore(newcopyop,retop);
}
}
/// \param op is the PcodeOp within \b iblock to test
/// \return \b true if it is removable
bool ConditionalExecution::testRemovability(PcodeOp *op)
{
list<PcodeOp *>::const_iterator iter;
PcodeOp *readop;
Varnode *vn;
if (op->code() == CPUI_MULTIEQUAL) {
vn = op->getOut();
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
readop = *iter;
if (!testMultiRead(vn,readop))
return false;
}
}
else {
if (op->isFlowBreak() || op->isCall()) return false;
if ((op->code()==CPUI_LOAD)||(op->code()==CPUI_STORE))
return false;
if (op->code()==CPUI_INDIRECT) return false;
vn = op->getOut();
if (vn != (Varnode *)0) {
bool hasnodescend = true;
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
readop = *iter;
if (!testOpRead(vn,readop))
return false;
hasnodescend = false;
}
if (hasnodescend && (!heritageyes[vn->getSpace()->getIndex()])) // Check if heritage is performed for this varnode's space
return false;
}
}
return true;
}
/// The \b iblock has been fixed. Test all control-flow conditions, and test removability
/// of all ops in the \b iblock.
/// \return \b true if the configuration can be modified
bool ConditionalExecution::verify(void)
{
prea_inslot = 0;
posta_outslot = 0;
directsplit = false;
if (!testIBlock()) return false;
if (!findInitPre()) return false;
if (!verifySameCondition()) return false;
// Cache some useful values
iblock2posta_true = (posta_outslot == 1);
camethruposta_slot = (init2a_true==iblock2posta_true) ? prea_inslot : 1-prea_inslot;
posta_block = (BlockBasic *)iblock->getOut(posta_outslot);
postb_block = (BlockBasic *)iblock->getOut(1-posta_outslot);
returnop.clear();
list<PcodeOp *>::const_iterator iter;
iter = iblock->endOp();
if (iter != iblock->beginOp())
--iter; // Skip branch
while(iter != iblock->beginOp()) {
--iter;
if (!testRemovability( *iter ))
return false;
}
return true;
}
/// Set up for testing ConditionalExecution on multiple iblocks
/// \param f is the function to do testing on
ConditionalExecution::ConditionalExecution(Funcdata *f)
{
fd = f;
buildHeritageArray(); // Cache an array depending on the particular heritage pass
}
/// The given block is tested as a possible \b iblock. If this configuration
/// works and is not a \b directsplit, \b true is returned.
/// If the configuration works as a \b directsplit, then recursively check that
/// its \b posta_block works as an \b iblock. If it does work, keep this
/// \b iblock, otherwise revert to the \b directsplit configuration. In either
/// case return \b true. Processing the \b directsplit first may prevent
/// posta_block from being an \b iblock.
/// \param ib is the trial \b iblock
/// \return \b true if (some) configuration is recognized and can be modified
bool ConditionalExecution::trial(BlockBasic *ib)
{
iblock = ib;
if (!verify()) return false;
PcodeOp *cbranch_copy;
BlockBasic *initblock_copy;
BlockBasic *iblock_copy;
int4 prea_inslot_copy;
bool init2a_true_copy;
bool iblock2posta_true_copy;
int4 camethruposta_slot_copy;
int4 posta_outslot_copy;
BlockBasic *posta_block_copy;
BlockBasic *postb_block_copy;
bool directsplit_copy;
for(;;) {
if (!directsplit) return true;
// Save off the data for current iblock
cbranch_copy = cbranch;
initblock_copy = initblock;
iblock_copy = iblock;
prea_inslot_copy = prea_inslot;
init2a_true_copy = init2a_true;
iblock2posta_true_copy = iblock2posta_true;
camethruposta_slot_copy = camethruposta_slot;
posta_outslot_copy = posta_outslot;
posta_block_copy = posta_block;
postb_block_copy = postb_block;
directsplit_copy = directsplit;
iblock = posta_block;
if (!verify()) {
cbranch = cbranch_copy;
initblock = initblock_copy;
iblock = iblock_copy;
prea_inslot = prea_inslot_copy;
init2a_true = init2a_true_copy;
iblock2posta_true = iblock2posta_true_copy;
camethruposta_slot = camethruposta_slot_copy;
posta_outslot = posta_outslot_copy;
posta_block = posta_block_copy;
postb_block = postb_block_copy;
directsplit = directsplit_copy;
return true;
}
}
}
/// We assume the last call to verify() returned \b true
void ConditionalExecution::execute(void)
{
list<PcodeOp *>::iterator iter;
PcodeOp *op;
fixReturnOp(); // Patch any data-flow thru to CPUI_RETURN
if (!directsplit) {
iter = iblock->beginOp();
while(iter != iblock->endOp()) {
op = *iter++;
if (!op->isBranch())
doReplacement(op); // Remove all read refs of op
fd->opDestroy(op); // Then destroy op
}
fd->removeFromFlowSplit(iblock,(posta_outslot != camethruposta_slot));
}
else {
adjustDirectMulti();
iter = iblock->beginOp();
while(iter != iblock->endOp()) {
op = *iter++;
if (op->code() == CPUI_MULTIEQUAL) { // Only adjust MULTIEQUALs
doReplacement(op);
fd->opDestroy(op);
}
// Branch stays, other operations stay
}
fd->switchEdge(iblock->getIn(camethruposta_slot),iblock,posta_block);
}
}
int4 ActionConditionalExe::apply(Funcdata &data)
{
bool changethisround;
int4 numhits = 0;
int4 i;
if (data.hasUnreachableBlocks()) // Conditional execution elimination logic may not work with unreachable blocks
return 0;
ConditionalExecution condexe(&data);
const BlockGraph &bblocks( data.getBasicBlocks() );
do {
changethisround = false;
for(i=0;i<bblocks.getSize();++i) {
BlockBasic *bb = (BlockBasic *)bblocks.getBlock(i);
if (condexe.trial(bb)) {
condexe.execute(); // Adjust dataflow
numhits += 1;
changethisround = true;
}
}
} while(changethisround);
count += numhits; // Number of changes
return 0;
}
/// \brief Check if \b vn is produced by a 2-branch MULTIEQUAL, one side of which is a zero constant
///
/// \param vn is the given Varnode
/// \return \b true if the expression producing \b vn matches the form
bool RuleOrPredicate::MultiPredicate::discoverZeroSlot(Varnode *vn)
{
if (!vn->isWritten()) return false;
op = vn->getDef();
if (op->code() != CPUI_MULTIEQUAL) return false;
if (op->numInput() != 2) return false;
for(zeroSlot=0;zeroSlot<2;++zeroSlot) {
Varnode *tmpvn = op->getIn(zeroSlot);
if (!tmpvn->isWritten()) continue;
PcodeOp *copyop = tmpvn->getDef();
if (copyop->code() != CPUI_COPY) continue; // Multiequal must have CPUI_COPY input
Varnode *zerovn = copyop->getIn(0);
if (!zerovn->isConstant()) continue;
if (zerovn->getOffset() != 0) continue; // which copies #0
otherVn = op->getIn(1-zeroSlot); // store off varnode from other path
if (otherVn->isFree()) return false;
return true;
}
return false;
}
/// \brief Find CBRANCH operation that determines whether zero is set or not
///
/// Assuming that \b op is a 2-branch MULTIEQUAL as per discoverZeroSlot(),
/// try to find a single CBRANCH whose two \b out edges correspond to the
/// \b in edges of the MULTIEQUAL. In this case, the boolean expression
/// controlling the CBRANCH is also controlling whether zero flows into
/// the MULTIEQUAL output Varnode.
/// \return \b true if a single controlling CBRANCH is found
bool RuleOrPredicate::MultiPredicate::discoverCbranch(void)
{
const FlowBlock *baseBlock = op->getParent();
zeroBlock = baseBlock->getIn(zeroSlot);
const FlowBlock *otherBlock = baseBlock->getIn(1-zeroSlot);
if (zeroBlock->sizeOut() == 1) {
if (zeroBlock->sizeIn() != 1) return false;
condBlock = zeroBlock->getIn(0);
}
else if (zeroBlock->sizeOut() == 2)
condBlock = zeroBlock;
else
return false;
if (condBlock->sizeOut() != 2) return false;
if (otherBlock->sizeOut() == 1) {
if (otherBlock->sizeIn() != 1) return false;
if (condBlock != otherBlock->getIn(0)) return false;
}
else if (otherBlock->sizeOut() == 2) {
if (condBlock != otherBlock) return false;
}
else
return false;
cbranch = condBlock->lastOp();
if (cbranch == (PcodeOp *)0) return false;
if (cbranch->code() != CPUI_CBRANCH) return false;
return true;
}
/// \brief Does the \b condBlock \b true outgoing edge flow to the block that sets zero
///
/// The \b zeroPathIsTrue variable is set based on the current configuration
void RuleOrPredicate::MultiPredicate::discoverPathIsTrue(void)
{
if (condBlock->getTrueOut() == zeroBlock)
zeroPathIsTrue = true;
else if (condBlock->getFalseOut() == zeroBlock)
zeroPathIsTrue = false;
else { // condBlock must be zeroBlock
zeroPathIsTrue = (condBlock->getTrueOut() == op->getParent()); // True if "true" path does not override zero set
}
}
/// \brief Verify that CBRANCH boolean expression is either (\b vn == 0) or (\b vn != 0)
///
/// Modify \b zeroPathIsTrue so that if it is \b true, then: A \b vn value equal to zero,
/// causes execution to flow to where the output of MULTIEQUAL is set to zero.
/// \param vn is the given Varnode
/// \return \b true if the boolean expression has a matching form
bool RuleOrPredicate::MultiPredicate::discoverConditionalZero(Varnode *vn)
{
Varnode *boolvn = cbranch->getIn(1);
if (!boolvn->isWritten()) return false;
PcodeOp *compareop = boolvn->getDef();
OpCode opc = compareop->code();
if (opc == CPUI_INT_NOTEQUAL) // Verify that CBRANCH depends on INT_NOTEQUAL
zeroPathIsTrue = !zeroPathIsTrue;
else if (opc != CPUI_INT_EQUAL) // or INT_EQUAL
return false;
Varnode *a1 = compareop->getIn(0);
Varnode *a2 = compareop->getIn(1);
Varnode *zerovn;
if (a1 == vn) // Verify one side of compare is vn
zerovn = a2;
else if (a2 == vn)
zerovn = a1;
else
return false;
if (!zerovn->isConstant()) return false;
if (zerovn->getOffset() != 0) return false; // Verify we are comparing to zero
if (cbranch->isBooleanFlip())
zeroPathIsTrue = !zeroPathIsTrue;
return true;
}
void RuleOrPredicate::getOpList(vector<uint4> &oplist) const
{
oplist.push_back(CPUI_INT_OR);
oplist.push_back(CPUI_INT_XOR);
}
/// \brief Check for the \e alternate form, tmp1 = (val2 == 0) ? val1 : 0;
///
/// We know we have the basic form
/// \code
/// tmp1 = cond ? val1 : 0;
/// result = tmp1 | other;
/// \endcode
/// So we just need to check that \b other plays the role of \b val2.
/// If we match the \e alternate form, perform the simplification
/// \param vn is the candidate \b other Varnode
/// \param branch holds the basic form
/// \param op is the INT_OR p-code op
/// \param data is the function being analyzed
/// \return 1 if the form was matched and simplified, 0 otherwise
int4 RuleOrPredicate::checkSingle(Varnode *vn,MultiPredicate &branch,PcodeOp *op,Funcdata &data)
{
if (vn->isFree()) return 0;
if (!branch.discoverCbranch()) return 0;
if (branch.op->getOut()->loneDescend() != op) return 0; // Must only be one use of MULTIEQUAL, because we rewrite it
branch.discoverPathIsTrue();
if (!branch.discoverConditionalZero(vn)) return 0;
if (branch.zeroPathIsTrue) return 0; // true condition (vn == 0) must not go to zero set
data.opSetInput(branch.op,vn,branch.zeroSlot);
data.opRemoveInput(op,1);
data.opSetOpcode(op,CPUI_COPY);
data.opSetInput(op,branch.op->getOut(),0);
return 1;
}
int4 RuleOrPredicate::applyOp(PcodeOp *op,Funcdata &data)
{
MultiPredicate branch0;
MultiPredicate branch1;
bool test0 = branch0.discoverZeroSlot(op->getIn(0));
bool test1 = branch1.discoverZeroSlot(op->getIn(1));
if ((test0==false) && (test1==false)) return 0;
if (!test0) // branch1 has MULTIEQUAL form, but branch0 does not
return checkSingle(op->getIn(0),branch1,op,data);
else if (!test1) // branch0 has MULTIEQUAL form, but branch1 does not
return checkSingle(op->getIn(1),branch0,op,data);
if (!branch0.discoverCbranch()) return 0;
if (!branch1.discoverCbranch()) return 0;
if (branch0.condBlock == branch1.condBlock) {
if (branch0.zeroBlock == branch1.zeroBlock) return 0; // zero sets must be along different paths
}
else { // Make sure cbranches have shared condition and the different zero sets have complementary paths
BooleanExpressionMatch condmarker;
if (!condmarker.verifyCondition(branch0.cbranch,branch1.cbranch)) return 0;
if (condmarker.getMultiSlot() != -1) return 0;
branch0.discoverPathIsTrue();
branch1.discoverPathIsTrue();
bool finalBool = branch0.zeroPathIsTrue == branch1.zeroPathIsTrue;
if (condmarker.getFlip())
finalBool = !finalBool;
if (finalBool) return 0; // One path hits both zero sets, they must be on different paths
}
int4 order = branch0.op->compareOrder(branch1.op);
if (order == 0) return 0; // can this happen?
BlockBasic *finalBlock;
bool slot0SetsBranch0; // True if non-zero setting of branch0 flows throw slot0
if (order < 0) { // branch1 happens after
finalBlock = branch1.op->getParent();
slot0SetsBranch0 = branch1.zeroSlot == 0;
}
else { // branch0 happens after
finalBlock = branch0.op->getParent();
slot0SetsBranch0 = branch0.zeroSlot == 1;
}
PcodeOp *newMulti = data.newOp(2,finalBlock->getStart());
data.opSetOpcode(newMulti,CPUI_MULTIEQUAL);
if (slot0SetsBranch0) {
data.opSetInput(newMulti,branch0.otherVn,0);
data.opSetInput(newMulti,branch1.otherVn,1);
}
else {
data.opSetInput(newMulti,branch1.otherVn,0);
data.opSetInput(newMulti,branch0.otherVn,1);
}
Varnode *newvn = data.newUniqueOut(branch0.otherVn->getSize(),newMulti);
data.opInsertBegin(newMulti,finalBlock);
data.opRemoveInput(op,1);
data.opSetInput(op,newvn,0);
data.opSetOpcode(op,CPUI_COPY);
return 1;
}
} // End namespace ghidra