Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Unreleased

- Add support for "library" components which don't export `http_incoming`.
- Add non-shift adapter when user module is using wit-bindgen.

## 0.16.4 (2026-01-26)

Expand Down
24 changes: 24 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ build-adapter:
--no-default-features \
--profile release-library \
)
# Build the non-shift "library" version of the adapter.
( \
cd wasm_abi/adapter && \
cargo build \
--package viceroy-component-adapter \
--target wasm32-unknown-unknown \
--no-default-features \
--profile release-library-noshift \
--features noshift \
)

# Build the component adapter for adapting the host-call abi to the
# component model. This is the normal version that includes the exports.
Expand All @@ -89,7 +99,21 @@ build-adapter:
--release \
)

# Build the non-shift normal version of the adapter.
( \
cd wasm_abi/adapter && \
cargo build \
--package viceroy-component-adapter \
--target wasm32-unknown-unknown \
--profile release-noshift \
--features noshift \
)

cp wasm_abi/adapter/target/wasm32-unknown-unknown/release/viceroy_component_adapter.wasm \
wasm_abi/data/viceroy-component-adapter.wasm
cp wasm_abi/adapter/target/wasm32-unknown-unknown/release-noshift/viceroy_component_adapter.wasm \
wasm_abi/data/viceroy-component-adapter.noshift.wasm
cp wasm_abi/adapter/target/wasm32-unknown-unknown/release-library/viceroy_component_adapter.wasm \
wasm_abi/data/viceroy-component-adapter.library.wasm
cp wasm_abi/adapter/target/wasm32-unknown-unknown/release-library-noshift/viceroy_component_adapter.wasm \
wasm_abi/data/viceroy-component-adapter.library.noshift.wasm
8 changes: 4 additions & 4 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,23 +129,23 @@ pub async fn main() -> ExitCode {
let text = match String::from_utf8(bytes) {
Ok(module) => module,
Err(e) => {
event!(Level::ERROR, "Failed to parse wat: {e}");
event!(Level::ERROR, "Failed to parse wat: {e:?}");
return ExitCode::FAILURE;
}
};

match viceroy_lib::adapt::adapt_wat(&text) {
Ok(module) => module,
Err(e) => {
event!(Level::ERROR, "Failed to adapt wat: {e}");
event!(Level::ERROR, "Failed to adapt wat: {e:?}");
return ExitCode::FAILURE;
}
}
} else {
match viceroy_lib::adapt::adapt_bytes(&bytes) {
Ok(module) => module,
Err(e) => {
event!(Level::ERROR, "Failed to adapt module: {e}");
event!(Level::ERROR, "Failed to adapt module: {e:?}");
return ExitCode::FAILURE;
}
}
Expand All @@ -155,7 +155,7 @@ pub async fn main() -> ExitCode {
match std::fs::write(output, module) {
Ok(_) => ExitCode::SUCCESS,
Err(e) => {
event!(Level::ERROR, "Failed to write component: {e}");
event!(Level::ERROR, "Failed to write component: {e:?}");
return ExitCode::FAILURE;
}
}
Expand Down
63 changes: 54 additions & 9 deletions src/adapt.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
/// The full adapter.
const ADAPTER_BYTES: &[u8] = include_bytes!("../wasm_abi/data/viceroy-component-adapter.wasm");
const ADAPTER_NOSHIFT_BYTES: &[u8] =
include_bytes!("../wasm_abi/data/viceroy-component-adapter.noshift.wasm");

/// A version of the adapter that doesn't provide the `http_incoming` export.
///
/// This is used by "library" components meant to be linked to a main component
/// that does provide the `http_incoming` export.
const LIBRARY_ADAPTER_BYTES: &[u8] =
include_bytes!("../wasm_abi/data/viceroy-component-adapter.library.wasm");
const LIBRARY_ADAPTER_NOSHIFT_BYTES: &[u8] =
include_bytes!("../wasm_abi/data/viceroy-component-adapter.library.noshift.wasm");

/// Check if the bytes represent a core wasm module, or a component.
pub fn is_component(bytes: &[u8]) -> bool {
Expand All @@ -25,14 +29,20 @@ pub fn adapt_wat(wat: &str) -> anyhow::Result<Vec<u8>> {
pub fn adapt_bytes(bytes: &[u8]) -> anyhow::Result<Vec<u8>> {
// Determine if we have a main module or a library module.
let library = !has_export(bytes, "_start");
let needs_no_shift_adapter = has_wit_bindgen_imports(bytes);

let bytes = crate::shift_mem::shift_main_module(bytes)?;
let bytes = if needs_no_shift_adapter {
bytes.to_vec()
} else {
crate::shift_mem::shift_main_module(bytes)?
};
let module = mangle_imports(&bytes)?;

let adapter_bytes = if library {
LIBRARY_ADAPTER_BYTES
} else {
ADAPTER_BYTES
let adapter_bytes = match (library, needs_no_shift_adapter) {
(true, true) => LIBRARY_ADAPTER_NOSHIFT_BYTES,
(true, false) => LIBRARY_ADAPTER_BYTES,
(false, true) => ADAPTER_NOSHIFT_BYTES,
(false, false) => ADAPTER_BYTES,
};

let component = wit_component::ComponentEncoder::default()
Expand Down Expand Up @@ -80,13 +90,22 @@ fn mangle_imports(bytes: &[u8]) -> anyhow::Result<wasm_encoder::Module> {
)
})?;

// Leave the existing preview1 imports alone
if import.module == "wasi_snapshot_preview1" {
imports.import(import.module, import.name, entity);
} else {
if is_fastly_module(import.module) {
// In order to build a single module that can serve as
// the adapter for the many "fastly_*" modules we have,
// as well as the "env" module we have, as well as for
// the "wasi_snapshot_preview1" module, we mangle
// "fastly_*" and "env" names and put them into the
// "wasi_snapshot_preview1" module.
let module = "wasi_snapshot_preview1";
let name = format!("{}#{}", import.module, import.name);
imports.import(module, &name, entity);
} else {
// It's not a "fastly_" module, so it may be
// "wasi_snapshot_preview1" which we should leave as-is,
// or a wit-bindgen-generated import which doesn't need
// adapting.
imports.import(import.module, import.name, entity);
}
}

Expand Down Expand Up @@ -130,3 +149,29 @@ fn has_export(bytes: &[u8], wanted: &str) -> bool {

false
}
fn is_fastly_module(module: &str) -> bool {
module.starts_with("fastly_") || module == "env" || module == "fastly" || module == "xqd"
}
fn has_wit_bindgen_imports(bytes: &[u8]) -> bool {
for payload in wasmparser::Parser::new(0).parse_all(&bytes) {
let Ok(payload) = payload else {
return false;
};
match payload {
wasmparser::Payload::ImportSection(section) => {
for import in section {
let Ok(import) = import else {
return false;
};
if !is_fastly_module(import.module) && import.module != "wasi_snapshot_preview1"
{
return true;
}
}
}
_ => {}
}
}

false
}
10 changes: 10 additions & 0 deletions wasm_abi/adapter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ strip = 'debuginfo'
[profile.release-library]
inherits = "release"

[profile.release-library-noshift]
inherits = "release"

[profile.release-noshift]
inherits = "release"

[profile.dev.package.viceroy-component-adapter]
# Make dev look like a release build since this adapter module won't work with
# a debug build that uses data segments and such.
Expand All @@ -57,6 +63,10 @@ default = ["exports"]
# imports-only "library" adapter, which is useful for building libraries that
# don't have their own `main` function.
exports = []
# Build an adapter that doesn't shift the memory address, which falls back
# to rely on `cabi_realloc` or `memory.grow` to maintain WASI state.
# This is only needed when using wit-bindgen and wac locally.
noshift = []

[lints.rust.unexpected_cfgs]
level = "warn"
Expand Down
6 changes: 6 additions & 0 deletions wasm_abi/adapter/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ fn main() {
// startup.
println!("cargo:rustc-link-arg=--import-memory");
// Set the stack pointer to use the first page of memory, as the stack space has been reserved by the `xqd_codegen::shift_mem` module.
#[cfg(not(feature = "noshift"))]
println!("cargo:rustc-link-arg=-zstack-size=65536");
#[cfg(feature = "noshift")]
println!("cargo:rustc-link-arg=-zstack-size=0");
}

/// This function will produce a wasm module which is itself an object file
Expand Down Expand Up @@ -108,7 +111,10 @@ fn build_raw_intrinsics() -> Vec<u8> {
// Default to StackAllocated, as the space is reserved by the `xqd_codegen::shift_mem` module.
// And it won't trigger the `[allocate_stack_via_realloc](https://github.com/bytecodealliance/wasm-tools/blob/main/crates/wit-component/src/gc.rs#L122)` function
// in `wit-component`.
#[cfg(not(feature = "noshift"))]
&ConstExpr::i32_const(2),
#[cfg(feature = "noshift")]
&ConstExpr::i32_const(0),
);
module.section(&globals);

Expand Down
2 changes: 1 addition & 1 deletion wasm_abi/adapter/src/fastly/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4520,7 +4520,7 @@ pub mod fastly_purge {
|res| {
let res = handle_buffer_len!(res, main_ptr!((*options).ret_buf_nwritten_out));
unsafe {
*main_ptr!(((*options).ret_buf_nwritten_out)) = res.len();
*main_ptr!((*options).ret_buf_nwritten_out) = res.len();
}
std::mem::forget(res);
}
Expand Down
23 changes: 23 additions & 0 deletions wasm_abi/adapter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ mod descriptors;
use crate::descriptors::{Descriptor, Descriptors, StreamType};

// This const should always equal to the OFFSET defined in crates/xqd-codegen/src/shift_mem.rs
#[cfg(not(feature = "noshift"))]
pub const OFFSET: usize = 2 * PAGE_SIZE;

pub mod bindings {
Expand Down Expand Up @@ -1651,14 +1652,36 @@ impl State {

#[cold]
fn new() -> *mut State {
#[cfg(feature = "noshift")]
#[link(wasm_import_module = "__main_module__")]
extern "C" {
fn cabi_realloc(
old_ptr: *mut u8,
old_len: usize,
align: usize,
new_len: usize,
) -> *mut u8;
}

assert!(matches!(
unsafe { get_allocation_state() },
AllocationState::StackAllocated
));

unsafe { set_allocation_state(AllocationState::StateAllocating) };

#[cfg(feature = "noshift")]
let ret = unsafe {
cabi_realloc(
std::ptr::null_mut(),
0,
mem::align_of::<UnsafeCell<State>>(),
mem::size_of::<UnsafeCell<State>>(),
) as *mut State
};

// Use the second page of memory to store state
#[cfg(not(feature = "noshift"))]
let ret = (OFFSET - PAGE_SIZE) as *mut State;

unsafe { set_allocation_state(AllocationState::StateAllocated) };
Expand Down
22 changes: 22 additions & 0 deletions wasm_abi/adapter/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,46 @@ use crate::bindings::wasi::cli::stderr::get_stderr;
/// starts two Wasm pages later. When accessing the main module memory from
/// the adapter, we need this annotation to map the pointer to the correct
/// memory address.
#[cfg(not(feature = "noshift"))]
macro_rules! main_ptr {
($ptr:expr) => {{
$ptr.byte_add(crate::OFFSET)
}};
}
#[cfg(not(feature = "noshift"))]
macro_rules! unsafe_main_ptr {
($ptr:expr) => {{
unsafe { main_ptr!($ptr) }
}};
}
/// Used to annotate the address sending back to the main module.
/// This macro does exactly the opposite of `main_ptr`.
#[cfg(not(feature = "noshift"))]
macro_rules! unshift_ptr {
($ptr:expr) => {{
$ptr.byte_sub(crate::OFFSET)
}};
}

#[cfg(feature = "noshift")]
macro_rules! main_ptr {
($ptr:expr) => {{
$ptr
}};
}
#[cfg(feature = "noshift")]
macro_rules! unsafe_main_ptr {
($ptr:expr) => {{
$ptr
}};
}
#[cfg(feature = "noshift")]
macro_rules! unshift_ptr {
($ptr:expr) => {{
$ptr
}};
}

#[allow(dead_code)]
#[cold]
pub(crate) fn print(message: &[u8]) {
Expand Down
Binary file not shown.
Binary file modified wasm_abi/data/viceroy-component-adapter.library.wasm
Binary file not shown.
Binary file not shown.
Binary file modified wasm_abi/data/viceroy-component-adapter.wasm
Binary file not shown.
Loading