sandboxedjs 0.1.8 → 0.1.9

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/dist/index.d.cts CHANGED
@@ -633,6 +633,8 @@ declare function createContext(init: ContextInit): ExecContext;
633
633
  * container can execute it" is far more use than `Exec format error`.
634
634
  */
635
635
 
636
+ /** How many bytes of the file head a handler is given to identify it. */
637
+ declare const MAGIC_BYTES = 256;
636
638
  interface ExecFormat {
637
639
  /** Short identifier, e.g. `wasi`. Shown by `file` and in diagnostics. */
638
640
  readonly name: string;
@@ -2026,6 +2028,539 @@ declare function isPythonAvailable(): Promise<boolean>;
2026
2028
 
2027
2029
  declare const NPM_VERSION = "10.9.0";
2028
2030
 
2031
+ /**
2032
+ * WebAssembly as an executable format.
2033
+ *
2034
+ * This is what makes a `.wasm` file in `$PATH` a program. The format handler
2035
+ * registered here is asked by the kernel whenever a file starts with `\0asm`,
2036
+ * so a WASI binary is run through exactly the same path as `grep` or a shell
2037
+ * script: `chmod +x jq.wasm && ./jq.wasm .name < data.json` works, and so does
2038
+ * dropping it in `/usr/local/bin` and calling it `jq`.
2039
+ *
2040
+ * Nothing here knows the name of a single program. What it knows is how to read
2041
+ * a module's import section and tell which *convention* the module was built
2042
+ * for, because that is what decides whether it can run:
2043
+ *
2044
+ * - `wasi_snapshot_preview1` — a command module. Runs, on the container's own
2045
+ * filesystem. This covers `wasm32-wasi` C/C++/Rust/Zig and Go's `wasip1`.
2046
+ * - `wasi:cli/*` — a preview 2 component. Not an instantiable module at all;
2047
+ * it needs to be transpiled first, and saying so beats a type error.
2048
+ * - `env` + a JS glue file — Emscripten or wasm-bindgen output, which is only
2049
+ * half a program; the loader beside it is the entry point, not this file.
2050
+ * - `gojs` — `GOOS=js`, which needs Go's `wasm_exec.js` harness.
2051
+ *
2052
+ * Each of those gets a specific diagnosis instead of "Exec format error", which
2053
+ * matters more than it sounds: the difference between "this will never work"
2054
+ * and "you are holding it wrong" is most of the debugging.
2055
+ */
2056
+
2057
+ declare function isWasmBinary(head: Uint8Array): boolean;
2058
+ type WasmFlavour = "wasi" | "wasi-component" | "emscripten" | "wasm-bindgen" | "go-js" | "core";
2059
+ interface WasmAnalysis {
2060
+ flavour: WasmFlavour;
2061
+ /** Distinct import module names, in declaration order. */
2062
+ modules: string[];
2063
+ /** True when the module wants a memory it does not own. */
2064
+ importsMemory: boolean;
2065
+ /** True when the imported memory is shared, i.e. the module wants threads. */
2066
+ wantsThreads: boolean;
2067
+ exports: string[];
2068
+ }
2069
+ /** Classify a compiled module by what it imports and exports. */
2070
+ declare function analyse(module: WebAssembly.Module): WasmAnalysis;
2071
+ /** Forget cached modules. Exposed for tests and for long-lived hosts. */
2072
+ declare function clearWasmCache(): void;
2073
+ /**
2074
+ * Run a WASI command module.
2075
+ *
2076
+ * `argv[0]` is the name the program was invoked as, so a module symlinked to
2077
+ * two names can tell which one it is — busybox-style multi-call binaries
2078
+ * depend on it.
2079
+ */
2080
+ declare function runWasi(ctx: ExecContext, path: string, argv0?: string): Promise<number>;
2081
+ declare const wasmFormat: ExecFormat;
2082
+
2083
+ /**
2084
+ * A WASI `preview1` host implemented over the container's filesystem.
2085
+ *
2086
+ * This is the counterpart to `emscripten-fs.ts`, for the other half of the
2087
+ * WebAssembly world. Emscripten programs come with their own JavaScript runtime
2088
+ * and are handed a filesystem backend; WASI programs come as a bare `.wasm`
2089
+ * module and expect the *host* to be the operating system. So this file is that
2090
+ * operating system: every `wasi_snapshot_preview1` import a compiled program can
2091
+ * ask for, answered out of the same `Vfs` the shell, Node.js and Python use.
2092
+ *
2093
+ * The consequence is the point of the whole exercise. Anything on earth that
2094
+ * compiles to `wasm32-wasi` — and that is most C, C++, Rust, Zig and Go code
2095
+ * that does not need threads or sockets — becomes a program this container can
2096
+ * run, without the container knowing anything about it. `path_open("data.csv")`
2097
+ * from a Rust binary opens the same file `echo > data.csv` just created.
2098
+ *
2099
+ * Deliberate limits, all of which report a real errno rather than crashing:
2100
+ * - no threads: `wasi-threads` modules fail at instantiation, not mid-run;
2101
+ * - no sockets: `sock_*` returns ENOTSUP, since the container has no raw ones;
2102
+ * - `poll_oneoff` handles clock subscriptions only, which is what `sleep` and
2103
+ * most timeouts actually use.
2104
+ *
2105
+ * Everything is synchronous, because WASI is. Standard input is therefore read
2106
+ * to completion before the module starts, the same compromise the Emscripten
2107
+ * mount makes.
2108
+ */
2109
+
2110
+ /** Thrown by `proc_exit` to unwind out of the module. */
2111
+ declare class WasiExit extends Error {
2112
+ readonly code: number;
2113
+ constructor(code: number);
2114
+ }
2115
+ interface WasiOptions {
2116
+ vfs: Vfs;
2117
+ cred: Cred;
2118
+ /** Full argv, including argv[0]. */
2119
+ args: string[];
2120
+ env: Record<string, string | undefined>;
2121
+ cwd: string;
2122
+ /** Standard input, already read to completion. */
2123
+ stdin?: Uint8Array;
2124
+ /** Called with each write to fd 1 and fd 2. */
2125
+ stdout: (bytes: Uint8Array) => void;
2126
+ stderr: (bytes: Uint8Array) => void;
2127
+ /**
2128
+ * Directories to preopen, as `guestName -> containerPath`. Programs can only
2129
+ * reach paths beneath a preopen, which is WASI's sandbox and is left intact:
2130
+ * the default grants `/`, so the container's own permissions do the limiting,
2131
+ * but a caller can hand over a single directory instead.
2132
+ */
2133
+ preopens?: Record<string, string>;
2134
+ /** Monotonic-ish clock, so tests can pin time. */
2135
+ now?: () => number;
2136
+ /** Fills a buffer for `random_get`. */
2137
+ randomFill?: (buffer: Uint8Array) => void;
2138
+ }
2139
+ /**
2140
+ * A WASI preview1 implementation bound to one process.
2141
+ *
2142
+ * Construct it, hand `imports` to `WebAssembly.instantiate`, then call
2143
+ * `bind(instance)` before starting the module so the host can reach its memory.
2144
+ */
2145
+ declare class WasiPreview1 {
2146
+ private memory;
2147
+ private readonly fds;
2148
+ private nextFd;
2149
+ private readonly opts;
2150
+ private readonly now;
2151
+ private readonly randomFill;
2152
+ /** Set once `proc_exit` has been seen, so the caller can tell exit 0 from a
2153
+ * module that simply returned. */
2154
+ exited: number | null;
2155
+ constructor(opts: WasiOptions);
2156
+ /** Give the host access to the instantiated module's memory. */
2157
+ bind(instance: WebAssembly.Instance): void;
2158
+ private get view();
2159
+ private get bytes();
2160
+ private readString;
2161
+ /** Scatter/gather list, as `fd_read` and `fd_write` take. */
2162
+ private iovecs;
2163
+ /**
2164
+ * Resolve a path supplied by the module against a directory descriptor.
2165
+ *
2166
+ * The result is confined to the descriptor's subtree: `..` that would climb
2167
+ * out of a preopen is rejected with ENOTCAPABLE, which is WASI's own answer
2168
+ * and stops a module reaching a directory it was never granted.
2169
+ */
2170
+ private resolveAt;
2171
+ private errnoOf;
2172
+ private filetypeOf;
2173
+ /** Write a 64-byte `filestat` for `path`. */
2174
+ private writeFilestat;
2175
+ /** Persist an open file's buffer back to the filesystem. */
2176
+ private flush;
2177
+ /**
2178
+ * The `wasi_snapshot_preview1` namespace, plus `wasi_unstable` for older
2179
+ * toolchains — the two differ only in `fd_seek`'s argument order, which is
2180
+ * handled by the alias below.
2181
+ */
2182
+ get imports(): WebAssembly.Imports;
2183
+ private syscalls;
2184
+ private readInto;
2185
+ private writeFrom;
2186
+ private allocateFd;
2187
+ private envPairs;
2188
+ /** Flush and drop every open descriptor. Safe to call twice. */
2189
+ close(): void;
2190
+ }
2191
+
2192
+ /**
2193
+ * Native executable formats other than the one this container can execute.
2194
+ *
2195
+ * x86-64 Linux binaries are handled in `x86/`, on an emulated CPU. Mach-O and
2196
+ * PE are a different matter: they are not just machine code but a different
2197
+ * operating system's process model, loader and system-call interface, and none
2198
+ * of that is worth building for programs that were never going to be in a
2199
+ * Linux container anyway.
2200
+ *
2201
+ * So these handlers parse enough of each header to name the thing and say what
2202
+ * would work instead. `command not found` sends someone hunting for a missing
2203
+ * `$PATH` entry when the real answer is that they installed a macOS binary;
2204
+ * `Exec format error` is technically right and practically useless.
2205
+ */
2206
+
2207
+ declare const machoFormat: ExecFormat;
2208
+ declare const peFormat: ExecFormat;
2209
+ declare function nativeFormats(): ExecFormat[];
2210
+
2211
+ declare class Memory {
2212
+ private readonly pages;
2213
+ /** Bump pointer for anonymous `mmap`, below the stack and above the heap. */
2214
+ mmapNext: number;
2215
+ /** Current program break, set by the loader and moved by `brk`. */
2216
+ brk: number;
2217
+ private page;
2218
+ /** Make `[address, address+length)` readable and writable. */
2219
+ map(address: number, length: number): void;
2220
+ unmap(address: number, length: number): void;
2221
+ isMapped(address: number): boolean;
2222
+ /** Reserve `length` bytes of anonymous space, page-aligned. */
2223
+ mmapAnonymous(length: number): number;
2224
+ read8(address: number): number;
2225
+ write8(address: number, value: number): void;
2226
+ /**
2227
+ * Read `size` bytes little-endian. Split across pages byte by byte rather
2228
+ * than assuming an access stays inside one — unaligned loads that straddle a
2229
+ * page boundary are entirely legal on x86.
2230
+ */
2231
+ read(address: number, size: number): bigint;
2232
+ write(address: number, size: number, value: bigint): void;
2233
+ readBytes(address: number, length: number): Uint8Array;
2234
+ writeBytes(address: number, bytes: Uint8Array): void;
2235
+ /** Read a NUL-terminated string, as every path argument to a syscall is. */
2236
+ readCString(address: number, limit?: number): string;
2237
+ writeCString(address: number, text: string): number;
2238
+ get pageCount(): number;
2239
+ }
2240
+
2241
+ /**
2242
+ * An x86-64 interpreter.
2243
+ *
2244
+ * Decode, execute, repeat. No JIT, no basic-block caching — correctness first,
2245
+ * because a subtly wrong flag is far more expensive to chase than a slow loop.
2246
+ *
2247
+ * Register values are BigInt. That is the slow choice and a deliberate one:
2248
+ * x86-64 semantics are full of 64-bit shifts, sign extensions and
2249
+ * multiplications whose high half matters, and splitting every value into two
2250
+ * 32-bit halves multiplies the number of places a bug can hide by roughly the
2251
+ * number of instructions. Addresses stay as JavaScript numbers, since they fit
2252
+ * in 48 bits and are used on every memory access.
2253
+ *
2254
+ * What is implemented is the integer instruction set a compiler actually emits
2255
+ * for ordinary code: the arithmetic and logic groups, moves and sign/zero
2256
+ * extensions, LEA, the jump/call/return family, conditional moves and sets,
2257
+ * shifts and rotates, multiply and divide, the string operations with their
2258
+ * REP prefixes, the bit-test and bit-scan instructions, CMPXCHG and XADD.
2259
+ * Anything outside that raises `UnsupportedInstruction`, which names the
2260
+ * opcode and where it was found rather than failing silently.
2261
+ */
2262
+
2263
+ interface CpuOptions {
2264
+ memory: Memory;
2265
+ /** Called on the `syscall` instruction; reads and writes registers directly. */
2266
+ onSyscall: (cpu: Cpu) => void | Promise<void>;
2267
+ /** Abort after this many instructions, so a runaway guest cannot hang the host. */
2268
+ instructionLimit?: number;
2269
+ }
2270
+ declare class Cpu {
2271
+ readonly memory: Memory;
2272
+ readonly regs: BigUint64Array<ArrayBuffer>;
2273
+ rip: number;
2274
+ cf: boolean;
2275
+ zf: boolean;
2276
+ sf: boolean;
2277
+ of: boolean;
2278
+ pf: boolean;
2279
+ af: boolean;
2280
+ df: boolean;
2281
+ /** `%fs` base, which is where thread-local storage lives on x86-64 Linux. */
2282
+ fsBase: number;
2283
+ gsBase: number;
2284
+ /**
2285
+ * The sixteen 128-bit vector registers, split into halves.
2286
+ *
2287
+ * Not optional extras: a compiler zeroes memory with `xorps`/`movups` and
2288
+ * finds a NUL byte with `pcmpeqb`/`pmovmskb`, so ordinary code reaches for
2289
+ * these within the first hundred instructions. Only the moves, the bitwise
2290
+ * operations and the byte compares are implemented — the arithmetic that
2291
+ * makes SSE interesting for numerics is not, and raises the usual error.
2292
+ */
2293
+ readonly xmmLo: BigUint64Array<ArrayBuffer>;
2294
+ readonly xmmHi: BigUint64Array<ArrayBuffer>;
2295
+ instructions: number;
2296
+ private readonly limit;
2297
+ private readonly onSyscall;
2298
+ private rex;
2299
+ private hasRex;
2300
+ private opSize;
2301
+ private addrSize;
2302
+ private rep;
2303
+ private opcodeStart;
2304
+ constructor(opts: CpuOptions);
2305
+ get(index: number): bigint;
2306
+ set(index: number, value: bigint): void;
2307
+ private fetch8;
2308
+ private fetch;
2309
+ private fetchSigned;
2310
+ /**
2311
+ * Read a register at a given width.
2312
+ *
2313
+ * The 8-bit encodings are the awkward part: with no REX prefix, indices 4–7
2314
+ * name AH/CH/DH/BH — the *high* byte of the first four registers — while any
2315
+ * REX prefix at all reassigns them to SPL/BPL/SIL/DIL.
2316
+ */
2317
+ private readReg;
2318
+ private writeReg;
2319
+ private modrm;
2320
+ /** Decode a ModRM byte, resolving the effective address when there is one. */
2321
+ private decodeModrm;
2322
+ private ripRelative;
2323
+ /**
2324
+ * Finish a RIP-relative address once the whole instruction has been decoded.
2325
+ * Called by the operand accessors, which run after any immediate is read.
2326
+ */
2327
+ private effectiveAddress;
2328
+ private readRm;
2329
+ private writeRm;
2330
+ private setLogicFlags;
2331
+ private setAddFlags;
2332
+ private setSubFlags;
2333
+ /** Evaluate one of the sixteen condition codes shared by Jcc/SETcc/CMOVcc. */
2334
+ private condition;
2335
+ push(value: bigint): void;
2336
+ pop(): bigint;
2337
+ /**
2338
+ * Run until the guest exits.
2339
+ *
2340
+ * Yields to the event loop periodically: the interpreter is synchronous, and
2341
+ * without this a long-running guest would block the host completely.
2342
+ */
2343
+ run(): Promise<number>;
2344
+ /**
2345
+ * Execute one instruction. Returns a promise only when the instruction was a
2346
+ * syscall that needs to await, so the common path allocates nothing.
2347
+ */
2348
+ step(): Promise<void> | null;
2349
+ /** Set by an `fs:`/`gs:` prefix, consumed by the next memory operand. */
2350
+ private segmentBase;
2351
+ private takeSegment;
2352
+ private execute;
2353
+ /**
2354
+ * Read an immediate that follows the ModRM byte without disturbing the
2355
+ * pending RIP-relative displacement.
2356
+ *
2357
+ * x86 puts the immediate after the address bytes but resolves RIP-relative
2358
+ * addresses against the end of the *whole* instruction, so the immediate has
2359
+ * to be consumed first and the address computed afterwards.
2360
+ */
2361
+ private peekImmediate;
2362
+ private applyArith;
2363
+ private shift;
2364
+ private setShiftFlags;
2365
+ private group3;
2366
+ /**
2367
+ * MOVS/STOS/LODS/SCAS/CMPS, with the REP prefixes.
2368
+ *
2369
+ * These are how a compiler open-codes `memcpy`, `memset` and `strlen`, so a
2370
+ * program that never calls libc still leans on them heavily.
2371
+ */
2372
+ private stringOp;
2373
+ private executeTwoByte;
2374
+ private readXmmRm;
2375
+ private writeXmmRm;
2376
+ /** Byte `index` of a 128-bit value held as two halves. */
2377
+ private static byteOf;
2378
+ private static fromBytes;
2379
+ /** Shift each element of a vector register: /2 logical right, /4 arithmetic right, /6 left. */
2380
+ private packedShift;
2381
+ private executeSse;
2382
+ /**
2383
+ * CPUID, reporting a deliberately plain processor.
2384
+ *
2385
+ * Advertising SSE4.2 or AVX would be a trap: glibc's IFUNC resolvers select
2386
+ * string routines by feature bit, and would immediately pick vector code
2387
+ * this interpreter does not implement. Claiming the x86-64 baseline steers
2388
+ * every such resolver to the generic path.
2389
+ */
2390
+ private cpuid;
2391
+ /** A snapshot for diagnostics when something goes wrong. */
2392
+ describe(): string;
2393
+ }
2394
+
2395
+ /**
2396
+ * Cooperative threads for the emulated machine.
2397
+ *
2398
+ * The interpreter is one JavaScript loop with one register file, so there is no
2399
+ * parallelism to be had. But `clone` cannot simply fail, and it cannot simply
2400
+ * lie either: a Go program launches a monitor thread during startup and treats
2401
+ * a failed `clone` as fatal, while a `clone` that returns a thread id nothing
2402
+ * ever schedules leaves the main thread spinning in `FUTEX_WAIT` on a wake-up
2403
+ * that can never arrive. Both were observed before this existed.
2404
+ *
2405
+ * So threads are real, just not simultaneous. Each holds a saved copy of the
2406
+ * register file, and the scheduler swaps contexts at the points where a thread
2407
+ * cannot make progress anyway — `futex` waits, `sched_yield`, `nanosleep`. That
2408
+ * is enough for the usual shapes: a runtime that wants a background thread it
2409
+ * never waits on, a worker that blocks on a queue, a mutex handed between two
2410
+ * threads.
2411
+ *
2412
+ * What it cannot do is preempt. A thread that spins on a memory location
2413
+ * without ever entering the kernel keeps the machine to itself, exactly as a
2414
+ * cooperative scheduler implies.
2415
+ */
2416
+
2417
+ declare class Scheduler {
2418
+ private readonly cpu;
2419
+ private readonly threads;
2420
+ private current;
2421
+ private nextId;
2422
+ constructor(cpu: Cpu);
2423
+ get currentId(): number;
2424
+ get count(): number;
2425
+ /**
2426
+ * Start a new thread at the caller's return address.
2427
+ *
2428
+ * A cloned thread resumes exactly where its parent is — immediately after the
2429
+ * `syscall` instruction — with its own stack and `rax` of zero, which is how
2430
+ * the child tells itself apart from the parent.
2431
+ */
2432
+ spawn(stackPointer: bigint, tlsBase: number, setTls: boolean): number;
2433
+ /** Park the running thread on a futex, and run something else. */
2434
+ wait(address: number, timeoutMs: number, now: number): boolean;
2435
+ /** Make up to `count` threads parked on `address` runnable. Returns how many. */
2436
+ wake(address: number, count: number): number;
2437
+ /** Retire the running thread. Returns false when it was the last one. */
2438
+ exit(now: number): boolean;
2439
+ /**
2440
+ * Switch to the next runnable thread.
2441
+ *
2442
+ * Returns false when there is nothing else to run — the caller then has to
2443
+ * decide whether that is a finished program or a deadlock.
2444
+ */
2445
+ yieldNow(now: number): boolean;
2446
+ /** True when every thread is parked — a genuine deadlock rather than a wait. */
2447
+ get allBlocked(): boolean;
2448
+ }
2449
+
2450
+ /**
2451
+ * The Linux x86-64 syscall interface, served from the container's filesystem.
2452
+ *
2453
+ * This is the same idea as `wasi-preview1.ts` one layer down. WASI programs ask
2454
+ * for `path_open`; native Linux programs execute the `syscall` instruction with
2455
+ * a number in `rax` and arguments in `rdi, rsi, rdx, r10, r8, r9`, and expect a
2456
+ * result — or a negative errno — back in `rax`. Underneath, both end up in the
2457
+ * same `Vfs`, which is what lets an emulated binary read a file the shell wrote.
2458
+ *
2459
+ * Choosing this as the boundary is the whole design. Above it sits libc, with
2460
+ * two thousand symbols, IFUNC resolvers and symbol versioning; there is no way
2461
+ * to stand in front of that without already emulating instructions. Below it
2462
+ * there are about eighty calls that matter, and they have been stable for
2463
+ * decades. So libc runs as the machine code it is, and only its syscalls are
2464
+ * answered here.
2465
+ */
2466
+
2467
+ interface LinuxOptions {
2468
+ vfs: Vfs;
2469
+ cred: Cred;
2470
+ cwd: string;
2471
+ memory: Memory;
2472
+ stdin: Uint8Array;
2473
+ stdout: (bytes: Uint8Array) => void;
2474
+ stderr: (bytes: Uint8Array) => void;
2475
+ hostname: string;
2476
+ now: () => number;
2477
+ /** Names every syscall the guest makes, for working out where it stalls. */
2478
+ trace?: (line: string) => void;
2479
+ }
2480
+ declare class LinuxSyscalls {
2481
+ private readonly files;
2482
+ private nextFd;
2483
+ private stdinPosition;
2484
+ private cwd;
2485
+ readonly opts: LinuxOptions;
2486
+ /** Syscall numbers the guest asked for that are not implemented. */
2487
+ readonly unimplemented: Set<number>;
2488
+ /**
2489
+ * Descriptors with no file behind them — epoll sets and eventfds. Tracked so
2490
+ * `close` accepts them and `epoll_ctl` can tell a real one from a wrong one.
2491
+ */
2492
+ private readonly pseudoFds;
2493
+ /** Threads the guest believes it started; see the `clone` case. */
2494
+ threadsRequested: number;
2495
+ constructor(opts: LinuxOptions);
2496
+ /** Thread scheduler, attached once the CPU exists. */
2497
+ scheduler: Scheduler | null;
2498
+ /**
2499
+ * Set by a syscall that switched threads. The result register then belongs to
2500
+ * the thread we switched *to*, and must not be overwritten with ours.
2501
+ */
2502
+ private switched;
2503
+ /** Entry point handed to the CPU. */
2504
+ handle: (cpu: Cpu) => void;
2505
+ private errnoOf;
2506
+ private resolve;
2507
+ private dispatch;
2508
+ private writeTo;
2509
+ private flush;
2510
+ private open;
2511
+ /** Fill a 144-byte `struct stat`. */
2512
+ private stat;
2513
+ /** `fstat` on stdin/stdout/stderr, which are character devices. */
2514
+ private statCharDevice;
2515
+ private getdents64;
2516
+ private writeBounded;
2517
+ /** `struct utsname`: six fixed 65-byte character arrays. */
2518
+ private uname;
2519
+ /** Persist everything still open. Called when the guest exits. */
2520
+ close(): void;
2521
+ }
2522
+
2523
+ /**
2524
+ * Native x86-64 Linux binaries, as an executable format.
2525
+ *
2526
+ * The kernel sees `\x7fELF`, hands the file here, and this boots a machine for
2527
+ * it: memory, a CPU, a Linux syscall layer over the container's filesystem, and
2528
+ * the process image a program expects at `_start`. From the guest's point of
2529
+ * view it is running on Linux. From the shell's point of view it is a command.
2530
+ *
2531
+ * Static binaries — `CGO_ENABLED=0 go build`, Rust's `musl` target, anything
2532
+ * linked with `-static` — need nothing else, because everything they call is
2533
+ * inside them and everything below that is a syscall.
2534
+ *
2535
+ * A dynamically linked binary needs its loader, and this will use a real one:
2536
+ * if `PT_INTERP` names a file that exists in the container, both images are
2537
+ * mapped and control goes to the loader, exactly as the kernel does it. That is
2538
+ * why `ld-linux-x86-64.so.2` and `libc.so.6` are not simulated — they are
2539
+ * ordinary x86-64 code, and a machine that can run the program can run them.
2540
+ * Copy them in and dynamic binaries work; leave them out and the failure says
2541
+ * precisely which file was missing.
2542
+ */
2543
+
2544
+ interface RunElfOptions {
2545
+ /** Stop after this many instructions rather than spinning forever. */
2546
+ instructionLimit?: number;
2547
+ /** Called with every syscall, for diagnosing where a program stalls. */
2548
+ trace?: (line: string) => void;
2549
+ }
2550
+ /**
2551
+ * Load and run an x86-64 Linux binary.
2552
+ *
2553
+ * Returns the guest's exit status, or throws with something specific about why
2554
+ * it could not start — a missing loader, an instruction outside the implemented
2555
+ * set, or a wild memory access.
2556
+ */
2557
+ declare function runElf(ctx: ExecContext, path: string, opts?: RunElfOptions): Promise<number>;
2558
+ /**
2559
+ * The ELF handler. Unlike the stub it replaces, this one runs the file.
2560
+ */
2561
+ declare const elfFormat: ExecFormat;
2562
+ declare function x86Commands(): Command[];
2563
+
2029
2564
  /**
2030
2565
  * sandboxedjs — a Linux-like container that runs entirely inside Node.js.
2031
2566
  *
@@ -2044,4 +2579,4 @@ declare const NPM_VERSION = "10.9.0";
2044
2579
  * ```
2045
2580
  */
2046
2581
 
2047
- export { ArithError, BufferSink, CallbackSink, type Command, CommandRegistry, Container, ContainerFs, type ContainerOptions, type ContextInit, type Cred, type DirEntry, ERRNO, type Env, type ErrnoCode, type ExecContext, type ExecOptions, type ExecResult, FileInput, FileOutput, type GroupEntry, type HttpResponse, IncompleteInputError, type InputStream, type Job, KERNEL_NAME, KERNEL_RELEASE, Kernel, type KernelOptions, type ListeningPort, type MountEntry, NODE_VERSION, NPM_VERSION, type NetInterface, type NetworkOptions, NetworkStack, NullInput, NullOutput, OS_RELEASE, type OutputStream, PYTHON_VERSION, type PasswdEntry, Pipe, Process, type ProcessKind, type ProcessOptions, type ProcessState, ProcessTable, ROOT_CRED, type ResolvedExecutable, type RootfsOptions, type RunOptions, type RunResult, SIGNALS, SIGNAL_NAMES, Session, type SessionInit, type SessionResult, type SessionRunOptions, Shell, ShellExit, type ShellIO, type ShellInit, Lexer as ShellLexer, type ShellOptions, ShellSyntaxError, type SpawnHandle, Stats, type Stdio, SysError, TeeOutput, Terminal, type TerminalOptions, UserDatabase, Variables, Vfs, type VirtualNode, type VirtualProvider, type WriteOptions, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, createContainer, createContext, createContainer as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isPythonAvailable, isSysError, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path as posixPath, resetPidCounter, shellQuote, strerror, unameInfo };
2582
+ export { ArithError, BufferSink, CallbackSink, type Command, CommandRegistry, Container, ContainerFs, type ContainerOptions, type ContextInit, type Cred, type DirEntry, ERRNO, type Env, type ErrnoCode, type ExecContext, type ExecFormat, ExecFormatRegistry, type ExecOptions, type ExecResult, FileInput, FileOutput, type GroupEntry, Memory as GuestMemory, type HttpResponse, IncompleteInputError, type InputStream, type Job, KERNEL_NAME, KERNEL_RELEASE, Kernel, type KernelOptions, LinuxSyscalls, type ListeningPort, MAGIC_BYTES, type MountEntry, NODE_VERSION, NPM_VERSION, type NetInterface, type NetworkOptions, NetworkStack, NullInput, NullOutput, OS_RELEASE, type OutputStream, PYTHON_VERSION, type PasswdEntry, Pipe, Process, type ProcessKind, type ProcessOptions, type ProcessState, ProcessTable, ROOT_CRED, type ResolvedExecutable, type RootfsOptions, type RunOptions, type RunResult, SIGNALS, SIGNAL_NAMES, Session, type SessionInit, type SessionResult, type SessionRunOptions, Shell, ShellExit, type ShellIO, type ShellInit, Lexer as ShellLexer, type ShellOptions, ShellSyntaxError, type SpawnHandle, Stats, type Stdio, SysError, TeeOutput, Terminal, type TerminalOptions, UserDatabase, Variables, Vfs, type VirtualNode, type VirtualProvider, WasiExit, type WasiOptions, WasiPreview1, type WasmAnalysis, type WasmFlavour, type WriteOptions, Cpu as X86Cpu, allCommands, analyse as analyseWasm, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, clearWasmCache, createContainer, createContext, createContainer as default, defineCommand, elfFormat, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isPythonAvailable, isSysError, isWasmBinary, machoFormat, makeCred, nativeFormats, normalizeSignal, octalMode, parse as parseShell, parseUmask, peFormat, path as posixPath, resetPidCounter, runElf, runWasi, shellQuote, strerror, unameInfo, wasmFormat, x86Commands };