Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
compile_trampoline.cpp
Go to the documentation of this file.
6
7#include <sys/prctl.h>
8#include <signal.h>
9#include <sys/resource.h>
10
11#include "IR/Module.h"
12#include "IR/Validate.h"
13#include "WASM/WASM.h"
14#include "LLVMJIT.h"
15
16using namespace IR;
17
18namespace sysio { namespace chain { namespace eosvmoc {
19
20void run_compile(wrapped_fd&& response_sock, wrapped_fd&& wasm_code) noexcept { //noexcept; we'll just blow up if anything tries to cross this boundry
21 std::vector<uint8_t> wasm = vector_for_memfd(wasm_code);
22
23 //ideally we catch exceptions and sent them upstream as strings for easier reporting
24
26 Serialization::MemoryInputStream stream(wasm.data(), wasm.size());
28 WASM::serialize(stream, module);
29 module.userSections.clear();
31 injector.inject();
32
34
36
37 const std::map<unsigned, uintptr_t>& function_to_offsets = code.function_offsets;
38
39 if(module.startFunctionIndex == UINTPTR_MAX)
40 result_message.start = no_offset{};
41 else if(module.startFunctionIndex < module.functions.imports.size()) {
42 const auto& f = module.functions.imports[module.startFunctionIndex];
43 const intrinsic_entry& ie = get_intrinsic_map().at(f.moduleName + "." + f.exportName);
44 result_message.start = intrinsic_ordinal{ie.ordinal};
45 }
46 else
47 result_message.start = code_offset{function_to_offsets.at(module.startFunctionIndex-module.functions.imports.size())};
48
49 for(const Export& exprt : module.exports) {
50 if(exprt.name == "apply")
51 result_message.apply_offset = function_to_offsets.at(exprt.index-module.functions.imports.size());
52 }
53
54 result_message.starting_memory_pages = -1;
55 if(module.memories.size())
56 result_message.starting_memory_pages = module.memories.defs.at(0).type.size.min;
57
58 std::vector<uint8_t> prologue(module.globals.defs.size() * 8); // Large enough to handle all mutable globals
59 std::vector<uint8_t>::iterator prologue_it = prologue.end();
60
61 //set up mutable globals
62 union global_union {
65 float f32;
66 double f64;
67 };
68
69 for(const GlobalDef& global : module.globals.defs) {
70 if(!global.type.isMutable)
71 continue;
72 prologue_it -= 8;
73 global_union* const u = (global_union* const)&*prologue_it;
74
75 switch(global.initializer.type) {
76 case InitializerExpression::Type::i32_const: u->i32 = global.initializer.i32; break;
77 case InitializerExpression::Type::i64_const: u->i64 = global.initializer.i64; break;
78 case InitializerExpression::Type::f32_const: u->f32 = global.initializer.f32; break;
79 case InitializerExpression::Type::f64_const: u->f64 = global.initializer.f64; break;
80 default: break; //impossible
81 }
82 }
83
84 struct table_entry {
86 int64_t func; //>= 0 means offset to code in wasm; < 0 means intrinsic call at offset address
87 };
88
89 for(const TableSegment& table_segment : module.tableSegments) {
90 struct table_entry* table_index_0 = (struct table_entry*)(code.code.data() + code.table_offset);
91
92 if(static_cast<uint64_t>(table_segment.baseOffset.i32) > module.tables.defs[0].type.size.min)
93 return;
94
95 if(static_cast<uint64_t>(table_segment.baseOffset.i32) > module.tables.defs[0].type.size.min)
96 return;
97
98 for(Uptr i = 0; i < table_segment.indices.size(); ++i) {
99 const Uptr function_index = table_segment.indices[i];
100 const uint64_t effective_table_index = table_segment.baseOffset.i32 + i;
101
102 if(effective_table_index >= module.tables.defs[0].type.size.min)
103 return;
104
105 if(function_index < module.functions.imports.size()) {
106 const auto& f = module.functions.imports[function_index];
107 const intrinsic_entry& ie = get_intrinsic_map().at(f.moduleName + "." + f.exportName);
108 table_index_0[effective_table_index].func = ie.ordinal*-8;
109 table_index_0[effective_table_index].type = (uintptr_t)module.types[module.functions.imports[function_index].type.index];
110 }
111 else {
112 table_index_0[effective_table_index].func = function_to_offsets.at(function_index - module.functions.imports.size());
113 table_index_0[effective_table_index].type = (uintptr_t)module.types[module.functions.defs[function_index - module.functions.imports.size()].type.index];
114 }
115 }
116 }
117
118 //this is somewhat copy pasta from wasm_interface_private, with the asserts removed
119 std::vector<uint8_t> initial_mem;
120 for(const DataSegment& data_segment : module.dataSegments) {
121 const U32 base_offset = data_segment.baseOffset.i32;
122
123 if(base_offset + data_segment.data.size() > initial_mem.size())
124 initial_mem.resize(base_offset + data_segment.data.size(), 0x00);
125 memcpy(initial_mem.data() + base_offset, data_segment.data.data(), data_segment.data.size());
126 }
127
128 result_message.initdata_prologue_size = prologue.end() - prologue_it;
129 std::vector<uint8_t> initdata_prep;
130 std::move(prologue_it, prologue.end(), std::back_inserter(initdata_prep));
131 std::move(initial_mem.begin(), initial_mem.end(), std::back_inserter(initdata_prep));
132
133 std::vector<wrapped_fd> fds_to_send;
134 fds_to_send.emplace_back(memfd_for_bytearray(code.code));
135 fds_to_send.emplace_back(memfd_for_bytearray(initdata_prep));
136 write_message_with_fds(response_sock, result_message, fds_to_send);
137}
138
140 prctl(PR_SET_NAME, "oc-trampoline");
141 prctl(PR_SET_PDEATHSIG, SIGKILL);
142
143 //squelching this for now, but it means we won't have ability to get compile metrics
144 struct sigaction act;
145 sigset_t set;
146 sigemptyset(&set);
147 act.sa_handler = SIG_IGN;
148 act.sa_mask = set;
149 act.sa_flags = SA_NOCLDWAIT;
150 act.sa_sigaction = nullptr;
151 sigaction(SIGCHLD, &act, nullptr);
152
153 while(true) {
154 auto [success, message, fds] = read_message_with_fds(fd);
155 if(!success)
156 break;
157
158 if(!std::holds_alternative<compile_wasm_message>(message) || fds.size() != 2) {
159 std::cerr << "SYS VM OC compile trampoline got unexpected message; ignoring" << std::endl;
160 continue;
161 }
162
163 pid_t pid = fork();
164 if(pid == 0) {
165 prctl(PR_SET_NAME, "oc-compile");
166 prctl(PR_SET_PDEATHSIG, SIGKILL);
167
168 struct rlimit cpu_limits = {20u, 20u};
169 setrlimit(RLIMIT_CPU, &cpu_limits);
170
171 struct rlimit vm_limits = {512u*1024u*1024u, 512u*1024u*1024u};
172 setrlimit(RLIMIT_AS, &vm_limits);
173
174 struct rlimit core_limits = {0u, 0u};
175 setrlimit(RLIMIT_CORE, &core_limits);
176
177 run_compile(std::move(fds[0]), std::move(fds[1]));
178 _exit(0);
179 }
180 else if(pid == -1)
181 std::cerr << "SYS VM OC compile trampoline failed to spawn compile task" << std::endl;
182 }
183
184 _exit(0);
185}
186
187}}}
188
189
uint32_t U32
Definition BasicTypes.h:9
PointerIntHelper< sizeof(size_t)>::UnsignedIntType Uptr
Definition BasicTypes.h:22
void serialize(Stream &stream, U8 &i)
instantiated_code instantiateModule(const IR::Module &module)
Definition LLVMJIT.cpp:278
void run_compile(wrapped_fd &&response_sock, wrapped_fd &&wasm_code) noexcept
std::vector< uint8_t > vector_for_memfd(const wrapped_fd &memfd)
bool write_message_with_fds(boost::asio::local::datagram_protocol::socket &s, const eosvmoc_message &message, const std::vector< wrapped_fd > &fds=std::vector< wrapped_fd >())
std::tuple< bool, eosvmoc_message, std::vector< wrapped_fd > > read_message_with_fds(boost::asio::local::datagram_protocol::socket &s)
const intrinsic_map_t & get_intrinsic_map()
Definition intrinsic.cpp:10
wrapped_fd memfd_for_bytearray(const T &bytes)
_W64 unsigned int uintptr_t
Definition stdint.h:165
signed __int64 int64_t
Definition stdint.h:135
signed int int32_t
Definition stdint.h:123
#define UINTPTR_MAX
Definition stdint.h:227
unsigned __int64 uint64_t
Definition stdint.h:136
std::vector< U8 > data
Definition Module.h:95
InitializerExpression baseOffset
Definition Module.h:94
std::string name
Definition Module.h:85
Uptr index
Definition Module.h:87
std::vector< Uptr > indices
Definition Module.h:103
InitializerExpression baseOffset
Definition Module.h:102
Definition intrinsic.hpp:18
const size_t ordinal
Definition intrinsic.hpp:21
yh_object_type type
Definition yubihsm.h:672
bool set
memcpy((char *) pInfo->slotDescription, s, l)