Repository: bytecodealliance/wasmtime
Pull request #8047 (2024-03-05 04:41:38)

Update object to 0.33.0
object 0.33.0 has a breaking change for relocations which needs a larger than usual fix here. I already did this fix for testing purposes, but I don't need the change myself.


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 494311255..e97968826 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -87,7 +87,7 @@ jobs:
     - uses: ./.github/actions/install-rust
     - run: |
         set -e
-        curl -L https://github.com/EmbarkStudios/cargo-deny/releases/download/0.8.5/cargo-deny-0.8.5-x86_64-unknown-linux-musl.tar.gz | tar xzf -
+        curl -L https://github.com/EmbarkStudios/cargo-deny/releases/download/0.14.5/cargo-deny-0.14.5-x86_64-unknown-linux-musl.tar.gz | tar xzf -
         mv cargo-deny-*-x86_64-unknown-linux-musl/cargo-deny cargo-deny
         echo `pwd` >> $GITHUB_PATH
     - run: cargo deny check bans licenses
diff --git a/Cargo.lock b/Cargo.lock
index 338273db2..5b7d43e2b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -151,7 +151,7 @@ dependencies = [
  "cfg-if",
  "libc",
  "miniz_oxide",
- "object",
+ "object 0.32.0",
  "rustc-demangle",
 ]
 
@@ -757,7 +757,7 @@ dependencies = [
  "cranelift-frontend",
  "cranelift-module",
  "log",
- "object",
+ "object 0.33.0",
  "target-lexicon",
 ]
 
@@ -1465,7 +1465,7 @@ dependencies = [
  "iana-time-zone-haiku",
  "js-sys",
  "wasm-bindgen",
- "windows-core 0.51.1",
+ "windows-core",
 ]
 
 [[package]]
@@ -1789,7 +1789,7 @@ version = "20.0.0"
 dependencies = [
  "anyhow",
  "libloading",
- "object",
+ "object 0.33.0",
 ]
 
 [[package]]
