-
Notifications
You must be signed in to change notification settings - Fork 49
/
fspec.hh
1779 lines (1631 loc) · 110 KB
/
fspec.hh
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.
*/
/// \file fspec.hh
/// \brief Definitions for specifying functions prototypes
#ifndef __FSPEC_HH__
#define __FSPEC_HH__
#include "modelrules.hh"
#include "rangemap.hh"
namespace ghidra {
class ProtoModel;
class JoinRecord;
extern AttributeId ATTRIB_CUSTOM; ///< Marshaling attribute "custom"
extern AttributeId ATTRIB_DOTDOTDOT; ///< Marshaling attribute "dotdotdot"
extern AttributeId ATTRIB_EXTENSION; ///< Marshaling attribute "extension"
extern AttributeId ATTRIB_HASTHIS; ///< Marshaling attribute "hasthis"
extern AttributeId ATTRIB_INLINE; ///< Marshaling attribute "inline"
extern AttributeId ATTRIB_KILLEDBYCALL; ///< Marshaling attribute "killedbycall"
extern AttributeId ATTRIB_MAXSIZE; ///< Marshaling attribute "maxsize"
extern AttributeId ATTRIB_MINSIZE; ///< Marshaling attribute "minsize"
extern AttributeId ATTRIB_MODELLOCK; ///< Marshaling attribute "modellock"
extern AttributeId ATTRIB_NORETURN; ///< Marshaling attribute "noreturn"
extern AttributeId ATTRIB_POINTERMAX; ///< Marshaling attribute "pointermax"
extern AttributeId ATTRIB_SEPARATEFLOAT; ///< Marshaling attribute "separatefloat"
extern AttributeId ATTRIB_STACKSHIFT; ///< Marshaling attribute "stackshift"
extern AttributeId ATTRIB_STRATEGY; ///< Marshaling attribute "strategy"
extern AttributeId ATTRIB_THISBEFORERETPOINTER; ///< Marshaling attribute "thisbeforeretpointer"
extern AttributeId ATTRIB_VOIDLOCK; ///< Marshaling attribute "voidlock"
extern ElementId ELEM_GROUP; ///< Marshaling element \<group>
extern ElementId ELEM_INTERNALLIST; ///< Marshaling element \<internallist>
extern ElementId ELEM_KILLEDBYCALL; ///< Marshaling element \<killedbycall>
extern ElementId ELEM_LIKELYTRASH; ///< Marshaling element \<likelytrash>
extern ElementId ELEM_LOCALRANGE; ///< Marshaling element \<localrange>
extern ElementId ELEM_MODEL; ///< Marshaling element \<model>
extern ElementId ELEM_PARAM; ///< Marshaling element \<param>
extern ElementId ELEM_PARAMRANGE; ///< Marshaling element \<paramrange>
extern ElementId ELEM_PENTRY; ///< Marshaling element \<pentry>
extern ElementId ELEM_PROTOTYPE; ///< Marshaling element \<prototype>
extern ElementId ELEM_RESOLVEPROTOTYPE; ///< Marshaling element \<resolveprototype>
extern ElementId ELEM_RETPARAM; ///< Marshaling element \<retparam>
extern ElementId ELEM_RETURNSYM; ///< Marshaling element \<returnsym>
extern ElementId ELEM_UNAFFECTED; ///< Marshaling element \<unaffected>
extern ElementId ELEM_INTERNAL_STORAGE; ///< Marshaling element \<internal_storage>
/// \brief Exception thrown when a prototype can't be modeled properly
struct ParamUnassignedError : public LowlevelError {
ParamUnassignedError(const string &s) : LowlevelError(s) {} ///< Constructor
};
/// \brief A contiguous range of memory that can be used to pass parameters
///
/// This range can be used to pass a single parameter (isExclusion() == \b true). This
/// is intended to model a parameter passed in a register. The logical value does not
/// have to fill the entire range. The size in bytes can range from a minimum, getMinSize(),
/// to the whole range, getSize(). Justification and extension of the logical value within
/// the range can be specified.
///
/// Alternately the range can be used as a resource for multiple parameters
/// (isExclusion() == \b false). In this case, the parameters are allocated sequentially
/// (usually) starting from the front of the range. The amount of space consumed by each
/// parameter is dictated by an \e alignment setting in bytes.
///
/// A ParamEntry can be associated with a particular class of data-types. Usually:
/// - TYPE_UNKNOWN for general purpose parameters
/// - TYPE_FLOAT for dedicated floating-point registers
class ParamEntry {
public:
enum {
force_left_justify = 1, ///< Big endian values are left justified within their slot
reverse_stack = 2, ///< Slots (for \e non-exlusion entries) are allocated in reverse order
smallsize_zext = 4, ///< Assume values that are below the max \b size are zero extended into this container
smallsize_sext = 8, ///< Assume values that are below the max \b size are sign extended into this container
// is_big_endian = 16, ///< Set if this value should be treated as big endian
smallsize_inttype = 0x20, ///< Assume values that are below the max \b size are sign OR zero extended based on integer type
smallsize_floatext = 0x40, ///< Assume values smaller than max \b size are floating-point extended to full size
extracheck_high = 0x80, ///< Perform extra checks during parameter recovery on most sig portion of the double
extracheck_low = 0x100, ///< Perform extra checks during parameter recovery on least sig portion of the double
is_grouped = 0x200, ///< This entry is grouped with other entries
overlapping = 0x400, ///< Overlaps an earlier entry (and doesn't consume additional resource slots)
first_storage = 0x800 ///< Entry is first in its storage class
};
enum {
no_containment, ///< Range neither contains nor is contained by a ParamEntry
contains_unjustified, ///< ParamEntry contains range, but the range does not cover the least significant bytes
contains_justified, ///< ParamEntry contains range, which covers the least significant bytes
contained_by ///< ParamEntry is contained by the range
};
private:
uint4 flags; ///< Boolean properties of the parameter
type_class type; ///< Data-type storage class that this entry must match
vector<int4> groupSet; ///< Group(s) \b this entry belongs to
AddrSpace *spaceid; ///< Address space containing the range
uintb addressbase; ///< Starting offset of the range
int4 size; ///< Size of the range in bytes
int4 minsize; ///< Minimum bytes allowed for the logical value
int4 alignment; ///< How much alignment (0 means only 1 logical value is allowed)
int4 numslots; ///< (Maximum) number of slots that can store separate parameters
JoinRecord *joinrec; ///< Non-null if this is logical variable from joined pieces
static const ParamEntry *findEntryByStorage(const list<ParamEntry> &entryList,const VarnodeData &vn);
void resolveFirst(list<ParamEntry> &curList); ///< Mark if \b this is the first ParamEntry in its storage class
void resolveJoin(list<ParamEntry> &curList); ///< Make adjustments for a \e join ParamEntry
void resolveOverlap(list<ParamEntry> &curList); ///< Make adjustments for ParamEntry that overlaps others
/// \brief Is the logical value left-justified within its container
bool isLeftJustified(void) const { return (((flags&force_left_justify)!=0)||(!spaceid->isBigEndian())); }
public:
ParamEntry(int4 grp) { groupSet.push_back(grp); } ///< Constructor for use with decode
int4 getGroup(void) const { return groupSet[0]; } ///< Get the group id \b this belongs to
const vector<int4> &getAllGroups(void) const { return groupSet; } ///< Get all group numbers \b this overlaps
bool groupOverlap(const ParamEntry &op2) const; ///< Check if \b this and op2 occupy any of the same groups
int4 getSize(void) const { return size; } ///< Get the size of the memory range in bytes.
int4 getMinSize(void) const { return minsize; } ///< Get the minimum size of a logical value contained in \b this
int4 getAlign(void) const { return alignment; } ///< Get the alignment of \b this entry
JoinRecord *getJoinRecord(void) const { return joinrec; } ///< Get record describing joined pieces (or null if only 1 piece)
type_class getType(void) const { return type; } ///< Get the data-type class associated with \b this
bool isExclusion(void) const { return (alignment==0); } ///< Return \b true if this holds a single parameter exclusively
bool isReverseStack(void) const { return ((flags & reverse_stack)!=0); } ///< Return \b true if parameters are allocated in reverse order
bool isGrouped(void) const { return ((flags & is_grouped)!=0); } ///< Return \b true if \b this is grouped with other entries
bool isOverlap(void) const { return ((flags & overlapping)!=0); } ///< Return \b true if \b this overlaps another entry
bool isFirstInClass(void) const { return ((flags & first_storage)!=0); } ///< Return \b true if \b this is the first entry in the storage class
bool subsumesDefinition(const ParamEntry &op2) const; ///< Does \b this subsume the definition of the given ParamEntry
bool containedBy(const Address &addr,int4 sz) const; ///< Is this entry contained by the given range
bool intersects(const Address &addr,int4 sz) const; ///< Does \b this intersect the given range in some way
int4 justifiedContain(const Address &addr,int4 sz) const; ///< Calculate endian aware containment
bool getContainer(const Address &addr,int4 sz,VarnodeData &res) const;
bool contains(const ParamEntry &op2) const; ///< Does \b this contain the given entry (as a subpiece)
OpCode assumedExtension(const Address &addr,int4 sz,VarnodeData &res) const;
int4 getSlot(const Address &addr,int4 skip) const;
AddrSpace *getSpace(void) const { return spaceid; } ///< Get the address space containing \b this entry
uintb getBase(void) const { return addressbase; } ///< Get the starting offset of \b this entry
Address getAddrBySlot(int4 &slot,int4 sz,int4 typeAlign) const;
void decode(Decoder &decoder,bool normalstack,bool grouped,list<ParamEntry> &curList);
bool isParamCheckHigh(void) const { return ((flags & extracheck_high)!=0); } ///< Return \b true if there is a high overlap
bool isParamCheckLow(void) const { return ((flags & extracheck_low)!=0); } ///< Return \b true if there is a low overlap
static void orderWithinGroup(const ParamEntry &entry1,const ParamEntry &entry2); ///< Enforce ParamEntry group ordering rules
};
/// \brief Class for storing ParamEntry objects in an interval range (rangemap)
class ParamEntryRange {
uintb first; ///< Starting offset of the ParamEntry's range
uintb last; ///< Ending offset of the ParamEntry's range
int4 position; ///< Position of the ParamEntry within the entire prototype list
ParamEntry *entry; ///< Pointer to the actual ParamEntry
/// \brief Helper class for initializing ParamEntryRange in a range map
class InitData {
friend class ParamEntryRange;
int4 position; ///< Position (within the full list) being assigned to the ParamEntryRange
ParamEntry *entry; ///< Underlying ParamEntry being assigned to the ParamEntryRange
public:
InitData(int4 pos,ParamEntry *e) { position = pos; entry = e; } ///< Constructor
};
/// \brief Helper class for subsorting on position
class SubsortPosition {
int4 position; ///< The position value
public:
SubsortPosition(void) {} ///< Constructor for use with rangemap
SubsortPosition(int4 pos) { position = pos; } ///< Construct given position
SubsortPosition(bool val) { position = val ? 1000000 : 0; } ///< Constructor minimal/maximal subsort
bool operator<(const SubsortPosition &op2) { return position < op2.position; } ///< Compare operation
};
public:
typedef uintb linetype; ///< The linear element for a rangemap
typedef SubsortPosition subsorttype; ///< The sub-sort object for a rangemap
typedef InitData inittype; ///< Initialization data for a ScopeMapper
ParamEntryRange(const inittype &data,uintb f,uintb l) {
first = f; last = l; position = data.position; entry = data.entry; } ///< Initialize the range
uintb getFirst(void) const { return first; } ///< Get the first address in the range
uintb getLast(void) const { return last; } ///< Get the last address in the range
subsorttype getSubsort(void) const { return SubsortPosition(position); } ///< Get the sub-subsort object
ParamEntry *getParamEntry(void) const { return entry; } ///< Get pointer to actual ParamEntry
};
typedef rangemap<ParamEntryRange> ParamEntryResolver; ///< A map from offset to ParamEntry
/// \brief A register or memory register that may be used to pass a parameter or return value
///
/// The parameter recovery utilities (see ParamActive) use this to denote a putative
/// parameter passing storage location. It is made up of the address and size of the memory range,
/// a set of properties about the use of the range (as a parameter) in context, and a link to
/// the matching part of the PrototypeModel.
///
/// Data-flow for the putative parameter is held directly by a Varnode. To quickly map to the
/// Varnode (which may or may not exist at points during the ParamTrial lifetime), the concept
/// of \b slot is used. ParamTrials are assigned a \e slot, starting at 1. For sub-function parameters,
/// this represents the actual input index of the Varnode in the corresponding CALL or CALLIND op.
/// For parameters, this gives the position within the list of possible input Varnodes in address order.
/// The \e slot ordering varies over the course of analysis and is unlikely to match
/// the final parameter ordering. The ParamTrial comparator sorts the trials in final parameter ordering.
class ParamTrial {
public:
enum {
checked = 1, ///< Trial has been checked
used = 2, ///< Trial is definitely used (final verdict)
defnouse = 4, ///< Trial is definitely not used
active = 8, ///< Trial looks active (hint that it is used)
unref = 0x10, ///< There is no direct reference to this parameter trial
killedbycall = 0x20, ///< Data in this location is unlikely to flow thru a func and still be a param
rem_formed = 0x40, ///< The trial is built out of a remainder operation
indcreate_formed = 0x80, ///< The trial is built out of an indirect creation
condexe_effect = 0x100, ///< This trial may be affected by conditional execution
ancestor_realistic = 0x200, ///< Trial has a realistic ancestor
ancestor_solid = 0x400 ///< Solid movement into the Varnode
};
private:
uint4 flags; ///< Boolean properties of the trial
Address addr; ///< Starting address of the memory range
int4 size; ///< Number of bytes in the memory range
int4 slot; ///< Slot assigned to this trial
const ParamEntry *entry; ///< PrototypeModel entry matching this trial
int4 offset; ///< "justified" offset into entry
int4 fixedPosition; ///< argument position if a fixed arg of a varargs function, else -1
public:
/// \brief Construct from components
ParamTrial(const Address &ad,int4 sz,int4 sl) { addr = ad; size = sz; slot = sl; flags=0; entry=(ParamEntry *)0; offset=-1; fixedPosition = -1; }
const Address &getAddress(void) const { return addr; } ///< Get the starting address of \b this trial
int4 getSize(void) const { return size; } ///< Get the number of bytes in \b this trial
int4 getSlot(void) const { return slot; } ///< Get the \e slot associated with \b this trial
void setSlot(int4 val) { slot = val; } ///< Set the \e slot associated with \b this trial
const ParamEntry *getEntry(void) const { return entry; } ///< Get the model entry associated with \b this trial
int4 getOffset(void) const { return offset; } ///< Get the offset associated with \b this trial
void setEntry(const ParamEntry *ent,int4 off) { entry=ent; offset=off; } ///< Set the model entry for this trial
void markUsed(void) { flags |= used; } ///< Mark the trial as a formal parameter
void markActive(void) { flags |= (active|checked); } ///< Mark that trial is actively used (in data-flow)
void markInactive(void) { flags &= ~((uint4)active); flags |= checked; } ///< Mark that trial is not actively used
void markNoUse(void) { flags &= ~((uint4)(active|used)); flags |= (checked|defnouse); } ///< Mark trial as definitely \e not a parameter
void markUnref(void) { flags |= (unref|checked); slot = -1; } ///< Mark that \b this trial has no Varnode representative
void markKilledByCall(void) { flags |= killedbycall; } ///< Mark that \b this storage is \e killed-by-call
bool isChecked(void) const { return ((flags & checked)!=0); } ///< Has \b this trial been checked
bool isActive(void) const { return ((flags & active)!=0); } ///< Is \b this trial actively used in data-flow
bool isDefinitelyNotUsed(void) const { return ((flags & defnouse)!=0); } ///< Is \b this trial as definitely not a parameter
bool isUsed(void) const { return ((flags & used)!=0); } ///< Is \b this trial as a formal parameter
bool isUnref(void) const { return ((flags & unref)!=0); } ///< Does \b this trial not have a Varnode representative
bool isKilledByCall(void) const { return ((flags & killedbycall)!=0); } ///< Is \b this storage \e killed-by-call
void setRemFormed(void) { flags |= rem_formed; } ///< Mark that \b this is formed by a INT_REM operation
bool isRemFormed(void) const { return ((flags & rem_formed)!=0); } ///< Is \b this formed by a INT_REM operation
void setIndCreateFormed(void) { flags |= indcreate_formed; } ///< Mark \b this trial as formed by \e indirect \e creation
bool isIndCreateFormed(void) const { return ((flags & indcreate_formed)!=0); } ///< Is \b this trial formed by \e indirect \e creation
void setCondExeEffect(void) { flags |= condexe_effect; } ///< Mark \b this trial as possibly affected by conditional execution
bool hasCondExeEffect(void) const { return ((flags & condexe_effect)!=0); } ///< Is \b this trial possibly affected by conditional execution
void setAncestorRealistic(void) { flags |= ancestor_realistic; } ///< Mark \b this as having a realistic ancestor
bool hasAncestorRealistic(void) const { return ((flags & ancestor_realistic)!=0); } ///< Does \b this have a realistic ancestor
void setAncestorSolid(void) { flags |= ancestor_solid; } ///< Mark \b this as showing solid movement into Varnode
bool hasAncestorSolid(void) const { return ((flags & ancestor_solid)!=0); } ///< Does \b this show solid movement into Varnode
int4 slotGroup(void) const { return entry->getSlot(addr,size-1); } ///< Get position of \b this within its parameter \e group
void setAddress(const Address &ad,int4 sz) { addr=ad; size=sz; } ///< Reset the memory range of \b this trial
ParamTrial splitHi(int4 sz) const; ///< Create a trial representing the first part of \b this
ParamTrial splitLo(int4 sz) const; ///< Create a trial representing the last part of \b this
bool testShrink(const Address &newaddr,int4 sz) const; ///< Test if \b this trial can be made smaller
bool operator<(const ParamTrial &b) const; ///< Sort trials in formal parameter order
void setFixedPosition(int4 pos) { fixedPosition = pos; } ///< Set fixed position
static bool fixedPositionCompare(const ParamTrial &a, const ParamTrial &b); ///< Sort by fixed position; stable for fixedPosition = -1
};
/// \brief Container class for ParamTrial objects
///
/// The parameter analysis algorithms use this class to maintain the collection
/// of parameter trials being actively considered for a given function. It holds the
/// ParamTrial objects and other information about the current state of analysis.
///
/// Trials are maintained in two stages, \e before parameter decisions have been made and \e after.
/// Before, trials are in input index order relative to the CALL or CALLIND op for a sub-function, or
/// they are in address order for input Varnodes to the active function.
/// After, the trials are put into formal parameter order, as dictated by the PrototypeModel.
class ParamActive {
vector<ParamTrial> trial; ///< The list of parameter trials
int4 slotbase; ///< Slot where next parameter will go
int4 stackplaceholder; ///< Which call input slot holds the stack placeholder
int4 numpasses; ///< Number of attempts at evaluating parameters
int4 maxpass; ///< Number of passes before we assume we have seen all params
bool isfullychecked; ///< True if all trials are fully examined (and no new trials are expected)
bool needsfinalcheck; ///< Should a final pass be made on trials (to take into account control-flow changes)
bool recoversubcall; ///< True if \b this is being used to recover prototypes of a sub-function call
public:
ParamActive(bool recoversub); ///< Construct an empty container
void clear(void); ///< Reset to an empty container
void registerTrial(const Address &addr,int4 sz); ///< Add a new trial to the container
int4 getNumTrials(void) const { return trial.size(); } ///< Get the number of trials in \b this container
ParamTrial &getTrial(int4 i) { return trial[i]; } ///< Get the i-th trial
const ParamTrial &getTrialForInputVarnode(int4 slot) const; ///< Get trial corresponding to the given input Varnode
int4 whichTrial(const Address &addr,int4 sz) const; ///< Get the trial overlapping with the given memory range
bool needsFinalCheck(void) const { return needsfinalcheck; } ///< Is a final check required
void markNeedsFinalCheck(void) { needsfinalcheck = true; } ///< Mark that a final check is required
bool isRecoverSubcall(void) const { return recoversubcall; } ///< Are these trials for a call to a sub-function
bool isFullyChecked(void) const { return isfullychecked; } ///< Are all trials checked with no new trials expected
void markFullyChecked(void) { isfullychecked = true; } ///< Mark that all trials are checked
void setPlaceholderSlot(void) { stackplaceholder = slotbase; slotbase += 1; } ///< Establish a stack placedholder slot
void freePlaceholderSlot(void); ///< Free the stack placeholder slot
int4 getNumPasses(void) const { return numpasses; } ///< How many trial analysis passes were performed
int4 getMaxPass(void) const { return maxpass; } ///< What is the maximum number of passes
void setMaxPass(int4 val) { maxpass = val; } ///< Set the maximum number of passes
void finishPass(void) { numpasses += 1; } ///< Mark that an analysis pass has completed
void sortTrials(void) { sort(trial.begin(),trial.end()); } ///< Sort the trials in formal parameter order
void deleteUnusedTrials(void); ///< Remove trials that were found not to be parameters
void splitTrial(int4 i,int4 sz); ///< Split the given trial in two
void joinTrial(int4 slot,const Address &addr,int4 sz); ///< Join adjacent parameter trials
int4 getNumUsed(void) const; ///< Get number of trials marked as formal parameters
/// \brief Test if the given trial can be shrunk to the given range
///
/// \param i is the index of the given trial
/// \param addr is the new address
/// \param sz is the new size
/// \return true if the trial can be shrunk to the new range
bool testShrink(int4 i,const Address &addr,int4 sz) const { return trial[i].testShrink(addr,sz); }
/// \brief Shrink the given trial to a new given range
///
/// \param i is the index of the given trial
/// \param addr is the new range's starting address
/// \param sz is the new range's size in bytes
void shrink(int4 i,const Address &addr,int4 sz) { trial[i].setAddress(addr,sz); }
void sortFixedPosition(void) {sort(trial.begin(),trial.end(),ParamTrial::fixedPositionCompare);} ///< sort the trials by fixed position then <
};
/// \brief A special space for encoding FuncCallSpecs
///
/// It is efficient and convenient to store the main subfunction
/// object (FuncCallSpecs) in the pcode operation which is actually making the
/// call. This address space allows a FuncCallSpecs to be encoded
/// as an address which replaces the formally encoded address of
/// the function being called, when manipulating the operation
/// internally. The space stored in the encoded address is
/// this special \b fspec space, and the offset is the actual
/// value of the pointer
class FspecSpace : public AddrSpace {
public:
FspecSpace(AddrSpaceManager *m,const Translate *t,int4 ind); ///< Constructor
virtual void encodeAttributes(Encoder &encoder,uintb offset) const;
virtual void encodeAttributes(Encoder &encoder,uintb offset,int4 size) const;
virtual void printRaw(ostream &s,uintb offset) const;
virtual void decode(Decoder &decoder);
static const string NAME; ///< Reserved name for the fspec space
};
/// \brief Basic elements of a parameter: address, data-type, properties
struct ParameterPieces {
enum {
isthis = 1, ///< Parameter is "this" pointer
hiddenretparm = 2, ///< Parameter is hidden pointer to return value, mirrors Varnode::hiddenretparm
indirectstorage = 4, ///< Parameter is indirect pointer to true parameter, mirrors Varnode::indirectstorage
namelock = 8, ///< Parameter's name is locked, mirrors Varnode::namelock
typelock = 16, ///< Parameter's data-type is locked, mirrors Varnode::typelock
sizelock = 32 ///< Size of the parameter is locked (but not the data-type)
};
Address addr; ///< Storage address of the parameter
Datatype *type; ///< The datatype of the parameter
uint4 flags; ///< additional attributes of the parameter
void swapMarkup(ParameterPieces &op); ///< Swap data-type markup between \b this and another parameter
};
/// \brief Raw components of a function prototype (obtained from parsing source code)
struct PrototypePieces {
ProtoModel *model; ///< (Optional) model on which prototype is based
string name; ///< Identifier (function name) associated with prototype
Datatype *outtype; ///< Return data-type
vector<Datatype *> intypes; ///< Input data-types
vector<string> innames; ///< Identifiers for input types
int4 firstVarArgSlot; ///< First position of a variable argument, or -1 if not varargs
};
/// \brief Description of the indirect effect a sub-function has on a memory range
///
/// This object applies only to the specific memory range, which is seen from the
/// point of view of the calling function as a particular
/// sub-function gets called. The main enumeration below lists the possible effects.
class EffectRecord {
public:
enum {
unaffected = 1, ///< The sub-function does not change the value at all
killedbycall = 2, ///< The memory is changed and is completely unrelated to its original value
return_address = 3, ///< The memory is being used to store the return address
unknown_effect = 4 ///< An unknown effect (indicates the absence of an EffectRecord)
};
private:
VarnodeData range; ///< The memory range affected
uint4 type; ///< The type of effect
public:
EffectRecord(void) {} ///< Constructor for use with decode()
EffectRecord(const EffectRecord &op2) { range = op2.range; type = op2.type; } ///< Copy constructor
EffectRecord(const Address &addr,int4 size); ///< Construct a memory range with an unknown effect
EffectRecord(const ParamEntry &entry,uint4 t); ///< Construct an effect on a parameter storage location
EffectRecord(const VarnodeData &addr,uint4 t); ///< Construct an effect on a memory range
uint4 getType(void) const { return type; } ///< Get the type of effect
Address getAddress(void) const { return Address(range.space,range.offset); } ///< Get the starting address of the affected range
int4 getSize(void) const { return range.size; } ///< Get the size of the affected range
bool operator==(const EffectRecord &op2) const; ///< Equality operator
bool operator!=(const EffectRecord &op2) const; ///< Inequality operator
void encode(Encoder &encoder) const; ///< Encode the record to a stream
void decode(uint4 grouptype,Decoder &decoder); ///< Decode the record from a stream
static bool compareByAddress(const EffectRecord &op1,const EffectRecord &op2);
};
/// A group of ParamEntry objects that form a complete set for passing
/// parameters in one direction (either input or output). The main tasks this class must
/// perform are:
/// - possibleParam() Quick test if a Varnode could ever be a parameter with this prototype
/// - fillinMap() Select trials completing prototype, given analysis info
/// - assignMap() Derive slot->address map, given a list of types
/// - checkJoin() Can two parameters be considered/converted into a single logical parameter
class ParamList {
public:
enum {
p_standard, ///< Standard input parameter model
p_standard_out, ///< Standard output (return value) model
p_register, ///< Unordered parameter passing locations model
p_register_out, ///< Multiple possible return value locations model
p_merged ///< A merged model (multiple models merged together)
};
virtual ~ParamList(void) {} ///< Destructor
virtual uint4 getType(void) const=0; ///< Get the type of parameter list
/// \brief Given list of data-types, map the list positions to storage locations
///
/// If we know the function prototype, recover how parameters are actually stored using the model.
/// \param proto is the ordered list of data-types
/// \param typefactory is the TypeFactory (for constructing pointers)
/// \param res will contain the storage locations corresponding to the datatypes
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const=0;
/// \brief Given an unordered list of storage locations, calculate a function prototype
///
/// A list of input (or output) trials is given, which may have holes, invalid inputs etc. Decide
/// on the formal ordered parameter list. Trials within the ParamActive are added, removed, or
/// reordered as needed.
/// \param active is the given list of trials
virtual void fillinMap(ParamActive *active) const=0;
/// \brief Check if the given two storage locations can represent a single logical parameter
///
/// Within the conventions of this model, do the two (hi/lo) locations represent
/// consecutive parameter locations that can be replaced by a single logical parameter.
/// \param hiaddr is the address of the most significant part of the value
/// \param hisize is the size of the most significant part in bytes
/// \param loaddr is the address of the least significant part of the value
/// \param losize is the size of the least significant part in bytes
/// \return \b true if the two pieces can be joined
virtual bool checkJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const=0;
/// \brief Check if it makes sense to split a single storage location into two parameters
///
/// A storage location and split point is provided, implying two new storage locations. Does
/// \b this model allow these locations to be considered parameters.
/// \param loc is the starting address of provided storage location
/// \param size is the size of the location in bytes
/// \param splitpoint is the number of bytes to consider in the first (in address order) piece
/// \return \b true if the storage location can be split
virtual bool checkSplit(const Address &loc,int4 size,int4 splitpoint) const=0;
/// \brief Characterize whether the given range overlaps parameter storage
///
/// Does the range naturally fit inside a potential parameter entry from this list or does
/// it contain a parameter entry. Return one of four enumerations indicating this characterization:
/// - no_containment - there is no containment between the range and any parameter in this list
/// - contains_unjustified - at least one parameter contains the range
/// - contains_justified - at least one parameter contains this range as its least significant bytes
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the given range
/// \return the characterization code
virtual int4 characterizeAsParam(const Address &loc,int4 size) const=0;
/// \brief Does the given storage location make sense as a parameter
///
/// Within \b this model, decide if the storage location can be considered a parameter.
/// \param loc is the starting address of the storage location
/// \param size is the number of bytes in the storage location
/// \return \b true if the location can be a parameter
virtual bool possibleParam(const Address &loc,int4 size) const=0;
/// \brief Pass-back the slot and slot size for the given storage location as a parameter
///
/// This checks if the given storage location acts as a parameter in \b this model and
/// passes back the number of slots that it occupies.
/// \param loc is the starting address of the storage location
/// \param size is the number of bytes in the storage location
/// \param slot if the \e slot number to pass back
/// \param slotsize is the number of consumed slots to pass back
/// \return \b true if the location can be a parameter
virtual bool possibleParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const=0;
/// \brief Pass-back the biggest parameter contained within the given range
///
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the range
/// \param res will hold the parameter storage description being passed back
/// \return \b true if there is at least one parameter contained in the range
virtual bool getBiggestContainedParam(const Address &loc,int4 size,VarnodeData &res) const=0;
/// \brief Check if the given storage location looks like an \e unjustified parameter
///
/// The storage for a value may be contained in a normal parameter location but be
/// unjustified within that container, i.e. the least significant bytes are not being used.
/// If this is the case, pass back the full parameter location and return \b true.
/// \param loc is the starting address of the given storage
/// \param size is the number of bytes in the given storage
/// \param res is the full parameter storage to pass back
/// \return \b true if the given storage is unjustified within its parameter container
virtual bool unjustifiedContainer(const Address &loc,int4 size,VarnodeData &res) const=0;
/// \brief Get the type of extension and containing parameter for the given storage
///
/// If the given storage is properly contained within a normal parameter and the model
/// typically extends a small value into the full container, pass back the full container
/// and the type of extension.
/// \param addr is the starting address of the given storage
/// \param size is the number of bytes in the given storage
/// \param res is the parameter storage to pass back
/// \return the extension operator (INT_ZEXT INT_SEXT) or INT_COPY if there is no extension.
/// INT_PIECE indicates the extension is determined by the specific prototype.
virtual OpCode assumedExtension(const Address &addr,int4 size,VarnodeData &res) const=0;
/// \brief Get the address space associated with any stack based parameters in \b this list.
///
/// \return the stack address space, if \b this models parameters passed on the stack, NULL otherwise
virtual AddrSpace *getSpacebase(void) const=0;
/// \brief Return \b true if \b this pointer occurs before an indirect return pointer
///
/// The \e automatic parameters: \b this parameter and the \e hidden return value pointer both
/// tend to be allocated from the initial general purpose registers reserved for parameter passing.
/// This method returns \b true if the \b this parameter is allocated first.
/// \return \b false if the \e hidden return value pointer is allocated first
virtual bool isThisBeforeRetPointer(void) const=0;
/// \brief For a given address space, collect all the parameter locations within that space
///
/// Pass back the memory ranges for any parameter that is stored in the given address space.
/// \param spc is the given address space
/// \param res will hold the set of matching memory ranges
virtual void getRangeList(AddrSpace *spc,RangeList &res) const=0;
/// \brief Return the maximum heritage delay across all possible parameters
///
/// Depending on the address space, data-flow for a parameter may not be available until
/// extra transform passes have completed. This method returns the number of passes
/// that must occur before we can guarantee that all parameters have data-flow info.
/// \return the maximum number of passes across all parameters in \b this model
virtual int4 getMaxDelay(void) const=0;
/// \brief Return \b true if ParamEntry locations should automatically be considered killed by call
///
/// \return \b true if automatically assume killbycall
virtual bool isAutoKillByCall(void) const=0;
/// \brief Restore the model from an \<input> or \<output> element in the stream
///
/// \param decoder is the stream decoder
/// \param effectlist is a container collecting EffectRecords across all parameters
/// \param normalstack is \b true if parameters are pushed on the stack in the normal order
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack)=0;
virtual ParamList *clone(void) const=0; ///< Clone this parameter list model
};
/// \brief A standard model for parameters as an ordered list of storage resources
///
/// This is a configurable model for passing (input) parameters as a list to a function.
/// The model allows 1 or more resource lists based on data-type, either TYPE_UNKNOWN for
/// general purpose or TYPE_FLOAT for floating-point registers. Within a resource list,
/// any number of parameters can be used but they must come starting at the beginning of
/// the list with no \e holes (skipped resources). A resource list can include (at the end)
/// \e stack parameters that are allocated based on an alignment. Optionally, the model supports
/// converting data-types larger than a specified size to pointers within the parameter list.
class ParamListStandard : public ParamList {
protected:
int4 numgroup; ///< Number of \e groups in this parameter convention
int4 maxdelay; ///< Maximum heritage delay across all parameters
bool thisbeforeret; ///< Does a \b this parameter come before a hidden return parameter
vector<int4> resourceStart; ///< The starting group for each resource section
list<ParamEntry> entry; ///< The ordered list of parameter entries
vector<ParamEntryResolver *> resolverMap; ///< Map from space id to resolver
list<ModelRule> modelRules; ///< Rules to apply when assigning addresses
AddrSpace *spacebase; ///< Address space containing relative offset parameters
const ParamEntry *findEntry(const Address &loc,int4 size,bool just) const; ///< Given storage location find matching ParamEntry
const ParamEntry *selectUnreferenceEntry(int4 grp,type_class prefType) const; ///< Select entry to fill an unreferenced param
void buildTrialMap(ParamActive *active) const; ///< Build map from parameter trials to model ParamEntrys
void separateSections(ParamActive *active,vector<int4> &trialStart) const;
static void markGroupNoUse(ParamActive *active,int4 activeTrial,int4 trialStart);
static void markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_class prefType);
static void forceExclusionGroup(ParamActive *active);
static void forceNoUse(ParamActive *active,int4 start,int4 stop);
static void forceInactiveChain(ParamActive *active,int4 maxchain,int4 start,int4 stop,int4 groupstart);
void calcDelay(void); ///< Calculate the maximum heritage delay for any potential parameter in this list
void addResolverRange(AddrSpace *spc,uintb first,uintb last,ParamEntry *paramEntry,int4 position);
void populateResolver(void); ///< Build the ParamEntry resolver maps
void parsePentry(Decoder &decoder,vector<EffectRecord> &effectlist,
int4 groupid,bool normalstack,bool autokill,bool splitFloat,bool grouped);
void parseGroup(Decoder &decoder,vector<EffectRecord> &effectlist,
int4 groupid,bool normalstack,bool autokill,bool splitFloat);
public:
ParamListStandard(void) {} ///< Construct for use with decode()
ParamListStandard(const ParamListStandard &op2); ///< Copy constructor
virtual ~ParamListStandard(void);
const list<ParamEntry> &getEntry(void) const { return entry; } ///< Get the list of parameter entries
list<ParamEntry>::const_iterator getFirstIter(type_class type) const; ///< Get iterator to first entry in a storage class
const ParamEntry *getStackEntry(void) const; ///< Get the stack entry
uint4 assignAddressFallback(type_class resource,Datatype *tp,bool matchExact,vector<int4> &status,
ParameterPieces ¶m) const;
uint4 assignAddress(Datatype *dt,const PrototypePieces &proto,int4 pos,TypeFactory &tlst,
vector<int4> &status,ParameterPieces &res) const;
virtual uint4 getType(void) const { return p_standard; }
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
virtual void fillinMap(ParamActive *active) const;
virtual bool checkJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const;
virtual bool checkSplit(const Address &loc,int4 size,int4 splitpoint) const;
virtual int4 characterizeAsParam(const Address &loc,int4 size) const;
virtual bool possibleParam(const Address &loc,int4 size) const;
virtual bool possibleParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const;
virtual bool getBiggestContainedParam(const Address &loc,int4 size,VarnodeData &res) const;
virtual bool unjustifiedContainer(const Address &loc,int4 size,VarnodeData &res) const;
virtual OpCode assumedExtension(const Address &addr,int4 size,VarnodeData &res) const;
virtual AddrSpace *getSpacebase(void) const { return spacebase; }
virtual bool isThisBeforeRetPointer(void) const { return thisbeforeret; }
virtual void getRangeList(AddrSpace *spc,RangeList &res) const;
virtual int4 getMaxDelay(void) const { return maxdelay; }
virtual bool isAutoKillByCall(void) const { return false; }
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
virtual ParamList *clone(void) const;
};
/// \brief A standard model for returning output parameters from a function
///
/// This has a more involved assignment strategy than its parent class.
/// Entries in the resource list are treated as a \e group, meaning that only one can
/// fit the desired storage size and type attributes of the return value. If no entry
/// fits, the return value is converted to a pointer data-type, storage allocation is
/// attempted again, and the return value is marked as a \e hidden return parameter
/// to inform the input model.
class ParamListStandardOut : public ParamListStandard {
bool useFillinFallback; ///< If \b true, use fillinMapFallback
void initialize(void); ///< Cache ModelRule information
public:
ParamListStandardOut(void) : ParamListStandard() {} ///< Constructor for use with decode()
ParamListStandardOut(const ParamListStandardOut &op2) : ParamListStandard(op2) {
useFillinFallback = op2.useFillinFallback; } ///< Copy constructor
void fillinMapFallback(ParamActive *active,bool firstOnly) const;
virtual uint4 getType(void) const { return p_standard_out; }
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
virtual void fillinMap(ParamActive *active) const;
virtual bool possibleParam(const Address &loc,int4 size) const;
virtual bool isAutoKillByCall(void) const { return useFillinFallback; }
virtual void decode(Decoder &decoder,vector<EffectRecord> &effectlist,bool normalstack);
virtual ParamList *clone(void) const;
};
/// \brief A model for passing back return values from a function
///
/// This is a resource list of potential storage locations for a return value,
/// at most 1 of which will be chosen for a given function. This models a simple strategy
/// for selecting a storage location. When assigning based on data-type (assignMap), the first list
/// entry that fits is chosen. When assigning from a set of actively used locations (fillinMap),
/// this class chooses the location that is the closest fitting match to an entry in the resource list.
class ParamListRegisterOut : public ParamListStandardOut {
public:
ParamListRegisterOut(void) : ParamListStandardOut() {} ///< Constructor
ParamListRegisterOut(const ParamListRegisterOut &op2) : ParamListStandardOut(op2) {} ///< Copy constructor
virtual uint4 getType(void) const { return p_register_out; }
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const;
virtual ParamList *clone(void) const;
};
/// \brief An unstructured model for passing input parameters to a function.
///
/// This is the \b register model, meaning a collection of registers, any of which
/// can be used to pass a parameter. This is nearly identical to ParamListStandard, but
/// rules banning \e holes are not enforced, any subset of the resource list can be used.
/// This makes sense for executables where parameters follow no conventions or only loose
/// conventions. The assignMap() method may make less sense in this scenario.
class ParamListRegister : public ParamListStandard {
public:
ParamListRegister(void) : ParamListStandard() {} ///< Constructor for use with decode()
ParamListRegister(const ParamListRegister &op2) : ParamListStandard(op2) {} ///< Copy constructor
virtual uint4 getType(void) const { return p_register; }
virtual void fillinMap(ParamActive *active) const;
virtual ParamList *clone(void) const;
};
/// \brief A union of other input parameter passing models
///
/// This model is viewed as a union of a constituent set of resource lists.
/// This allows initial data-flow analysis to proceed when the exact model
/// isn't known. The assignMap() and fillinMap() methods are disabled for
/// instances of this class. The controlling prototype model (ProtoModelMerged)
/// decides from among the constituent ParamList models before these routines
/// need to be invoked.
class ParamListMerged : public ParamListStandard {
public:
ParamListMerged(void) : ParamListStandard() {} ///< Constructor for use with decode
ParamListMerged(const ParamListMerged &op2) : ParamListStandard(op2) {} ///< Copy constructor
void foldIn(const ParamListStandard &op2); ///< Add another model to the union
void finalize(void) { populateResolver(); } ///< Fold-ins are finished, finalize \b this
virtual uint4 getType(void) const { return p_merged; }
virtual void assignMap(const PrototypePieces &proto,TypeFactory &typefactory,vector<ParameterPieces> &res) const {
throw LowlevelError("Cannot assign prototype before model has been resolved"); }
virtual void fillinMap(ParamActive *active) const {
throw LowlevelError("Cannot determine prototype before model has been resolved"); }
virtual ParamList *clone(void) const;
};
/// \brief A \b prototype \b model: a model for passing parameters between functions
///
/// This encompasses both input parameters and return values. It attempts to
/// describe the ABI, Application Binary Interface, of the processor or compiler.
/// Any number of function prototypes (FuncProto) can be implemented under a
/// \b prototype \b model, which represents a static rule set the compiler uses
/// to decide:
/// - Storage locations for input parameters
/// - Storage locations for return values
/// - Expected side-effects of a function on other (non-parameter) registers and storage locations
/// - Behavior of the stack and the stack pointer across function calls
///
/// Major analysis concerns are:
/// - Recovering function prototypes from data-flow information: deriveInputMap() and deriveOutputMap()
/// - Calculating parameter storage locations given a function prototype: assignParameterStorage()
/// - Behavior of data-flow around call sites
///
/// A prototype model supports the concept of \b extrapop, which is defined as the change in
/// value of the stack pointer (or the number of bytes popped from the stack) across a call.
/// This value is calculated starting from the point of the p-code CALL or CALLIND op, when the
/// stack parameters have already been pushed by the calling function. So \e extrapop only reflects
/// changes made by the callee.
class ProtoModel {
friend class ProtoModelMerged;
Architecture *glb; ///< The Architecture owning this prototype model
string name; ///< Name of the model
int4 extrapop; ///< Extra bytes popped from stack
ParamList *input; ///< Resource model for input parameters
ParamList *output; ///< Resource model for output parameters
const ProtoModel *compatModel; ///< The model \b this is a copy of
vector<EffectRecord> effectlist; ///< List of side-effects
vector<VarnodeData> likelytrash; ///< Storage locations potentially carrying \e trash values
vector<VarnodeData> internalstorage; ///< Registers that hold internal compiler constants
int4 injectUponEntry; ///< Id of injection to perform at beginning of function (-1 means not used)
int4 injectUponReturn; ///< Id of injection to perform after a call to this function (-1 means not used)
RangeList localrange; ///< Memory range(s) of space-based locals
RangeList paramrange; ///< Memory range(s) of space-based parameters
bool stackgrowsnegative; ///< True if stack parameters have (normal) low address to high address ordering
bool hasThis; ///< True if this model has a \b this parameter (auto-parameter)
bool isConstruct; ///< True if this model is a constructor for a particular object
bool isPrinted; ///< True if this model should be printed as part of function declarations
void defaultLocalRange(void); ///< Set the default stack range used for local variables
void defaultParamRange(void); ///< Set the default stack range used for input parameters
void buildParamList(const string &strategy); ///< Establish the main resource lists for input and output parameters.
public:
enum {
extrapop_unknown = 0x8000 ///< Reserved extrapop value meaning the function's \e extrapop is unknown
};
ProtoModel(Architecture *g); ///< Constructor for use with decode()
ProtoModel(const string &nm,const ProtoModel &op2); ///< Copy constructor changing the name
virtual ~ProtoModel(void); ///< Destructor
const string &getName(void) const { return name; } ///< Get the name of the prototype model
Architecture *getArch(void) const { return glb; } ///< Get the owning Architecture
const ProtoModel *getAliasParent(void) const { return compatModel; } ///< Return \e model \b this is an alias of (or null)
uint4 hasEffect(const Address &addr,int4 size) const; ///< Determine side-effect of \b this on the given memory range
int4 getExtraPop(void) const { return extrapop; } ///< Get the stack-pointer \e extrapop for \b this model
void setExtraPop(int4 ep) { extrapop = ep; } ///< Set the stack-pointer \e extrapop
int4 getInjectUponEntry(void) const { return injectUponEntry; } ///< Get the inject \e uponentry id
int4 getInjectUponReturn(void) const { return injectUponReturn; } ///< Get the inject \e uponreturn id
bool isCompatible(const ProtoModel *op2) const; ///< Return \b true if other given model can be substituted for \b this
/// \brief Given a list of input \e trials, derive the most likely input prototype
///
/// Trials are sorted and marked as \e used or not.
/// \param active is the collection of Varnode input trials
void deriveInputMap(ParamActive *active) const {
input->fillinMap(active); }
/// \brief Given a list of output \e trials, derive the most likely output prototype
///
/// One trial (at most) is marked \e used and moved to the front of the list
/// \param active is the collection of output trials
void deriveOutputMap(ParamActive *active) const {
output->fillinMap(active); }
void assignParameterStorage(const PrototypePieces &proto,vector<ParameterPieces> &res,bool ignoreOutputError);
/// \brief Check if the given two input storage locations can represent a single logical parameter
///
/// Within the conventions of this model, do the two (hi/lo) locations represent
/// consecutive input parameter locations that can be replaced by a single logical parameter.
/// \param hiaddr is the address of the most significant part of the value
/// \param hisize is the size of the most significant part in bytes
/// \param loaddr is the address of the least significant part of the value
/// \param losize is the size of the least significant part in bytes
/// \return \b true if the two pieces can be joined
bool checkInputJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const {
return input->checkJoin(hiaddr,hisize,loaddr,losize); }
/// \brief Check if the given two output storage locations can represent a single logical return value
///
/// Within the conventions of this model, do the two (hi/lo) locations represent
/// consecutive locations that can be replaced by a single logical return value.
/// \param hiaddr is the address of the most significant part of the value
/// \param hisize is the size of the most significant part in bytes
/// \param loaddr is the address of the least significant part of the value
/// \param losize is the size of the least significant part in bytes
/// \return \b true if the two pieces can be joined
bool checkOutputJoin(const Address &hiaddr,int4 hisize,const Address &loaddr,int4 losize) const {
return output->checkJoin(hiaddr,hisize,loaddr,losize); }
/// \brief Check if it makes sense to split a single storage location into two input parameters
///
/// A storage location and split point is provided, implying two new storage locations. Does
/// \b this model allow these locations to be considered separate parameters.
/// \param loc is the starting address of provided storage location
/// \param size is the size of the location in bytes
/// \param splitpoint is the number of bytes to consider in the first (in address order) piece
/// \return \b true if the storage location can be split
bool checkInputSplit(const Address &loc,int4 size,int4 splitpoint) const {
return input->checkSplit(loc,size,splitpoint); }
const RangeList &getLocalRange(void) const { return localrange; } ///< Get the range of (possible) local stack variables
const RangeList &getParamRange(void) const { return paramrange; } ///< Get the range of (possible) stack parameters
vector<EffectRecord>::const_iterator effectBegin(void) const { return effectlist.begin(); } ///< Get an iterator to the first EffectRecord
vector<EffectRecord>::const_iterator effectEnd(void) const { return effectlist.end(); } ///< Get an iterator to the last EffectRecord
vector<VarnodeData>::const_iterator trashBegin(void) const { return likelytrash.begin(); } ///< Get an iterator to the first \e likelytrash
vector<VarnodeData>::const_iterator trashEnd(void) const { return likelytrash.end(); } ///< Get an iterator to the last \e likelytrash
vector<VarnodeData>::const_iterator internalBegin(void) const { return internalstorage.begin(); } ///< Get an iterator to the first \e internalstorage
vector<VarnodeData>::const_iterator internalEnd(void) const { return internalstorage.end(); } ///< Get an iterator to the last \e internalstorage
/// \brief Characterize whether the given range overlaps parameter storage
///
/// Does the range naturally fit inside a potential parameter entry from this model or does
/// it contain a parameter entry. Return one of four values indicating this characterization:
/// - no_containment - there is no containment between the range and any parameter in this list
/// - contains_unjustified - at least one parameter contains the range
/// - contains_justified - at least one parameter contains this range as its least significant bytes
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the given range
/// \return the characterization code
int4 characterizeAsInputParam(const Address &loc,int4 size) const {
return input->characterizeAsParam(loc, size);
}
/// \brief Characterize whether the given range overlaps output storage
///
/// Does the range naturally fit inside a potential output entry from this model or does
/// it contain an output entry. Return one of four values indicating this characterization:
/// - no_containment - there is no containment between the range and any parameter in this list
/// - contains_unjustified - at least one parameter contains the range
/// - contains_justified - at least one parameter contains this range as its least significant bytes
/// - contained_by - no parameter contains this range, but the range contains at least one parameter
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the given range
/// \return the characterization code
int4 characterizeAsOutput(const Address &loc,int4 size) const {
return output->characterizeAsParam(loc, size);
}
/// \brief Does the given storage location make sense as an input parameter
///
/// Within \b this model, decide if the storage location can be considered an input parameter.
/// \param loc is the starting address of the storage location
/// \param size is the number of bytes in the storage location
/// \return \b true if the location can be a parameter
bool possibleInputParam(const Address &loc,int4 size) const {
return input->possibleParam(loc,size); }
/// \brief Does the given storage location make sense as a return value
///
/// Within \b this model, decide if the storage location can be considered an output parameter.
/// \param loc is the starting address of the storage location
/// \param size is the number of bytes in the storage location
/// \return \b true if the location can be a parameter
bool possibleOutputParam(const Address &loc,int4 size) const {
return output->possibleParam(loc,size); }
/// \brief Pass-back the slot and slot size for the given storage location as an input parameter
///
/// This checks if the given storage location acts as an input parameter in \b this model and
/// passes back the number of slots that it occupies.
/// \param loc is the starting address of the storage location
/// \param size is the number of bytes in the storage location
/// \param slot if the \e slot number to pass back
/// \param slotsize is the number of consumed slots to pass back
/// \return \b true if the location can be a parameter
bool possibleInputParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const {
return input->possibleParamWithSlot(loc,size,slot,slotsize); }
/// \brief Pass-back the slot and slot size for the given storage location as a return value
///
/// This checks if the given storage location acts as an output parameter in \b this model and
/// passes back the number of slots that it occupies.
/// \param loc is the starting address of the storage location
/// \param size is the number of bytes in the storage location
/// \param slot if the \e slot number to pass back
/// \param slotsize is the number of consumed slots to pass back
/// \return \b true if the location can be a parameter
bool possibleOutputParamWithSlot(const Address &loc,int4 size,int4 &slot,int4 &slotsize) const {
return output->possibleParamWithSlot(loc,size,slot,slotsize); }
/// \brief Check if the given storage location looks like an \e unjustified input parameter
///
/// The storage for a value may be contained in a normal parameter location but be
/// unjustified within that container, i.e. the least significant bytes are not being used.
/// If this is the case, pass back the full parameter location and return \b true.
/// \param loc is the starting address of the given storage
/// \param size is the number of bytes in the given storage
/// \param res is the full parameter storage to pass back
/// \return \b true if the given storage is unjustified within its parameter container
bool unjustifiedInputParam(const Address &loc,int4 size,VarnodeData &res) const {
return input->unjustifiedContainer(loc,size,res); }
/// \brief Get the type of extension and containing input parameter for the given storage
///
/// If the given storage is properly contained within a normal parameter and the model
/// typically extends a small value into the full container, pass back the full container
/// and the type of extension.
/// \param addr is the starting address of the given storage
/// \param size is the number of bytes in the given storage
/// \param res is the parameter storage to pass back
/// \return the extension operator (INT_ZEXT INT_SEXT) or INT_COPY if there is no extension.
/// INT_PIECE indicates the extension is determined by the specific prototype.
OpCode assumedInputExtension(const Address &addr,int4 size,VarnodeData &res) const {
return input->assumedExtension(addr,size,res); }
/// \brief Get the type of extension and containing return value location for the given storage
///
/// If the given storage is properly contained within a normal return value location and the model
/// typically extends a small value into the full container, pass back the full container
/// and the type of extension.
/// \param addr is the starting address of the given storage
/// \param size is the number of bytes in the given storage
/// \param res is the parameter storage to pass back
/// \return the extension operator (INT_ZEXT INT_SEXT) or INT_COPY if there is no extension.
/// INT_PIECE indicates the extension is determined by the specific prototype.
OpCode assumedOutputExtension(const Address &addr,int4 size,VarnodeData &res) const {
return output->assumedExtension(addr,size,res); }
/// \brief Pass-back the biggest input parameter contained within the given range
///
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the range
/// \param res will hold the parameter storage description being passed back
/// \return \b true if there is at least one parameter contained in the range
bool getBiggestContainedInputParam(const Address &loc,int4 size,VarnodeData &res) const {
return input->getBiggestContainedParam(loc, size, res);
}
/// \brief Pass-back the biggest possible output parameter contained within the given range
///
/// \param loc is the starting address of the given range
/// \param size is the number of bytes in the range
/// \param res will hold the storage description being passed back
/// \return \b true if there is at least one possible output parameter contained in the range
bool getBiggestContainedOutput(const Address &loc,int4 size,VarnodeData &res) const {
return output->getBiggestContainedParam(loc, size, res);
}
AddrSpace *getSpacebase(void) const { return input->getSpacebase(); } ///< Get the stack space associated with \b this model
bool isStackGrowsNegative(void) const { return stackgrowsnegative; } ///< Return \b true if the stack \e grows toward smaller addresses
bool hasThisPointer(void) const { return hasThis; } ///< Is \b this a model for (non-static) class methods
bool isConstructor(void) const { return isConstruct; } ///< Is \b this model for class constructors
bool printInDecl(void) const { return isPrinted; } ///< Return \b true if name should be printed in function declarations
void setPrintInDecl(bool val) { isPrinted = val; } ///< Set whether \b this name should be printed in function declarations
/// \brief Return the maximum heritage delay across all possible input parameters
///
/// Depending on the address space, data-flow for a parameter may not be available until
/// extra transform passes have completed. This method returns the number of passes
/// that must occur before we can guarantee that all parameters have data-flow info.
/// \return the maximum number of passes across all input parameters in \b this model
int4 getMaxInputDelay(void) const { return input->getMaxDelay(); }
/// \brief Return the maximum heritage delay across all possible return values
///
/// Depending on the address space, data-flow for a parameter may not be available until
/// extra transform passes have completed. This method returns the number of passes
/// that must occur before we can guarantee that any return value has data-flow info.
/// \return the maximum number of passes across all output parameters in \b this model
int4 getMaxOutputDelay(void) const { return output->getMaxDelay(); }
/// \brief Does \b this model automatically consider potential output locations as killed by call
///
/// \return \b true if output locations should be considered killed by call
bool isAutoKillByCall(void) const { return output->isAutoKillByCall(); }
/// \brief Is \b this a merged prototype model