forked from cseagle/blc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
funcdata_varnode.cc
2118 lines (1937 loc) · 75 KB
/
funcdata_varnode.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"
namespace ghidra {
// Funcdata members pertaining directly to varnodes
/// Properties of a given storage location are gathered from symbol information and
/// applied to the given Varnode.
/// \param vn is the given Varnode
void Funcdata::setVarnodeProperties(Varnode *vn) const
{
if (!vn->isMapped()) {
// One more chance to find entry, now that we know usepoint
uint4 vflags=0;
SymbolEntry *entry = localmap->queryProperties(vn->getAddr(),vn->getSize(),vn->getUsePoint(*this),vflags);
if (entry != (SymbolEntry *)0) // Let entry try to force type
vn->setSymbolProperties(entry);
else
vn->setFlags(vflags & ~Varnode::typelock); // typelock set by updateType
}
if (vn->cover == (Cover *)0) {
if (isHighOn())
vn->calcCover();
}
}
/// If HighVariables are enabled, make sure the given Varnode has one assigned. Allocate
/// a dedicated HighVariable, that contains only the one Varnode if necessary.
/// \param vn is the given Varnode
/// \return the assigned HighVariable or NULL if one is not assigned
HighVariable *Funcdata::assignHigh(Varnode *vn)
{
if ((flags & highlevel_on)!=0) {
if (vn->hasCover())
vn->calcCover();
if (!vn->isAnnotation()) {
return new HighVariable(vn);
}
}
return (HighVariable *)0;
}
/// A Varnode is allocated which represents the indicated constant value.
/// Its storage address is in the \e constant address space.
/// \param s is the size of the Varnode in bytes
/// \param constant_val is the indicated constant value
/// \return the new Varnode object
Varnode *Funcdata::newConstant(int4 s,uintb constant_val)
{
Datatype *ct = glb->types->getBase(s,TYPE_UNKNOWN);
Varnode *vn = vbank.create(s,glb->getConstant(constant_val),ct);
assignHigh(vn);
// There is no chance of matching localmap
return vn;
}
/// A new temporary register storage location is allocated from the \e unique
/// address space
/// \param s is the size of the Varnode in bytes
/// \param ct is an optional data-type to associated with the Varnode
/// \return the newly allocated \e temporary Varnode
Varnode *Funcdata::newUnique(int4 s,Datatype *ct)
{
if (ct == (Datatype *)0)
ct = glb->types->getBase(s,TYPE_UNKNOWN);
Varnode *vn = vbank.createUnique(s,ct);
assignHigh(vn);
if (s >= minLanedSize)
checkForLanedRegister(s, vn->getAddr());
// No chance of matching localmap
return vn;
}
/// Create a new Varnode which is already defined as output of a given PcodeOp.
/// This if more efficient as it avoids the initial insertion of the free form of the
/// Varnode into the tree, and queryProperties only needs to be called once.
/// \param s is the size of the new Varnode in bytes
/// \param m is the storage Address of the Varnode
/// \param op is the given PcodeOp whose output is created
/// \return the new Varnode object
Varnode *Funcdata::newVarnodeOut(int4 s,const Address &m,PcodeOp *op)
{
Datatype *ct = glb->types->getBase(s,TYPE_UNKNOWN);
Varnode *vn = vbank.createDef(s,m,ct,op);
op->setOutput(vn);
assignHigh(vn);
if (s >= minLanedSize)
checkForLanedRegister(s,m);
uint4 vflags = 0;
SymbolEntry *entry = localmap->queryProperties(m,s,op->getAddr(),vflags);
if (entry != (SymbolEntry *)0)
vn->setSymbolProperties(entry);
else
vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
return vn;
}
/// Allocate a new register from the \e unique address space and create a new
/// Varnode object representing it as an output to the given PcodeOp
/// \param s is the size of the new Varnode in bytes
/// \param op is the given PcodeOp whose output is created
/// \return the new temporary register Varnode
Varnode *Funcdata::newUniqueOut(int4 s,PcodeOp *op)
{
Datatype *ct = glb->types->getBase(s,TYPE_UNKNOWN);
Varnode *vn = vbank.createDefUnique(s,ct,op);
op->setOutput(vn);
assignHigh(vn);
if (s >= minLanedSize)
checkForLanedRegister(s, vn->getAddr());
// No chance of matching localmap
return vn;
}
/// \brief Create a new unattached Varnode object
///
/// \param s is the size of the new Varnode in bytes
/// \param m is the storage Address of the Varnode
/// \param ct is a data-type to associate with the Varnode
/// \return the newly allocated Varnode object
Varnode *Funcdata::newVarnode(int4 s,const Address &m,Datatype *ct)
{
Varnode *vn;
if (ct == (const Datatype *)0)
ct = glb->types->getBase(s,TYPE_UNKNOWN);
vn = vbank.create(s,m,ct);
assignHigh(vn);
if (s >= minLanedSize)
checkForLanedRegister(s,m);
uint4 vflags=0;
SymbolEntry *entry = localmap->queryProperties(vn->getAddr(),vn->getSize(),Address(),vflags);
if (entry != (SymbolEntry *)0) // Let entry try to force type
vn->setSymbolProperties(entry);
else
vn->setFlags(vflags & ~Varnode::typelock); // Typelock set by updateType
return vn;
}
/// Create a special \e annotation Varnode that holds a pointer reference to a specific
/// PcodeOp. This is used specifically to let a CPUI_INDIRECT op refer to the PcodeOp
/// it is holding an indirect effect for.
/// \param op is the PcodeOp to encode in the annotation
/// \return the newly allocated \e annotation Varnode
Varnode *Funcdata::newVarnodeIop(PcodeOp *op)
{
Datatype *ct = glb->types->getBase(sizeof(op),TYPE_UNKNOWN);
AddrSpace *cspc = glb->getIopSpace();
Varnode *vn = vbank.create(sizeof(op),Address(cspc,(uintb)(uintp)op),ct);
assignHigh(vn);
return vn;
}
/// A reference to a particular address space is encoded as a constant Varnode.
/// These are used for LOAD and STORE p-code ops in particular.
/// \param spc is the address space to encode
/// \return the newly allocated constant Varnode
Varnode *Funcdata::newVarnodeSpace(AddrSpace *spc)
{
Datatype *ct = glb->types->getBase(sizeof(spc),TYPE_UNKNOWN);
Varnode *vn = vbank.create(sizeof(spc),glb->createConstFromSpace(spc),ct);
assignHigh(vn);
return vn;
}
/// A call specification (FuncCallSpecs) is encoded into an \e annotation Varnode.
/// The Varnode is used specifically as an input to CPUI_CALL ops to speed up access
/// to their associated call specification.
/// \param fc is the call specification to encode
/// \return the newly allocated \e annotation Varnode
Varnode *Funcdata::newVarnodeCallSpecs(FuncCallSpecs *fc)
{
Datatype *ct = glb->types->getBase(sizeof(fc),TYPE_UNKNOWN);
AddrSpace *cspc = glb->getFspecSpace();
Varnode *vn = vbank.create(sizeof(fc),Address(cspc,(uintb)(uintp)fc),ct);
assignHigh(vn);
return vn;
}
/// A reference to a specific Address is encoded in a Varnode. The Varnode is
/// an \e annotation in the sense that it will hold no value in the data-flow, it will
/// will only hold a reference to an address. This is used specifically by the branch
/// p-code operations to hold destination addresses.
/// \param m is the Address to encode
/// \return the newly allocated \e annotation Varnode
Varnode *Funcdata::newCodeRef(const Address &m)
{
Varnode *vn;
Datatype *ct;
ct = glb->types->getTypeCode();
vn = vbank.create(1,m,ct);
vn->setFlags(Varnode::annotation);
assignHigh(vn);
return vn;
}
/// \param s is the size of the Varnode in bytes
/// \param base is the address space of the Varnode
/// \param off is the offset into the address space of the Varnode
/// \return the newly allocated Varnode
Varnode *Funcdata::newVarnode(int4 s,AddrSpace *base,uintb off)
{
Varnode *vn;
vn = newVarnode(s,Address(base,off));
return vn;
}
/// Internal factory for copying Varnodes from another Funcdata object into \b this.
/// \param vn is the Varnode to clone
/// \return the cloned Varnode (contained by \b this)
Varnode *Funcdata::cloneVarnode(const Varnode *vn)
{
Varnode *newvn;
newvn = vbank.create(vn->getSize(),vn->getAddr(),vn->getType());
uint4 vflags = vn->getFlags();
// These are the flags we allow to be cloned
vflags &= (Varnode::annotation | Varnode::externref |
Varnode::readonly | Varnode::persist |
Varnode::addrtied | Varnode::addrforce |
Varnode::indirect_creation | Varnode::incidental_copy |
Varnode::volatil | Varnode::mapped);
newvn->setFlags(vflags);
return newvn;
}
/// References to the Varnode are replaced with NULL pointers and the object is freed,
/// with no possibility of resuse.
/// \param vn is the Varnode to delete
void Funcdata::destroyVarnode(Varnode *vn)
{
list<PcodeOp *>::const_iterator iter;
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter;
#ifdef OPACTION_DEBUG
if (opactdbg_active)
debugModCheck(op);
#endif
op->clearInput(op->getSlot(vn));
}
if (vn->def != (PcodeOp *)0) {
vn->def->setOutput((Varnode *)0);
vn->def = (PcodeOp *)0;
}
vn->destroyDescend();
vbank.destroy(vn);
}
/// Check if the given storage range is a potential laned register.
/// If so, record the storage with the matching laned register record.
/// \param sz is the size of the storage range in bytes
/// \param addr is the starting address of the storage range
void Funcdata::checkForLanedRegister(int4 sz,const Address &addr)
{
const LanedRegister *lanedRegister = glb->getLanedRegister(addr,sz);
if (lanedRegister == (const LanedRegister *)0)
return;
VarnodeData storage;
storage.space = addr.getSpace();
storage.offset = addr.getOffset();
storage.size = sz;
lanedMap[storage] = lanedRegister;
}
/// Look up the Symbol visible in \b this function's Scope and return the HighVariable
/// associated with it. If the Symbol doesn't exist or there is no Varnode holding at least
/// part of the value of the Symbol, NULL is returned.
/// \param nm is the name to search for
/// \return the matching HighVariable or NULL
HighVariable *Funcdata::findHigh(const string &nm) const
{
vector<Symbol *> symList;
localmap->queryByName(nm,symList);
if (symList.empty()) return (HighVariable *)0;
Symbol *sym = symList[0];
Varnode *vn = findLinkedVarnode(sym->getFirstWholeMap());
if (vn != (Varnode *)0)
return vn->getHigh();
return (HighVariable *)0;
}
/// An \e input Varnode has a special designation within SSA form as not being defined
/// by a p-code operation and is a formal input to the data-flow of the function. It is
/// not necessarily a formal function parameter.
///
/// The given Varnode to be marked is also returned unless there is an input Varnode that
/// already exists which overlaps the given Varnode. If the Varnodes have the same size and
/// storage address, the preexisting input Varnode is returned instead. Otherwise an
/// exception is thrown.
/// \param vn is the given Varnode to mark as an input
/// \return the marked Varnode
Varnode *Funcdata::setInputVarnode(Varnode *vn)
{
Varnode *invn;
if (vn->isInput()) return vn; // Already an input
// First we check if it overlaps any other varnode
VarnodeDefSet::const_iterator iter;
iter = vbank.beginDef(Varnode::input,vn->getAddr()+vn->getSize());
// Iter points at first varnode AFTER vn
if (iter != vbank.beginDef()) {
--iter; // previous varnode
invn = *iter; // comes before vn or intersects
if (invn->isInput()) {
if ((-1 != vn->overlap(*invn))||(-1 != invn->overlap(*vn))) {
if ((vn->getSize() == invn->getSize())&&(vn->getAddr() == invn->getAddr()))
return invn;
throw LowlevelError("Overlapping input varnodes");
}
}
}
vn = vbank.setInput(vn);
setVarnodeProperties(vn);
uint4 effecttype = funcp.hasEffect(vn->getAddr(),vn->getSize());
if (effecttype == EffectRecord::unaffected)
vn->setUnaffected();
if (effecttype == EffectRecord::return_address) {
vn->setUnaffected(); // Should be unaffected over the course of the function
vn->setReturnAddress();
}
return vn;
}
/// Construct a constant Varnode up to 128 bits, using INT_ZEXT and PIECE if necessary.
/// This method is temporary until we have full extended precision constants.
/// \param s is the size of the Varnode in bytes
/// \param val is the 128-bit value in 2 64-bit chunks
/// \param op is point before which any new PcodeOp should get inserted
/// \return the new effective constant Varnode
Varnode *Funcdata::newExtendedConstant(int4 s,uint8 *val,PcodeOp *op)
{
if (s <= 8)
return newConstant(s, val[0]);
Varnode *newConstVn;
if (val[1] == 0) {
PcodeOp *extOp = newOp(1,op->getAddr());
opSetOpcode(extOp,CPUI_INT_ZEXT);
newConstVn = newUniqueOut(s,extOp);
opSetInput(extOp,newConstant(8,val[0]),0);
opInsertBefore(extOp,op);
}
else {
PcodeOp *pieceOp = newOp(2,op->getAddr());
opSetOpcode(pieceOp,CPUI_PIECE);
newConstVn = newUniqueOut(s,pieceOp);
opSetInput(pieceOp,newConstant(8,val[1]),0); // Most significant piece
opSetInput(pieceOp,newConstant(8,val[0]),1); // Least significant piece
opInsertBefore(pieceOp,op);
}
return newConstVn;
}
/// \brief Adjust input Varnodes contained in the given range
///
/// After this call, a single \e input Varnode will exist that fills the given range.
/// Any previous input Varnodes contained in this range are redefined using a SUBPIECE
/// op off of the new single input. If an overlapping Varnode isn't fully contained
/// an exception is thrown.
/// \param addr is the starting address of the range
/// \param sz is the number of bytes in the range
void Funcdata::adjustInputVarnodes(const Address &addr,int4 sz)
{
Address endaddr = addr + (sz-1);
vector<Varnode *> inlist;
VarnodeDefSet::const_iterator iter,enditer;
iter = vbank.beginDef(Varnode::input,addr);
enditer = vbank.endDef(Varnode::input,endaddr);
while(iter != enditer) {
Varnode *vn = *iter;
++iter;
if (vn->getOffset() + (vn->getSize()-1) > endaddr.getOffset())
throw LowlevelError("Cannot properly adjust input varnodes");
inlist.push_back(vn);
}
for(uint4 i=0;i<inlist.size();++i) {
Varnode *vn = inlist[i];
int4 sa = addr.justifiedContain(sz,vn->getAddr(),vn->getSize(),false);
if ((!vn->isInput())||(sa < 0)||(sz<=vn->getSize()))
throw LowlevelError("Bad adjustment to input varnode");
PcodeOp *subop = newOp(2,getAddress());
opSetOpcode(subop,CPUI_SUBPIECE);
opSetInput(subop,newConstant(4,sa),1);
Varnode *newvn = newVarnodeOut(vn->getSize(),vn->getAddr(),subop);
// newvn must not be free in order to give all vn's descendants
opInsertBegin(subop,(BlockBasic *)bblocks.getBlock(0));
totalReplace(vn,newvn);
deleteVarnode(vn); // Get rid of old input before creating new input
inlist[i] = newvn;
}
// Now that all the intersecting inputs have been pulled out, we can create the new input
Varnode *invn = newVarnode(sz,addr);
invn = setInputVarnode(invn);
// The new input may cause new heritage and "Heritage AFTER dead removal" errors
// So tell heritage to ignore it
// FIXME: It would probably be better to insert this directly into heritage's globaldisjoint
invn->setWriteMask();
// Now change all old inputs to be created as SUBPIECE from the new input
for(uint4 i=0;i<inlist.size();++i) {
PcodeOp *op = inlist[i]->getDef();
opSetInput(op,invn,0);
}
}
/// All p-code ops that read the Varnode are transformed so that they read
/// a special constant instead (associate with unreachable block removal).
/// \param vn is the given Varnode
/// \return \b true if a PcodeOp is modified
bool Funcdata::descend2Undef(Varnode *vn)
{
PcodeOp *op,*copyop;
BlockBasic *inbl;
Varnode *badconst;
list<PcodeOp *>::const_iterator iter;
int4 i,sz;
bool res;
res = false;
sz = vn->getSize();
iter = vn->beginDescend();
while(iter != vn->endDescend()) {
op = *iter++; // Move to next in list before deletion
if (op->getParent()->isDead()) continue;
if (op->getParent()->sizeIn()!=0) res = true;
i = op->getSlot(vn);
badconst = newConstant(sz,0xBADDEF);
if (op->code()==CPUI_MULTIEQUAL) { // Cannot put constant directly into MULTIEQUAL
inbl = (BlockBasic *) op->getParent()->getIn(i);
copyop = newOp(1,inbl->getStart());
Varnode *inputvn = newUniqueOut(sz,copyop);
opSetOpcode(copyop,CPUI_COPY);
opSetInput(copyop,badconst,0);
opInsertEnd(copyop,inbl);
opSetInput(op,inputvn,i);
}
else if (op->code()==CPUI_INDIRECT) { // Cannot put constant directly into INDIRECT
copyop = newOp(1,op->getAddr());
Varnode *inputvn = newUniqueOut(sz,copyop);
opSetOpcode(copyop,CPUI_COPY);
opSetInput(copyop,badconst,0);
opInsertBefore(copyop,op);
opSetInput(op,inputvn,i);
}
else
opSetInput(op,badconst,i);
}
return res;
}
void Funcdata::initActiveOutput(void)
{
activeoutput = new ParamActive(false);
int4 maxdelay = funcp.getMaxOutputDelay();
if (maxdelay > 0)
maxdelay = 3;
activeoutput->setMaxPass(maxdelay);
}
void Funcdata::setHighLevel(void)
{
if ((flags & highlevel_on)!=0) return;
flags |= highlevel_on;
high_level_index = vbank.getCreateIndex();
VarnodeLocSet::const_iterator iter;
for(iter=vbank.beginLoc();iter!=vbank.endLoc();++iter)
assignHigh(*iter);
}
/// \brief Copy properties from an existing Varnode to a new Varnode
///
/// The new Varnode is assumed to overlap the storage of the existing Varnode.
/// Properties like boolean flags and \e consume bits are copied as appropriate.
/// \param vn is the existing Varnode
/// \param newVn is the new Varnode that has its properties set
/// \param lsbOffset is the significance offset of the new Varnode within the exising
void Funcdata::transferVarnodeProperties(Varnode *vn,Varnode *newVn,int4 lsbOffset)
{
uintb newConsume = (vn->getConsume() >> 8*lsbOffset) & calc_mask(newVn->getSize());
uint4 vnFlags = vn->getFlags() & (Varnode::directwrite|Varnode::addrforce);
newVn->setFlags(vnFlags); // Preserve addrforce setting
newVn->setConsume(newConsume);
}
/// Treat the given Varnode as read-only, look up its value in LoadImage
/// and replace read references with the value as a constant Varnode.
/// \param vn is the given Varnode
/// \return \b true if any change was made
bool Funcdata::fillinReadOnly(Varnode *vn)
{
if (vn->isWritten()) { // Can't replace output with constant
PcodeOp *defop = vn->getDef();
if (defop->isMarker())
defop->setAdditionalFlag(PcodeOp::warning); // Not a true write, ignore it
else if (!defop->isWarning()) { // No warning generated before
defop->setAdditionalFlag(PcodeOp::warning);
ostringstream s;
if ((!vn->isAddrForce())||(!vn->hasNoDescend())) {
s << "Read-only address (";
s << vn->getSpace()->getName();
s << ',';
vn->getAddr().printRaw(s);
s << ") is written";
warning(s.str(),defop->getAddr());
}
}
return false; // No change was made
}
if (vn->getSize() > sizeof(uintb))
return false; // Constant will exceed precision
uintb res;
uint1 bytes[32];
try {
glb->loader->loadFill(bytes,vn->getSize(),vn->getAddr());
} catch(DataUnavailError &err) { // Could not get value from LoadImage
vn->clearFlags(Varnode::readonly); // Treat as writeable
return true;
}
if (vn->getSpace()->isBigEndian()) { // Big endian
res = 0;
for(int4 i=0;i<vn->getSize();++i) {
res <<= 8;
res |= bytes[i];
}
}
else {
res = 0;
for(int4 i=vn->getSize()-1;i>=0;--i) {
res <<= 8;
res |= bytes[i];
}
}
// Replace all references to vn
bool changemade = false;
list<PcodeOp *>::const_iterator iter;
PcodeOp *op;
int4 i;
Datatype *locktype = vn->isTypeLock() ? vn->getType() : (Datatype *)0;
iter = vn->beginDescend();
while(iter != vn->endDescend()) {
op = *iter++;
i = op->getSlot(vn);
if (op->isMarker()) { // Must be careful putting constants in here
if ((op->code()!=CPUI_INDIRECT)||(i!=0)) continue;
Varnode *outvn = op->getOut();
if (outvn->getAddr() == vn->getAddr()) continue; // Ignore indirect to itself
// Change the indirect to a COPY
opRemoveInput(op,1);
opSetOpcode(op,CPUI_COPY);
}
Varnode *cvn = newConstant(vn->getSize(),res);
if (locktype != (Datatype *)0)
cvn->updateType(locktype,true,true); // Try to pass on the locked datatype
opSetInput(op,cvn,i);
changemade = true;
}
return changemade;
}
/// The Varnode is assumed not fully linked. The read or write action is
/// modeled by inserting a special \e user op that represents the action. The given Varnode is
/// replaced by a temporary Varnode within the data-flow, and the original address becomes
/// a parameter to the user op.
/// \param vn is the given Varnode to model as volatile
/// \return \b true if a change was made
bool Funcdata::replaceVolatile(Varnode *vn)
{
PcodeOp *newop;
if (vn->isWritten()) { // A written value
VolatileWriteOp *vw_op = glb->userops.getVolatileWrite();
if (!vn->hasNoDescend()) throw LowlevelError("Volatile memory was propagated");
PcodeOp *defop = vn->getDef();
newop = newOp(3,defop->getAddr());
opSetOpcode(newop,CPUI_CALLOTHER);
// Create a userop of type specified by vw_op
opSetInput(newop,newConstant(4,vw_op->getIndex()),0);
// The first parameter is the offset of volatile memory location
Varnode *annoteVn = newCodeRef(vn->getAddr());
annoteVn->setFlags(Varnode::volatil);
opSetInput(newop,annoteVn,1);
// Replace the volatile variable with a temp
Varnode *tmp = newUnique(vn->getSize());
opSetOutput(defop,tmp);
// The temp is the second parameter to the userop
opSetInput(newop,tmp,2);
opInsertAfter(newop,defop); // Insert after defining op
}
else { // A read value
VolatileReadOp *vr_op = glb->userops.getVolatileRead();
if (vn->hasNoDescend()) return false; // Dead
PcodeOp *readop = vn->loneDescend();
if (readop == (PcodeOp *)0)
throw LowlevelError("Volatile memory value used more than once");
newop = newOp(2,readop->getAddr());
opSetOpcode(newop,CPUI_CALLOTHER);
// Create a temp to replace the volatile variable
Varnode *tmp = newUniqueOut(vn->getSize(),newop);
// Create a userop of type specified by vr_op
opSetInput(newop,newConstant(4,vr_op->getIndex()),0);
// The first parameter is the offset of the volatile memory loc
Varnode *annoteVn = newCodeRef(vn->getAddr());
annoteVn->setFlags(Varnode::volatil);
opSetInput(newop,annoteVn,1);
opSetInput(readop,tmp,readop->getSlot(vn));
opInsertBefore(newop,readop); // Insert before read
if (vr_op->getDisplay() != 0) // Unless the display is functional,
newop->setHoldOutput(); // read value may not be used. Keep it around anyway.
}
if (vn->isTypeLock()) // If the original varnode had a type locked on it
newop->setAdditionalFlag(PcodeOp::special_prop); // Mark this op as doing special propagation
return true;
}
/// \brief Check if the given Varnode only flows into call-based INDIRECT ops
///
/// Flow is only followed through MULTIEQUAL ops.
/// \param vn is the given Varnode
/// \return \b true if all flows hit an INDIRECT op
bool Funcdata::checkIndirectUse(Varnode *vn)
{
vector<Varnode *> vlist;
int4 i = 0;
vlist.push_back(vn);
vn->setMark();
bool result = true;
while((i<vlist.size())&&result) {
vn = vlist[i++];
list<PcodeOp *>::const_iterator iter;
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter;
OpCode opc = op->code();
if (opc == CPUI_INDIRECT) {
if (op->isIndirectStore()) {
// INDIRECT from a STORE is not a negative result but continue to follow data-flow
Varnode *outvn = op->getOut();
if (!outvn->isMark()) {
vlist.push_back(outvn);
outvn->setMark();
}
}
}
else if (opc == CPUI_MULTIEQUAL) {
Varnode *outvn = op->getOut();
if (!outvn->isMark()) {
vlist.push_back(outvn);
outvn->setMark();
}
}
else {
result = false;
break;
}
}
}
for(i=0;i<vlist.size();++i)
vlist[i]->clearMark();
return result;
}
/// The illegal inputs are additionally marked as \b indirectonly and
/// isIndirectOnly() returns \b true.
void Funcdata::markIndirectOnly(void)
{
VarnodeDefSet::const_iterator iter,enditer;
iter = beginDef(Varnode::input);
enditer = endDef(Varnode::input);
for(;iter!=enditer;++iter) { // Loop over all inputs
Varnode *vn = *iter;
if (!vn->isIllegalInput()) continue; // Only check illegal inputs
if (checkIndirectUse(vn))
vn->setFlags(Varnode::indirectonly);
}
}
/// Free any Varnodes not attached to anything. This is only performed at fixed times so that
/// editing operations can detach (and then reattach) Varnodes without losing them.
void Funcdata::clearDeadVarnodes(void)
{
VarnodeLocSet::const_iterator iter;
Varnode *vn;
iter = vbank.beginLoc();
while(iter!=vbank.endLoc()) {
vn = *iter++;
if (vn->hasNoDescend()) {
if (vn->isInput() && !vn->isLockedInput()) {
vbank.makeFree(vn);
vn->clearCover();
}
if (vn->isFree())
vbank.destroy(vn);
}
}
}
/// All Varnodes are initialized assuming that all its bits are possibly non-zero. This method
/// looks for situations where a p-code produces a value that is known to have some bits that are
/// guaranteed to be zero. It updates the state of the output Varnode then tries to push the
/// information forward through the data-flow until additional changes are apparent.
void Funcdata::calcNZMask(void)
{
vector<PcodeOpNode> opstack;
list<PcodeOp *>::const_iterator oiter;
for(oiter=beginOpAlive();oiter!=endOpAlive();++oiter) {
PcodeOp *op = *oiter;
if (op->isMark()) continue;
opstack.push_back(PcodeOpNode(op,0));
op->setMark();
do {
// Get next edge
PcodeOpNode &node( opstack.back() );
if (node.slot >= node.op->numInput()) { // If no edge left
Varnode *outvn = node.op->getOut();
if (outvn != (Varnode *)0) {
outvn->nzm = node.op->getNZMaskLocal(true);
}
opstack.pop_back(); // Pop a level
continue;
}
int4 oldslot = node.slot;
node.slot += 1; // Advance to next input
// Determine if we want to traverse this edge
if (node.op->code() == CPUI_MULTIEQUAL) {
if (node.op->getParent()->isLoopIn(oldslot)) // Clip looping edges
continue;
}
// Traverse edge indicated by slot
Varnode *vn = node.op->getIn(oldslot);
if (!vn->isWritten()) {
if (vn->isConstant())
vn->nzm = vn->getOffset();
else {
vn->nzm = calc_mask(vn->getSize());
if (vn->isSpacebase())
vn->nzm &= ~((uintb)0xff); // Treat spacebase input as aligned
}
}
else if (!vn->getDef()->isMark()) { // If haven't traversed before
opstack.push_back(PcodeOpNode(vn->getDef(),0));
vn->getDef()->setMark();
}
} while(!opstack.empty());
}
vector<PcodeOp *> worklist;
// Clear marks and push ops with looping edges onto worklist
for(oiter=beginOpAlive();oiter!=endOpAlive();++oiter) {
PcodeOp *op = *oiter;
op->clearMark();
if (op->code() == CPUI_MULTIEQUAL)
worklist.push_back(op);
}
// Continue to propagate changes along all edges
while(!worklist.empty()) {
PcodeOp *op = worklist.back();
worklist.pop_back();
Varnode *vn = op->getOut();
if (vn == (Varnode *)0) continue;
uintb nzmask = op->getNZMaskLocal(false);
if (nzmask != vn->nzm) {
vn->nzm = nzmask;
for(oiter=vn->beginDescend();oiter!=vn->endDescend();++oiter)
worklist.push_back(*oiter);
}
}
}
/// \brief Update Varnode properties based on (new) Symbol information
///
/// Boolean properties \b addrtied, \b addrforce, and \b nolocalalias
/// for Varnodes are updated based on new Symbol information they map to.
/// The caller can elect to update data-type information as well, where Varnodes
/// and their associated HighVariables have their data-type finalized based symbols.
/// \param lm is the Symbol scope within which to search for mapped Varnodes
/// \param updateDatatypes is \b true if the caller wants to update data-types
/// \param unmappedAliasCheck is \b true if an alias check should be performed on unmapped Varnodes
/// \return \b true if any Varnode was updated
bool Funcdata::syncVarnodesWithSymbols(const ScopeLocal *lm,bool updateDatatypes,bool unmappedAliasCheck)
{
bool updateoccurred = false;
VarnodeLocSet::const_iterator iter,enditer;
Datatype *ct;
SymbolEntry *entry;
uint4 fl;
iter = vbank.beginLoc(lm->getSpaceId());
enditer = vbank.endLoc(lm->getSpaceId());
while(iter != enditer) {
Varnode *vnexemplar = *iter;
entry = lm->findOverlap(vnexemplar->getAddr(),vnexemplar->getSize());
ct = (Datatype *)0;
if (entry != (SymbolEntry *)0) {
fl = entry->getAllFlags();
if (entry->getSize() >= vnexemplar->getSize()) {
if (updateDatatypes) {
ct = entry->getSizedType(vnexemplar->getAddr(), vnexemplar->getSize());
if (ct != (Datatype *)0 && ct->getMetatype() == TYPE_UNKNOWN)
ct = (Datatype *)0;
}
}
else { // Overlapping but not containing
// This is usual indicative of a small locked symbol
// getting put in a bigger register
// Don't try to figure out type
// Don't keep typelock and namelock
fl &= ~((uint4)(Varnode::typelock|Varnode::namelock));
// we do particularly want to keep the nolocalalias
}
}
else { // Could not find any symbol
if (lm->inScope(vnexemplar->getAddr(),vnexemplar->getSize(),
vnexemplar->getUsePoint(*this))) {
// This is technically an error, there should be some
// kind of symbol, if we are in scope
fl = Varnode::mapped | Varnode::addrtied;
}
else if (unmappedAliasCheck) {
// If the varnode is not in scope, check if we should treat as unaliased
fl = lm->isUnmappedUnaliased(vnexemplar) ? Varnode::nolocalalias : 0;
}
else
fl = 0;
}
if (syncVarnodesWithSymbol(iter,fl,ct))
updateoccurred = true;
}
return updateoccurred;
}
/// A Varnode overlaps the given SymbolEntry. Make sure the Varnode is part of the variable
/// underlying the Symbol. If not, remap things so that the Varnode maps to a distinct Symbol.
/// In either case, attach the appropriate Symbol to the Varnode
/// \param entry is the given SymbolEntry
/// \param vn is the overlapping Varnode
/// \return the Symbol attached to the Varnode
Symbol *Funcdata::handleSymbolConflict(SymbolEntry *entry,Varnode *vn)
{
if (vn->isInput() || vn->isAddrTied() ||
vn->isPersist() || vn->isConstant() || entry->isDynamic()) {
vn->setSymbolEntry(entry);
return entry->getSymbol();
}
HighVariable *high = vn->getHigh();
Varnode *otherVn;
HighVariable *otherHigh = (HighVariable *)0;
// Look for a conflicting HighVariable
VarnodeLocSet::const_iterator iter = beginLoc(entry->getSize(),entry->getAddr());
while(iter != endLoc()) {
otherVn = *iter;
if (otherVn->getSize() != entry->getSize()) break;
if (otherVn->getAddr() != entry->getAddr()) break;
HighVariable *tmpHigh = otherVn->getHigh();
if (tmpHigh != high) {
otherHigh = tmpHigh;
break;
}
++iter;
}
if (otherHigh == (HighVariable *)0) {
vn->setSymbolEntry(entry);
return entry->getSymbol();
}
// If we reach here, we have a conflicting variable
buildDynamicSymbol(vn);
return vn->getSymbolEntry()->getSymbol();
}
/// \brief Update properties (and the data-type) for a set of Varnodes associated with one Symbol
///
/// The set of Varnodes with the same size and address all have their boolean properties
/// updated to the given values. The set is specified by providing an iterator reference
/// to the first Varnode in the set assuming a 'loc' ordering. This iterator is updated
/// to point to the first Varnode after the affected set.
///
/// The only properties that can be effectively changed with this
/// routine are \b mapped, \b addrtied, \b addrforce, and \b nolocalalias.
/// HighVariable splits must occur if \b addrtied is cleared.
///
/// If the given data-type is non-null, an attempt is made to update all the Varnodes
/// to this data-type. The \b typelock and \b namelock properties cannot be changed here.
/// \param iter points to the first Varnode in the set
/// \param fl holds the new set of boolean properties
/// \param ct is the given data-type to set (or NULL)
/// \return \b true if at least one Varnode was modified
bool Funcdata::syncVarnodesWithSymbol(VarnodeLocSet::const_iterator &iter,uint4 fl,Datatype *ct)
{
VarnodeLocSet::const_iterator enditer;
Varnode *vn;
uint4 vnflags;
bool updateoccurred = false;
// These are the flags we are going to try to update
uint4 mask = Varnode::mapped;
// We take special care with the addrtied flag
// as we cannot set it here if it is clear
// We can CLEAR but not SET the addrtied flag
// If addrtied is cleared, so should addrforce
if ((fl&Varnode::addrtied)==0) // Is the addrtied flags cleared
mask |= Varnode::addrtied | Varnode::addrforce;
// We can set the nolocalalias flag, but not clear it
// If nolocalalias is set, then addrforce should be cleared
if ((fl&Varnode::nolocalalias)!=0)
mask |= Varnode::nolocalalias | Varnode::addrforce;
fl &= mask;
vn = *iter;
enditer = vbank.endLoc(vn->getSize(),vn->getAddr());
do {
vn = *iter++;
if (vn->isFree()) continue;
vnflags = vn->getFlags();
if (vn->mapentry != (SymbolEntry *)0) { // If there is already an attached SymbolEntry (dynamic)
uint4 localMask = mask & ~Varnode::mapped; // Make sure 'mapped' bit is unchanged
uint4 localFlags = fl & localMask;
if ((vnflags & localMask) != localFlags) {
updateoccurred = true;
vn->setFlags(localFlags);
vn->clearFlags((~localFlags)&localMask);
}
}
else if ((vnflags & mask) != fl) { // We have a change
updateoccurred = true;
vn->setFlags(fl);
vn->clearFlags((~fl)&mask);