@@ -1852,6 +1852,15 @@ name = "object"
 version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "object"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d"
 dependencies = [
  "crc32fast",
  "hashbrown 0.14.0",
@@ -3046,7 +3055,7 @@ name = "wasi-preview1-component-adapter"
 version = "20.0.0"
 dependencies = [
  "byte-array-literals",
- "object",
+ "object 0.33.0",
  "wasi",
  "wasm-encoder",
  "wit-bindgen",
@@ -3246,7 +3255,7 @@ dependencies = [
  "ittapi",
  "libc",
  "log",
- "object",
+ "object 0.33.0",
  "once_cell",
  "paste",
  "rayon",
@@ -3457,7 +3466,7 @@ dependencies = [
  "cranelift-wasm",
  "gimli",
  "log",
- "object",
+ "object 0.33.0",
  "target-lexicon",
  "thiserror",
  "wasmparser",
@@ -3475,7 +3484,7 @@ dependencies = [
  "cranelift-control",
  "cranelift-native",
  "gimli",
- "object",
+ "object 0.33.0",
  "target-lexicon",
  "wasmtime-environ",
 ]
@@ -3493,7 +3502,7 @@ dependencies = [
  "gimli",
  "indexmap 2.0.0",
  "log",
- "object",
+ "object 0.33.0",
  "rustc-demangle",
  "serde",
  "serde_derive",
@@ -3608,7 +3617,7 @@ dependencies = [
 name = "wasmtime-jit-debug"
 version = "20.0.0"
 dependencies = [
- "object",
+ "object 0.33.0",
  "once_cell",
  "rustix",
  "wasmtime-versioned-export-macros",
@@ -3782,7 +3791,7 @@ dependencies = [
  "anyhow",
  "cranelift-codegen",
  "gimli",
- "object",
+ "object 0.33.0",
  "target-lexicon",
  "wasmparser",
  "wasmtime-cranelift-shared",
@@ -4017,19 +4026,10 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
 dependencies = [
- "windows-core 0.52.0",
+ "windows-core",
  "windows-targets 0.52.0",
 ]
 
-[[package]]
-name = "windows-core"
-version = "0.51.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
-dependencies = [
- "windows-targets 0.48.5",
-]
-
 [[package]]
 name = "windows-core"
 version = "0.52.0"
diff --git a/Cargo.toml b/Cargo.toml
index f68a5278e..9a8dcd502 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -243,7 +243,7 @@ wit-component = "0.201.0"
 
 # Non-Bytecode Alliance maintained dependencies:
 # --------------------------
-object = { version = "0.32", default-features = false, features = ['read_core', 'elf', 'std'] }
+object = { version = "0.33", default-features = false, features = ['read_core', 'elf', 'std'] }
 gimli = { version = "0.28.0", default-features = false, features = ['read', 'std'] }
 anyhow = "1.0.22"
 windows-sys = "0.52.0"
diff --git a/cranelift/object/src/backend.rs b/cranelift/object/src/backend.rs
index 67a71a9c3..3c37a5de5 100644
--- a/cranelift/object/src/backend.rs
+++ b/cranelift/object/src/backend.rs
@@ -15,7 +15,8 @@ use object::write::{
     Object, Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection,
 };
 use object::{
-    RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
+    RelocationEncoding, RelocationFlags, RelocationKind, SectionKind, SymbolFlags, SymbolKind,
+    SymbolScope,
 };
 use std::collections::hash_map::Entry;
 use std::collections::HashMap;
@@ -502,9 +503,7 @@ impl ObjectModule {
             for &ObjectRelocRecord {
                 offset,
                 ref name,
-                kind,
-                encoding,
-                size,
+                flags,
                 addend,
             } in &symbol.relocs
             {
@@ -514,9 +513,7 @@ impl ObjectModule {
                         symbol.section,
                         Relocation {
                             offset: symbol.offset + u64::from(offset),
-                            size,
-                            kind,
-                            encoding,
+                            flags,
                             symbol: target_symbol,
                             addend,
                         },
@@ -637,40 +634,58 @@ impl ObjectModule {
 
     fn process_reloc(&self, record: &ModuleReloc) -> ObjectRelocRecord {
         let mut addend = record.addend;
-        let (kind, encoding, size) = match record.kind {
-            Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32),
-            Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64),
-            Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32),
-            Reloc::X86CallPCRel4 => (RelocationKind::Relative, RelocationEncoding::X86Branch, 32),
+        let flags = match record.kind {
+            Reloc::Abs4 => RelocationFlags::Generic {
+                kind: RelocationKind::Absolute,
+                encoding: RelocationEncoding::Generic,
+                size: 32,
+            },
+            Reloc::Abs8 => RelocationFlags::Generic {
+                kind: RelocationKind::Absolute,
+                encoding: RelocationEncoding::Generic,
+                size: 64,
+            },
+            Reloc::X86PCRel4 => RelocationFlags::Generic {
+                kind: RelocationKind::Relative,
+                encoding: RelocationEncoding::Generic,
+                size: 32,
+            },
+            Reloc::X86CallPCRel4 => RelocationFlags::Generic {
+                kind: RelocationKind::Relative,
+                encoding: RelocationEncoding::X86Branch,
+                size: 32,
+            },
             // TODO: Get Cranelift to tell us when we can use
             // R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX.
-            Reloc::X86CallPLTRel4 => (
-                RelocationKind::PltRelative,
-                RelocationEncoding::X86Branch,
-                32,
-            ),
-            Reloc::X86SecRel => (
-                RelocationKind::SectionOffset,
-                RelocationEncoding::Generic,
-                32,
-            ),
-            Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32),
-            Reloc::Arm64Call => (
-                RelocationKind::Relative,
-                RelocationEncoding::AArch64Call,
-                26,
-            ),
+            Reloc::X86CallPLTRel4 => RelocationFlags::Generic {
+                kind: RelocationKind::PltRelative,
+                encoding: RelocationEncoding::X86Branch,
+                size: 32,
+            },
+            Reloc::X86SecRel => RelocationFlags::Generic {
+                kind: RelocationKind::SectionOffset,
+                encoding: RelocationEncoding::Generic,
+                size: 32,
+            },
+            Reloc::X86GOTPCRel4 => RelocationFlags::Generic {
+                kind: RelocationKind::GotRelative,
+                encoding: RelocationEncoding::Generic,
+                size: 32,
+            },
+            Reloc::Arm64Call => RelocationFlags::Generic {
+                kind: RelocationKind::Relative,
+                encoding: RelocationEncoding::AArch64Call,
+                size: 26,
+            },
             Reloc::ElfX86_64TlsGd => {
                 assert_eq!(
                     self.object.format(),
                     object::BinaryFormat::Elf,
                     "ElfX86_64TlsGd is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_X86_64_TLSGD),
-                    RelocationEncoding::Generic,
-                    32,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_X86_64_TLSGD,
+                }
             }
             Reloc::MachOX86_64Tlv => {
                 assert_eq!(
@@ -679,14 +694,11 @@ impl ObjectModule {
                     "MachOX86_64Tlv is not supported for this file format"
                 );
                 addend += 4; // X86_64_RELOC_TLV has an implicit addend of -4
-                (
-                    RelocationKind::MachO {
-                        value: object::macho::X86_64_RELOC_TLV,
-                        relative: true,
-                    },
-                    RelocationEncoding::Generic,
-                    32,
-                )
+                RelocationFlags::MachO {
+                    r_type: object::macho::X86_64_RELOC_TLV,
+                    r_pcrel: true,
+                    r_length: 2,
+                }
             }
             Reloc::MachOAarch64TlsAdrPage21 => {
                 assert_eq!(
@@ -694,14 +706,11 @@ impl ObjectModule {
                     object::BinaryFormat::MachO,
                     "MachOAarch64TlsAdrPage21 is not supported for this file format"
                 );
-                (
-                    RelocationKind::MachO {
-                        value: object::macho::ARM64_RELOC_TLVP_LOAD_PAGE21,
-                        relative: true,
-                    },
-                    RelocationEncoding::Generic,
-                    21,
-                )
+                RelocationFlags::MachO {
+                    r_type: object::macho::ARM64_RELOC_TLVP_LOAD_PAGE21,
+                    r_pcrel: true,
+                    r_length: 2,
+                }
             }
             Reloc::MachOAarch64TlsAdrPageOff12 => {
                 assert_eq!(
@@ -709,14 +718,11 @@ impl ObjectModule {
                     object::BinaryFormat::MachO,
                     "MachOAarch64TlsAdrPageOff12 is not supported for this file format"
                 );
-                (
-                    RelocationKind::MachO {
-                        value: object::macho::ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
-                        relative: false,
-                    },
-                    RelocationEncoding::Generic,
-                    12,
-                )
+                RelocationFlags::MachO {
+                    r_type: object::macho::ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
+                    r_pcrel: false,
+                    r_length: 2,
+                }
             }
             Reloc::Aarch64TlsDescAdrPage21 => {
                 assert_eq!(
@@ -724,11 +730,9 @@ impl ObjectModule {
                     object::BinaryFormat::Elf,
                     "Aarch64TlsDescAdrPage21 is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_AARCH64_TLSDESC_ADR_PAGE21),
-                    RelocationEncoding::Generic,
-                    21,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_AARCH64_TLSDESC_ADR_PAGE21,
+                }
             }
             Reloc::Aarch64TlsDescLd64Lo12 => {
                 assert_eq!(
@@ -736,11 +740,9 @@ impl ObjectModule {
                     object::BinaryFormat::Elf,
                     "Aarch64TlsDescLd64Lo12 is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_AARCH64_TLSDESC_LD64_LO12),
-                    RelocationEncoding::Generic,
-                    12,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_AARCH64_TLSDESC_LD64_LO12,
+                }
             }
             Reloc::Aarch64TlsDescAddLo12 => {
                 assert_eq!(
@@ -748,11 +750,9 @@ impl ObjectModule {
                     object::BinaryFormat::Elf,
                     "Aarch64TlsDescAddLo12 is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_AARCH64_TLSDESC_ADD_LO12),
-                    RelocationEncoding::Generic,
-                    12,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_AARCH64_TLSDESC_ADD_LO12,
+                }
             }
             Reloc::Aarch64TlsDescCall => {
                 assert_eq!(
@@ -760,62 +760,52 @@ impl ObjectModule {
                     object::BinaryFormat::Elf,
                     "Aarch64TlsDescCall is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_AARCH64_TLSDESC_CALL),
-                    RelocationEncoding::Generic,
-                    0,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_AARCH64_TLSDESC_CALL,
+                }
             }
 
             Reloc::Aarch64AdrGotPage21 => match self.object.format() {
-                object::BinaryFormat::Elf => (
-                    RelocationKind::Elf(object::elf::R_AARCH64_ADR_GOT_PAGE),
-                    RelocationEncoding::Generic,
-                    21,
-                ),
-                object::BinaryFormat::MachO => (
-                    RelocationKind::MachO {
-                        value: object::macho::ARM64_RELOC_GOT_LOAD_PAGE21,
-                        relative: true,
-                    },
-                    RelocationEncoding::Generic,
-                    21,
-                ),
+                object::BinaryFormat::Elf => RelocationFlags::Elf {
+                    r_type: object::elf::R_AARCH64_ADR_GOT_PAGE,
+                },
+                object::BinaryFormat::MachO => RelocationFlags::MachO {
+                    r_type: object::macho::ARM64_RELOC_GOT_LOAD_PAGE21,
+                    r_pcrel: true,
+                    r_length: 2,
+                },
                 _ => unimplemented!("Aarch64AdrGotPage21 is not supported for this file format"),
             },
             Reloc::Aarch64Ld64GotLo12Nc => match self.object.format() {
-                object::BinaryFormat::Elf => (
-                    RelocationKind::Elf(object::elf::R_AARCH64_LD64_GOT_LO12_NC),
-                    RelocationEncoding::Generic,
-                    12,
-                ),
-                object::BinaryFormat::MachO => (
-                    RelocationKind::MachO {
-                        value: object::macho::ARM64_RELOC_GOT_LOAD_PAGEOFF12,
-                        relative: false,
-                    },
-                    RelocationEncoding::Generic,
-                    12,
-                ),
+                object::BinaryFormat::Elf => RelocationFlags::Elf {
+                    r_type: object::elf::R_AARCH64_LD64_GOT_LO12_NC,
+                },
+                object::BinaryFormat::MachO => RelocationFlags::MachO {
+                    r_type: object::macho::ARM64_RELOC_GOT_LOAD_PAGEOFF12,
+                    r_pcrel: false,
+                    r_length: 2,
+                },
                 _ => unimplemented!("Aarch64Ld64GotLo12Nc is not supported for this file format"),
             },
-            Reloc::S390xPCRel32Dbl => (RelocationKind::Relative, RelocationEncoding::S390xDbl, 32),
-            Reloc::S390xPLTRel32Dbl => (
-                RelocationKind::PltRelative,
-                RelocationEncoding::S390xDbl,
-                32,
-            ),
+            Reloc::S390xPCRel32Dbl => RelocationFlags::Generic {
+                kind: RelocationKind::Relative,
+                encoding: RelocationEncoding::S390xDbl,
+                size: 32,
+            },
+            Reloc::S390xPLTRel32Dbl => RelocationFlags::Generic {
+                kind: RelocationKind::PltRelative,
+                encoding: RelocationEncoding::S390xDbl,
+                size: 32,
+            },
             Reloc::S390xTlsGd64 => {
                 assert_eq!(
                     self.object.format(),
                     object::BinaryFormat::Elf,
                     "S390xTlsGd64 is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_390_TLS_GD64),
-                    RelocationEncoding::Generic,
-                    64,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_390_TLS_GD64,
+                }
             }
             Reloc::S390xTlsGdCall => {
                 assert_eq!(
@@ -823,11 +813,9 @@ impl ObjectModule {
                     object::BinaryFormat::Elf,
                     "S390xTlsGdCall is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_390_TLS_GDCALL),
-                    RelocationEncoding::Generic,
-                    0,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_390_TLS_GDCALL,
+                }
             }
             Reloc::RiscvCallPlt => {
                 assert_eq!(
@@ -835,11 +823,9 @@ impl ObjectModule {
                     object::BinaryFormat::Elf,
                     "RiscvCallPlt is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_RISCV_CALL_PLT),
-                    RelocationEncoding::Generic,
-                    0,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_RISCV_CALL_PLT,
+                }
             }
             Reloc::RiscvTlsGdHi20 => {
                 assert_eq!(
@@ -847,11 +833,9 @@ impl ObjectModule {
                     object::BinaryFormat::Elf,
                     "RiscvTlsGdHi20 is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_RISCV_TLS_GD_HI20),
-                    RelocationEncoding::Generic,
-                    0,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_RISCV_TLS_GD_HI20,
+                }
             }
             Reloc::RiscvPCRelLo12I => {
                 assert_eq!(
@@ -859,11 +843,9 @@ impl ObjectModule {
                     object::BinaryFormat::Elf,
                     "RiscvPCRelLo12I is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_RISCV_PCREL_LO12_I),
-                    RelocationEncoding::Generic,
-                    0,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_RISCV_PCREL_LO12_I,
+                }
             }
             Reloc::RiscvGotHi20 => {
                 assert_eq!(
@@ -871,11 +853,9 @@ impl ObjectModule {
                     object::BinaryFormat::Elf,
                     "RiscvGotHi20 is not supported for this file format"
                 );
-                (
-                    RelocationKind::Elf(object::elf::R_RISCV_GOT_HI20),
-                    RelocationEncoding::Generic,
-                    0,
-                )
+                RelocationFlags::Elf {
+                    r_type: object::elf::R_RISCV_GOT_HI20,
+                }
             }
             // FIXME
             reloc => unimplemented!("{:?}", reloc),
@@ -884,9 +864,7 @@ impl ObjectModule {
         ObjectRelocRecord {
             offset: record.offset,
             name: record.name.clone(),
-            kind,
-            encoding,
-            size,
+            flags,
             addend,
         }
     }
@@ -948,8 +926,6 @@ struct SymbolRelocs {
 struct ObjectRelocRecord {
     offset: CodeOffset,
     name: ModuleRelocTarget,
-    kind: RelocationKind,
-    encoding: RelocationEncoding,
-    size: u8,
+    flags: RelocationFlags,
     addend: Addend,
 }
diff --git a/crates/cranelift-shared/src/obj.rs b/crates/cranelift-shared/src/obj.rs
index e430e5ff6..8f1519bd6 100644
--- a/crates/cranelift-shared/src/obj.rs
+++ b/crates/cranelift-shared/src/obj.rs
@@ -183,12 +183,12 @@ impl<'a> ModuleTextBuilder<'a> {
                             flags: SymbolFlags::None,
                         })
                     });
-                    let (encoding, kind, size) = match r.reloc {
-                        Reloc::Abs8 => (
-                            object::RelocationEncoding::Generic,
-                            object::RelocationKind::Absolute,
-                            8,
-                        ),
+                    let flags = match r.reloc {
+                        Reloc::Abs8 => object::RelocationFlags::Generic {
+                            encoding: object::RelocationEncoding::Generic,
+                            kind: object::RelocationKind::Absolute,
+                            size: 8,
+                        },
                         other => unimplemented!("unimplemented relocation kind {other:?}"),
                     };
                     self.obj
@@ -196,9 +196,7 @@ impl<'a> ModuleTextBuilder<'a> {
                             self.text_section,
                             object::write::Relocation {
                                 symbol,
-                                size,
-                                kind,
-                                encoding,
+                                flags,
                                 offset: off + u64::from(r.offset),
                                 addend: r.addend,
                             },
diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs
index 326c4eb01..ceb8d2ecf 100644
--- a/crates/cranelift/src/compiler.rs
+++ b/crates/cranelift/src/compiler.rs
@@ -20,7 +20,7 @@ use cranelift_wasm::{
     WasmValType,
 };
 use object::write::{Object, StandardSegment, SymbolId};
-use object::{RelocationEncoding, RelocationKind, SectionKind};
+use object::{RelocationEncoding, RelocationFlags, RelocationKind, SectionKind};
 use std::any::Any;
 use std::cmp;
 use std::collections::HashMap;
@@ -613,11 +613,13 @@ impl wasmtime_environ::Compiler for Compiler {
                     section_id,
                     object::write::Relocation {
                         offset: u64::from(reloc.offset),
-                        size: reloc.size << 3,
-                        kind: RelocationKind::Absolute,
-                        encoding: RelocationEncoding::Generic,
                         symbol: target_symbol,
                         addend: i64::from(reloc.addend),
+                        flags: RelocationFlags::Generic {
+                            size: reloc.size << 3,
+                            kind: RelocationKind::Absolute,
+                            encoding: RelocationEncoding::Generic,
+                        },
                     },
                 )?;
             }
diff --git a/crates/wasi-http/src/body.rs b/crates/wasi-http/src/body.rs
index ae8e9ff9a..64819e027 100644
--- a/crates/wasi-http/src/body.rs
+++ b/crates/wasi-http/src/body.rs
@@ -10,7 +10,8 @@ use std::task::{Context, Poll};
 use std::{pin::Pin, sync::Arc, time::Duration};
 use tokio::sync::{mpsc, oneshot};
 use wasmtime_wasi::{
-    poll_noop, AbortOnDropJoinHandle, HostInputStream, HostOutputStream, StreamError, Subscribe,
+    runtime::{poll_noop, AbortOnDropJoinHandle},
+    HostInputStream, HostOutputStream, StreamError, Subscribe,
 };
 
 pub type HyperIncomingBody = BoxBody<Bytes, types::ErrorCode>;
@@ -35,7 +36,7 @@ impl BodyWithTimeout {
             inner,
             between_bytes_timeout,
             reset_sleep: true,
-            timeout: Box::pin(wasmtime_wasi::with_ambient_tokio_runtime(|| {
+            timeout: Box::pin(wasmtime_wasi::runtime::with_ambient_tokio_runtime(|| {
                 tokio::time::sleep(Duration::new(0, 0))
             })),
         }
diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs
index 5d8e9e5c4..6966ed08b 100644
--- a/crates/wasi-http/src/types.rs
+++ b/crates/wasi-http/src/types.rs
@@ -15,7 +15,7 @@ use std::time::Duration;
 use tokio::net::TcpStream;
 use tokio::time::timeout;
 use wasmtime::component::{Resource, ResourceTable};
-use wasmtime_wasi::{AbortOnDropJoinHandle, Subscribe};
+use wasmtime_wasi::{runtime::AbortOnDropJoinHandle, Subscribe};
 
 /// Capture the state necessary for use in the wasi-http API implementation.
 pub struct WasiHttpCtx;
@@ -122,7 +122,7 @@ pub fn default_send_request(
         between_bytes_timeout,
     }: OutgoingRequest,
 ) -> wasmtime::Result<Resource<HostFutureIncomingResponse>> {
-    let handle = wasmtime_wasi::spawn(async move {
+    let handle = wasmtime_wasi::runtime::spawn(async move {
         let resp = handler(
             authority,
             use_tls,
@@ -212,7 +212,7 @@ async fn handler(
             .map_err(|_| types::ErrorCode::ConnectionTimeout)?
             .map_err(hyper_request_error)?;
 
-            let worker = wasmtime_wasi::spawn(async move {
+            let worker = wasmtime_wasi::runtime::spawn(async move {
                 match conn.await {
                     Ok(()) => {}
                     // TODO: shouldn't throw away this error and ideally should
@@ -234,7 +234,7 @@ async fn handler(
         .map_err(|_| types::ErrorCode::ConnectionTimeout)?
         .map_err(hyper_request_error)?;
 
-        let worker = wasmtime_wasi::spawn(async move {
+        let worker = wasmtime_wasi::runtime::spawn(async move {
             match conn.await {
                 Ok(()) => {}
                 // TODO: same as above, shouldn't throw this error away.
diff --git a/crates/wasi/src/bindings.rs b/crates/wasi/src/bindings.rs
new file mode 100644
index 000000000..034aaa92d
--- /dev/null
+++ b/crates/wasi/src/bindings.rs
@@ -0,0 +1,120 @@
+// Generate traits for synchronous bindings.
+//
+// Note that this is only done for interfaces which can block, or those which
+// have some functions in `only_imports` below for being async.
+pub mod sync_io {
+    pub(crate) mod _internal {
+        use crate::{FsError, StreamError};
+
+        wasmtime::component::bindgen!({
+            path: "wit",
+            interfaces: "
+                    import wasi:io/poll@0.2.0;
+                    import wasi:io/streams@0.2.0;
+                    import wasi:filesystem/types@0.2.0;
+                ",
+            tracing: true,
+            trappable_error_type: {
+                "wasi:io/streams/stream-error" => StreamError,
+                "wasi:filesystem/types/error-code" => FsError,
+            },
+            with: {
+                "wasi:clocks/wall-clock": crate::bindings::clocks::wall_clock,
+                "wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor,
+                "wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream,
+                "wasi:io/poll/pollable": super::super::io::poll::Pollable,
+                "wasi:io/streams/input-stream": super::super::io::streams::InputStream,
+                "wasi:io/streams/output-stream": super::super::io::streams::OutputStream,
+                "wasi:io/error/error": super::super::io::error::Error,
+            }
+        });
+    }
+    pub use self::_internal::wasi::{filesystem, io};
+}
+
+wasmtime::component::bindgen!({
+    path: "wit",
+    world: "wasi:cli/imports",
+    tracing: true,
+    async: {
+        // Only these functions are `async` and everything else is sync
+        // meaning that it basically doesn't need to block. These functions
+        // are the only ones that need to block.
+        //
+        // Note that at this time `only_imports` works on function names
+        // which in theory can be shared across interfaces, so this may
+        // need fancier syntax in the future.
+        only_imports: [
+            "[method]descriptor.access-at",
+            "[method]descriptor.advise",
+            "[method]descriptor.change-directory-permissions-at",
+            "[method]descriptor.change-file-permissions-at",
+            "[method]descriptor.create-directory-at",
+            "[method]descriptor.get-flags",
+            "[method]descriptor.get-type",
+            "[method]descriptor.is-same-object",
+            "[method]descriptor.link-at",
+            "[method]descriptor.lock-exclusive",
+            "[method]descriptor.lock-shared",
+            "[method]descriptor.metadata-hash",
+            "[method]descriptor.metadata-hash-at",
+            "[method]descriptor.open-at",
+            "[method]descriptor.read",
+            "[method]descriptor.read-directory",
+            "[method]descriptor.readlink-at",
+            "[method]descriptor.remove-directory-at",
+            "[method]descriptor.rename-at",
+            "[method]descriptor.set-size",
+            "[method]descriptor.set-times",
+            "[method]descriptor.set-times-at",
+            "[method]descriptor.stat",
+            "[method]descriptor.stat-at",
+            "[method]descriptor.symlink-at",
+            "[method]descriptor.sync",
+            "[method]descriptor.sync-data",
+            "[method]descriptor.try-lock-exclusive",
+            "[method]descriptor.try-lock-shared",
+            "[method]descriptor.unlink-file-at",
+            "[method]descriptor.unlock",
+            "[method]descriptor.write",
+            "[method]input-stream.read",
+            "[method]input-stream.blocking-read",
+            "[method]input-stream.blocking-skip",
+            "[method]input-stream.skip",
+            "[method]output-stream.forward",
+            "[method]output-stream.splice",
+            "[method]output-stream.blocking-splice",
+            "[method]output-stream.blocking-flush",
+            "[method]output-stream.blocking-write",
+            "[method]output-stream.blocking-write-and-flush",
+            "[method]output-stream.blocking-write-zeroes-and-flush",
+            "[method]directory-entry-stream.read-directory-entry",
+            "poll",
+            "[method]pollable.block",
+            "[method]pollable.ready",
+        ],
+    },
+    trappable_error_type: {
+        "wasi:io/streams/stream-error" => crate::StreamError,
+        "wasi:filesystem/types/error-code" => crate::FsError,
+        "wasi:sockets/network/error-code" => crate::SocketError,
+    },
+    with: {
+        "wasi:sockets/network/network": super::network::Network,
+        "wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket,
+        "wasi:sockets/udp/udp-socket": super::udp::UdpSocket,
+        "wasi:sockets/udp/incoming-datagram-stream": super::udp::IncomingDatagramStream,
+        "wasi:sockets/udp/outgoing-datagram-stream": super::udp::OutgoingDatagramStream,
+        "wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream,
+        "wasi:filesystem/types/directory-entry-stream": super::filesystem::ReaddirIterator,
+        "wasi:filesystem/types/descriptor": super::filesystem::Descriptor,
+        "wasi:io/streams/input-stream": super::stream::InputStream,
+        "wasi:io/streams/output-stream": super::stream::OutputStream,
+        "wasi:io/error/error": super::stream::Error,
+        "wasi:io/poll/pollable": super::poll::Pollable,
+        "wasi:cli/terminal-input/terminal-input": super::stdio::TerminalInput,
+        "wasi:cli/terminal-output/terminal-output": super::stdio::TerminalOutput,
+    },
+});
+
+pub use wasi::*;
diff --git a/crates/wasi/src/filesystem.rs b/crates/wasi/src/filesystem.rs
index a71305501..86bd9398d 100644
--- a/crates/wasi/src/filesystem.rs
+++ b/crates/wasi/src/filesystem.rs
@@ -1,7 +1,6 @@
 use crate::bindings::filesystem::types;
-use crate::{
-    spawn_blocking, AbortOnDropJoinHandle, HostOutputStream, StreamError, Subscribe, TrappableError,
-};
+use crate::runtime::{spawn_blocking, AbortOnDropJoinHandle};
+use crate::{HostOutputStream, StreamError, Subscribe, TrappableError};
 use anyhow::anyhow;
 use bytes::{Bytes, BytesMut};
 use std::io;
diff --git a/crates/wasi/src/host/filesystem/sync.rs b/crates/wasi/src/host/filesystem/sync.rs
index 08b446e45..6d12e50dd 100644
--- a/crates/wasi/src/host/filesystem/sync.rs
+++ b/crates/wasi/src/host/filesystem/sync.rs
@@ -1,7 +1,8 @@
 use crate::bindings::filesystem::types as async_filesystem;
 use crate::bindings::sync_io::filesystem::types as sync_filesystem;
 use crate::bindings::sync_io::io::streams;
-use crate::{in_tokio, FsError, FsResult};
+use crate::runtime::in_tokio;
+use crate::{FsError, FsResult};
 use wasmtime::component::Resource;
 
 impl<T: async_filesystem::Host> sync_filesystem::Host for T {
diff --git a/crates/wasi/src/host/io.rs b/crates/wasi/src/host/io.rs
index 656b31bad..168088d9b 100644
--- a/crates/wasi/src/host/io.rs
+++ b/crates/wasi/src/host/io.rs
@@ -232,7 +232,8 @@ pub mod sync {
         },
         bindings::sync_io::io::poll::Pollable,
         bindings::sync_io::io::streams::{self, InputStream, OutputStream},
-        in_tokio, StreamError, StreamResult, WasiView,
+        runtime::in_tokio,
+        StreamError, StreamResult, WasiView,
     };
     use wasmtime::component::Resource;
 
diff --git a/crates/wasi/src/host/tcp.rs b/crates/wasi/src/host/tcp.rs
index 0f08862f7..11d44d882 100644
--- a/crates/wasi/src/host/tcp.rs
+++ b/crates/wasi/src/host/tcp.rs
@@ -1,5 +1,6 @@
 use crate::host::network::util;
 use crate::network::SocketAddrUse;
+use crate::runtime::with_ambient_tokio_runtime;
 use crate::tcp::{TcpReadStream, TcpSocket, TcpState, TcpWriteStream};
 use crate::{
     bindings::{
@@ -9,7 +10,7 @@ use crate::{
     },
     network::SocketAddressFamily,
 };
-use crate::{with_ambient_tokio_runtime, Pollable, SocketResult, WasiView};
+use crate::{Pollable, SocketResult, WasiView};
 use io_lifetimes::AsSocketlike;
 use rustix::io::Errno;
 use rustix::net::sockopt;
diff --git a/crates/wasi/src/ip_name_lookup.rs b/crates/wasi/src/ip_name_lookup.rs
index 85f5e8e6e..1ab3099a8 100644
--- a/crates/wasi/src/ip_name_lookup.rs
+++ b/crates/wasi/src/ip_name_lookup.rs
@@ -2,7 +2,8 @@ use crate::bindings::sockets::ip_name_lookup::{Host, HostResolveAddressStream};
 use crate::bindings::sockets::network::{ErrorCode, IpAddress, Network};
 use crate::host::network::util;
 use crate::poll::{subscribe, Pollable, Subscribe};
-use crate::{spawn_blocking, AbortOnDropJoinHandle, SocketError, WasiView};
+use crate::runtime::{spawn_blocking, AbortOnDropJoinHandle};
+use crate::{SocketError, WasiView};
 use anyhow::Result;
 use std::mem;
 use std::net::{Ipv6Addr, ToSocketAddrs};
@@ -48,12 +49,14 @@ impl<T: WasiView> HostResolveAddressStream for T {
         let stream: &mut ResolveAddressStream = self.table().get_mut(&resource)?;
         loop {
             match stream {
-                ResolveAddressStream::Waiting(future) => match crate::poll_noop(Pin::new(future)) {
-                    Some(result) => {
-                        *stream = ResolveAddressStream::Done(result.map(|v| v.into_iter()));
+                ResolveAddressStream::Waiting(future) => {
+                    match crate::runtime::poll_noop(Pin::new(future)) {
+                        Some(result) => {
+                            *stream = ResolveAddressStream::Done(result.map(|v| v.into_iter()));
+                        }
+                        None => return Err(ErrorCode::WouldBlock.into()),
                     }
-                    None => return Err(ErrorCode::WouldBlock.into()),
-                },
+                }
                 ResolveAddressStream::Done(slot @ Err(_)) => {
                     mem::replace(slot, Ok(Vec::new().into_iter()))?;
                     unreachable!();
diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs
index 8262d889b..225245b47 100644
--- a/crates/wasi/src/lib.rs
+++ b/crates/wasi/src/lib.rs
@@ -6,10 +6,7 @@
 //!
 //!
 
-use std::future::Future;
-use std::pin::Pin;
-use std::task::{Context, Poll};
-
+pub mod bindings;
 mod clocks;
 pub mod command;
 mod ctx;
@@ -25,6 +22,7 @@ pub mod preview0;
 #[cfg(feature = "preview1")]
 pub mod preview1;
 mod random;
+pub mod runtime;
 mod stdio;
 mod stream;
 mod tcp;
@@ -47,272 +45,3 @@ pub use self::stream::{
 pub use cap_fs_ext::SystemTimeSpec;
 pub use cap_rand::RngCore;
 pub use wasmtime::component::{ResourceTable, ResourceTableError};
-
-pub mod bindings {
-    // Generate traits for synchronous bindings.
-    //
-    // Note that this is only done for interfaces which can block, or those which
-    // have some functions in `only_imports` below for being async.
-    pub mod sync_io {
-        pub(crate) mod _internal {
-            use crate::{FsError, StreamError};
-
-            wasmtime::component::bindgen!({
-                path: "wit",
-                interfaces: "
-                    import wasi:io/poll@0.2.0;
-                    import wasi:io/streams@0.2.0;
-                    import wasi:filesystem/types@0.2.0;
-                ",
-                tracing: true,
-                trappable_error_type: {
-                    "wasi:io/streams/stream-error" => StreamError,
-                    "wasi:filesystem/types/error-code" => FsError,
-                },
-                with: {
-                    "wasi:clocks/wall-clock": crate::bindings::clocks::wall_clock,
-                    "wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor,
-                    "wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream,
-                    "wasi:io/poll/pollable": super::super::io::poll::Pollable,
-                    "wasi:io/streams/input-stream": super::super::io::streams::InputStream,
-                    "wasi:io/streams/output-stream": super::super::io::streams::OutputStream,
-                    "wasi:io/error/error": super::super::io::error::Error,
-                }
-            });
-        }
-        pub use self::_internal::wasi::{filesystem, io};
-    }
-
-    wasmtime::component::bindgen!({
-        path: "wit",
-        world: "wasi:cli/imports",
-        tracing: true,
-        async: {
-            // Only these functions are `async` and everything else is sync
-            // meaning that it basically doesn't need to block. These functions
-            // are the only ones that need to block.
-            //
-            // Note that at this time `only_imports` works on function names
-            // which in theory can be shared across interfaces, so this may
-            // need fancier syntax in the future.
-            only_imports: [
-                "[method]descriptor.access-at",
-                "[method]descriptor.advise",
-                "[method]descriptor.change-directory-permissions-at",
-                "[method]descriptor.change-file-permissions-at",
-                "[method]descriptor.create-directory-at",
-                "[method]descriptor.get-flags",
-                "[method]descriptor.get-type",
-                "[method]descriptor.is-same-object",
-                "[method]descriptor.link-at",
-                "[method]descriptor.lock-exclusive",
-                "[method]descriptor.lock-shared",
-                "[method]descriptor.metadata-hash",
-                "[method]descriptor.metadata-hash-at",
-                "[method]descriptor.open-at",
-                "[method]descriptor.read",
-                "[method]descriptor.read-directory",
-                "[method]descriptor.readlink-at",
-                "[method]descriptor.remove-directory-at",
-                "[method]descriptor.rename-at",
-                "[method]descriptor.set-size",
-                "[method]descriptor.set-times",
-                "[method]descriptor.set-times-at",
-                "[method]descriptor.stat",
-                "[method]descriptor.stat-at",
-                "[method]descriptor.symlink-at",
-                "[method]descriptor.sync",
-                "[method]descriptor.sync-data",
-                "[method]descriptor.try-lock-exclusive",
-                "[method]descriptor.try-lock-shared",
-                "[method]descriptor.unlink-file-at",
-                "[method]descriptor.unlock",
-                "[method]descriptor.write",
-                "[method]input-stream.read",
-                "[method]input-stream.blocking-read",
-                "[method]input-stream.blocking-skip",
-                "[method]input-stream.skip",
-                "[method]output-stream.forward",
-                "[method]output-stream.splice",
-                "[method]output-stream.blocking-splice",
-                "[method]output-stream.blocking-flush",
-                "[method]output-stream.blocking-write",
-                "[method]output-stream.blocking-write-and-flush",
-                "[method]output-stream.blocking-write-zeroes-and-flush",
-                "[method]directory-entry-stream.read-directory-entry",
-                "poll",
-                "[method]pollable.block",
-                "[method]pollable.ready",
-            ],
-        },
-        trappable_error_type: {
-            "wasi:io/streams/stream-error" => crate::StreamError,
-            "wasi:filesystem/types/error-code" => crate::FsError,
-            "wasi:sockets/network/error-code" => crate::SocketError,
-        },
-        with: {
-            "wasi:sockets/network/network": super::network::Network,
-            "wasi:sockets/tcp/tcp-socket": super::tcp::TcpSocket,
-            "wasi:sockets/udp/udp-socket": super::udp::UdpSocket,
-            "wasi:sockets/udp/incoming-datagram-stream": super::udp::IncomingDatagramStream,
-            "wasi:sockets/udp/outgoing-datagram-stream": super::udp::OutgoingDatagramStream,
-            "wasi:sockets/ip-name-lookup/resolve-address-stream": super::ip_name_lookup::ResolveAddressStream,
-            "wasi:filesystem/types/directory-entry-stream": super::filesystem::ReaddirIterator,
-            "wasi:filesystem/types/descriptor": super::filesystem::Descriptor,
-            "wasi:io/streams/input-stream": super::stream::InputStream,
-            "wasi:io/streams/output-stream": super::stream::OutputStream,
-            "wasi:io/error/error": super::stream::Error,
-            "wasi:io/poll/pollable": super::poll::Pollable,
-            "wasi:cli/terminal-input/terminal-input": super::stdio::TerminalInput,
-            "wasi:cli/terminal-output/terminal-output": super::stdio::TerminalOutput,
-        },
-    });
-
-    pub use wasi::*;
-}
-
-pub(crate) static RUNTIME: once_cell::sync::Lazy<tokio::runtime::Runtime> =
-    once_cell::sync::Lazy::new(|| {
-        tokio::runtime::Builder::new_current_thread()
-            .enable_time()
-            .enable_io()
-            .build()
-            .unwrap()
-    });
-
-pub struct AbortOnDropJoinHandle<T>(tokio::task::JoinHandle<T>);
-impl<T> Drop for AbortOnDropJoinHandle<T> {
-    fn drop(&mut self) {
-        self.0.abort()
-    }
-}
-impl<T> std::ops::Deref for AbortOnDropJoinHandle<T> {
-    type Target = tokio::task::JoinHandle<T>;
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-impl<T> std::ops::DerefMut for AbortOnDropJoinHandle<T> {
-    fn deref_mut(&mut self) -> &mut tokio::task::JoinHandle<T> {
-        &mut self.0
-    }
-}
-impl<T> From<tokio::task::JoinHandle<T>> for AbortOnDropJoinHandle<T> {
-    fn from(jh: tokio::task::JoinHandle<T>) -> Self {
-        AbortOnDropJoinHandle(jh)
-    }
-}
-impl<T> Future for AbortOnDropJoinHandle<T> {
-    type Output = T;
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        match Pin::new(&mut self.as_mut().0).poll(cx) {
-            Poll::Pending => Poll::Pending,
-            Poll::Ready(r) => Poll::Ready(r.expect("child task panicked")),
-        }
-    }
-}
-
-pub fn spawn<F>(f: F) -> AbortOnDropJoinHandle<F::Output>
-where
-    F: Future + Send + 'static,
-    F::Output: Send + 'static,
-{
-    let j = with_ambient_tokio_runtime(|| tokio::task::spawn(f));
-    AbortOnDropJoinHandle(j)
-}
-
-pub fn spawn_blocking<F, R>(f: F) -> AbortOnDropJoinHandle<R>
-where
-    F: FnOnce() -> R + Send + 'static,
-    R: Send + 'static,
-{
-    let j = with_ambient_tokio_runtime(|| tokio::task::spawn_blocking(f));
-    AbortOnDropJoinHandle(j)
-}
-
-pub fn in_tokio<F: Future>(f: F) -> F::Output {
-    match tokio::runtime::Handle::try_current() {
-        Ok(h) => {
-            let _enter = h.enter();
-            h.block_on(f)
-        }
-        // The `yield_now` here is non-obvious and if you're reading this
-        // you're likely curious about why it's here. This is currently required
-        // to get some features of "sync mode" working correctly, such as with
-        // the CLI. To illustrate why this is required, consider a program
-        // organized as:
-        //
-        // * A program has a `pollable` that it's waiting on.
-        // * This `pollable` is always ready .
-        // * Actually making the corresponding operation ready, however,
-        //   requires some background work on Tokio's part.
-        // * The program is looping on "wait for readiness" coupled with
-        //   performing the operation.
-        //
-        // In this situation this program ends up infinitely looping in waiting
-        // for pollables. The reason appears to be that when we enter the tokio
-        // runtime here it doesn't necessary yield to background work because
-        // the provided future `f` is ready immediately. The future `f` will run
-        // through the list of pollables and determine one of them is ready.
-        //
-        // Historically this happened with UDP sockets. A test send a datagram
-        // from one socket to another and the other socket infinitely didn't
-        // receive the data. This appeared to be because the server socket was
-        // waiting on `READABLE | WRITABLE` (which is itself a bug but ignore
-        // that) and the socket was currently in the "writable" state but never
-        // ended up receiving a notification for the "readable" state. Moving
-        // the socket to "readable" would require Tokio to perform some
-        // background work via epoll/kqueue/handle events but if the future
-        // provided here is always ready, then that never happened.
-        //
-        // Thus the `yield_now()` is an attempt to force Tokio to go do some
-        // background work eventually and look at new interest masks for
-        // example. This is a bit of a kludge but everything's already a bit
-        // wonky in synchronous mode anyway. Note that this is hypothesized to
-        // not be an issue in async mode because async mode typically has the
-        // Tokio runtime in a separate thread or otherwise participating in a
-        // larger application, it's only here in synchronous mode where we
-        // effectively own the runtime that we need some special care.
-        Err(_) => {
-            let _enter = RUNTIME.enter();
-            RUNTIME.block_on(async move {
-                tokio::task::yield_now().await;
-                f.await
-            })
-        }
-    }
-}
-
-/// Executes the closure `f` with an "ambient Tokio runtime" which basically
-/// means that if code in `f` tries to get a runtime `Handle` it'll succeed.
-///
-/// If a `Handle` is already available, e.g. in async contexts, then `f` is run
-/// immediately. Otherwise for synchronous contexts this crate's fallback
-/// runtime is configured and then `f` is executed.
-pub fn with_ambient_tokio_runtime<R>(f: impl FnOnce() -> R) -> R {
-    match tokio::runtime::Handle::try_current() {
-        Ok(_) => f(),
-        Err(_) => {
-            let _enter = RUNTIME.enter();
-            f()
-        }
-    }
-}
-
-/// Attempts to get the result of a `future`.
-///
-/// This function does not block and will poll the provided future once. If the
-/// result is here then `Some` is returned, otherwise `None` is returned.
-///
-/// Note that by polling `future` this means that `future` must be re-polled
-/// later if it's to wake up a task.
-pub fn poll_noop<F>(future: Pin<&mut F>) -> Option<F::Output>
-where
-    F: Future,
-{
-    let mut task = Context::from_waker(futures::task::noop_waker_ref());
-    match future.poll(&mut task) {
-        Poll::Ready(result) => Some(result),
-        Poll::Pending => None,
-    }
-}
diff --git a/crates/wasi/src/pipe.rs b/crates/wasi/src/pipe.rs
index 48706a2c8..819455c59 100644
--- a/crates/wasi/src/pipe.rs
+++ b/crates/wasi/src/pipe.rs
@@ -112,7 +112,7 @@ pub struct AsyncReadStream {
     closed: bool,
     buffer: Option<Result<Bytes, StreamError>>,
     receiver: mpsc::Receiver<Result<Bytes, StreamError>>,
-    _join_handle: crate::AbortOnDropJoinHandle<()>,
+    _join_handle: crate::runtime::AbortOnDropJoinHandle<()>,
 }
 
 impl AsyncReadStream {
@@ -120,7 +120,7 @@ impl AsyncReadStream {
     /// provided by this struct, the argument must impl [`tokio::io::AsyncRead`].
     pub fn new<T: tokio::io::AsyncRead + Send + Unpin + 'static>(mut reader: T) -> Self {
         let (sender, receiver) = mpsc::channel(1);
-        let join_handle = crate::spawn(async move {
+        let join_handle = crate::runtime::spawn(async move {
             loop {
                 use tokio::io::AsyncReadExt;
                 let mut buf = bytes::BytesMut::with_capacity(4096);
diff --git a/crates/wasi/src/poll.rs b/crates/wasi/src/poll.rs
index 1edeae693..6d89e7fee 100644
--- a/crates/wasi/src/poll.rs
+++ b/crates/wasi/src/poll.rs
@@ -154,7 +154,8 @@ pub mod sync {
     use crate::{
         bindings::io::poll as async_poll,
         bindings::sync_io::io::poll::{self, Pollable},
-        in_tokio, WasiView,
+        runtime::in_tokio,
+        WasiView,
     };
     use anyhow::Result;
     use wasmtime::component::Resource;
diff --git a/crates/wasi/src/preview0.rs b/crates/wasi/src/preview0.rs
index 64c28aeb7..b7c8c40a7 100644
--- a/crates/wasi/src/preview0.rs
+++ b/crates/wasi/src/preview0.rs
@@ -52,7 +52,7 @@ mod sync {
     // Small wrapper around `in_tokio` to add a `Result` layer which is always
     // `Ok`
     fn in_tokio<F: Future>(future: F) -> Result<F::Output> {
-        Ok(crate::in_tokio(future))
+        Ok(crate::runtime::in_tokio(future))
     }
 }
 
diff --git a/crates/wasi/src/preview1.rs b/crates/wasi/src/preview1.rs
index 2d0f0fb7b..16a5e58ee 100644
--- a/crates/wasi/src/preview1.rs
+++ b/crates/wasi/src/preview1.rs
@@ -542,7 +542,7 @@ mod sync {
     // Small wrapper around `in_tokio` to add a `Result` layer which is always
     // `Ok`
     fn in_tokio<F: Future>(future: F) -> Result<F::Output> {
-        Ok(crate::in_tokio(future))
+        Ok(crate::runtime::in_tokio(future))
     }
 }
 
diff --git a/crates/wasi/src/runtime.rs b/crates/wasi/src/runtime.rs
new file mode 100644
index 000000000..31d8135ff
--- /dev/null
+++ b/crates/wasi/src/runtime.rs
@@ -0,0 +1,175 @@
+//! This module provides an "ambient Tokio runtime"
+//! [`with_ambient_tokio_runtime`]. Embedders of wasmtime-wasi may do so from
+//! synchronous Rust, and not use tokio directly. The implementation of
+//! wasmtime-wasi requires a tokio executor in a way that is [deeply tied to
+//! its
+//! design](https://github.com/bytecodealliance/wasmtime/issues/7973#issuecomment-1960513214).
+//! When used from a sychrnonous wasmtime context, this module provides the
+//! wrapper function [`in_tokio`] used throughout the shim implementations of
+//! synchronous component binding `Host` traits in terms of the async ones.
+//!
+//! This module also provides a thin wrapper on tokio's tasks.
+//! [`AbortOnDropJoinHandle`], which is exactly like a
+//! [`tokio::task::JoinHandle`] except for the obvious behavioral change. This
+//! whole crate, and any child crates which spawn tasks as part of their
+//! implementations, should please use this crate's [`spawn`] and
+//! [`spawn_blocking`] over tokio's. so we wanted the type name to stick out
+//! if someone misses it.
+//!
+//! Each of these facilities should be used by dependencies of wasmtime-wasi
+//! which when implementing component bindings.
+
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+pub(crate) static RUNTIME: once_cell::sync::Lazy<tokio::runtime::Runtime> =
+    once_cell::sync::Lazy::new(|| {
+        tokio::runtime::Builder::new_current_thread()
+            .enable_time()
+            .enable_io()
+            .build()
+            .unwrap()
+    });
+
+/// Exactly like a [`tokio::task::JoinHandle`], except that it aborts the task when
+/// the handle is dropped.
+///
+/// This behavior makes it easier to tie a worker task to the lifetime of a Resource
+/// by keeping this handle owned by the Resource.
+pub struct AbortOnDropJoinHandle<T>(tokio::task::JoinHandle<T>);
+impl<T> Drop for AbortOnDropJoinHandle<T> {
+    fn drop(&mut self) {
+        self.0.abort()
+    }
+}
+impl<T> std::ops::Deref for AbortOnDropJoinHandle<T> {
+    type Target = tokio::task::JoinHandle<T>;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+impl<T> std::ops::DerefMut for AbortOnDropJoinHandle<T> {
+    fn deref_mut(&mut self) -> &mut tokio::task::JoinHandle<T> {
+        &mut self.0
+    }
+}
+impl<T> From<tokio::task::JoinHandle<T>> for AbortOnDropJoinHandle<T> {
+    fn from(jh: tokio::task::JoinHandle<T>) -> Self {
+        AbortOnDropJoinHandle(jh)
+    }
+}
+impl<T> Future for AbortOnDropJoinHandle<T> {
+    type Output = T;
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        match Pin::new(&mut self.as_mut().0).poll(cx) {
+            Poll::Pending => Poll::Pending,
+            Poll::Ready(r) => Poll::Ready(r.expect("child task panicked")),
+        }
+    }
+}
+
+pub fn spawn<F>(f: F) -> AbortOnDropJoinHandle<F::Output>
+where
+    F: Future + Send + 'static,
+    F::Output: Send + 'static,
+{
+    let j = with_ambient_tokio_runtime(|| tokio::task::spawn(f));
+    AbortOnDropJoinHandle(j)
+}
+
+pub fn spawn_blocking<F, R>(f: F) -> AbortOnDropJoinHandle<R>
+where
+    F: FnOnce() -> R + Send + 'static,
+    R: Send + 'static,
+{
+    let j = with_ambient_tokio_runtime(|| tokio::task::spawn_blocking(f));
+    AbortOnDropJoinHandle(j)
+}
+
+pub fn in_tokio<F: Future>(f: F) -> F::Output {
+    match tokio::runtime::Handle::try_current() {
+        Ok(h) => {
+            let _enter = h.enter();
+            h.block_on(f)
+        }
+        // The `yield_now` here is non-obvious and if you're reading this
+        // you're likely curious about why it's here. This is currently required
+        // to get some features of "sync mode" working correctly, such as with
+        // the CLI. To illustrate why this is required, consider a program
+        // organized as:
+        //
+        // * A program has a `pollable` that it's waiting on.
+        // * This `pollable` is always ready .
+        // * Actually making the corresponding operation ready, however,
+        //   requires some background work on Tokio's part.
+        // * The program is looping on "wait for readiness" coupled with
+        //   performing the operation.
+        //
+        // In this situation this program ends up infinitely looping in waiting
+        // for pollables. The reason appears to be that when we enter the tokio
+        // runtime here it doesn't necessary yield to background work because
+        // the provided future `f` is ready immediately. The future `f` will run
+        // through the list of pollables and determine one of them is ready.
+        //
+        // Historically this happened with UDP sockets. A test send a datagram
+        // from one socket to another and the other socket infinitely didn't
+        // receive the data. This appeared to be because the server socket was
+        // waiting on `READABLE | WRITABLE` (which is itself a bug but ignore
+        // that) and the socket was currently in the "writable" state but never
+        // ended up receiving a notification for the "readable" state. Moving
+        // the socket to "readable" would require Tokio to perform some
+        // background work via epoll/kqueue/handle events but if the future
+        // provided here is always ready, then that never happened.
+        //
+        // Thus the `yield_now()` is an attempt to force Tokio to go do some
+        // background work eventually and look at new interest masks for
+        // example. This is a bit of a kludge but everything's already a bit
+        // wonky in synchronous mode anyway. Note that this is hypothesized to
+        // not be an issue in async mode because async mode typically has the
+        // Tokio runtime in a separate thread or otherwise participating in a
+        // larger application, it's only here in synchronous mode where we
+        // effectively own the runtime that we need some special care.
+        Err(_) => {
+            let _enter = RUNTIME.enter();
+            RUNTIME.block_on(async move {
+                tokio::task::yield_now().await;
+                f.await
+            })
+        }
+    }
+}
+
+/// Executes the closure `f` with an "ambient Tokio runtime" which basically
+/// means that if code in `f` tries to get a runtime `Handle` it'll succeed.
+///
+/// If a `Handle` is already available, e.g. in async contexts, then `f` is run
+/// immediately. Otherwise for synchronous contexts this crate's fallback
+/// runtime is configured and then `f` is executed.
+pub fn with_ambient_tokio_runtime<R>(f: impl FnOnce() -> R) -> R {
+    match tokio::runtime::Handle::try_current() {
+        Ok(_) => f(),
+        Err(_) => {
+            let _enter = RUNTIME.enter();
+            f()
+        }
+    }
+}
+
+/// Attempts to get the result of a `future`.
+///
+/// This function does not block and will poll the provided future once. If the
+/// result is here then `Some` is returned, otherwise `None` is returned.
+///
+/// Note that by polling `future` this means that `future` must be re-polled
+/// later if it's to wake up a task.
+pub fn poll_noop<F>(future: Pin<&mut F>) -> Option<F::Output>
+where
+    F: Future,
+{
+    let mut task = Context::from_waker(futures::task::noop_waker_ref());
+    match future.poll(&mut task) {
+        Poll::Ready(result) => Some(result),
+        Poll::Pending => None,
+    }
+}
diff --git a/crates/wasi/src/tcp.rs b/crates/wasi/src/tcp.rs
index 468bbb75c..bc970c3bf 100644
--- a/crates/wasi/src/tcp.rs
+++ b/crates/wasi/src/tcp.rs
@@ -1,8 +1,6 @@
-use super::network::SocketAddressFamily;
-use super::{
-    with_ambient_tokio_runtime, HostInputStream, HostOutputStream, SocketResult, StreamError,
-};
-use crate::{AbortOnDropJoinHandle, Subscribe};
+use crate::network::SocketAddressFamily;
+use crate::runtime::{with_ambient_tokio_runtime, AbortOnDropJoinHandle};
+use crate::{HostInputStream, HostOutputStream, SocketResult, StreamError, Subscribe};
 use anyhow::{Error, Result};
 use cap_net_ext::AddressFamily;
 use futures::Future;
@@ -164,7 +162,7 @@ impl TcpWriteStream {
         assert!(matches!(self.last_write, LastWrite::Done));
 
         let stream = self.stream.clone();
-        self.last_write = LastWrite::Waiting(crate::spawn(async move {
+        self.last_write = LastWrite::Waiting(crate::runtime::spawn(async move {
             // Note: we are not using the AsyncWrite impl here, and instead using the TcpStream
             // primitive try_write, which goes directly to attempt a write with mio. This has
             // two advantages: 1. this operation takes a &TcpStream instead of a &mut TcpStream
@@ -236,7 +234,7 @@ impl HostOutputStream for TcpWriteStream {
 
         let writable = self.stream.writable();
         futures::pin_mut!(writable);
-        if super::poll_noop(writable).is_none() {
+        if crate::runtime::poll_noop(writable).is_none() {
             return Ok(0);
         }
         Ok(SOCKET_READY_SIZE)
diff --git a/crates/wasi/src/udp.rs b/crates/wasi/src/udp.rs
index bb9819f2e..1e0b1a199 100644
--- a/crates/wasi/src/udp.rs
+++ b/crates/wasi/src/udp.rs
@@ -1,6 +1,6 @@
 use crate::host::network::util;
 use crate::poll::Subscribe;
-use crate::with_ambient_tokio_runtime;
+use crate::runtime::with_ambient_tokio_runtime;
 use async_trait::async_trait;
 use cap_net_ext::{AddressFamily, Blocking};
 use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike};
diff --git a/crates/wasi/src/write_stream.rs b/crates/wasi/src/write_stream.rs
index eb76e2c3b..e08546b60 100644
--- a/crates/wasi/src/write_stream.rs
+++ b/crates/wasi/src/write_stream.rs
@@ -139,7 +139,7 @@ impl Worker {
 /// Provides a [`HostOutputStream`] impl from a [`tokio::io::AsyncWrite`] impl
 pub struct AsyncWriteStream {
     worker: Arc<Worker>,
-    _join_handle: crate::AbortOnDropJoinHandle<()>,
+    _join_handle: crate::runtime::AbortOnDropJoinHandle<()>,
 }
 
 impl AsyncWriteStream {
@@ -152,7 +152,7 @@ impl AsyncWriteStream {
         let worker = Arc::new(Worker::new(write_budget));
 
         let w = Arc::clone(&worker);
-        let join_handle = crate::spawn(async move { w.work(writer).await });
+        let join_handle = crate::runtime::spawn(async move { w.work(writer).await });
 
         AsyncWriteStream {
             worker,
diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml
index 8b6560f7a..d3551da5f 100644
--- a/supply-chain/audits.toml
+++ b/supply-chain/audits.toml
@@ -1826,6 +1826,15 @@ criteria = "safe-to-deploy"
 delta = "0.31.1 -> 0.32.0"
 notes = "Various new features and refactorings as one would expect from an object parsing crate, all looks good."
 
+[[audits.object]]
+who = "Alex Crichton <alex@alexcrichton.com>"
+criteria = "safe-to-deploy"
+delta = "0.32.0 -> 0.33.0"
+notes = """
+No `unsafe` code in this update. Lots of changes but all
+object-file-format-related, everything looks good.
+"""
+
 [[audits.once_cell]]
 who = "Chris Fallin <chris@cfallin.org>"
 criteria = "safe-to-deploy"
diff --git a/winch/codegen/src/codegen/mod.rs b/winch/codegen/src/codegen/mod.rs
index 8e933ed74..475c7f74d 100644
--- a/winch/codegen/src/codegen/mod.rs
+++ b/winch/codegen/src/codegen/mod.rs
@@ -658,8 +658,8 @@ where
                 .wasm_store(src.reg.into(), self.masm.address_at_reg(addr, 0), size);
 
             self.context.free_reg(addr);
-            self.context.free_reg(src);
         }
+        self.context.free_reg(src);
     }
 }
 


Test patch:
diff --git a/crates/wasi-http/tests/all/main.rs b/crates/wasi-http/tests/all/main.rs
index af1a4d457..800f3a0bc 100644
--- a/crates/wasi-http/tests/all/main.rs
+++ b/crates/wasi-http/tests/all/main.rs
@@ -170,7 +170,7 @@ async fn run_wasi_http(
     let (sender, receiver) = tokio::sync::oneshot::channel();
     let out = store.data_mut().new_response_outparam(sender)?;
 
-    let handle = wasmtime_wasi::spawn(async move {
+    let handle = wasmtime_wasi::runtime::spawn(async move {
         proxy
             .wasi_http_incoming_handler()
             .call_handle(&mut store, req, out)
@@ -278,7 +278,7 @@ async fn do_wasi_http_hash_all(override_send_request: bool) -> Result<()> {
                 let response = handle(request.into_parts().0).map(|resp| {
                     Ok(IncomingResponseInternal {
                         resp,
-                        worker: Arc::new(wasmtime_wasi::spawn(future::ready(()))),
+                        worker: Arc::new(wasmtime_wasi::runtime::spawn(future::ready(()))),
                         between_bytes_timeout,
                     })
                 });
diff --git a/tests/all/winch.rs b/tests/all/winch.rs
index fc8a66744..addfc2783 100644
--- a/tests/all/winch.rs
+++ b/tests/all/winch.rs
@@ -310,3 +310,33 @@ fn dynamic_heap() -> Result<()> {
 
     Ok(())
 }
+
+#[test]
+#[cfg_attr(miri, ignore)]
+#[cfg_attr(windows, ignore)]
+fn static_oob() -> Result<()> {
+    let mut c = Config::new();
+    c.static_memory_maximum_size(65536);
+    c.strategy(Strategy::Winch);
+    let engine = Engine::new(&c)?;
+    let wat = r#"
+        (module
+          (memory 0 1)
+          (func (export "") (result i32)
+            (i32.const 0)
+            (i32.const 1)
+            (i32.store offset=726020653)
+            (i32.const 1)
+            (memory.grow)
+          )
+        )
+    "#;
+    let mut store = Store::new(&engine, ());
+    let module = Module::new(&engine, wat)?;
+    let instance = Instance::new(&mut store, &module, &[])?;
+    let f = instance.get_typed_func::<(), i32>(&mut store, "")?;
+    let result = f.call(&mut store, ()).unwrap_err();
+    assert!(result.downcast_ref::<WasmBacktrace>().is_some());
+
+    Ok(())
+}