forked from bluecataudio/AngelScript-JIT-Compiler
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvirtual_asm.h
574 lines (462 loc) · 16.3 KB
/
virtual_asm.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
#pragma once
#include <stddef.h>
#include <stdio.h>
namespace assembler {
typedef unsigned char byte;
struct Register;
struct MemAddress;
enum RegCode : byte {
EAX = 0,
ECX = 1,
EDX = 2,
EBX = 3,
ESP = 4, SIB = 4,
EBP = 5, ADDR = 5,
ESI = 6,
EDI = 7,
R8 = 8,
R9 = 9,
R10 = 10,
R12 = 12,
R13 = 13,
R14 = 14,
R15 = 15,
//R11 is supervolatile. Can change in between
//virtual asm ops (used as a temporary), so be careful
//with using it.
R11 = 11,
//XMM registers have unique numbers so we can recognize them
XMM0 = 16,
XMM1 = 17,
XMM2 = 18,
XMM3 = 19,
XMM4 = 20,
XMM5 = 21,
XMM6 = 22,
XMM7 = 23,
XMM8 = 24,
XMM9 = 25,
XMM10 = 26,
XMM11 = 27,
XMM12 = 28,
XMM13 = 29,
XMM14 = 30,
XMM15 = 31,
NONE = 0,
};
//Floating Point Register codes representing the stack registers on the FPU
// The top of the stack is always FPU_0
enum FloatReg : byte {
FPU_0 = 0,
FPU_1 = 1,
FPU_2 = 2,
FPU_3 = 3,
FPU_4 = 4,
FPU_5 = 5,
FPU_6 = 6,
FPU_7 = 7,
};
enum JumpType {
Overflow,
NotOverflow,
Below, Carry = Below,
NotBelow, NotCarry = NotBelow,
Equal, Zero = Equal,
NotEqual, NotZero = NotEqual,
NotAbove,
Above,
Sign,
NotSign,
Parity,
NotParity,
Less,
GreaterOrEqual,
LessOrEqual,
Greater,
Jump,
JumpTypeCount
};
//Handles thread safety for the JIT
struct CriticalSection {
void* pLock;
void enter();
void leave();
CriticalSection();
~CriticalSection();
};
struct AddrPrefix {
MemAddress& adr;
bool defLong;
unsigned char further;
AddrPrefix(MemAddress& Adr, bool DefLong, unsigned char Further)
: adr(Adr), defLong(DefLong), further(Further) {
}
};
struct RegPrefix {
Register& reg;
unsigned short other;
bool defLong;
RegPrefix(Register& Reg, unsigned short Other, bool DefLong)
: reg(Reg), other(Other), defLong(DefLong) {
}
};
//Stores information about the code page
// Generates an executable page in memory when created
// Deletes the asssociated page when deleted
//Implementation in virtual_asm_<operating system>.cpp (e.g. virtual_asm_windows.cpp)
struct CodePage {
void* page;
unsigned int size, used, references;
bool final;
CodePage(unsigned int Size, void* requestedStart = 0);
~CodePage();
void grab();
void drop();
//Call finalize when done writing to the code page to guarantee that it can be executed
//No more writing may be done to the allocated pages
void finalize();
//Returns the pointer to the first currently unused chunk of the page
template<class T>
T getFunctionPointer() {
return reinterpret_cast<T>((byte*)page+used);
}
byte* getActivePage() const {
return (byte*)page+used;
}
//Marks bytes as used;
//future calls to getFunctionPointer() will not reference the location that is being marked as used
void markBytesUsed(unsigned int count) {
used += count;
}
//Marks bytes up to <address> as used
void markUsedAddress(void* address) {
unsigned newUsed = (unsigned)((byte*)address - (byte*)page);
if(newUsed > used && newUsed <= size)
used = newUsed;
}
//Returns the number of bytes not yet allocated to a function
unsigned int getFreeSize() const {
return size-used;
}
//Returns the smallest page (in bytes) that can be allocated by a code page (Sizes other than multiples of this size allocate an extra page)
static unsigned int getMinimumPageSize();
private:
CodePage() {}
};
//Stores the code pointer and provides access to various processor-level operations
// To work with the processor, create a set of 'Register' instances, each taking the RegCode of the associated register (e.g. Register eax(cpu, EAX))
//Implementation in virtual_asm_<processor instruction set>.cpp (e.g. virtual_asm_x86.cpp)
struct Processor {
//Pointer to the location for the next opcode
byte* op;
byte* pageStart;
//The current mode of operation, in bits
// e.g. 32 bits for x86, indicating that operations should treat addresses as if they were unsigned integers
unsigned bitMode, lastBitMode;
//The number of bytes currently on the stack that we are responsible for
unsigned stackDepth;
//Reserved jump space
unsigned jumpSpace;
byte* jumpPtr;
//Initializes the processor to point to the active page of the code page
//Optionally takes a bitMode override (defaults to the same bitMode as the exe)
Processor(CodePage& codePage, unsigned defaultBitMode = sizeof(void*)*8 );
//Creates a jump to the new code page, and marks the current address as used on the old code page
//Updates output pointer to the new code page's active page
void migrate(CodePage& prevPage, CodePage& newPage);
//Changes the current bitMode, and stores the previous bitMode
void setBitMode(unsigned bits) {
lastBitMode = bitMode;
bitMode = bits;
}
//Restores the previous bitMode
void resetBitMode() {
bitMode = lastBitMode;
}
//Returns the alignment of the stack (number of bytes a push increments esp)
static unsigned pushSize();
//Pushes data to the opcode output
template<class T>
Processor& operator<<(T b) {
*(T*)op = b; op += sizeof(T);
return *this;
}
//Pushes bytes representing a memory address to the opcode output
template<class T>
Processor& operator<<(MemAddress addr);
//Pushes bytes representing a prefix
template<class T>
Processor& operator<<(AddrPrefix pr);
template<class T>
Processor& operator<<(RegPrefix pr);
//Calls the function, passing the arguments specified by 'args'
//args is a string like "rrcmrm" which specifies arguments as sourced by a Register*, MemAddres*, or a constant
//EBP is invalid during the call
void call_cdecl(void* func, const char* args, va_list ap);
void call_cdecl(void* func, const char* args, ...);
//Use call() in between these to set up a call with an arbitrary function
unsigned call_cdecl_args(const char* args, ...);
unsigned call_cdecl_args(const char* args, va_list ap);
unsigned call_thiscall_args(Register* obj, const char* args, ...);
unsigned call_thiscall_args(Register* obj, const char* args, va_list ap);
//Prepares for a call to manual call to a cdecl function (Do not use with call_cdecl)
// Use before pushing arguments
// Invalidates EBP until call_cdecl_end()
void call_cdecl_prep(unsigned argBytes);
//Ends a manual call to a cdecl function (Do not use with call_cdecl)
// Use after returning from the function
void call_cdecl_end(unsigned argBytes, bool returnPointer = false);
//Note: stdcall is like cdecl, but does not use cdecl_end
//Calls the function, passing the arguments specified by 'args'
//args is a string like "rrcmrm" which specifies arguments as sourced by a Register*, MemAddres*, or a constant
//EBP is invalid during the call
void call_stdcall(void* func, const char* args, ...);
//To call a thiscall:
// cpu.call_thiscall_prep(total argument size)
// cpu.push(arguments)
// cpu.call_thiscall_this(source of 'this' pointer)
// cpu.call(function)
// cpu.call_thiscall_end(total argument size)
void call_thiscall_prep(unsigned argBytes);
void call_thiscall_this(MemAddress address);
void call_thiscall_this(Register& reg);
void call_thiscall_this_mem(MemAddress address, Register& memreg);
void call_thiscall_this_mem(Register& reg, Register& memreg);
void call_thiscall_end(unsigned argBytes, bool returnPointer = false);
//Calls a function (push code pointer, jump to function)
void call(Register& reg);
void call(void* func);
//Pushes a constant value onto the stack (Pushes are always pushSize() large, values beyond this size are an error)
void push(size_t value);
//Pops <count> times (Pops are always pushSize() large)
void pop(unsigned int count);
//Pushes the value of <reg> onto the stack
void push(Register& reg);
//Pops the alue of <reg> from the stack
void pop(Register& reg);
//Get a register corresponding to an argument on 64-bit calling convention
unsigned maxIntArgs64();
unsigned maxFloatArgs64();
bool isIntArg64Register(unsigned char number, unsigned char arg);
bool isFloatArg64Register(unsigned char number, unsigned char arg);
Register intArg64(unsigned char number, unsigned char arg);
Register floatArg64(unsigned char number, unsigned char arg);
Register intArg64(unsigned char number, unsigned char arg, Register defaultReg);
Register floatArg64(unsigned char number, unsigned char arg, Register defaultReg);
Register floatReturn64();
Register intReturn64();
//Pushes the memory at <address> onto the stack (Pushes are always pushSize() large, pushing larger values invokes multiple pushes)
void push(MemAddress address);
//Pops the value on the stack to the memory at <address> (Pops are always pushSize() large, popping larger values invokes multiple pops)
void pop(MemAddress address);
//Prepares a short jump (fewer than approx. 120 bytes in either direction)
// Pass the return to a matching end_short_jump
void* prep_short_jump(JumpType type);
//Ends a short jump
void end_short_jump(void* p);
//Prepares a large jump (can jump to any location)
// Pass the return to a matching end_long_jump
void* prep_long_jump(JumpType type);
//Ends a large jump
void end_long_jump(void* p);
//Jumps to <dest>
void jump(JumpType type, volatile byte* dest);
//Jumps to the address in <reg>
void jump(Register& reg);
//Decrements ecx and jumps if it becomes 0; Optionally conditionally jumps based on a Zero/NotZero test
void loop(volatile byte* dest, JumpType type = Jump);
//Copies from *esi to *edi, and adjusts them both by the data size according to the direction flag
void string_copy(unsigned size);
//Sets direction flag for string copy
void setDirFlag(bool forward);
//Returns from a function (pop code pointer, jump there)
void ret();
//Triggers a debug break
void debug_interrupt();
private:
Processor() {}
};
//Provides access to the floating point unit's state
//Implementation in virtual_asm_<processor instruction set>.cpp (e.g. virtual_asm_x86.cpp)
struct FloatingPointUnit {
Processor& cpu;
FloatingPointUnit(Processor& CPU);
//Clears the FPU's state and registers
void init();
//Negates FPU_0
void negate();
//Pushes
void load_const_0();
void load_const_1();
//FPU_1 becomes FPU_0 (Pops the fpu stack)
void pop();
//Exchanges contents of FPU_n and FPU_0
void exchange(FloatReg floatReg);
//Compares FPU_0 to floatReg, setting the CPU's flags according to the values' relation
// Optionally pops the fpu stack
void compare_toCPU(FloatReg floatReg, bool pop = true);
//Pushes the specified data type stored at <address> onto the FPU stack (becomes FPU_0)
void load_float(MemAddress address);
void load_dword(MemAddress address);
void load_qword(MemAddress address);
void load_double(MemAddress address);
//Stores the value on FPU_0 to <address> according to the data type
// Optionally pops the fpu stack
void store_float(MemAddress address, bool pop = true);
void store_dword(MemAddress address, bool pop = true);
void store_double(MemAddress address, bool pop = true);
//Control words
void store_control_word(MemAddress address);
void load_control_word(MemAddress address);
//Effect: FPU_0 -= <reg>
void operator-=(FloatReg reg);
//Effect: FPU_0 += *(float*)address
void add_float(MemAddress address);
//Effect: FPU_0 -= *(float*)address
void sub_float(MemAddress address);
//Effect: FPU_0 *= *(float*)address
void mult_float(MemAddress address);
//Effect: FPU_0 /= *(float*)address
void div_float(MemAddress address);
//Effect: FPU_0 += *(double*)address
void add_double(MemAddress address);
void add_double(FloatReg reg, bool pop = true);
//Effect: FPU_0 -= *(double*)address
// If Reversed: FPU_0 = *(double*)address - FPU_0
void sub_double(MemAddress address, bool reversed = false);
void sub_double(FloatReg reg, bool reversed = false, bool pop = true);
//Effect: FPU_0 *= *(double*)address
void mult_double(MemAddress address);
void mult_double(FloatReg reg, bool pop = true);
//Effect: FPU_0 /= *(double*)address
// If Reversed: FPU_0 = *(double*)address / FPU_0
void div_double(MemAddress address, bool reversed = false);
void div_double(FloatReg reg, bool reversed = false, bool pop = true);
};
//Temporary struct that represents an addition to a memory address, with optional scaling
struct ScaledIndex {
RegCode reg;
unsigned char scaleFactor;
ScaledIndex(RegCode Reg, unsigned char Scale) : reg(Reg), scaleFactor(Scale) {}
};
//Temporary struct that stores data necessary for memory access
// Provides operations that can be performed on a memory address
//Implementation in virtual_asm_<processor instruction set>.cpp (e.g. virtual_asm_x86.cpp)
struct MemAddress {
Processor& cpu;
void* absolute_address;
int offset;
unsigned bitMode;
RegCode code;
RegCode scaleReg;
unsigned char other;
unsigned char scaleFactor;
bool Float;
bool Signed;
MemAddress(Processor& CPU, void* address);
MemAddress(Processor& CPU, RegCode Code);
MemAddress(Processor& CPU, RegCode Code, int Offset);
MemAddress operator+(ScaledIndex scale);
MemAddress operator+(int Offset);
MemAddress operator-(int Offset);
void operator++();
void operator--();
void operator-();
void operator~();
void operator+=(unsigned int amount);
void operator-=(unsigned int amount);
void operator=(unsigned int value);
void operator=(void* pointer);
void operator=(Register fromReg);
void operator&=(unsigned int value);
void operator|=(unsigned int value);
//Copies memory using an intermediate register
void direct_copy(MemAddress address, Register& intermediate);
AddrPrefix prefix(unsigned char further = 0, bool defLong = false);
};
//Converts a MemAddress from the default unsigned <cpu bit mode> to match the passed type
template<class T>
MemAddress as(MemAddress addr) {
addr.bitMode = sizeof(T) * 8;
addr.Signed = (T)-1 < (T)0;
return addr;
}
template<>
MemAddress as<float>(MemAddress addr);
template<>
MemAddress as<double>(MemAddress addr);
//Structure that provides operations that can be performed on a register
// Also provides the means to generate MemAddresses relative to a register via dereference (e.g. *eax+8)
//Implementation in virtual_asm_<processor instruction set>.cpp (e.g. virtual_asm_x86.cpp)
struct Register {
Processor& cpu;
RegCode code;
unsigned bitMode;
Register(Processor& CPU, RegCode Code);
Register(Processor& CPU, RegCode Code, unsigned BitModeOverride);
void set_regCode(Register& other) {
code = other.code;
bitMode = other.bitMode;
}
unsigned getBitMode() const;
unsigned getBitMode(const MemAddress& addr) const;
MemAddress operator*() const;
ScaledIndex operator*(unsigned char scale) const;
//Loads the address pointed to by <address> into this register
void copy_address(MemAddress address);
void swap(MemAddress address);
void swap(Register& other);
void operator<<=(Register& other);
void operator>>=(Register& other);
void rightshift_logical(Register& other);
void operator+=(unsigned int amount);
void operator+=(MemAddress address);
void operator+=(Register& other);
void operator-=(unsigned int amount);
void operator-=(Register& other);
void operator-=(MemAddress address);
void operator*=(MemAddress address);
void operator-();
void operator~();
void operator--();
void operator++();
void operator&=(unsigned long long mask);
void operator&=(MemAddress address);
void operator&=(Register other);
void operator^=(MemAddress address);
void operator^=(Register& other);
void operator|=(MemAddress address);
void operator|=(unsigned long long mask);
//Copies a smaller data type, retaining the sign
void copy_expanding(MemAddress address);
//Copies an 8 bit register, leaving 0s in higher bytes
void copy_zeroing(Register& other);
void operator=(unsigned long long value);
void operator=(void* pointer);
void operator=(Register other);
void operator=(MemAddress addr);
void operator==(Register other);
void operator==(MemAddress addr);
void operator==(unsigned int test);
void setIf(JumpType condition);
void* setDeferred(unsigned long long def = 0);
bool xmm();
bool extended();
RegCode index();
RegPrefix prefix(unsigned short other = 0, bool defaultLong = false);
RegPrefix prefix(Register& other, bool defaultLong = false);
unsigned char modrm(unsigned short other);
unsigned char modrm(Register& other);
//Multiplies *address with value, stores the result in this register
void multiply_signed(MemAddress address, int value);
//Divides {eax,edx} by this register; result in eax, remainder in edx
void divide();
void divide_signed();
};
//Converts a MemAddress from the default unsigned <cpu bit mode> to match the passed type
template<class T>
Register as(Register reg) {
reg.bitMode = sizeof(T) * 8;
return reg;
}
};