-
Notifications
You must be signed in to change notification settings - Fork 49
/
funcdata_block.cc
1093 lines (983 loc) · 37.6 KB
/
funcdata_block.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
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* ###
* 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 "funcdata.hh"
#include "flow.hh"
namespace ghidra {
// Funcdata members pertaining directly to blocks
/// A description of each block in the current structure hierarchy is
/// printed to stream. This is suitable for a console mode or debug view
/// of the state of control-flow structuring at any point during analysis.
/// \param s is the output stream
void Funcdata::printBlockTree(ostream &s) const
{
if (sblocks.getSize() != 0)
sblocks.printTree(s,0);
}
void Funcdata::clearBlocks(void)
{
bblocks.clear();
sblocks.clear();
}
/// Any override information is preserved.
void Funcdata::clearJumpTables(void)
{
vector<JumpTable *> remain;
vector<JumpTable *>::iterator iter;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
JumpTable *jt = *iter;
if (jt->isOverride()) {
jt->clear(); // Clear out any derived data
remain.push_back(jt); // Keep the override itself
}
else
delete jt;
}
jumpvec = remain;
}
/// The JumpTable object is freed, and the associated BRANCHIND is no longer marked
/// as a \e switch point.
/// \param jt is the given JumpTable object
void Funcdata::removeJumpTable(JumpTable *jt)
{
vector<JumpTable *> remain;
vector<JumpTable *>::iterator iter;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter)
if ((*iter) != jt)
remain.push_back(*iter);
PcodeOp *op = jt->getIndirectOp();
delete jt;
if (op != (PcodeOp *)0)
op->getParent()->clearFlag(FlowBlock::f_switch_out);
jumpvec = remain;
}
/// Assuming the given basic block is being removed, force any Varnode defined by
/// a MULTIEQUAL in the block to be defined in the output block instead. This is used
/// as part of the basic block removal process to patch up data-flow.
/// \param bb is the given basic block
void Funcdata::pushMultiequals(BlockBasic *bb)
{
BlockBasic *outblock;
PcodeOp *origop,*replaceop;
Varnode *origvn,*replacevn;
list<PcodeOp *>::iterator iter;
list<PcodeOp *>::const_iterator citer;
if (bb->sizeOut()==0) return;
if (bb->sizeOut()>1)
warningHeader("push_multiequal on block with multiple outputs");
outblock = (BlockBasic *) bb->getOut(0); // Take first output block. If this is a
// donothing block, it is the only output block
int4 outblock_ind = bb->getOutRevIndex(0);
for(iter=bb->beginOp();iter!=bb->endOp();++iter) {
origop = *iter;
if (origop->code() != CPUI_MULTIEQUAL) continue;
origvn = origop->getOut();
if (origvn->hasNoDescend()) continue;
bool needreplace = false;
bool neednewunique = false;
for(citer=origvn->beginDescend();citer!=origvn->endDescend();++citer) {
PcodeOp *op = *citer;
if ((op->code()==CPUI_MULTIEQUAL)&&(op->getParent()==outblock)) {
bool deadEdge = true; // Check for reference to origvn NOT thru the dead edge
for(int4 i=0;i<op->numInput();++i) {
if (i == outblock_ind) continue; // Not going thru dead edge
if (op->getIn(i) == origvn) { // Reference to origvn
deadEdge = false;
break;
}
}
if (deadEdge) {
if ((origvn->getAddr() == op->getOut()->getAddr())&&origvn->isAddrTied())
// If origvn is addrtied and feeds into a MULTIEQUAL at same address in outblock
// Then any use of origvn beyond outblock that did not go thru this MULTIEQUAL must have
// propagated through some other register. So we make the new MULTIEQUAL write to a unique register
neednewunique = true;
continue;
}
}
needreplace = true;
break;
}
if (!needreplace) continue;
// Construct artificial MULTIEQUAL
vector<Varnode *> branches;
if (neednewunique)
replacevn = newUnique(origvn->getSize());
else
replacevn = newVarnode(origvn->getSize(),origvn->getAddr());
for(int4 i=0;i<outblock->sizeIn();++i) {
if (outblock->getIn(i) == bb)
branches.push_back(origvn);
else
branches.push_back( replacevn );
// In this situation there are other blocks "beyond" outblock which read
// origvn defined in bb, but there are other blocks falling into outblock
// Assuming the only out of bb is outblock, all heritages of origvn must
// come through outblock. Thus any alternate ins to outblock must be
// dominated by bb. So the artificial MULTIEQUAL we construct must have
// all inputs be origvn
}
replaceop = newOp(branches.size(),outblock->getStart());
opSetOpcode(replaceop,CPUI_MULTIEQUAL);
opSetOutput(replaceop,replacevn);
opSetAllInput(replaceop,branches);
opInsertBegin(replaceop,outblock);
// Replace obsolete origvn with replacevn
int4 i;
list<PcodeOp *>::iterator titer = origvn->descend.begin();
while(titer != origvn->descend.end()) {
PcodeOp *op = *titer++;
i = op->getSlot(origvn);
// Do not replace MULTIEQUAL references in the same block
// as replaceop. These are patched by block_remove
if ((op->code()==CPUI_MULTIEQUAL)&&(op->getParent()==outblock)&&(i==outblock_ind))
continue;
opSetInput(op,replacevn,i);
}
}
}
/// If the MULTIEQUAL has no inputs, presumably the basic block is unreachable, so we treat
/// the p-code op as a COPY from a new input Varnode. If there is 1 input, the MULTIEQUAL
/// is transformed directly into a COPY.
/// \param op is the given MULTIEQUAL
void Funcdata::opZeroMulti(PcodeOp *op)
{
if (op->numInput()==0) { // If no branches left
opInsertInput(op,newVarnode(op->getOut()->getSize(),op->getOut()->getAddr()),0);
setInputVarnode(op->getIn(0)); // Then this is an input
opSetOpcode(op,CPUI_COPY);
}
else if (op->numInput()==1)
opSetOpcode(op,CPUI_COPY);
}
/// \brief Remove an outgoing branch of the given basic block
///
/// MULTIEQUAL p-code ops (in other blocks) that take inputs from the outgoing branch
/// are patched appropriately.
/// \param bb is the given basic block
/// \param num is the index of the outgoing edge to remove
void Funcdata::branchRemoveInternal(BlockBasic *bb,int4 num)
{
BlockBasic *bbout;
list<PcodeOp *>::iterator iter;
PcodeOp *op;
int4 blocknum;
if (bb->sizeOut() == 2) // If there is no decision left
opDestroy(bb->lastOp()); // Remove the branch instruction
bbout = (BlockBasic *) bb->getOut(num);
blocknum = bbout->getInIndex(bb);
bblocks.removeEdge(bb,bbout); // Sever (one) connection between bb and bbout
for(iter=bbout->beginOp();iter!=bbout->endOp();++iter) {
op = *iter;
if (op->code() != CPUI_MULTIEQUAL) continue;
opRemoveInput(op,blocknum);
opZeroMulti(op);
}
}
/// The edge is removed from control-flow and affected MULTIEQUAL ops are adjusted.
/// \param bb is the basic block
/// \param num is the index of the out edge to remove
void Funcdata::removeBranch(BlockBasic *bb,int4 num)
{
branchRemoveInternal(bb,num);
structureReset();
}
/// \brief Check if given Varnode has any descendants in a dead block
///
/// Assuming a basic block is marked \e dead, return \b true if any PcodeOp reading
/// the Varnode is in the dead block.
/// \param vn is the given Varnode
/// \return \b true if the Varnode is read in the dead block
bool Funcdata::descendantsOutside(Varnode *vn)
{
list<PcodeOp *>::const_iterator iter;
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter)
if (!(*iter)->getParent()->isDead()) return true;
return false;
}
/// \brief Remove an active basic block from the function
///
/// PcodeOps in the block are deleted. Data-flow and control-flow are otherwise
/// patched up. Most of the work is patching up MULTIEQUALs and other remaining
/// references to Varnodes flowing through the block to be removed.
///
/// If descendant Varnodes are stranded by removing the block, either an exception is
/// thrown, or optionally, the descendant Varnodes can be replaced with constants and
/// a warning is printed.
/// \param bb is the given basic block
/// \param unreachable is \b true if the caller wants a warning for stranded Varnodes
void Funcdata::blockRemoveInternal(BlockBasic *bb,bool unreachable)
{
BlockBasic *bbout;
Varnode *deadvn;
PcodeOp *op,*deadop;
list<PcodeOp *>::iterator iter;
int4 i,j,blocknum;
bool desc_warning;
op = bb->lastOp();
if ((op != (PcodeOp *)0)&&(op->code() == CPUI_BRANCHIND)) {
JumpTable *jt = findJumpTable(op);
if (jt != (JumpTable *)0)
removeJumpTable(jt);
}
if (!unreachable) {
pushMultiequals(bb); // Make sure data flow is preserved
for(i=0;i<bb->sizeOut();++i) {
bbout = (BlockBasic *) bb->getOut(i);
if (bbout->isDead()) continue;
blocknum = bbout->getInIndex(bb); // Get index of bb into bbout
for(iter=bbout->beginOp();iter!=bbout->endOp();++iter) {
op = *iter;
if (op->code() != CPUI_MULTIEQUAL) continue;
deadvn = op->getIn(blocknum);
opRemoveInput(op,blocknum); // Remove the deleted blocks branch
deadop = deadvn->getDef();
if ((deadvn->isWritten())&&(deadop->code()==CPUI_MULTIEQUAL)&&(deadop->getParent()==bb)) {
// Append new branches
for(j=0;j<bb->sizeIn();++j)
opInsertInput(op,deadop->getIn(j),op->numInput());
}
else {
for(j=0;j<bb->sizeIn();++j)
opInsertInput(op,deadvn,op->numInput()); // Otherwise make copies
}
opZeroMulti(op);
}
}
}
bblocks.removeFromFlow(bb);
desc_warning = false;
iter = bb->beginOp();
while(iter!=bb->endOp()) { // Finally remove all the ops
op = *iter;
if (op->isAssignment()) { // op still has some descendants
deadvn = op->getOut();
if (unreachable) {
bool undef = descend2Undef(deadvn);
if (undef&&(!desc_warning)) { // Mark descendants as undefined
warningHeader("Creating undefined varnodes in (possibly) reachable block");
desc_warning = true; // Print the warning only once
}
}
if (descendantsOutside(deadvn)) // If any descendants outside of bb
throw LowlevelError("Deleting op with descendants\n");
}
if (op->isCall())
deleteCallSpecs(op);
iter++; // Increment iterator before unlinking
opDestroy(op); // No longer has descendants
}
bblocks.removeBlock(bb); // Remove the block altogether
}
/// The block must contain only \e marker operations (MULTIEQUAL) and possibly a single
/// unconditional branch operation. The block and its PcodeOps are completely removed from
/// the current control-flow and data-flow. This forces a reset of the control-flow structuring
/// hierarchy.
/// \param bb is the given basic block
void Funcdata::removeDoNothingBlock(BlockBasic *bb)
{
if (bb->sizeOut()>1)
throw LowlevelError("Cannot delete a reachable block unless it has 1 out or less");
bb->setDead();
blockRemoveInternal(bb,false);
structureReset(); // Delete any structure we had before
}
/// \brief Remove any unreachable basic blocks
///
/// A quick check for unreachable blocks can optionally be made, otherwise
/// the cached state is checked via hasUnreachableBlocks(), which is turned on
/// during analysis by calling the structureReset() method.
/// \param issuewarning is \b true if warning comments are desired
/// \param checkexistence is \b true to force an active search for unreachable blocks
/// \return \b true if unreachable blocks were actually found and removed
bool Funcdata::removeUnreachableBlocks(bool issuewarning,bool checkexistence)
{
vector<FlowBlock *> list;
uint4 i;
if (checkexistence) { // Quick check for the existence of unreachable blocks
for(i=0;i<bblocks.getSize();++i) {
FlowBlock *blk = bblocks.getBlock(i);
if (blk->isEntryPoint()) continue; // Don't remove starting component
if (blk->getImmedDom() == (FlowBlock *)0) break;
}
if (i==bblocks.getSize()) return false;
}
else if (!hasUnreachableBlocks()) // Use cached check
return false;
// There must be at least one unreachable block if we reach here
for(i=0;i<bblocks.getSize();++i) // Find entry point
if (bblocks.getBlock(i)->isEntryPoint()) break;
bblocks.collectReachable(list,bblocks.getBlock(i),true); // Collect (un)reachable blocks
for(i=0;i<list.size();++i) {
list[i]->setDead();
if (issuewarning) {
ostringstream s;
BlockBasic *bb = (BlockBasic *)list[i];
s << "Removing unreachable block (";
s << bb->getStart().getSpace()->getName();
s << ',';
bb->getStart().printRaw(s);
s << ')';
warningHeader(s.str());
}
}
for(i=0;i<list.size();++i) {
BlockBasic *bb = (BlockBasic *)list[i];
while(bb->sizeOut() > 0)
branchRemoveInternal(bb,0);
}
for(i=0;i<list.size();++i) {
BlockBasic *bb = (BlockBasic *)list[i];
blockRemoveInternal(bb,true);
}
structureReset();
return true;
}
/// \brief Move a control-flow edge from one block to another
///
/// This is intended for eliminating switch guard artifacts. The edge
/// must be for a conditional jump and must be moved to a block hosting
/// multiple out edges for a BRANCHIND.
/// \param bb is the basic block out of which the edge to move flows
/// \param slot is the index of the (out) edge
/// \param bbnew is the basic block where the edge should get moved to
void Funcdata::pushBranch(BlockBasic *bb,int4 slot,BlockBasic *bbnew)
{
PcodeOp *cbranch = bb->lastOp();
if ((cbranch->code() != CPUI_CBRANCH)||(bb->sizeOut() != 2))
throw LowlevelError("Cannot push non-conditional edge");
PcodeOp *indop = bbnew->lastOp();
if (indop->code() != CPUI_BRANCHIND)
throw LowlevelError("Can only push branch into indirect jump");
// Turn the conditional branch into a branch
opRemoveInput(cbranch,1); // Remove the conditional variable
opSetOpcode(cbranch,CPUI_BRANCH);
bblocks.moveOutEdge(bb,slot,bbnew);
// No change needs to be made to the indirect branch
// we assume it handles its new branch implicitly
structureReset();
}
/// Look up the jump-table object with the matching PcodeOp address, then
/// attach the given PcodeOp to it.
/// \param op is the given BRANCHIND PcodeOp
/// \return the matching jump-table object or NULL
JumpTable *Funcdata::linkJumpTable(PcodeOp *op)
{
vector<JumpTable *>::iterator iter;
JumpTable *jt;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
jt = *iter;
if (jt->getOpAddress() == op->getAddr()) {
jt->setIndirectOp(op);
return jt;
}
}
return (JumpTable *)0;
}
/// Look up the jump-table object with the matching PcodeOp address
/// \param op is the given BRANCHIND PcodeOp
/// \return the matching jump-table object or NULL
JumpTable *Funcdata::findJumpTable(const PcodeOp *op) const
{
vector<JumpTable *>::const_iterator iter;
JumpTable *jt;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
jt = *iter;
if (jt->getOpAddress() == op->getAddr()) return jt;
}
return (JumpTable *)0;
}
/// The given address must have a BRANCHIND op attached to it.
/// This is suitable for installing an override and must be called before
/// flow has been traced.
/// \param addr is the given Address
/// \return the new jump-table object
JumpTable *Funcdata::installJumpTable(const Address &addr)
{
if (isProcStarted())
throw LowlevelError("Cannot install jumptable if flow is already traced");
for(int4 i=0;i<jumpvec.size();++i) {
JumpTable *jt = jumpvec[i];
if (jt->getOpAddress() == addr)
throw LowlevelError("Trying to install over existing jumptable");
}
JumpTable *newjt = new JumpTable(glb,addr);
jumpvec.push_back(newjt);
return newjt;
}
/// \brief Recover a jump-table for a given BRANCHIND using existing flow information
///
/// A partial function (copy) is built using the flow info. Simplification is performed on the
/// partial function (using the "jumptable" strategy), then destination addresses of the
/// branch are recovered by examining the simplified data-flow. The jump-table object
/// is populated with the recovered addresses. A code indicating success or the type of
/// failure is returned.
///
/// \param partial is a function object for caching analysis
/// \param jt is the jump-table object to populate
/// \param op is the BRANCHIND p-code op to analyze
/// \param flow is the existing flow information
/// \return the success/failure code
JumpTable::RecoveryMode Funcdata::stageJumpTable(Funcdata &partial,JumpTable *jt,PcodeOp *op,FlowInfo *flow)
{
if (!partial.isJumptableRecoveryOn()) {
// Do full analysis on the table if we haven't before
partial.flags |= jumptablerecovery_on; // Mark that this Funcdata object is dedicated to jumptable recovery
partial.truncatedFlow(this,flow);
string oldactname = glb->allacts.getCurrentName(); // Save off old action
try {
glb->allacts.setCurrent("jumptable");
#ifdef OPACTION_DEBUG
if (jtcallback != (void (*)(Funcdata &orig,Funcdata &fd))0)
(*jtcallback)(*this,partial); // Alternative reset/perform
else {
#endif
glb->allacts.getCurrent()->reset( partial );
glb->allacts.getCurrent()->perform( partial ); // Simplify the partial function
#ifdef OPACTION_DEBUG
}
#endif
glb->allacts.setCurrent(oldactname); // Restore old action
}
catch(LowlevelError &err) {
glb->allacts.setCurrent(oldactname);
warning(err.explain,op->getAddr());
return JumpTable::fail_normal;
}
}
PcodeOp *partop = partial.findOp(op->getSeqNum());
if (partop==(PcodeOp *)0 || partop->code() != CPUI_BRANCHIND || partop->getAddr() != op->getAddr())
throw LowlevelError("Error recovering jumptable: Bad partial clone");
if (partop->isDead()) // Indirectop we were trying to recover was eliminated as dead code (unreachable)
return JumpTable::success; // Return jumptable as
// Test if the branch target is copied from the return address.
if (testForReturnAddress(partop->getIn(0)))
return JumpTable::fail_return; // Return special failure code. Switch would not recover anyway.
try {
jt->setLoadCollect(flow->doesJumpRecord());
jt->setIndirectOp(partop);
if (jt->getStage()>0)
jt->recoverMultistage(&partial);
else
jt->recoverAddresses(&partial); // Analyze partial to recover jumptable addresses
}
catch(JumptableNotReachableError &err) { // Thrown by recoverAddresses
return JumpTable::fail_noflow;
}
catch(JumptableThunkError &err) { // Thrown by recoverAddresses
return JumpTable::fail_thunk;
}
catch(LowlevelError &err) {
warning(err.explain,op->getAddr());
return JumpTable::fail_normal;
}
return JumpTable::success;
}
/// Backtrack from the BRANCHIND, looking for ops that might affect the destination.
/// If a CALLOTHER, which is not injected/inlined in some way, is in the flow path of
/// the destination calculation, we know the jump-table analysis will fail and the failure mode is returned.
/// \param op is the BRANCHIND op
/// \return \b success if there is no early failure, or the failure mode otherwise
JumpTable::RecoveryMode Funcdata::earlyJumpTableFail(PcodeOp *op)
{
Varnode *vn = op->getIn(0);
list<PcodeOp *>::const_iterator iter = op->insertiter;
list<PcodeOp *>::const_iterator startiter = beginOpDead();
int4 countMax = 8;
while(iter != startiter) {
if (vn->getSize() == 1) return JumpTable::success;
countMax -= 1;
if (countMax < 0) return JumpTable::success; // Don't iterate too many times
--iter;
op = *iter;
Varnode *outvn = op->getOut();
bool outhit = false;
if (outvn != (Varnode *)0)
outhit = vn->intersects(*outvn);
if (op->getEvalType() == PcodeOp::special) {
if (op->isCall()) {
OpCode opc = op->code();
if (opc == CPUI_CALLOTHER) {
int4 id = (int4)op->getIn(0)->getOffset();
UserPcodeOp *userOp = glb->userops.getOp(id);
if (dynamic_cast<InjectedUserOp *>(userOp) != (InjectedUserOp *)0)
return JumpTable::success; // Don't try to back track through injection
if (dynamic_cast<JumpAssistOp *>(userOp) != (JumpAssistOp *)0)
return JumpTable::success;
if (dynamic_cast<SegmentOp *>(userOp) != (SegmentOp *)0)
return JumpTable::success;
if (outhit)
return JumpTable::fail_callother; // Address formed via uninjected CALLOTHER, analysis will fail
// Assume CALLOTHER will not interfere with address and continue backtracking
}
else {
// CALL or CALLIND - Output has not been established yet
return JumpTable::success; // Don't try to back track through CALL
}
}
else if (op->isBranch())
return JumpTable::success; // Don't try to back track further
else {
if (op->code() == CPUI_STORE) return JumpTable::success; // Don't try to back track through STORE
if (outhit)
return JumpTable::success; // Some special op (CPOOLREF, NEW, etc) generates address, don't assume failure
// Assume special will not interfere with address and continue backtracking
}
}
else if (op->getEvalType() == PcodeOp::unary) {
if (outhit) {
Varnode *invn = op->getIn(0);
if (invn->getSize() != vn->getSize()) return JumpTable::success;
vn = invn; // Treat input as address
}
// Continue backtracking
}
else if (op->getEvalType() == PcodeOp::binary) {
if (outhit) {
OpCode opc = op->code();
if (opc != CPUI_INT_ADD && opc != CPUI_INT_SUB && opc != CPUI_INT_XOR)
return JumpTable::success;
if (!op->getIn(1)->isConstant()) return JumpTable::success; // Don't back-track thru binary op, don't assume failure
Varnode *invn = op->getIn(0);
if (invn->getSize() != vn->getSize()) return JumpTable::success;
vn = invn; // Treat input as address
}
// Continue backtracking
}
else {
if (outhit)
return JumpTable::success;
}
}
return JumpTable::success;
}
/// \brief Recover control-flow destinations for a BRANCHIND
///
/// If an existing and complete JumpTable exists for the BRANCHIND, it is returned immediately.
/// Otherwise an attempt is made to analyze the current partial function and recover the set of destination
/// addresses, which if successful will be returned as a new JumpTable object.
/// \param partial is the Funcdata copy to perform analysis on if necessary
/// \param op is the given BRANCHIND PcodeOp
/// \param flow is current flow information for \b this function
/// \param mode will hold the final success/failure code
/// \return the recovered JumpTable or NULL if there was no success
JumpTable *Funcdata::recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *flow,JumpTable::RecoveryMode &mode)
{
JumpTable *jt;
mode = JumpTable::success;
jt = linkJumpTable(op); // Search for pre-existing jumptable
if (jt != (JumpTable *)0) {
if (!jt->isOverride()) {
if (jt->getStage() != 1)
return jt; // Previously calculated jumptable (NOT an override and NOT incomplete)
}
mode = stageJumpTable(partial,jt,op,flow); // Recover based on override information
if (mode != JumpTable::success)
return (JumpTable *)0;
jt->setIndirectOp(op); // Relink table back to original op
return jt;
}
if ((flags & jumptablerecovery_dont)!=0)
return (JumpTable *)0; // Explicitly told not to recover jumptables
mode = earlyJumpTableFail(op);
if (mode != JumpTable::success)
return (JumpTable *)0;
JumpTable trialjt(glb);
mode = stageJumpTable(partial,&trialjt,op,flow);
if (mode != JumpTable::success)
return (JumpTable *)0;
// if (trialjt.is_twostage())
// warning("Jumptable maybe incomplete. Second-stage recovery not implemented",trialjt.Opaddress());
jt = new JumpTable(&trialjt); // Make the jumptable permanent
jumpvec.push_back(jt);
jt->setIndirectOp(op); // Relink table back to original op
return jt;
}
/// For each jump-table, for each address, the corresponding basic block index is computed.
/// This also calculates the \e default branch for each jump-table.
/// \param flow is the flow object (mapping addresses to p-code ops)
void Funcdata::switchOverJumpTables(const FlowInfo &flow)
{
vector<JumpTable *>::iterator iter;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter)
(*iter)->switchOver(flow);
}
void Funcdata::installSwitchDefaults(void)
{
vector<JumpTable *>::iterator iter;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
JumpTable *jt = *iter;
PcodeOp *indop = jt->getIndirectOp();
BlockBasic *ind = indop->getParent();
// Mark any switch blocks default edge
if (jt->getDefaultBlock() != -1) // If a default case is present
ind->setDefaultSwitch(jt->getDefaultBlock());
}
}
/// For the current control-flow graph, (re)calculate the loop structure and dominance.
/// This can be called multiple times as changes are made to control-flow.
/// The structured hierarchy is also reset.
void Funcdata::structureReset(void)
{
vector<JumpTable *>::iterator iter;
vector<FlowBlock *> rootlist;
flags &= ~blocks_unreachable; // Clear any old blocks flag
bblocks.structureLoops(rootlist);
bblocks.calcForwardDominator(rootlist);
if (rootlist.size() > 1)
flags |= blocks_unreachable;
// Check for dead jumptables
vector<JumpTable *> alivejumps;
for(iter=jumpvec.begin();iter!=jumpvec.end();++iter) {
JumpTable *jt = *iter;
PcodeOp *indop = jt->getIndirectOp();
if (indop->isDead()) {
warningHeader("Recovered jumptable eliminated as dead code");
delete jt;
continue;
}
alivejumps.push_back(jt);
}
jumpvec = alivejumps;
sblocks.clear(); // Force structuring algorithm to start over
// sblocks.build_copy(bblocks); // Make copy of the basic block control flow graph
heritage.forceRestructure();
}
/// \brief Force a specific control-flow edge to be marked as \e unstructured
///
/// The edge is specified by a source and destination Address (of the branch).
/// The resulting control-flow structure will have a \e goto statement modeling
/// the edge.
/// \param pcop is the source Address
/// \param pcdest is the destination Address
/// \return \b true if a control-flow edge was successfully labeled
bool Funcdata::forceGoto(const Address &pcop,const Address &pcdest)
{
FlowBlock *bl,*bl2;
PcodeOp *op,*op2;
int4 i,j;
for(i=0;i<bblocks.getSize();++i) {
bl = bblocks.getBlock(i);
op = bl->lastOp();
if (op == (PcodeOp *)0) continue;
if (op->getAddr() != pcop) continue; // Find op to mark unstructured
for(j=0;j<bl->sizeOut();++j) {
bl2 = bl->getOut(j);
op2 = bl2->lastOp();
if (op2 == (PcodeOp *)0) continue;
if (op2->getAddr() != pcdest) continue; // Find particular branch
bl->setGotoBranch(j);
return true;
}
}
return false;
}
/// \brief Create a new basic block for holding a merged CBRANCH
///
/// This is used by ConditionalJoin to do the low-level control-flow manipulation
/// to merge identical conditional branches. Given basic blocks containing the two
/// CBRANCH ops to merge, the new block gets one of the two out edges from each block,
/// and the remaining out edges are changed to point into the new block.
/// \param block1 is the basic block containing the first CBRANCH to merge
/// \param block2 is the basic block containing the second CBRANCH
/// \param exita is the first common exit block for the CBRANCHs
/// \param exitb is the second common exit block
/// \param fora_block1ishigh designates which edge is moved for exita
/// \param forb_block1ishigh designates which edge is moved for exitb
/// \param addr is the Address associated with (1 of the) CBRANCH ops
/// \return the new basic block
BlockBasic *Funcdata::nodeJoinCreateBlock(BlockBasic *block1,BlockBasic *block2,
BlockBasic *exita,BlockBasic *exitb,
bool fora_block1ishigh,bool forb_block1ishigh,const Address &addr)
{
BlockBasic *newblock = bblocks.newBlockBasic(this);
newblock->setFlag(FlowBlock::f_joined_block);
newblock->setInitialRange(addr, addr);
FlowBlock *swapa,*swapb;
// Delete 2 of the original edges into exita and exitb
if (fora_block1ishigh) { // Remove the edge from block1
bblocks.removeEdge(block1,exita);
swapa = block2;
}
else {
bblocks.removeEdge(block2,exita);
swapa = block1;
}
if (forb_block1ishigh) {
bblocks.removeEdge(block1,exitb);
swapb = block2;
}
else {
bblocks.removeEdge(block2,exitb);
swapb = block1;
}
// Move the remaining two from block1,block2 to newblock
bblocks.moveOutEdge(swapa,swapa->getOutIndex(exita),newblock);
bblocks.moveOutEdge(swapb,swapb->getOutIndex(exitb),newblock);
bblocks.addEdge(block1,newblock);
bblocks.addEdge(block2,newblock);
structureReset();
return newblock;
}
/// \brief Split given basic block b along an \e in edge
///
/// A copy of the block is made, inheriting the same \e out edges but only the
/// one indicated \e in edge, which is removed from the original block.
/// Other data-flow is \b not affected.
/// \param b is the given basic block
/// \param inedge is the index of the indicated \e in edge
BlockBasic *Funcdata::nodeSplitBlockEdge(BlockBasic *b,int4 inedge)
{
FlowBlock *a = b->getIn(inedge);
BlockBasic *bprime;
bprime = bblocks.newBlockBasic(this);
bprime->setFlag(FlowBlock::f_duplicate_block);
bprime->copyRange(b);
bblocks.switchEdge(a,b,bprime);
for(int4 i=0;i<b->sizeOut();++i)
bblocks.addEdge(bprime,b->getOut(i));
return bprime;
}
/// \brief Duplicate the given PcodeOp as part of splitting a block
///
/// Make a basic clone of the p-code op copying its basic control-flow properties
/// \param op is the given PcodeOp
/// \return the cloned op
PcodeOp *Funcdata::nodeSplitCloneOp(PcodeOp *op)
{
PcodeOp *dup;
if (op->isBranch()) {
if (op->code() != CPUI_BRANCH)
throw LowlevelError("Cannot duplicate 2-way or n-way branch in nodeplit");
return (PcodeOp *)0;
}
dup = newOp(op->numInput(),op->getAddr());
opSetOpcode(dup,op->code());
uint4 fl = op->flags & (PcodeOp::startbasic | PcodeOp::nocollapse |
PcodeOp::startmark);
dup->setFlag(fl);
return dup;
}
/// \brief Duplicate output Varnode of the given p-code op, as part of splitting a block
///
/// Make a basic clone of the Varnode and its basic flags. The clone is created
/// as an output of a previously cloned PcodeOp.
/// \param op is the given op whose output should be cloned
/// \param newop is the cloned version
void Funcdata::nodeSplitCloneVarnode(PcodeOp *op,PcodeOp *newop)
{
Varnode *opvn = op->getOut();
Varnode *newvn;
if (opvn == (Varnode *)0) return;
newvn = newVarnodeOut(opvn->getSize(),opvn->getAddr(),newop);
uint4 vflags = opvn->getFlags();
vflags &= (Varnode::externref | Varnode::volatil | Varnode::incidental_copy |
Varnode::readonly | Varnode::persist |
Varnode::addrtied | Varnode::addrforce);
newvn->setFlags(vflags);
}
/// \brief Clone all p-code ops from a block into its copy
///
/// P-code in a basic block is cloned into the split version of the block.
/// Only the output Varnodes are cloned, not the inputs.
/// \param b is the original basic block
/// \param bprime is the cloned block
void Funcdata::nodeSplitRawDuplicate(BlockBasic *b,BlockBasic *bprime)
{
PcodeOp *b_op,*prime_op;
list<PcodeOp *>::iterator iter;
for(iter=b->beginOp();iter!=b->endOp();++iter) {
b_op = *iter;
prime_op = nodeSplitCloneOp(b_op);
if (prime_op == (PcodeOp *)0) continue;
nodeSplitCloneVarnode(b_op,prime_op);
opInsertEnd(prime_op,bprime);
}
}
/// \brief Patch Varnode inputs to p-code ops in split basic block
///
/// Map Varnodes that are inputs for PcodeOps in the original basic block to the
/// input slots of the cloned ops in the split block. Constants and code ref Varnodes
/// need to be duplicated, other Varnodes are shared between the ops. This routine
/// also pulls an input Varnode out of riginal MULTIEQUAL ops and adds it back
/// to the cloned MULTIEQUAL ops.
/// \param b is the original basic block
/// \param bprime is the split clone of the block
/// \param inedge is the incoming edge index that was split on
void Funcdata::nodeSplitInputPatch(BlockBasic *b,BlockBasic *bprime,int4 inedge)
{
list<PcodeOp *>::iterator biter,piter;
PcodeOp *bop,*pop;
Varnode *bvn,*pvn;
map<PcodeOp *,PcodeOp *> btop; // Map from b to bprime
vector<PcodeOp *> pind; // pop needing b input
vector<PcodeOp *> bind; // bop giving input
vector<int4> pslot; // slot within pop needing b input
biter = b->beginOp();
piter = bprime->beginOp();
while(piter != bprime->endOp()) {
bop = *biter;
pop = *piter;
btop[bop] = pop; // Establish mapping
if (bop->code() == CPUI_MULTIEQUAL) {
pop->setNumInputs(1); // One edge now goes into bprime
opSetOpcode(pop,CPUI_COPY);
opSetInput(pop,bop->getIn(inedge),0);
opRemoveInput(bop,inedge); // One edge is removed from b
if (bop->numInput() == 1)
opSetOpcode(bop,CPUI_COPY);
}
else if (bop->code() == CPUI_INDIRECT) {
throw LowlevelError("Can't handle INDIRECTs in nodesplit");
}
else if (bop->isCall()) {
throw LowlevelError("Can't handle CALLs in nodesplit");
}
else {
for(int4 i=0;i<pop->numInput();++i) {
bvn = bop->getIn(i);
if (bvn->isConstant())
pvn = newConstant(bvn->getSize(),bvn->getOffset());
else if (bvn->isAnnotation())
pvn = newCodeRef(bvn->getAddr());
else if (bvn->isFree())
throw LowlevelError("Can't handle free varnode in nodesplit");
else {
if (bvn->isWritten()) {
if (bvn->getDef()->getParent() == b) {
pind.push_back(pop); // Need a cross reference
bind.push_back(bvn->getDef());
pslot.push_back(i);
continue;
}
else
pvn = bvn;
}
else
pvn = bvn;
}
opSetInput(pop,pvn,i);
}
}
++piter;
++biter;
}
for(int4 i=0;i<pind.size();++i) {
pop = pind[i];
PcodeOp *cross = btop[bind[i]];
opSetInput(pop,cross->getOut(),pslot[i]);
}
}
/// \brief Split control-flow into a basic block, duplicating its p-code into a new block
///
/// P-code is duplicated into another block, and control-flow is modified so that the new
/// block takes over flow from one input edge to the original block.
/// \param b is the basic block to be duplicated and split
/// \param inedge is the index of the input edge to move to the duplicate block
void Funcdata::nodeSplit(BlockBasic *b,int4 inedge)
{ // Split node b along inedge
if (b->sizeOut() != 0)
throw LowlevelError("Cannot (currently) nodesplit block with out flow");
if (b->sizeIn()<=1)
throw LowlevelError("Cannot nodesplit block with only 1 in edge");
for(int4 i=0;i<b->sizeIn();++i) {
if (b->getIn(i)->isMark())
throw LowlevelError("Cannot nodesplit block with redundant in edges");
b->setMark();
}