| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent d926ddc commit 9b5642c
13 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -68,6 +68,7 @@ const { | |||
| 68 | 68 | } = require('internal/ffi-shared-buffer'); | |
| 69 | 69 | ||
| 70 | 70 | const { | |
| 71 | + markFastLibraryClosed, | ||
| 71 | 72 | wrapWithRawPointerConversions, | |
| 72 | 73 | } = require('internal/ffi/fast-api'); | |
| 73 | 74 | ||
@@ -100,6 +101,27 @@ function wrapFFIFunction(rawFn, owner) { | |||
| 100 | 101 | ||
| 101 | 102 | const rawGetFunction = DynamicLibrary.prototype.getFunction; | |
| 102 | 103 | const rawGetFunctions = DynamicLibrary.prototype.getFunctions; | |
| 104 | + const rawClose = DynamicLibrary.prototype.close; | ||
| 105 | + | ||
| 106 | + function close() { | ||
| 107 | + const result = FunctionPrototypeCall(rawClose, this); | ||
| 108 | + markFastLibraryClosed(this); | ||
| 109 | + return result; | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + ObjectDefineProperty(DynamicLibrary.prototype, 'close', { | ||
| 113 | + __proto__: null, | ||
| 114 | + configurable: true, | ||
| 115 | + value: close, | ||
| 116 | + writable: true, | ||
| 117 | + }); | ||
| 118 | + | ||
| 119 | + ObjectDefineProperty(DynamicLibrary.prototype, SymbolDispose, { | ||
| 120 | + __proto__: null, | ||
| 121 | + configurable: true, | ||
| 122 | + value: close, | ||
| 123 | + writable: true, | ||
| 124 | + }); | ||
| 103 | 125 | ||
| 104 | 126 | DynamicLibrary.prototype.getFunction = function getFunction(name, signature) { | |
| 105 | 127 | const raw = FunctionPrototypeCall(rawGetFunction, this, name, signature); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1232,6 +1232,7 @@ E('ERR_FEATURE_UNAVAILABLE_ON_PLATFORM', | |||
| 1232 | 1232 | 'The feature %s is unavailable on the current platform' + | |
| 1233 | 1233 | ', which is being used to run Node.js', | |
| 1234 | 1234 | TypeError); | |
| 1235 | + E('ERR_FFI_LIBRARY_CLOSED', 'Library is closed', Error); | ||
| 1235 | 1236 | E('ERR_FS_CP_DIR_TO_NON_DIR', | |
| 1236 | 1237 | 'Cannot overwrite non-directory with directory', SystemError); | |
| 1237 | 1238 | E('ERR_FS_CP_EEXIST', 'Target already exists', SystemError); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,6 +5,7 @@ const { | |||
| 5 | 5 | NumberIsInteger, | |
| 6 | 6 | ObjectDefineProperty, | |
| 7 | 7 | ReflectApply, | |
| 8 | + SafeWeakMap, | ||
| 8 | 9 | StringPrototypeIncludes, | |
| 9 | 10 | TypeError, | |
| 10 | 11 | } = primordials; | |
@@ -25,9 +26,16 @@ const { | |||
| 25 | 26 | kFastBufferInvoke, | |
| 26 | 27 | } = internalBinding('ffi'); | |
| 27 | 28 | ||
| 29 | + const { | ||
| 30 | + codes: { | ||
| 31 | + ERR_FFI_LIBRARY_CLOSED, | ||
| 32 | + }, | ||
| 33 | + } = require('internal/errors'); | ||
| 34 | + | ||
| 28 | 35 | const U64_MAX = 0xFFFFFFFFFFFFFFFFn; | |
| 29 | 36 | const I64_MAX = 0x7FFFFFFFFFFFFFFFn; | |
| 30 | 37 | const I64_MIN = -0x8000000000000000n; | |
| 38 | + const fastLibraryStates = new SafeWeakMap(); | ||
| 31 | 39 | ||
| 32 | 40 | // These ranges mirror ToFFIArgument in src/ffi/types.cc. V8's Fast API | |
| 33 | 41 | // exposes narrow integers as 32-bit values and uses truncating BigInt | |
@@ -202,7 +210,20 @@ function inheritMetadata(wrapper, rawFn, nargs) { | |||
| 202 | 210 | return wrapper; | |
| 203 | 211 | } | |
| 204 | 212 | ||
| 205 | - function wrapWithRawPointerConversions(rawFn, argumentTypes, _owner) { | ||
| 213 | + function markFastLibraryClosed(owner) { | ||
| 214 | + const state = fastLibraryStates.get(owner); | ||
| 215 | + if (state !== undefined) { | ||
| 216 | + state.closed = true; | ||
| 217 | + } | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + function throwIfFastLibraryClosed(state) { | ||
| 221 | + if (state.closed) { | ||
| 222 | + throw new ERR_FFI_LIBRARY_CLOSED(); | ||
| 223 | + } | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + function wrapWithRawPointerConversions(rawFn, argumentTypes, owner) { | ||
| 206 | 227 | if (rawFn === undefined || rawFn === null) { | |
| 207 | 228 | return rawFn; | |
| 208 | 229 | } | |
@@ -213,11 +234,14 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, _owner) { | |||
| 213 | 234 | return rawFn; | |
| 214 | 235 | } | |
| 215 | 236 | ||
| 216 | - const indexes = getFastArgumentIndexes(argumentTypes); | ||
| 217 | - if (indexes === null) { | ||
| 218 | - return rawFn; | ||
| 237 | + let state = fastLibraryStates.get(owner); | ||
| 238 | + if (state === undefined) { | ||
| 239 | + state = { __proto__: null, closed: false }; | ||
| 240 | + fastLibraryStates.set(owner, state); | ||
| 219 | 241 | } | |
| 220 | 242 | ||
| 243 | + const indexes = getFastArgumentIndexes(argumentTypes) ?? []; | ||
| 244 | + | ||
| 221 | 245 | const stringState = { | |
| 222 | 246 | __proto__: null, | |
| 223 | 247 | buffers: [], | |
@@ -233,6 +257,7 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, _owner) { | |||
| 233 | 257 | const fastBufferInvoke = needsPointerLikeConversion(t0) ? | |
| 234 | 258 | rawFn[kFastBufferInvoke] : undefined; | |
| 235 | 259 | wrapper = function(a0) { | |
| 260 | + throwIfFastLibraryClosed(state); | ||
| 236 | 261 | if (arguments.length !== 1) { | |
| 237 | 262 | throwFFIArgCountError(1, arguments.length); | |
| 238 | 263 | } | |
@@ -262,6 +287,7 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, _owner) { | |||
| 262 | 287 | const t0 = argumentTypes[0]; | |
| 263 | 288 | const t1 = argumentTypes[1]; | |
| 264 | 289 | wrapper = function(a0, a1) { | |
| 290 | + throwIfFastLibraryClosed(state); | ||
| 265 | 291 | if (arguments.length !== 2) { | |
| 266 | 292 | throwFFIArgCountError(2, arguments.length); | |
| 267 | 293 | } | |
@@ -283,6 +309,7 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, _owner) { | |||
| 283 | 309 | const t1 = argumentTypes[1]; | |
| 284 | 310 | const t2 = argumentTypes[2]; | |
| 285 | 311 | wrapper = function(a0, a1, a2) { | |
| 312 | + throwIfFastLibraryClosed(state); | ||
| 286 | 313 | if (arguments.length !== 3) { | |
| 287 | 314 | throwFFIArgCountError(3, arguments.length); | |
| 288 | 315 | } | |
@@ -300,6 +327,7 @@ function wrapWithRawPointerConversions(rawFn, argumentTypes, _owner) { | |||
| 300 | 327 | }; | |
| 301 | 328 | } else { | |
| 302 | 329 | wrapper = function(...args) { | |
| 330 | + throwIfFastLibraryClosed(state); | ||
| 303 | 331 | if (args.length !== nargs) { | |
| 304 | 332 | throwFFIArgCountError(nargs, args.length); | |
| 305 | 333 | } | |
@@ -332,5 +360,6 @@ module.exports = { | |||
| 332 | 360 | convertPointerArg, | |
| 333 | 361 | hasPointerMemoryArg, | |
| 334 | 362 | hasStringPointerArg, | |
| 363 | + markFastLibraryClosed, | ||
| 335 | 364 | wrapWithRawPointerConversions, | |
| 336 | 365 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -241,12 +241,26 @@ extern "C" uintptr_t node_ffi_fast_buffer_data(v8::Local<v8::Value> value, | |||
| 241 | 241 | ||
| 242 | 242 | v8::Isolate* isolate = options != nullptr ? options->isolate : nullptr; | |
| 243 | 243 | if (isolate != nullptr) { | |
| 244 | + // No HandleScope is active during a Fast API call, so open one before | ||
| 245 | + // creating the error object. | ||
| 246 | + v8::HandleScope scope(isolate); | ||
| 244 | 247 | THROW_ERR_INVALID_ARG_VALUE( | |
| 245 | 248 | isolate, "Argument %u must be a buffer or an ArrayBuffer", index); | |
| 246 | 249 | } | |
| 247 | 250 | return kInvalidBuffer; | |
| 248 | 251 | } | |
| 249 | 252 | ||
| 253 | + extern "C" void node_ffi_fast_library_closed(v8::Isolate* isolate) { | ||
| 254 | + if (isolate != nullptr) { | ||
| 255 | + // Fast API calls do not enter a HandleScope, and the generated trampolines | ||
| 256 | + // call this helper directly. Building the error object allocates handles, | ||
| 257 | + // so open a scope here. The scheduled exception lives on the isolate and | ||
| 258 | + // outlives the scope. | ||
| 259 | + v8::HandleScope scope(isolate); | ||
| 260 | + THROW_ERR_FFI_LIBRARY_CLOSED(isolate); | ||
| 261 | + } | ||
| 262 | + } | ||
| 263 | + | ||
| 250 | 264 | FastFFIMetadata::~FastFFIMetadata() { | |
| 251 | 265 | // Metadata owns executable memory through `trampoline`; releasing it here | |
| 252 | 266 | // ties code lifetime to the V8 function's weak FFIFunctionInfo cleanup. | |
@@ -265,7 +279,18 @@ bool IsFastCallSupported() { | |||
| 265 | 279 | #endif | |
| 266 | 280 | } | |
| 267 | 281 | ||
| 268 | - std::unique_ptr<FastFFIMetadata> CreateFastFFIMetadata(const FFIFunction& fn) { | ||
| 282 | + bool IsFastLibraryGuardSupported() { | ||
| 283 | + #if defined(__aarch64__) || defined(_M_ARM64) || \ | ||
| 284 | + (defined(__x86_64__) && !defined(_WIN32)) | ||
| 285 | + return true; | ||
| 286 | + #else | ||
| 287 | + return false; | ||
| 288 | + #endif | ||
| 289 | + } | ||
| 290 | + | ||
| 291 | + std::unique_ptr<FastFFIMetadata> CreateFastFFIMetadata(const FFIFunction& fn, | ||
| 292 | + const bool* closed, | ||
| 293 | + v8::Isolate* isolate) { | ||
| 269 | 294 | // Bail early if executable memory allocation doesn't work on this process | |
| 270 | 295 | // (missing MAP_JIT entitlement, hardened runtime, SELinux execmem, etc.). | |
| 271 | 296 | // The self-test runs once and caches the result. | |
@@ -299,6 +324,7 @@ std::unique_ptr<FastFFIMetadata> CreateFastFFIMetadata(const FFIFunction& fn) { | |||
| 299 | 324 | std::vector<FastFFIType> args; | |
| 300 | 325 | args.reserve(fn.arg_type_names.size()); | |
| 301 | 326 | bool needs_bigint = NeedsBigIntRepresentation(result); | |
| 327 | + const bool guards_library = IsFastLibraryGuardSupported(); | ||
| 302 | 328 | bool needs_callback_options = false; | |
| 303 | 329 | // Normalize public argument names into FastFFIType values while collecting | |
| 304 | 330 | // signature-wide flags required by V8 CFunctionInfo. | |
@@ -320,8 +346,9 @@ std::unique_ptr<FastFFIMetadata> CreateFastFFIMetadata(const FFIFunction& fn) { | |||
| 320 | 346 | // The platform-specific trampoline is the executable entrypoint V8 calls. | |
| 321 | 347 | // If the platform rejects the signature, the whole fast metadata object is | |
| 322 | 348 | // discarded and the caller chooses another invocation path. | |
| 349 | + FastFFITrampolineConfig config{fn.ptr, closed, isolate}; | ||
| 323 | 350 | if (!node_ffi_create_fast_trampoline( | |
| 324 | - fn.ptr, args.data(), args.size(), result, &metadata->trampoline)) { | ||
| 351 | + config, args.data(), args.size(), result, &metadata->trampoline)) { | ||
| 325 | 352 | return nullptr; | |
| 326 | 353 | } | |
| 327 | 354 | ||
@@ -348,6 +375,7 @@ std::unique_ptr<FastFFIMetadata> CreateFastFFIMetadata(const FFIFunction& fn) { | |||
| 348 | 375 | : CFunctionInfo::Int64Representation::kNumber); | |
| 349 | 376 | metadata->c_function = | |
| 350 | 377 | v8::CFunction(metadata->trampoline.code, metadata->c_function_info.get()); | |
| 378 | + metadata->guards_library = guards_library; | ||
| 351 | 379 | return metadata; | |
| 352 | 380 | } | |
| 353 | 381 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,6 +36,12 @@ struct FastFFITrampoline { | |||
| 36 | 36 | size_t size = 0; | |
| 37 | 37 | }; | |
| 38 | 38 | ||
| 39 | + struct FastFFITrampolineConfig { | ||
| 40 | + void* target; | ||
| 41 | + const bool* closed; | ||
| 42 | + v8::Isolate* isolate; | ||
| 43 | + }; | ||
| 44 | + | ||
| 39 | 45 | struct FastFFIMetadata { | |
| 40 | 46 | FastFFIMetadata() = default; | |
| 41 | 47 | ~FastFFIMetadata(); | |
@@ -47,6 +53,7 @@ struct FastFFIMetadata { | |||
| 47 | 53 | std::vector<v8::CTypeInfo> arg_info; | |
| 48 | 54 | std::unique_ptr<v8::CFunctionInfo> c_function_info; | |
| 49 | 55 | v8::CFunction c_function; | |
| 56 | + bool guards_library = false; | ||
| 50 | 57 | }; | |
| 51 | 58 | ||
| 52 | 59 | // Public detection queries. | |
@@ -56,6 +63,7 @@ struct FastFFIMetadata { | |||
| 56 | 63 | // of any particular signature — if this returns false, no signature can | |
| 57 | 64 | // use the fast-call path. | |
| 58 | 65 | bool IsFastCallSupported(); | |
| 66 | + bool IsFastLibraryGuardSupported(); | ||
| 59 | 67 | ||
| 60 | 68 | bool SignatureNeedsRawPointerConversions(const FFIFunction& fn); | |
| 61 | 69 | bool SignatureNeedsFastIntegerValidation(const FFIFunction& fn); | |
@@ -65,19 +73,23 @@ std::shared_ptr<FFIFunction> CloneWithRawPointerArgNames( | |||
| 65 | 73 | const std::shared_ptr<FFIFunction>& fn); | |
| 66 | 74 | std::shared_ptr<FFIFunction> CloneWithFastBufferArgNames( | |
| 67 | 75 | const std::shared_ptr<FFIFunction>& fn); | |
| 68 | - std::unique_ptr<FastFFIMetadata> CreateFastFFIMetadata(const FFIFunction& fn); | ||
| 76 | + std::unique_ptr<FastFFIMetadata> CreateFastFFIMetadata(const FFIFunction& fn, | ||
| 77 | + const bool* closed, | ||
| 78 | + v8::Isolate* isolate); | ||
| 69 | 79 | ||
| 70 | 80 | } // namespace node::ffi | |
| 71 | 81 | ||
| 72 | 82 | extern "C" { | |
| 73 | 83 | uintptr_t node_ffi_fast_buffer_data(v8::Local<v8::Value> value, | |
| 74 | 84 | v8::FastApiCallbackOptions* options, | |
| 75 | 85 | uint32_t index); | |
| 76 | - bool node_ffi_create_fast_trampoline(void* target, | ||
| 77 | - const node::ffi::FastFFIType* args, | ||
| 78 | - size_t argc, | ||
| 79 | - node::ffi::FastFFIType result, | ||
| 80 | - node::ffi::FastFFITrampoline* out); | ||
| 86 | + void node_ffi_fast_library_closed(v8::Isolate* isolate); | ||
| 87 | + bool node_ffi_create_fast_trampoline( | ||
| 88 | + const node::ffi::FastFFITrampolineConfig& config, | ||
| 89 | + const node::ffi::FastFFIType* args, | ||
| 90 | + size_t argc, | ||
| 91 | + node::ffi::FastFFIType result, | ||
| 92 | + node::ffi::FastFFITrampoline* out); | ||
| 81 | 93 | void node_ffi_free_fast_trampoline(node::ffi::FastFFITrampoline* trampoline); | |
| 82 | 94 | } | |
| 83 | 95 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -81,6 +81,14 @@ uint32_t LdrXSp(unsigned reg, unsigned offset) { | |||
| 81 | 81 | return 0xf94003e0 | ((offset / 8) << 10) | reg; | |
| 82 | 82 | } | |
| 83 | 83 | ||
| 84 | + uint32_t LdrbW(unsigned dst, unsigned base) { | ||
| 85 | + return 0x39400000 | (base << 5) | dst; | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + uint32_t CbzW(unsigned reg, unsigned instruction_offset) { | ||
| 89 | + return 0x34000000 | ((instruction_offset & 0x7ffff) << 5) | reg; | ||
| 90 | + } | ||
| 91 | + | ||
| 84 | 92 | uint32_t MovzW(unsigned dst, uint16_t value) { | |
| 85 | 93 | // Load a small immediate into a W register. The buffer helper's argument | |
| 86 | 94 | // index is uint32_t, but current Fast API signatures are capped well below | |
@@ -213,14 +221,15 @@ bool ProtectCode(void* code, size_t code_size) { | |||
| 213 | 221 | } // namespace | |
| 214 | 222 | ||
| 215 | 223 | extern "C" bool node_ffi_create_fast_trampoline( | |
| 216 | - void* target, | ||
| 224 | + const node::ffi::FastFFITrampolineConfig& config, | ||
| 217 | 225 | const node::ffi::FastFFIType* args, | |
| 218 | 226 | size_t argc, | |
| 219 | 227 | node::ffi::FastFFIType result, | |
| 220 | 228 | node::ffi::FastFFITrampoline* out) { | |
| 221 | 229 | // Null inputs mean the caller cannot safely create executable code for this | |
| 222 | 230 | // signature. Report rejection so the generic FFI path can be used instead. | |
| 223 | - if (target == nullptr || out == nullptr) { | ||
| 231 | + if (config.target == nullptr || config.closed == nullptr || | ||
| 232 | + config.isolate == nullptr || out == nullptr) { | ||
| 224 | 233 | return false; | |
| 225 | 234 | } | |
| 226 | 235 | ||
@@ -275,6 +284,24 @@ extern "C" bool node_ffi_create_fast_trampoline( | |||
| 275 | 284 | // call can return through this generated trampoline safely. | |
| 276 | 285 | *cursor++ = kStpFpLrPreIndex; | |
| 277 | 286 | ||
| 287 | + // Fast calls bypass DynamicLibrary::InvokeFunction, so check the stable | ||
| 288 | + // FFIFunction::closed flag before touching the target address. The open | ||
| 289 | + // branch is the hot path. On close, schedule the standard JS exception and | ||
| 290 | + // return; V8 checks for pending exceptions after Fast API calls. | ||
| 291 | + EmitLoadX16(&cursor, reinterpret_cast<uintptr_t>(config.closed)); | ||
| 292 | + *cursor++ = LdrbW(17, 16); | ||
| 293 | + uint32_t* open_branch = cursor++; | ||
| 294 | + EmitLoadX16(&cursor, reinterpret_cast<uintptr_t>(config.isolate)); | ||
| 295 | + *cursor++ = MovX(0, 16); | ||
| 296 | + EmitLoadX16( | ||
| 297 | + &cursor, reinterpret_cast<uintptr_t>(node_ffi_fast_library_closed)); | ||
| 298 | + *cursor++ = kBlrX16; | ||
| 299 | + *cursor++ = MovX(0, 31); | ||
| 300 | + *cursor++ = kLdpFpLrPostIndex; | ||
| 301 | + *cursor++ = kRet; | ||
| 302 | + *open_branch = | ||
| 303 | + CbzW(17, static_cast<unsigned>(cursor - open_branch)); | ||
| 304 | + | ||
| 278 | 305 | if (has_buffer_args) { | |
| 279 | 306 | // Buffer conversion calls a C++ helper before the target call, so spill all | |
| 280 | 307 | // incoming GP registers that may be clobbered by that helper. | |
@@ -361,7 +388,7 @@ extern "C" bool node_ffi_create_fast_trampoline( | |||
| 361 | 388 | ||
| 362 | 389 | // Tail of the trampoline: load the actual library symbol address and call it | |
| 363 | 390 | // with arguments now arranged according to the native ABI. | |
| 364 | - EmitLoadX16(&cursor, reinterpret_cast<uintptr_t>(target)); | ||
| 391 | + EmitLoadX16(&cursor, reinterpret_cast<uintptr_t>(config.target)); | ||
| 365 | 392 | *cursor++ = kBlrX16; | |
| 366 | 393 | ||
| 367 | 394 | if (has_buffer_args) { | |
@@ -414,7 +441,7 @@ extern "C" void node_ffi_free_fast_trampoline( | |||
| 414 | 441 | !(defined(__riscv) && __riscv_xlen == 64) && !defined(__s390x__) | |
| 415 | 442 | ||
| 416 | 443 | extern "C" bool node_ffi_create_fast_trampoline( | |
| 417 | - void* target, | ||
| 444 | + const node::ffi::FastFFITrampolineConfig& config, | ||
| 418 | 445 | const node::ffi::FastFFIType* args, | |
| 419 | 446 | size_t argc, | |
| 420 | 447 | node::ffi::FastFFIType result, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -74,12 +74,12 @@ void FreeCode(void* code, size_t code_size) { | |||
| 74 | 74 | } // namespace | |
| 75 | 75 | ||
| 76 | 76 | extern "C" bool node_ffi_create_fast_trampoline( | |
| 77 | - void* target, | ||
| 77 | + const node::ffi::FastFFITrampolineConfig& config, | ||
| 78 | 78 | const node::ffi::FastFFIType* args, | |
| 79 | 79 | size_t argc, | |
| 80 | 80 | node::ffi::FastFFIType result, | |
| 81 | 81 | node::ffi::FastFFITrampoline* out) { | |
| 82 | - if (target == nullptr || out == nullptr || IsNarrowType(result)) { | ||
| 82 | + if (config.target == nullptr || out == nullptr || IsNarrowType(result)) { | ||
| 83 | 83 | return false; | |
| 84 | 84 | } | |
| 85 | 85 | ||
@@ -126,7 +126,7 @@ extern "C" bool node_ffi_create_fast_trampoline( | |||
| 126 | 126 | Emit32(&cursor, LdD(12, 12, 16)); // ld.d t0, t0, literal | |
| 127 | 127 | Emit32(&cursor, Jirl(0, 12, 0)); // jr t0 | |
| 128 | 128 | Emit32(&cursor, Or(0, 0, 0)); // nop; align literal to 8 bytes | |
| 129 | - Emit64(&cursor, reinterpret_cast<uintptr_t>(target)); | ||
| 129 | + Emit64(&cursor, reinterpret_cast<uintptr_t>(config.target)); | ||
| 130 | 130 | ||
| 131 | 131 | const size_t written = reinterpret_cast<uint8_t*>(cursor) - | |
| 132 | 132 | static_cast<uint8_t*>(code); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments