hexcore-remill 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "hexcore-remill",
3
+ "version": "0.1.1",
4
+ "description": "N-API bindings for Remill — lifts machine code to LLVM IR bitcode",
5
+ "main": "./index.js",
6
+ "module": "./index.mjs",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./index.mjs",
11
+ "require": "./index.js",
12
+ "types": "./index.d.ts"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "install": "prebuild-install -r napi || node-gyp rebuild",
17
+ "build": "node-gyp rebuild",
18
+ "build:debug": "node-gyp rebuild --debug",
19
+ "prebuild": "prebuildify --napi --strip",
20
+ "test": "node test/test.js",
21
+ "clean": "node-gyp clean"
22
+ },
23
+ "keywords": [
24
+ "remill",
25
+ "lifter",
26
+ "llvm",
27
+ "llvm-ir",
28
+ "bitcode",
29
+ "binary-lifting",
30
+ "decompilation",
31
+ "reverse-engineering",
32
+ "binary-analysis",
33
+ "x86",
34
+ "x64",
35
+ "arm",
36
+ "arm64",
37
+ "aarch64",
38
+ "sparc",
39
+ "native",
40
+ "napi",
41
+ "async"
42
+ ],
43
+ "author": "HikariSystem <hikarisystem@example.com>",
44
+ "license": "MIT",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/LXrdKnowkill/hexcore-remill.git"
48
+ },
49
+ "bugs": {
50
+ "url": "https://github.com/LXrdKnowkill/hexcore-remill/issues"
51
+ },
52
+ "homepage": "https://github.com/LXrdKnowkill/hexcore-remill#readme",
53
+ "engines": {
54
+ "vscode": "^1.0.0",
55
+ "node": ">=18.0.0"
56
+ },
57
+ "activationEvents": [],
58
+ "files": [
59
+ "src/",
60
+ "deps/remill/include/",
61
+ "deps/remill/lib/",
62
+ "deps/llvm/include/",
63
+ "deps/llvm/lib/",
64
+ "prebuilds/",
65
+ "binding.gyp",
66
+ "index.js",
67
+ "index.mjs",
68
+ "index.d.ts",
69
+ "README.md"
70
+ ],
71
+ "binary": {
72
+ "napi_versions": [8]
73
+ },
74
+ "devDependencies": {
75
+ "prebuildify": "^6.0.0",
76
+ "prebuild-install": "^7.1.0",
77
+ "node-addon-api": "^7.0.0",
78
+ "node-gyp": "^10.0.0"
79
+ }
80
+ }
package/src/main.cpp ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * HexCore Remill - N-API Entry Point
3
+ * Lifts machine code to LLVM IR bitcode
4
+ *
5
+ * Copyright (c) HikariSystem. All rights reserved.
6
+ * Licensed under MIT License.
7
+ */
8
+
9
+ #include <napi.h>
10
+ #include "remill_wrapper.h"
11
+
12
+ /**
13
+ * Module constants — architecture name aliases for convenience.
14
+ */
15
+ static Napi::Object CreateArchConstants(Napi::Env env) {
16
+ Napi::Object arch = Napi::Object::New(env);
17
+ arch.Set("X86", Napi::String::New(env, "x86"));
18
+ arch.Set("X86_AVX", Napi::String::New(env, "x86_avx"));
19
+ arch.Set("X86_AVX512", Napi::String::New(env, "x86_avx512"));
20
+ arch.Set("AMD64", Napi::String::New(env, "amd64"));
21
+ arch.Set("AMD64_AVX", Napi::String::New(env, "amd64_avx"));
22
+ arch.Set("AMD64_AVX512", Napi::String::New(env, "amd64_avx512"));
23
+ arch.Set("AARCH64", Napi::String::New(env, "aarch64"));
24
+ arch.Set("SPARC32", Napi::String::New(env, "sparc32"));
25
+ arch.Set("SPARC64", Napi::String::New(env, "sparc64"));
26
+ return arch;
27
+ }
28
+
29
+ /**
30
+ * Module constants — OS name aliases.
31
+ */
32
+ static Napi::Object CreateOSConstants(Napi::Env env) {
33
+ Napi::Object os = Napi::Object::New(env);
34
+ os.Set("LINUX", Napi::String::New(env, "linux"));
35
+ os.Set("MACOS", Napi::String::New(env, "macos"));
36
+ os.Set("WINDOWS", Napi::String::New(env, "windows"));
37
+ os.Set("SOLARIS", Napi::String::New(env, "solaris"));
38
+ return os;
39
+ }
40
+
41
+ /**
42
+ * N-API module initialization.
43
+ */
44
+ Napi::Object Init(Napi::Env env, Napi::Object exports) {
45
+ RemillLifter::Init(env, exports);
46
+
47
+ exports.Set("ARCH", CreateArchConstants(env));
48
+ exports.Set("OS", CreateOSConstants(env));
49
+ exports.Set("version", Napi::String::New(env, "0.1.0"));
50
+
51
+ return exports;
52
+ }
53
+
54
+ NODE_API_MODULE(hexcore_remill, Init)
@@ -0,0 +1,481 @@
1
+ /**
2
+ * HexCore Remill - N-API Wrapper Implementation
3
+ * Lifts machine code to LLVM IR bitcode
4
+ *
5
+ * Copyright (c) HikariSystem. All rights reserved.
6
+ * Licensed under MIT License.
7
+ */
8
+
9
+ #include "remill_wrapper.h"
10
+
11
+ // MSVC doesn't define __x86_64__, so Remill's Name.h #error fires.
12
+ // Define REMILL_ARCH before including any Remill headers.
13
+ #if defined(_M_X64) && !defined(REMILL_ARCH)
14
+ #define REMILL_ARCH "amd64_avx"
15
+ #define REMILL_ON_AMD64 1
16
+ #define REMILL_ON_X86 0
17
+ #define REMILL_ON_AARCH64 0
18
+ #define REMILL_ON_AARCH32 0
19
+ #define REMILL_ON_SPARC64 0
20
+ #define REMILL_ON_SPARC32 0
21
+ #define REMILL_ON_PPC 0
22
+ #elif defined(_M_IX86) && !defined(REMILL_ARCH)
23
+ #define REMILL_ARCH "x86"
24
+ #define REMILL_ON_AMD64 0
25
+ #define REMILL_ON_X86 1
26
+ #define REMILL_ON_AARCH64 0
27
+ #define REMILL_ON_AARCH32 0
28
+ #define REMILL_ON_SPARC64 0
29
+ #define REMILL_ON_SPARC32 0
30
+ #define REMILL_ON_PPC 0
31
+ #endif
32
+
33
+ #include <remill/Arch/Arch.h>
34
+ #include <remill/Arch/Name.h>
35
+ #include <remill/BC/IntrinsicTable.h>
36
+ #include <remill/BC/Lifter.h>
37
+ #include <remill/BC/Util.h>
38
+ #include <remill/OS/OS.h>
39
+
40
+ #include <llvm/IR/LLVMContext.h>
41
+ #include <llvm/IR/Module.h>
42
+ #include <llvm/IR/Verifier.h>
43
+ #include <llvm/Support/raw_ostream.h>
44
+ #include <llvm/Transforms/Utils/Cloning.h>
45
+
46
+ #include <sstream>
47
+ #include <filesystem>
48
+
49
+ // Forward-declare Win32 functions to avoid #include <windows.h> which
50
+ // conflicts with Sleigh's CHAR token (ghidra::sleightokentype::CHAR
51
+ // vs winnt.h typedef char CHAR).
52
+ #ifdef _WIN32
53
+ extern "C" {
54
+ __declspec(dllimport) void* __stdcall GetModuleHandleA(const char*);
55
+ __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long);
56
+ }
57
+ #endif
58
+
59
+ // Helper: resolve the semantics directory at runtime.
60
+ //
61
+ // Priority:
62
+ // 1. REMILL_SEMANTICS_DIR env var (explicit override)
63
+ // 2. Relative to the .node binary — works for both layouts:
64
+ // build/Release/hexcore_remill.node (dev build)
65
+ // prebuilds/win32-x64/hexcore_remill.node (packaged)
66
+ // Both are 2 directories below the extension root, so we need
67
+ // 3 parent_path() calls: file → containing dir → intermediate → root.
68
+ // 3. Relative to CWD as last resort (dev convenience)
69
+ // 4. Compile-time __FILE__ path (dev only)
70
+ //
71
+ // On Windows we use GetModuleHandleA("hexcore_remill.node") +
72
+ // GetModuleFileNameA to find the DLL path at runtime.
73
+ static std::filesystem::path GetSemanticsDir() {
74
+ const std::filesystem::path relSem =
75
+ std::filesystem::path("deps") / "remill" / "share" / "semantics";
76
+
77
+ // 1. Environment variable override
78
+ const char* envDir = std::getenv("REMILL_SEMANTICS_DIR");
79
+ if (envDir && envDir[0] != '\0') {
80
+ std::filesystem::path envPath(envDir);
81
+ if (std::filesystem::is_directory(envPath)) {
82
+ return envPath;
83
+ }
84
+ }
85
+
86
+ // 2. Resolve from the .node binary location
87
+ #ifdef _WIN32
88
+ void* hMod = GetModuleHandleA("hexcore_remill.node");
89
+ if (hMod) {
90
+ char dllPath[260] = {0}; // MAX_PATH = 260
91
+ unsigned long len = GetModuleFileNameA(hMod, dllPath, 260);
92
+ if (len > 0 && len < 260) {
93
+ // dllPath example:
94
+ // .../extensions/hexcore-remill/build/Release/hexcore_remill.node
95
+ // .../extensions/hexcore-remill/prebuilds/win32-x64/hexcore_remill.node
96
+ //
97
+ // parent_path(1): removes filename → .../build/Release
98
+ // parent_path(2): removes Release → .../build
99
+ // parent_path(3): removes build → .../hexcore-remill (extension root)
100
+ auto extensionRoot = std::filesystem::path(dllPath)
101
+ .parent_path()
102
+ .parent_path()
103
+ .parent_path();
104
+ auto semDir = extensionRoot / relSem;
105
+ if (std::filesystem::is_directory(semDir)) {
106
+ return semDir;
107
+ }
108
+ }
109
+ }
110
+ #endif
111
+
112
+ // 3. Fallback: relative to CWD (works when running from module root)
113
+ auto cwdSem = std::filesystem::current_path() / relSem;
114
+ if (std::filesystem::is_directory(cwdSem)) {
115
+ return cwdSem;
116
+ }
117
+
118
+ // 4. Last resort: compile-time path (dev only — won't work on other machines)
119
+ std::filesystem::path srcFile(__FILE__);
120
+ return srcFile.parent_path().parent_path() / relSem;
121
+ }
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // RemillLifter
125
+ // ---------------------------------------------------------------------------
126
+
127
+ Napi::Object RemillLifter::Init(Napi::Env env, Napi::Object exports) {
128
+ Napi::Function func = DefineClass(env, "RemillLifter", {
129
+ InstanceMethod("liftBytes", &RemillLifter::LiftBytes),
130
+ InstanceMethod("liftBytesAsync", &RemillLifter::LiftBytesAsync),
131
+ InstanceMethod("getArch", &RemillLifter::GetArch),
132
+ InstanceMethod("close", &RemillLifter::Close),
133
+ InstanceMethod("isOpen", &RemillLifter::IsOpen),
134
+ StaticMethod("getSupportedArchs", &RemillLifter::GetSupportedArchs),
135
+ });
136
+
137
+ Napi::FunctionReference* constructor = new Napi::FunctionReference();
138
+ *constructor = Napi::Persistent(func);
139
+ env.SetInstanceData(constructor);
140
+
141
+ exports.Set("RemillLifter", func);
142
+ return exports;
143
+ }
144
+
145
+ RemillLifter::RemillLifter(const Napi::CallbackInfo& info)
146
+ : Napi::ObjectWrap<RemillLifter>(info) {
147
+
148
+ Napi::Env env = info.Env();
149
+
150
+ if (info.Length() < 1 || !info[0].IsString()) {
151
+ Napi::TypeError::New(env,
152
+ "Expected architecture name string (e.g. 'amd64', 'x86', 'aarch64')")
153
+ .ThrowAsJavaScriptException();
154
+ return;
155
+ }
156
+
157
+ archName_ = info[0].As<Napi::String>().Utf8Value();
158
+
159
+ // Determine OS name — default to linux semantics for lifting
160
+ std::string osName = "linux";
161
+ if (info.Length() >= 2 && info[1].IsString()) {
162
+ osName = info[1].As<Napi::String>().Utf8Value();
163
+ }
164
+
165
+ // Create LLVM context
166
+ context_ = std::make_unique<llvm::LLVMContext>();
167
+
168
+ // Validate architecture name
169
+ auto archEnum = remill::GetArchName(archName_);
170
+ if (archEnum == remill::kArchInvalid) {
171
+ Napi::Error::New(env,
172
+ "Unsupported architecture: " + archName_ +
173
+ ". Use RemillLifter.getSupportedArchs() for valid names.")
174
+ .ThrowAsJavaScriptException();
175
+ return;
176
+ }
177
+
178
+ auto osEnum = remill::GetOSName(osName);
179
+
180
+ // Arch::Get returns unique_ptr<const Arch>
181
+ arch_ = remill::Arch::Get(*context_, osEnum, archEnum);
182
+ if (!arch_) {
183
+ Napi::Error::New(env, "Failed to initialize Remill arch: " + archName_)
184
+ .ThrowAsJavaScriptException();
185
+ return;
186
+ }
187
+
188
+ // Load semantics module (contains instruction implementations as LLVM IR)
189
+ std::vector<std::filesystem::path> semDirs = { GetSemanticsDir() };
190
+ semanticsModule_ = remill::LoadArchSemantics(arch_.get(), semDirs);
191
+ if (!semanticsModule_) {
192
+ Napi::Error::New(env,
193
+ "Failed to load semantics module for arch: " + archName_)
194
+ .ThrowAsJavaScriptException();
195
+ return;
196
+ }
197
+
198
+ // Create intrinsic table from the semantics module
199
+ intrinsics_ = std::make_unique<remill::IntrinsicTable>(semanticsModule_.get());
200
+ }
201
+
202
+ RemillLifter::~RemillLifter() {
203
+ closed_ = true;
204
+ intrinsics_.reset();
205
+ semanticsModule_.reset();
206
+ arch_.reset();
207
+ context_.reset();
208
+ }
209
+
210
+ Napi::Value RemillLifter::LiftBytes(const Napi::CallbackInfo& info) {
211
+ Napi::Env env = info.Env();
212
+
213
+ if (closed_) {
214
+ Napi::Error::New(env, "Lifter is closed").ThrowAsJavaScriptException();
215
+ return env.Undefined();
216
+ }
217
+
218
+ if (info.Length() < 2) {
219
+ Napi::TypeError::New(env, "Expected (buffer, address)")
220
+ .ThrowAsJavaScriptException();
221
+ return env.Undefined();
222
+ }
223
+
224
+ // Get the byte buffer
225
+ const uint8_t* bytes = nullptr;
226
+ size_t length = 0;
227
+
228
+ if (info[0].IsBuffer()) {
229
+ auto buf = info[0].As<Napi::Buffer<uint8_t>>();
230
+ bytes = buf.Data();
231
+ length = buf.Length();
232
+ } else if (info[0].IsTypedArray()) {
233
+ auto arr = info[0].As<Napi::Uint8Array>();
234
+ bytes = arr.Data();
235
+ length = arr.ByteLength();
236
+ } else {
237
+ Napi::TypeError::New(env, "First argument must be Buffer or Uint8Array")
238
+ .ThrowAsJavaScriptException();
239
+ return env.Undefined();
240
+ }
241
+
242
+ // Get the base address
243
+ uint64_t address = 0;
244
+ if (info[1].IsNumber()) {
245
+ address = static_cast<uint64_t>(info[1].As<Napi::Number>().Int64Value());
246
+ } else if (info[1].IsBigInt()) {
247
+ bool lossless = false;
248
+ address = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
249
+ } else {
250
+ Napi::TypeError::New(env, "Second argument must be number or BigInt (address)")
251
+ .ThrowAsJavaScriptException();
252
+ return env.Undefined();
253
+ }
254
+
255
+ LiftResult result = DoLift(bytes, length, address);
256
+ return LiftResultToJS(env, result);
257
+ }
258
+
259
+ Napi::Value RemillLifter::LiftBytesAsync(const Napi::CallbackInfo& info) {
260
+ Napi::Env env = info.Env();
261
+
262
+ if (closed_) {
263
+ Napi::Error::New(env, "Lifter is closed").ThrowAsJavaScriptException();
264
+ return env.Undefined();
265
+ }
266
+
267
+ if (info.Length() < 2) {
268
+ Napi::TypeError::New(env, "Expected (buffer, address)")
269
+ .ThrowAsJavaScriptException();
270
+ return env.Undefined();
271
+ }
272
+
273
+ // Copy bytes into a vector for the worker thread
274
+ std::vector<uint8_t> bytesCopy;
275
+ if (info[0].IsBuffer()) {
276
+ auto buf = info[0].As<Napi::Buffer<uint8_t>>();
277
+ bytesCopy.assign(buf.Data(), buf.Data() + buf.Length());
278
+ } else if (info[0].IsTypedArray()) {
279
+ auto arr = info[0].As<Napi::Uint8Array>();
280
+ bytesCopy.assign(arr.Data(), arr.Data() + arr.ByteLength());
281
+ } else {
282
+ Napi::TypeError::New(env, "First argument must be Buffer or Uint8Array")
283
+ .ThrowAsJavaScriptException();
284
+ return env.Undefined();
285
+ }
286
+
287
+ uint64_t address = 0;
288
+ if (info[1].IsNumber()) {
289
+ address = static_cast<uint64_t>(info[1].As<Napi::Number>().Int64Value());
290
+ } else if (info[1].IsBigInt()) {
291
+ bool lossless = false;
292
+ address = info[1].As<Napi::BigInt>().Uint64Value(&lossless);
293
+ }
294
+
295
+ auto* worker = new LiftBytesWorker(env, this, std::move(bytesCopy), address);
296
+ auto promise = worker->GetDeferred().Promise();
297
+ worker->Queue();
298
+ return promise;
299
+ }
300
+
301
+ Napi::Value RemillLifter::GetArch(const Napi::CallbackInfo& info) {
302
+ return Napi::String::New(info.Env(), archName_);
303
+ }
304
+
305
+ Napi::Value RemillLifter::GetSupportedArchs(const Napi::CallbackInfo& info) {
306
+ Napi::Env env = info.Env();
307
+ Napi::Array result = Napi::Array::New(env);
308
+
309
+ const char* archs[] = {
310
+ "x86", "x86_avx", "x86_avx512",
311
+ "amd64", "amd64_avx", "amd64_avx512",
312
+ "aarch64",
313
+ "sparc32", "sparc64",
314
+ nullptr
315
+ };
316
+
317
+ uint32_t idx = 0;
318
+ for (const char** p = archs; *p; ++p) {
319
+ result.Set(idx++, Napi::String::New(env, *p));
320
+ }
321
+
322
+ return result;
323
+ }
324
+
325
+ Napi::Value RemillLifter::Close(const Napi::CallbackInfo& info) {
326
+ if (!closed_) {
327
+ closed_ = true;
328
+ intrinsics_.reset();
329
+ semanticsModule_.reset();
330
+ arch_.reset();
331
+ context_.reset();
332
+ }
333
+ return info.Env().Undefined();
334
+ }
335
+
336
+ Napi::Value RemillLifter::IsOpen(const Napi::CallbackInfo& info) {
337
+ return Napi::Boolean::New(info.Env(), !closed_);
338
+ }
339
+
340
+ // ---------------------------------------------------------------------------
341
+ // Internal: DoLift
342
+ // ---------------------------------------------------------------------------
343
+
344
+ LiftResult RemillLifter::DoLift(
345
+ const uint8_t* bytes, size_t length, uint64_t address) {
346
+
347
+ LiftResult result;
348
+ result.address = address;
349
+ result.bytesConsumed = 0;
350
+ result.success = false;
351
+
352
+ if (!arch_ || !semanticsModule_ || !intrinsics_) {
353
+ result.error = "Lifter not properly initialized";
354
+ return result;
355
+ }
356
+
357
+ if (length == 0) {
358
+ result.error = "Empty buffer";
359
+ return result;
360
+ }
361
+
362
+ // Clone the cached semantics module instead of reloading from disk.
363
+ // LoadArchSemantics reads ~50MB of .bc per call; CloneModule copies the
364
+ // in-memory IR in microseconds with zero disk I/O.
365
+ auto liftModule = llvm::CloneModule(*semanticsModule_);
366
+ if (!liftModule) {
367
+ result.error = "Failed to clone semantics module";
368
+ return result;
369
+ }
370
+
371
+ auto intrinsics = std::make_unique<remill::IntrinsicTable>(liftModule.get());
372
+
373
+ // DefaultLifter returns shared_ptr<OperandLifter>
374
+ auto lifter = arch_->DefaultLifter(*intrinsics);
375
+
376
+ // DefaultLifter always returns an InstructionLifterIntf-derived object.
377
+ // Use static_pointer_cast (dynamic_pointer_cast requires RTTI which is
378
+ // disabled by NAPI_DISABLE_CPP_EXCEPTIONS).
379
+ auto instLifter = std::static_pointer_cast<remill::InstructionLifterIntf>(lifter);
380
+
381
+ // Decode and lift each instruction
382
+ remill::Instruction inst;
383
+ uint64_t pc = address;
384
+ size_t offset = 0;
385
+
386
+ // Create a lifted function to hold the instructions
387
+ auto func = arch_->DeclareLiftedFunction(
388
+ "lifted_" + std::to_string(address), liftModule.get());
389
+ arch_->InitializeEmptyLiftedFunction(func);
390
+
391
+ auto block = &func->getEntryBlock();
392
+
393
+ // Create initial decoding context for this architecture
394
+ auto context = arch_->CreateInitialContext();
395
+
396
+ while (offset < length) {
397
+ std::string_view instrBytes(
398
+ reinterpret_cast<const char*>(bytes + offset), length - offset);
399
+
400
+ // DecodeInstruction requires DecodingContext as 4th parameter
401
+ if (!arch_->DecodeInstruction(pc, instrBytes, inst, context)) {
402
+ if (offset == 0) {
403
+ result.error = "Failed to decode instruction at 0x" +
404
+ std::to_string(address);
405
+ return result;
406
+ }
407
+ break; // Stop at first undecoded instruction
408
+ }
409
+
410
+ // LiftIntoBlock with 2-arg overload (inst, block)
411
+ auto status = instLifter->LiftIntoBlock(inst, block, false);
412
+ if (status != remill::kLiftedInstruction) {
413
+ if (offset == 0) {
414
+ result.error = "Failed to lift instruction at 0x" +
415
+ std::to_string(pc);
416
+ return result;
417
+ }
418
+ break;
419
+ }
420
+
421
+ offset += inst.bytes.size();
422
+ pc += inst.bytes.size();
423
+ }
424
+
425
+ result.bytesConsumed = offset;
426
+
427
+ // Print the function IR to string
428
+ std::string irStr;
429
+ llvm::raw_string_ostream os(irStr);
430
+ func->print(os);
431
+ os.flush();
432
+
433
+ result.ir = irStr;
434
+ result.success = true;
435
+ return result;
436
+ }
437
+
438
+ Napi::Object RemillLifter::LiftResultToJS(
439
+ Napi::Env env, const LiftResult& result) {
440
+
441
+ Napi::Object obj = Napi::Object::New(env);
442
+ obj.Set("success", Napi::Boolean::New(env, result.success));
443
+ obj.Set("ir", Napi::String::New(env, result.ir));
444
+ obj.Set("error", Napi::String::New(env, result.error));
445
+ obj.Set("address", Napi::Number::New(env,
446
+ static_cast<double>(result.address)));
447
+ obj.Set("bytesConsumed", Napi::Number::New(env,
448
+ static_cast<double>(result.bytesConsumed)));
449
+ return obj;
450
+ }
451
+
452
+ // ---------------------------------------------------------------------------
453
+ // LiftBytesWorker
454
+ // ---------------------------------------------------------------------------
455
+
456
+ LiftBytesWorker::LiftBytesWorker(
457
+ Napi::Env env,
458
+ RemillLifter* lifter,
459
+ std::vector<uint8_t> bytes,
460
+ uint64_t address)
461
+ : Napi::AsyncWorker(env),
462
+ lifter_(lifter),
463
+ bytes_(std::move(bytes)),
464
+ address_(address),
465
+ deferred_(Napi::Promise::Deferred::New(env)) {}
466
+
467
+ void LiftBytesWorker::Execute() {
468
+ result_ = lifter_->DoLift(bytes_.data(), bytes_.size(), address_);
469
+ if (!result_.success) {
470
+ SetError(result_.error);
471
+ }
472
+ }
473
+
474
+ void LiftBytesWorker::OnOK() {
475
+ Napi::Env env = Env();
476
+ deferred_.Resolve(lifter_->LiftResultToJS(env, result_));
477
+ }
478
+
479
+ void LiftBytesWorker::OnError(const Napi::Error& error) {
480
+ deferred_.Reject(error.Value());
481
+ }