-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain-jit-step-1.cpp
More file actions
115 lines (86 loc) · 3.49 KB
/
Copy pathmain-jit-step-1.cpp
File metadata and controls
115 lines (86 loc) · 3.49 KB
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
// THIS IS STEP #1
/*
Project description.
====================
Overall goal: JIT and run assembler fragment in figure (A) on x86-64 (post 2013 architectures, i.e. Core iX 6XXX and better).
Use only the Linux API - assure POSIX compliance whenever possible, prefer C++/C standard library fuctions over OS if feasible.
Use the official Intel/AMD Manuals, the relevant System V ABI and man pages, an occasional peek into https://cppreference.com/ is also allowed.
!! NEVER apply AI !!
References:
[1] Intel 64 and IA-32 Architectures Software Developer's Manual (June 2026 version), relevant here is especially the
[2] https://wiki.osdev.org/System_V_ABI (System V ABI Application Binary Interface)
(A) The assembler fragment to be compiled and its semantics.
============================================================
kind Event;
OblectamentaDataLabel counter; // section .dara
oblectamenta{global{data{counter;0;};};}; // counter dd 0
sm{
S;
states{Initial;Final;};
t{Initial;Final;doIncrementCounter;};
Actions{
doIncrementCounter{
oblectamenta{text{asm{
OblectamentaCodeLabel start_loop, end_loop; // labels
val i = R0; // use 'i' to reference 64 bit wide register R0
ldi32(counter); // Put value at location counter on the compute stack
sti32(i); // Store 32 bit wide value at top of compute stack to i aka R0
// ==> MOV R0,counter
start_loop;
ldi32(i);
ldi32(10000000);
blteq(end_loop);
ldi32(i);
ldi32(1);
addi32;
sti32(i);
buc(start_loop);
end_loop;
};};};
};
};
};
Simulation{
Start{S;};
};
How to build and run
====================
c++ -std=C++2x -Wall -Wextra -fPIC main-jit-step-1.cpp -o main-jit-step-1 && ./main-jit-step-1
*/
// Step #1: request a chunk of memory big enough to hold a RET instruction, i.e. 1 byte, which is executable.
// Write a ret opcode at the very start of the chunk, jump to the instruction.
// Expectation: Successful return from the jump
#include <iostream>
#include <sys/mman.h>
using gen_ret_t = int;
namespace x86_64{
gen_ret_t opcode_ret(char* text){
// RET - Return From Procedure.
// Near return to calling procedure ([1] Vol. 2B Chapter 4, p.569)
*text = 0xc3; return 1;
}
void jump_to_program(char* text_seg);
}
void x86_64::jump_to_program(char* text_seg){
// We have to cast code_frag to void(*)(), i.e. function pointer to void(), and call it: ( (void(*)() )(code_frag))()
((void(*)())(text_seg))();
}
void create_ret_fragment_and_execute(bool debug_info = true){
using namespace x86_64;
void * code_frag_raw = mmap(0, 128, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1,0) ;
// call memory map sys service, request 128 byte of writable, readable, executable private shared, anonymous, not file
// backed memory
if (code_frag_raw == MAP_FAILED){
std::cerr << "***Error: mmap()\n";return;
}
auto code_frag = (char *)code_frag_raw;
opcode_ret(code_frag); // write ret opcode
if(debug_info) std::cerr << "Jump in\n";
jump_to_program(code_frag);
if(debug_info)std::cerr << "Jump out\n";
}
int main([[maybe_unused]]int argc, [[maybe_unused]]char ** argv){
constexpr auto print_debug_messages{false};
create_ret_fragment_and_execute(print_debug_messages);
return 0;
}