///|
/// Raw address returned by the native JIT-memory allocator.
type MemPointer = UInt64

///|
/// Opaque handle returned by `dlopen`.
#external
priv type LibHandle

///|
/// Raw C function address returned by `dlsym`.
type FnPointer = UInt64

///|
/// Allocate one RW memory region for generated code.
extern "c" fn __mmap(length : UInt64) -> MemPointer = "asm_dot_mbt_mmap"

///|
/// Check whether a raw mmap result is `MAP_FAILED`.
extern "c" fn mmap_failed(pointer : MemPointer) -> Bool = "asm_dot_mbt_mmap_failed"

///|
/// Return the current C `errno` value.
extern "c" fn errno() -> Int32 = "asm_dot_mbt_errno"

///|
/// Allocate one RW memory region for generated code, raising on failure.
fn mmap(length : UInt64) -> MemPointer raise ASMContextError {
  let pointer = __mmap(length)
  if mmap_failed(pointer) {
    raise ExecutableMemoryAllocationFailed(length, errno())
  }
  pointer
}

///|
/// Switch a generated-code mapping from RW to RX.
extern "c" fn __mprotect_rx(start : UInt64, length : UInt64) -> Int32 = "asm_dot_mbt_mprotect_rx"

///|
/// Switch a generated-code mapping from RW to RX, raising on failure.
fn mprotect_rx(start : UInt64, length : UInt64) -> Unit raise ASMContextError {
  if __mprotect_rx(start, length) != 0 {
    raise ExecutableMemoryProtectionFailed(start, length, errno())
  }
}

///|
/// Open the current process as a dynamic-library handle.
extern "c" fn dlopen() -> LibHandle = "asm_dot_mbt_dlopen"

///|
/// Resolve a symbol from a dynamic-library handle.
#borrow(symbol)
extern "c" fn __dlsym(handle : LibHandle, symbol : Bytes) -> FnPointer = "dlsym"

///|
/// Resolve a UTF-8 symbol name to a raw function address.
fn dlsym(handle : LibHandle, symbol : String) -> FnPointer {
  let symbol = @utf8.encode(symbol)
  __dlsym(handle, symbol)
}

///|
/// Look up a Linux syscall number by canonical syscall name, such as
/// `"getpid"` or `"write"`.
///
/// Returns `None` when the current native target does not expose that syscall
/// macro, or when the name is not in the stub's syscall table.
pub fn linux_syscall_number(name : String) -> Int32? {
  let number = __linux_syscall_number(@utf8.encode(name))
  if number >= 0 {
    Some(number)
  } else {
    None
  }
}

///|
/// Raw Linux syscall lookup backed by `SYS_*` macros in the C stub.
#borrow(name)
extern "c" fn __linux_syscall_number(name : Bytes) -> Int32 = "asm_dot_mbt_linux_syscall_number"

///|
/// Copy generated bytes into writable JIT memory.
#borrow(machcode)
extern "c" fn copy_code(start : UInt64, machcode : Bytes, len : UInt64) -> Unit = "asm_dot_mbt_copy_code"

///|
/// Cast a finalized code address to a nullary Int64 function reference.
extern "c" fn code_func_ref_i64(start : UInt64) -> FuncRef[() -> Int64] = "asm_dot_mbt_func_ref_i64"