-
Notifications
You must be signed in to change notification settings - Fork 10
/
DotNetPELib.h
1736 lines (1669 loc) · 75.5 KB
/
DotNetPELib.h
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
/* Software License Agreement
*
* Copyright(C) 1994-2023 David Lindauer, (LADSoft)
*
* This file is part of the Orange C Compiler package.
*
* The Orange C Compiler package is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Orange C Compiler package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Orange C. If not, see <http://www.gnu.org/licenses/>.
*
* contact information:
* email: [email protected] <David Lindauer>
*
*/
#ifndef DOTNETPELIB_H
#define DOTNETPELIB_H
#include <list>
#include <vector>
#include <deque>
#include <unordered_map>
#include <string>
#include <set>
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <cstring>
#include <memory>
#include <map>
// reference changelog.txt to see what the changes are
//
#define DOTNETPELIB_VERSION "4.00"
// this is the main library header
// it allows creation of the methods and data that would be dumped into
// a .net assembly. The data can be either directly written to an
// executable file or DLL, or dumped into a .IL file suitable for
// viewing or assembling with the ilasm assembler.
//
//
// The general usage of this library is to first create a PELib object,
// then populate it with objects related to your program. If you want you can
// use members of the Allocator class to instantiate the various objects,
// if you do this they will be deleted when the PELib object is deleted. Otherwise
// if you use operator new you are responsible for your own memory management.
//
// Once you have created a PELib object, it will create an AssemblyRef object
// Which is the container for your program. If you want to write a free standing program
// you would put your methods and fields into this object. However if you want to
// write a DLL that .net assemblies can access, you need to create a namespace in the assemblyref
// and at least one class in the namespace. Methods and fields go into the inner class.
//
// Another thing you have to do for .net compatibility is set the CIL flag
// in the PELib object. IF you have a free-standing program you might not
// want to set it, for example you can simplify code generation by putting
// initialized data in the SDATA segment. To put data in the SDATA segment
// you add an initializer to a static field. (it makes the equivalent of a .DATA statement)
// this library has full support for 32- bit pinvoke
// you have to help it just a little in the case of pinvokes with varargs
// by creating a method signature for each unique invocation of the pinvoke
// that is independent of the main method for the pinvoke.
// the main method does not have a vararg list but they unique invocations do
// internally, the exe generator will store the main method in the MethodDef table
// and the unique invocations in the MemberRef table. An entry in the ImplMap
// table will give information about what DLL the pinvoke is related to.
//
// this library has only been tested with 32 bit programs, however it should
// work with 64 bit programs as long as pinvoke isn't used.
//
// Note that if you intend to pass managed methods to unmanaged code or vice
// versa you may need to create transition thunks somewhere. OCCIL has
// a library that does this for you, assuming you only want to marshal
// simple types.
// OCCIL also has a library which does simple marshalling between C-style strings
// and .net style strings.
//
// This libray does simple peephole optimizations when an instruction
// can be replaced with a shorter equivalent. For example:
// ldc.i4 # can sometimes be shortened into one of several optimized instructions
// branches can sometimes be shortened
// parameter and local variable indexes can be optimized
// for local variables the library will also modify the local variable
// list and put the most used local variables first (as a further optimization)
// maximum stack is calculated
//
// there are various things missing:
// Public and Private are the only scope modifiers currently
// no support for character set modifiers
//
// Custom Attributes cannot in general be defined however internally
// we do handle the C# vararg one behind the scenes
// Parameter handling is missing support for [in][out][opt] and default values
// you can define static data in the CIL data area, but I haven't vetted it enough
// to know if you can write to it.
//
namespace DotNetPELib
{
// definitions for some common types
typedef long long longlong;
typedef unsigned long long ulonglong;
typedef unsigned char Byte; /* 1 byte */
typedef unsigned short Word; /* two bytes */
typedef unsigned DWord; /* four bytes */
// forward references
class NetCore;
class PELib;
class Allocator;
class Method;
class AssemblyDef;
class Namespace;
class Class;
class Field;
class Enum;
class Instruction;
class Value;
class Local;
class Param;
class MethodSignature;
class Operand;
class Type;
class FieldName;
class DataContainer;
class PEWriter;
class PEReader;
class PEMethod;
class CustomAttribute;
class MethodSemanticsTableEntry;
class Callback;
/* contains data, types, methods */
///** Destructor base.
// Used to support cleaning up objects when the allocator is used to create
// them. Every object that can be allocated eventualy inherits
// this virtual destructor, so that the allocator can destruct them again later.
class DestructorBase
{
public:
DestructorBase() { }
DestructorBase(const DestructorBase &) { }
DestructorBase& operator=(const DestructorBase &) { return *this; }
virtual ~DestructorBase() { }
};
///** errors can be thrown, generally when the IL code is being 'optimized'
// but sometimes at other times
class PELibError : public std::runtime_error
{
public:
enum ErrorList
{
///** More params are being aded after a vararg param has been added
VarargParamsAlreadyDeclared,
///** If calculations show that the stack would underflow somewhere in the method
StackUnderflow,
///** A label can be reached but the paths don't agree on how many
// things are currently on the stack
MismatchedStack,
///** The stack is unbalanced when the RET statement is hit
// This can mean either that there are elements on the stack in
// a function that has no return value, or there is not exactly '
//one element on the stack when the function has a return value.
StackNotEmpty,
///** A label has been defined twice
DuplicateLabel,
///** A label that has been referenced was not found
MissingLabel,
///** the short version of some branch was requested, but the target
// is out of range.
ShortBranchOutOfRange,
///** the short version of an instruction which references a
// local variable or a parameter was referenced, but the index is out
// of range
IndexOutOfRange,
///** There are multiple entry points
MultipleEntryPoints,
///** There is no entry point (note that if it is a DLL an entry point isn't needed)
MissingEntryPoint,
///** Expected an SEH Try block
ExpectedSEHTry,
///** Expected an SEH handler
ExpectedSEHHandler,
///** Mismatched SEH end tag
MismatchedSEHTag,
///** SEH tag is orphaned
OrphanedSEHTag,
///** Invalid SEH Filter
InvalidSEHFilter,
///** Seh section not correctly ended
InvalidSEHEpilogue,
};
PELibError(ErrorList err, const std::string &Name = "") : errnum(err), std::runtime_error(std::string(errorNames[err]) + " " + Name)
{
}
virtual ~PELibError() { }
ErrorList Errnum() const { return errnum; }
private:
ErrorList errnum;
static const char *errorNames[];
};
class ObjectError : public std::runtime_error
{
public:
ObjectError(char *a) : runtime_error(a) { }
};
// an internal enumeration for errors associated with invalidly formatted
// object files. Usually only used for debugging the object file handlers
enum {
oe_syntax,
oe_nonamespace,
oe_noclass,
oe_noenum,
oe_nofield,
oe_nomethod,
oe_typemismatch,
oe_corFlagsMismatch,
};
class StringHash
{
public:
unsigned operator() (const std::string& aa) const
{
unsigned rv = 0;
const unsigned char *x = (const unsigned char *)aa.c_str();
for (;*x; ++x)
rv = (rv << 8) + (rv << 1) + rv + *x;
return rv;
}
};
// Qualifiers is a generic class that holds all the 'tags' you would see on various objects in
// the assembly file. Where possible things are handled implicitly for example 'nested'
// will automatically be added when a class is nested in another class.
class Qualifiers : public DestructorBase
{
public:
enum {
Public = 0x1,
Private = 0x2,
Static = 0x4,
Instance = 0x8,
Explicit = 0x10,
Ansi = 0x20,
Sealed = 0x40,
Enum = 0x80,
Value = 0x100,
Sequential = 0x200,
Auto = 0x400,
Literal = 0x800,
HideBySig = 0x1000,
PreserveSig = 0x2000,
SpecialName = 0x4000,
RTSpecialName = 0x8000,
CIL = 0x10000,
Managed = 0x20000,
Runtime = 0x40000,
Virtual = 0x100000, // sealed
NewSlot = 0x200000, // value
BeforeFieldInit = 0x400000
};
enum
{
// settings appropriate for occil, e.g. everything is static. add public/private...
MainClass = Ansi | Sealed,
ClassClass = Value | Sequential | Ansi | Sealed,
ClassUnion = Value | Explicit | Ansi | Sealed,
ClassField = 0,
FieldInitialized = Static,
EnumClass = Enum | Auto | Ansi | Sealed,
EnumField = Static | Literal | Public,
PInvokeFunc = HideBySig | Static | PreserveSig,
ManagedFunc = HideBySig | Static | CIL | Managed
};
Qualifiers() : flags_(0)
{
}
Qualifiers(int Flags) : flags_(Flags)
{
}
Qualifiers(const Qualifiers &old)
{
flags_ = old.flags_;
}
Qualifiers &operator |=(int flags)
{
flags_ |= flags;
return *this;
}
///** most qualifiers come before the name of the item
void ILSrcDumpBeforeFlags(PELib &) const;
///** but a couple of the method qualifiers come after the method definition
void ILSrcDumpAfterFlags(PELib &) const;
///** get a name for a DataContainer object, suitable for use in an ASM file
// The main problem is there is a separator character between the first class encountered
// and its members, which is different depending on whether it is a type or a field
static std::string GetName(const std::string& root, const DataContainer *parent, bool type = false);
static std::string GetObjName(const std::string& root, const DataContainer *parent);
virtual void ObjOut(PELib &, int pass) const;
void ObjIn(PELib &, bool definition = true);
int Flags() const { return flags_; }
void Flags(int flags) { flags_ = flags; }
protected:
static void ReverseNamePrefix(std::string&rv, const DataContainer *parent, int &pos, bool type);
static std::string GetNamePrefix(const DataContainer *parent, bool type);
private:
static const char * qualifierNames_[];
static int afterFlags_;
int flags_;
};
///** base class that contains instructions/ labels
// will be further overridden later to make a 'method'
// definition
class CodeContainer : public DestructorBase
{
public:
CodeContainer(Qualifiers Flags) :flags_(Flags), hasSEH_(false), parent_(nullptr) { }
///** This is the interface to add a single CIL instruction
void AddInstruction(Instruction *instruction);
///** it is possible to remove the last instruction
Instruction *RemoveLastInstruction() {
Instruction *rv = instructions_.back();
instructions_.pop_back();
return rv;
}
///** Retrieve the last instruction
Instruction *LastInstruction() const {
return instructions_.back();
}
///** Validate instructions
void ValidateInstructions();
///** Validate SEH tags, e.g. make sure there are matching begin and end tags and that filters are organized properly
void ValidateSEH();
///** Validate one level of tags (recursive)
int ValidateSEHTags(std::vector<Instruction *>&tags, int offset);
///** Validate that SEH filter expressions are in the proper place
void ValidateSEHFilters(std::vector<Instruction *>&tags);
///** Validate the epilogue for each SEH section
void ValidateSEHEpilogues();
///** return flags member
Qualifiers &Flags() { return flags_; }
const Qualifiers &Flags() const { return flags_; }
///** set parent
void SetContainer(DataContainer *parent) { parent_ = parent; }
///** get parent
DataContainer *GetContainer() const { return parent_; }
struct SEHData
{
enum {
Exception = 0,
Filter = 1,
Finally = 2,
Fault = 4
} flags;
size_t tryOffset;
size_t tryLength;
size_t handlerOffset;
size_t handlerLength;
union
{
size_t filterOffset;
size_t classToken;
};
};
// some internal functions
bool InAssemblyRef() const;
void BaseTypes(int &types) const;
virtual void Optimize(PELib &);
virtual bool ILSrcDump(PELib &) const;
virtual bool PEDump(PELib &) { return false; }
virtual void Render(PELib&) { }
virtual void ObjOut(PELib &, int pass) const;
void ObjIn(PELib &, const std::vector<Local *>& locals);
Byte *Compile(PELib &, size_t &sz);
int CompileSEH(std::vector<Instruction *>tags, int offset, std::vector<SEHData> &sehData);
void CompileSEH(PELib &, std::vector<SEHData> &sehData);
virtual void Compile(PELib&) { }
std::list<Instruction *>::iterator begin() { return instructions_.begin(); }
std::list<Instruction *>::iterator end() { return instructions_.end(); }
protected:
std::unordered_map<std::string, Instruction *, StringHash> labels;
void LoadLabels();
void OptimizeLDC(PELib &);
void OptimizeLDLOC(PELib &);
void OptimizeLDARG(PELib &);
void OptimizeBranch(PELib &);
void CalculateOffsets();
bool ModifyBranches();
std::list<Instruction *> instructions_;
Qualifiers flags_;
DataContainer *parent_;
bool hasSEH_;
};
///** base class that contains other datacontainers or codecontainers
// that means it can contain namespaces, classes, methods, or fields
// The main assemblyref which holds everything is one of these,
// which means it acts as the 'unnamed' namespace.
// when this class is overridden as something other than a namespace,
// it cannot contain namespaces
class DataContainer : public DestructorBase
{
public:
///** all classes have to extend from SOMETHING...
// this is enumerations for the ones we can create by default
enum
{
///** reference to 'System::Object'
basetypeObject = 1,
///** reference to 'System::Value'
basetypeValue = 2,
///** reference to 'System::Enum'
basetypeEnum = 4,
///** reference to 'System' namespace
baseIndexSystem = 8
};
DataContainer(const std::string& Name, Qualifiers Flags) : name_(Name), flags_(Flags),
parent_(nullptr), instantiated_(false), peIndex_(0), assemblyRef_(false)
{
}
///** Add another data container
// This could be an assemblydef, namespace, class, or enumeration
void Add(DataContainer *item)
{
if (item)
{
item->parent_ = this;
children_.push_back(item);
sortedChildren_[item->name_].push_back(item);
}
}
///** Add a code container
// This is always a Method definition
void Add(CodeContainer *item)
{
if (item)
{
item->SetContainer(this);
methods_.push_back(item);
}
}
///** Add a field
void Add(Field *field);
///** A flag an app can use to tell if the class has been instantiated,
// for example it might be used after a forward reference is resolved.
// it is not used internally to the library
bool IsInstantiated() const { return instantiated_; }
void SetInstantiated() { instantiated_ = true; }
///** The immediate parent
DataContainer *Parent() const { return parent_; }
void Parent(DataContainer* parent) { parent_ = parent; }
///** The inner namespace parent
size_t ParentNamespace(PELib &peLib) const;
///** The closest parent class
size_t ParentClass(PELib &peLib) const;
///** The parent assembly
size_t ParentAssembly(PELib &peLib) const;
///** The name
const std::string &Name() const { return name_; }
///** The qualifiers
Qualifiers &Flags() { return flags_; }
///** metatable index in the PE file for this data container
size_t PEIndex() const { return peIndex_; }
///** metatable index in the PE file for this data container
void PEIndex(size_t index) { peIndex_ = index; }
///* find a sub-container
DataContainer *FindContainer(const std::string& name, std::deque<Type*>* generics = nullptr);
///** Find a sub- container
DataContainer *FindContainer(std::vector<std::string>& split, size_t &n, std::deque<Type*>* generics = nullptr, bool method = false);
const std::list<Field *>&Fields() const { return fields_; }
const std::list<CodeContainer *>&Methods() const { return methods_; }
///** Traverse the declaration tree
virtual bool Traverse(Callback &callback) const;
// internal functions
virtual bool InAssemblyRef() const { return parent_->InAssemblyRef(); }
virtual bool ILSrcDump(PELib &) const;
virtual bool PEDump(PELib &);
virtual void Compile(PELib&);
virtual void ObjOut(PELib &, int pass) const;
void ObjIn(PELib &, bool definition = true);
void Number(int &n);
// sometimes we want to traverse upwards in the tree
void Render(PELib&);
void BaseTypes(int &types) const;
void Clear() { children_.clear(); methods_.clear(); sortedChildren_.clear(); fields_.clear(); }
protected:
std::list<DataContainer *> children_;
std::list<CodeContainer *> methods_;
std::unordered_map<std::string, std::deque<DataContainer *>, StringHash> sortedChildren_;
std::list<Field *> fields_;
DataContainer *parent_;
Qualifiers flags_;
std::string name_;
bool instantiated_;
size_t peIndex_; // generic index into a table or stream
bool assemblyRef_;
};
///** class to hold custom attributes. only parses them at this point, so that
// you can retrieve attributes from .net assemblies if you want to. if you
// want to generate them you are on your own.
class CustomAttributeContainer : public DestructorBase
{
public:
struct lt
{
bool operator()(const CustomAttribute *left, const CustomAttribute *right) const;
};
class CustomAttributeDescriptor
{
public:
std::string name;
Byte *data;
size_t sz;
CustomAttributeDescriptor() : data(nullptr), sz(0) { }
~CustomAttributeDescriptor() { delete data; }
bool operator() (const CustomAttributeDescriptor *left, const CustomAttributeDescriptor *right) const;
};
CustomAttributeContainer() { }
~CustomAttributeContainer();
void Load(PELib &peLib, AssemblyDef &assembly, PEReader &reader);
const std::vector<CustomAttributeDescriptor *>& Lookup(CustomAttribute *attribute) const;
bool Has(CustomAttribute &attribute, const std::string& name, Byte *data = nullptr, size_t sz = 0) const;
private:
std::map<CustomAttribute *, std::vector<CustomAttributeDescriptor *>, lt> attributes;
std::set<CustomAttributeDescriptor *, CustomAttributeDescriptor> descriptors;
};
///** base class for assembly definitions
// this holds the main assembly ( as a non-external assembly)
// or can hold an external assembly
class AssemblyDef : public DataContainer
{
public:
AssemblyDef(const std::string& Name, bool External, Byte * KeyToken = nullptr) : DataContainer(Name, 0), external_(External),
major_(0), minor_(0), build_(0), revision_(0), loaded_(false)
{
if (KeyToken)
memcpy(publicKeyToken_, KeyToken, 8);
else
memset(publicKeyToken_, 0, 8);
}
void SetVersion(int major, int minor, int build, int revision)
{
major_ = major;
minor_ = minor;
build_ = build;
revision_ = revision;
}
virtual ~AssemblyDef() { }
///** get name of strong name key file (will be "" by default)
const std::string& SNKFile() const { return snkFile_; }
///** set name of strong name key file
void SNKFile(const std::string& file) { snkFile_ = file; }
///** root for Load assembly from file
void Load(PELib &lib, PEReader &reader);
///** lookup or create a class
Class *LookupClass(PELib &lib, const std::string& nameSpace, const std::string& name);
///** Set a public key
void SetPublicKey(PEReader &reader, size_t index);
const CustomAttributeContainer &CustomAttributes() const { return customAttributes_; }
virtual bool InAssemblyRef() const override { return external_; }
bool IsLoaded() { return loaded_; }
void SetLoaded() { loaded_ = true; }
bool ILHeaderDump(PELib &);
bool PEHeaderDump(PELib &);
virtual void ObjOut(PELib &, int pass) const override;
static AssemblyDef *ObjIn(PELib &, bool definition = true);
int GetMajor() const { return major_; }
protected:
Namespace *InsertNameSpaces(PELib &lib, std::unordered_map<std::string, Namespace *, StringHash> &nameSpaces, const std::string& name);
Namespace *InsertNameSpaces(PELib &lib, Namespace *nameSpace, std::string nameSpaceName);
Class *InsertClasses(PELib &lib, Namespace *nameSpace, Class *cls, std::string name);
private:
std::string snkFile_;
bool external_;
Byte publicKeyToken_[8];
int major_, minor_, build_, revision_;
bool loaded_;
CustomAttributeContainer customAttributes_;
std::unordered_map<std::string, Namespace *, StringHash> namespaceCache;
std::unordered_map<std::string, Class *, StringHash> classCache;
};
///** a namespace
class Namespace : public DataContainer
{
public:
Namespace(const std::string& Name) : DataContainer(Name, Qualifiers(0))
{
}
///** Get the full namespace name including all parents
std::string ReverseName(DataContainer *child);
virtual bool ILSrcDump(PELib &) const override;
virtual bool PEDump(PELib &) override;
virtual void ObjOut(PELib &, int pass) const override;
static Namespace *ObjIn(PELib &, bool definition = true);
};
/* a property, note we are only supporting classic properties here, not any
* extensions that are allowed in the image file format
*/
class Property : public DestructorBase
{
public:
enum {
SpecialName = 0x200,
RTSpecialName = 0x400,
HasDefault = 0x1000
};
Property(PELib &peLib, const std::string& name, Type *type, std::vector<Type *>& indices, bool hasSetter = true, DataContainer *parent = nullptr)
: name_(name), parent_(parent), type_(type), flags_(SpecialName), instance_(true), getter_(nullptr), setter_(nullptr)
{
CreateFunctions(peLib, indices, hasSetter);
}
Property() : parent_(NULL), type_(nullptr), flags_(SpecialName),
instance_(true), getter_(nullptr), setter_(nullptr) { }
///** Set the parent container (always a class)
void SetContainer(DataContainer *parent, bool add = true);
///** Get the parent container (always a class)
DataContainer* GetContainer() const { return parent_; }
///** choose whether it is an instance member or static property
void Instance(bool instance);
///** return whether it is an instance member or static property
bool Instance() const { return instance_; }
///** set the name
void Name(const std::string& name) { name_ = name; }
///** Get the name
const std::string &Name() const { return name_; }
///* set the type
void SetType(Type *type) { type_ = type; }
///* get the type
Type *GetType() const { return type_; }
///** Call the getter, leaving property on stack
/// If you had other arguments you should push them before the call
void CallGet(PELib &peLib, CodeContainer *code);
///** Call the setter,
/// If you had other arguments you should push them before the call
/// then push the value you want to set
void CallSet(PELib &peLib, CodeContainer *code);
///** Get the getter
Method *Getter() { return getter_; }
///** Get the setter
Method *Setter() { return setter_; }
void Getter(Method *getter) { getter_ = getter; }
void Setter(Method *setter) { setter_ = setter; }
// internal functions
///** root for Load assembly from file
void Load(PELib &lib, AssemblyDef &assembly, PEReader &reader, size_t propIndex, size_t startIndex, size_t startSemantics, size_t endSemantics, std::vector<Method *>& methods);
virtual bool ILSrcDump(PELib &) const;
virtual bool PEDump(PELib &);
virtual void ObjOut(PELib &, int pass) const;
static Property *ObjIn(PELib &);
protected:
void CreateFunctions(PELib &peLib, std::vector<Type *>& indices, bool hasSetter);
private:
int flags_;
bool instance_;
std::string name_;
Type *type_;
DataContainer *parent_;
Method *getter_;
Method *setter_;
};
/* a class, note that it cannot contain namespaces which is enforced at compile time*/
/* note that all classes have to eventually derive from one of the System base classes
* but that is handled internally */
/* enums derive from this */
class Class : public DataContainer
{
public:
Class(const std::string& Name, Qualifiers Flags, int Pack, int Size) : DataContainer(Name, Flags),
pack_(Pack), size_(Size), extendsFrom_(nullptr), external_(false), genericParent_(nullptr)
{
}
Class(const Class&) = default;
Class& operator=(const Class&) = default;
///** set the structure packing
void pack(int pk) { pack_ = pk; }
///** set the structure size
void size(int sz) { size_ = sz; }
int size() { return size_; }
///**set the class we are extending from, if this is unset
// a system class will be chosen based on whether or not the class is a valuetype
// this may be unset when reading in an assembly, in that case ExtendsName may give the name of a class which is being extended from
void Extends(Class *extendsFrom) { extendsFrom_ = extendsFrom; }
Class *Extends() const { return extendsFrom_; }
void ExtendsName(std::string&name) { extendsName_ = name; }
std::string ExtendsName() const { return extendsName_; }
///** not locally defined
bool External() const { return external_; }
///** not locally defined
void External(bool external) { external_ = external; }
///** add a property to this container
void Add(Property *property, bool add = true)
{
if (property)
{
property->SetContainer(this, add);
properties_.push_back(property);
}
}
using DataContainer::Add;
void GenericParent(Class* cls) { genericParent_ = cls; }
Class* GenericParent() const { return genericParent_; }
///** Traverse the declaration tree
virtual bool Traverse(Callback &callback) const override;
///** return the list of properties
const std::vector<Property *>& Properties() const { return properties_; }
///** return the list of generics
std::deque<Type*>& Generic() { return generic_; }
const std::deque<Type*>& Generic() const { return generic_; }
///** root for Load assembly from file
void Load(PELib &lib, AssemblyDef &assembly, PEReader &reader, size_t index, int startField, int endField, int startMethod, int endMethod, int startSemantics, int endSemantics);
virtual bool ILSrcDump(PELib &) const override;
virtual bool PEDump(PELib &) override;
void ILSrcDumpClassHeader(PELib &) const;
virtual void ObjOut(PELib &, int pass) const override;
static Class *ObjIn(PELib &, bool definition = true);
std::string AdornGenerics(PELib& peLib, bool names = false) const;
bool MatchesGeneric(std::deque<Type*>* generics) const;
protected:
int TransferFlags() const;
int pack_;
int size_;
Class *extendsFrom_;
std::string extendsName_;
std::vector<Property *>properties_;
bool external_;
std::deque<Type*> generic_;
Class* genericParent_;
};
///** A method with code
// CIL instructions are added with the 'Add' member of code container
class Method : public CodeContainer
{
public:
///** a call to either managed or unmanaged code
enum InvokeMode { CIL, PInvoke };
///** linkage type for unmanaged call.
enum InvokeType { Cdecl, Stdcall };
Method(MethodSignature *Prototype, Qualifiers flags, bool entry = false);
///** Set Pinvoke DLL name
void SetPInvoke(const std::string& name, InvokeType type = Stdcall)
{
invokeMode_ = PInvoke;
pInvokeName_ = name;
pInvokeType_ = type;
}
bool IsPInvoke() const { return invokeMode_ == PInvoke; }
///** Add a local variable
void AddLocal(Local *local);
const std::vector<Local *>& Locals() { return varList_; }
void Instance(bool instance);
bool Instance() const { return !!(Flags().Value & Qualifiers::Instance); }
///** return the signature
MethodSignature *Signature() const { return prototype_; }
///** is it an entry point function
bool HasEntryPoint() const { return entryPoint_; }
///** Iterate through local variables
typedef std::vector<Local *>::iterator iterator;
iterator begin() { return varList_.begin(); }
iterator end() { return varList_.end(); }
size_t size() const { return varList_.size(); }
// Internal functions
void MaxStack(int stack) { maxStack_ = stack; }
virtual bool ILSrcDump(PELib &) const override;
virtual bool PEDump(PELib &) override;
virtual void Compile(PELib&) override;
virtual void Optimize(PELib &) override;
virtual void ObjOut(PELib &, int pass) const override;
static Method *ObjIn(PELib &, bool definition = true, Method **found = nullptr);
protected:
void OptimizeLocals(PELib &);
void CalculateMaxStack();
void CalculateLive();
MethodSignature *prototype_;
std::vector<Local *> varList_;
std::string pInvokeName_;
InvokeMode invokeMode_;
InvokeType pInvokeType_;
int maxStack_;
bool entryPoint_;
PEMethod *rendering_;
};
///** a field, could be either static or non-static
class Field : public DestructorBase
{
public:
///** Size for enumerated values
enum ValueSize { i8, i16, i32, i64 };
///** Mode for the initialized value
enum ValueMode {
///** No initialized value
None,
///** Enumerated value, goes into the constant table
Enum,
///** Byte stream, goes into the sdata
Bytes
};
Field(const std::string& Name, Type *tp, Qualifiers Flags) : mode_(Field::None), name_(Name), flags_(Flags), parent_(nullptr), size_(i8),
type_(tp), /*enumValue_(0),*/ byteValue_(nullptr), byteLength_(0), ref_(0), peIndex_(0), explicitOffset_(0), external_(false), definitions_(0)
{
}
///** Add an enumeration constant
// Note that the field does need to be part of an enumeration
void AddEnumValue(longlong Value, ValueSize Size);
longlong EnumValue() const { return enumValue_; }
///** Add an SDATA initializer
void AddInitializer(Byte *bytes, int len); // this will be readonly in ILONLY assemblies
///** Field Name
const std::string &Name() const { return name_; }
///** Set the field's container
void SetContainer(DataContainer *Parent) { parent_ = Parent; }
DataContainer *GetContainer() const { return parent_; }
///** Type of field
Type *FieldType() const { return type_; }
void FieldType(Type *tp) { type_ = tp; }
///** Field qualifiers
const Qualifiers &Flags() const { return flags_; }
//* Is field referenced
void Ref(bool Ref) { ref_ = Ref; }
//* Is field referenced
bool IsRef() const { return ref_; }
///** not locally defined
bool External() const { return external_; }
///** not locally defined
void External(bool external) { external_ = external; }
///** increment definition count
void Definition() { definitions_++; }
///** get definition count
size_t Definitions() const { return definitions_; }
//* field offset for explicit structures
void ExplicitOffset(size_t offset) { explicitOffset_ = offset;}
//* field offset for explicit structures
size_t ExplicitOffset() const { return explicitOffset_; }
///** Index in the fielddef table
size_t PEIndex() const { return peIndex_; }
void PEIndex(size_t val) { peIndex_ = val; }
// internal functions
bool InAssemblyRef() const { return parent_->InAssemblyRef(); }
static bool ILSrcDumpTypeName(PELib &peLib, ValueSize size);
virtual bool ILSrcDump(PELib &) const;
virtual bool PEDump(PELib &);
virtual void ObjOut(PELib &, int pass) const;
static Field *ObjIn(PELib &, bool definition = true);
protected:
DataContainer *parent_;
std::string name_;
Qualifiers flags_;
ValueMode mode_;
Type *type_;
union {
longlong enumValue_;
Byte *byteValue_;
};
int byteLength_;
ValueSize size_;
size_t peIndex_;
size_t explicitOffset_;
bool ref_;
bool external_;
size_t definitions_;
};
///** A special kind of class: enum
class Enum : public Class
{
public:
Enum(const std::string& Name, Qualifiers Flags, Field::ValueSize Size) :
size(Size), Class(Name, Flags.Flags() | Qualifiers::Value, -1, -1)
{
}
///** Add an enumeration, give it a name and a value
// This creates the Field definition for the enumerated value
Field *AddValue(Allocator &allocator, const std::string& Name, longlong Value);
// internal functions
virtual bool ILSrcDump(PELib &) const override;
virtual bool PEDump(PELib &) override;
virtual void ObjOut(PELib &, int pass) const override;
static Enum *ObjIn(PELib &, bool definition = true);
protected:
Field::ValueSize size;
};
///** the operand to an instruction
// this can contain a number, a string, or a reference to value
// a value can be a field, methodsignature, local, or param reference
//
class Operand : public DestructorBase
{
public:
enum OpSize { any, i8, u8, i16, u16, i32, u32, i64, u64, inative, r4, r8 };
enum OpType { t_none, t_value, t_int, t_real, t_string, t_label };
///** Default constructor
Operand() : type_(t_none), intValue_(0), sz_(i8), refValue_(nullptr), floatValue_(0), property_(0) // no operand
{
}
///** Operand is a complex value
Operand(Value *V) : type_(t_value), refValue_(V), property_(false), sz_(i8), intValue_(0), floatValue_(0)
{
}
///** Operand is an integer constant
Operand(longlong Value, OpSize Size) : type_(t_int), intValue_(Value), sz_(Size), refValue_(nullptr), floatValue_(0), property_(0)
{
}
Operand(int Value, OpSize Size) : Operand((longlong)Value, Size) { }
Operand(unsigned Value, OpSize Size) : Operand((longlong)Value, Size) { }
///** Operand is a floating point constant
Operand(double Value, OpSize Size) : type_(t_real), floatValue_(Value), sz_(Size), intValue_(0), refValue_(nullptr), property_(0)
{
}
///** Operand is a string
Operand(const std::string& Value, bool) : type_(t_string), intValue_(0), sz_(i8), refValue_(nullptr), floatValue_(0), property_(0) // string
{
stringValue_ = Value;
}
///** Operand is a label
Operand(const std::string& Value) : type_(t_label), intValue_(0), sz_(i8), refValue_(nullptr), floatValue_(0), property_(0) // label
{
stringValue_ = Value;
}
///** Get type of operand
OpType OperandType() const { return type_; }
///** When operand is a complex value, return it
Value * GetValue() const { return type_ == t_value ? refValue_ : nullptr; }
///** return the int value
longlong IntValue() const { return intValue_; }
///** return the string value
std::string StringValue() const { return stringValue_; }
///** return the float value
double FloatValue() const { return floatValue_; }
///** return the is-a-property flag
///** only has meaning for 'value' operands
bool Property() const { return property_; }
///** set the is-a-property flag
void Property(bool state) { property_ = state; }
///** Internal functions
virtual bool ILSrcDump(PELib &) const;
size_t Render(PELib &peLib, int opcode, int operandType, Byte *);
virtual void ObjOut(PELib &, int pass) const;
static Operand * ObjIn(PELib &, const std::map<const std::string, Local*>& locals);
std::string EscapedString() const;
protected:
OpType type_;
OpSize sz_;
Value *refValue_;
std::string stringValue_;
longlong intValue_;
double floatValue_;
bool property_;
bool isnanorinf() const;
};
/* a CIL instruction */
class Instruction : public DestructorBase
{
public:
// names of opcodes
enum iop
{
///** should never occur
i_unknown,
///** This instruction is a placeholder for a label
i_label,
///** This instruction is a placeholder for a comment
i_comment,
///** This instruction is an SEH specifier
i_SEH,
///** actual CIL instructions start here
i_add, i_add_ovf, i_add_ovf_un, i_and, i_arglist, i_beq, i_beq_s, i_bge,
i_bge_s, i_bge_un, i_bge_un_s, i_bgt, i_bgt_s, i_bgt_un, i_bgt_un_s, i_ble,
i_ble_s, i_ble_un, i_ble_un_s, i_blt, i_blt_s, i_blt_un, i_blt_un_s, i_bne_un,
i_bne_un_s, i_box, i_br, i_br_s, i_break, i_brfalse, i_brfalse_s, i_brinst,
i_brinst_s, i_brnull, i_brnull_s, i_brtrue, i_brtrue_s, i_brzero, i_brzero_s, i_call,
i_calli, i_callvirt, i_castclass, i_ceq, i_cgt, i_cgt_un, i_ckfinite, i_clt,
i_clt_un, i_constrained_, i_conv_i, i_conv_i1, i_conv_i2, i_conv_i4, i_conv_i8, i_conv_ovf_i,
i_conv_ovf_i_un, i_conv_ovf_i1, i_conv_ovf_i1_un, i_conv_ovf_i2, i_conv_ovf_i2_un, i_conv_ovf_i4, i_conv_ovf_i4_un, i_conv_ovf_i8,
i_conv_ovf_i8_un, i_conv_ovf_u, i_conv_ovf_u_un, i_conv_ovf_u1, i_conv_ovf_u1_un, i_conv_ovf_u2, i_conv_ovf_u2_un, i_conv_ovf_u4,
i_conv_ovf_u4_un, i_conv_ovf_u8, i_conv_ovf_u8_un, i_conv_r_un, i_conv_r4, i_conv_r8, i_conv_u, i_conv_u1,
i_conv_u2, i_conv_u4, i_conv_u8, i_cpblk, i_cpobj, i_div, i_div_un, i_dup,