Repository: bytecodealliance/wasm-tools
Pull request #1228 (2023-10-03 20:04:07)

support `_initialize` export as well as `__wasm_call_ctors`
Per https://github.com/WebAssembly/tool-conventions/pull/203 and recent LLVM and wasi-libc changes, libraries may export an `_initialize` function instead of `__wasm_call_ctors`, and we should call whichever one we find.  Note that we expect to find at most one of the two, and will return an error if both are found.


Patch:
diff --git a/crates/wit-component/src/linking.rs b/crates/wit-component/src/linking.rs
index 1aef9084..3af76c6a 100644
--- a/crates/wit-component/src/linking.rs
+++ b/crates/wit-component/src/linking.rs
@@ -604,6 +604,14 @@ fn make_init_module(
             )));
         }
 
+        if metadata.has_ctors && metadata.has_initialize {
+            bail!(
+                "library {} exports both `__wasm_call_ctors` and `_initialize`; \
+                 expected at most one of the two",
+                metadata.name
+            );
+        }
+
         if metadata.has_ctors {
             ctor_calls.push(Ins::Call(add_function_import(
                 &mut imports,
@@ -613,6 +621,15 @@ fn make_init_module(
             )));
         }
 
+        if metadata.has_initialize {
+            ctor_calls.push(Ins::Call(add_function_import(
+                &mut imports,
+                metadata.name,
+                "_initialize",
+                thunk_ty,
+            )));
+        }
+
         if metadata.has_set_libraries {
             ctor_calls.push(Ins::I32Const(
                 i32::try_from(dl_openables.libraries_address).unwrap(),
diff --git a/crates/wit-component/src/linking/metadata.rs b/crates/wit-component/src/linking/metadata.rs
index 6893199f..430f6b97 100644
--- a/crates/wit-component/src/linking/metadata.rs
+++ b/crates/wit-component/src/linking/metadata.rs
@@ -199,6 +199,9 @@ pub struct Metadata<'a> {
     /// Whether this module exports `__wasm_call_ctors`
     pub has_ctors: bool,
 
+    /// Whether this module exports `_initialize`
+    pub has_initialize: bool,
+
     /// Whether this module exports `__wasm_set_libraries`
     pub has_set_libraries: bool,
 
@@ -303,6 +306,7 @@ impl<'a> Metadata<'a> {
             needed_libs: Vec::new(),
             has_data_relocs: false,
             has_ctors: false,
+            has_initialize: false,
             has_set_libraries: false,
             has_component_exports,
             env_imports: BTreeSet::new(),
@@ -491,6 +495,7 @@ impl<'a> Metadata<'a> {
                         match export.name {
                             "__wasm_apply_data_relocs" => result.has_data_relocs = true,
                             "__wasm_call_ctors" => result.has_ctors = true,
+                            "_initialize" => result.has_initialize = true,
                             "__wasm_set_libraries" => result.has_set_libraries = true,
                             _ => {
                                 let ty = match export.kind {


Test patch:
diff --git a/crates/wit-component/tests/components/error-link-duplicate-initializers/error.txt b/crates/wit-component/tests/components/error-link-duplicate-initializers/error.txt
new file mode 100644
index 00000000..86de6c15
--- /dev/null
+++ b/crates/wit-component/tests/components/error-link-duplicate-initializers/error.txt
@@ -0,0 +1,1 @@
+library foo exports both `__wasm_call_ctors` and `_initialize`; expected at most one of the two
diff --git a/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-bar.wat b/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-bar.wat
new file mode 100644
index 00000000..d6be27ae
--- /dev/null
+++ b/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-bar.wat
@@ -0,0 +1,12 @@
+(module
+  (@dylink.0
+    (mem-info (memory 0 4))
+    (needed "foo")
+  )
+  (type (func (param i32) (result i32)))
+  (import "env" "foo" (func $import_foo (type 0)))
+  (func $bar (type 0) (param i32) (result i32)
+    unreachable
+  )
+  (export "bar" (func $bar))
+)
diff --git a/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-bar.wit b/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-bar.wit
new file mode 100644
index 00000000..ad16c9bf
--- /dev/null
+++ b/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-bar.wit
@@ -0,0 +1,3 @@
+package test:test;
+
+world lib-bar { }
diff --git a/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-foo.wat b/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-foo.wat
new file mode 100644
index 00000000..a7395dfe
--- /dev/null
+++ b/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-foo.wat
@@ -0,0 +1,21 @@
+(module
+  (@dylink.0
+    (mem-info (memory 0 4))
+    (needed "bar")
+  )
+  (type (func (param i32) (result i32)))
+  (type (func))
+  (import "test:test/test" "foo" (func $import_foo (type 0)))
+  (import "env" "foo" (func $import_foo2 (type 0)))
+  (import "env" "bar" (func $import_bar (type 0)))
+  (func $foo (type 0) (param i32) (result i32)
+    unreachable
+  )
+  (func $_initialize (type 1)
+    unreachable
+  )
+  (export "test:test/test#foo" (func $foo))
+  (export "foo" (func $foo))
+  (export "_initialize" (func $_initialize))
+  (export "__wasm_call_ctors" (func $_initialize))  
+)
diff --git a/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-foo.wit b/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-foo.wit
new file mode 100644
index 00000000..576de434
--- /dev/null
+++ b/crates/wit-component/tests/components/error-link-duplicate-initializers/lib-foo.wit
@@ -0,0 +1,10 @@
+package test:test;
+
+interface test {
+   foo: func(v: s32) -> s32;
+}
+
+world lib-foo {
+    import test;
+    export test;
+}
diff --git a/crates/wit-component/tests/components/link-initialize/component.wat b/crates/wit-component/tests/components/link-initialize/component.wat
new file mode 100644
index 00000000..bb382862
--- /dev/null
+++ b/crates/wit-component/tests/components/link-initialize/component.wat
@@ -0,0 +1,300 @@
+(component
+  (type (;0;)
+    (instance
+      (type (;0;) (func (param "v" s32) (result s32)))
+      (export (;0;) "bar" (func (type 0)))
+    )
+  )
+  (import (interface "test:test/test") (instance (;0;) (type 0)))
+  (core module (;0;)
+    (table (;0;) 0 funcref)
+    (memory (;0;) 17)
+    (global (;0;) (mut i32) i32.const 1048576)
+    (global (;1;) i32 i32.const 1048592)
+    (global (;2;) i32 i32.const 0)
+    (global (;3;) (mut i32) i32.const 0)
+    (global (;4;) i32 i32.const 1048624)
+    (global (;5;) i32 i32.const 0)
+    (global (;6;) i32 i32.const 1048624)
+    (global (;7;) i32 i32.const 0)
+    (global (;8;) (mut i32) i32.const 0)
+    (global (;9;) (mut i32) i32.const 1048640)
+    (global (;10;) (mut i32) i32.const 1114112)
+    (export "__stack_pointer" (global 0))
+    (export "bar:memory_base" (global 1))
+    (export "bar:table_base" (global 2))
+    (export "bar:well" (global 3))
+    (export "c:memory_base" (global 4))
+    (export "c:table_base" (global 5))
+    (export "foo:memory_base" (global 6))
+    (export "foo:table_base" (global 7))
+    (export "foo:um" (global 8))
+    (export "__heap_base" (global 9))
+    (export "__heap_end" (global 10))
+    (export "__indirect_function_table" (table 0))
+    (export "memory" (memory 0))
+    (@producers
+      (processed-by "wit-component" "$CARGO_PKG_VERSION")
+    )
+  )
+  (core module (;1;)
+    (@dylink.0
+      (mem-info (memory 0 4))
+    )
+    (type (;0;) (func))
+    (type (;1;) (func (param i32) (result i32)))
+    (import "GOT.mem" "__heap_base" (global $__heap_base (;0;) (mut i32)))
+    (import "GOT.mem" "__heap_end" (global $__heap_end (;1;) (mut i32)))
+    (func $start (;0;) (type 0)
+      global.get $__heap_base
+      global.set $heap
+    )
+    (func $malloc (;1;) (type 1) (param i32) (result i32)
+      global.get $heap
+      global.get $heap
+      local.get 0
+      i32.add
+      global.set $heap
+    )
+    (func $abort (;2;) (type 0)
+      unreachable
+    )
+    (global $heap (;2;) (mut i32) i32.const 0)
+    (export "malloc" (func $malloc))
+    (export "abort" (func $abort))
+    (start $start)
+  )
+  (core module (;2;)
+    (@dylink.0
+      (mem-info (memory 4 4))
+      (needed "c")
+    )
+    (type $.data (;0;) (func))
+    (type (;1;) (func (param i32) (result i32)))
+    (import "env" "memory" (memory (;0;) 1))
+    (import "env" "__indirect_function_table" (table (;0;) 0 funcref))
+    (import "env" "__stack_pointer" (global $__stack_pointer (;0;) (mut i32)))
+    (import "env" "__memory_base" (global $__memory_base (;1;) i32))
+    (import "env" "__table_base" (global $__table_base (;2;) i32))
+    (import "env" "malloc" (func $malloc (;0;) (type 1)))
+    (import "env" "abort" (func $abort (;1;) (type $.data)))
+    (import "GOT.mem" "um" (global $um (;3;) (mut i32)))
+    (import "test:test/test" "bar" (func $bar (;2;) (type 1)))
+    (func $_initialize (;3;) (type $.data))
+    (func $__wasm_apply_data_relocs (;4;) (type $.data))
+    (func $foo (;5;) (type 1) (param i32) (result i32)
+      global.get $__stack_pointer
+      i32.const 16
+      i32.sub
+      global.set $__stack_pointer
+      i32.const 4
+      call $malloc
+      i32.const 0
+      i32.eq
+      if ;; label = @1
+        call $abort
+        unreachable
+      end
+      local.get 0
+      global.get $um
+      i32.load offset=16
+      i32.add
+      i32.const 42
+      i32.add
+      call $bar
+      global.get $__stack_pointer
+      i32.const 16
+      i32.add
+      global.set $__stack_pointer
+    )
+    (global (;4;) i32 i32.const 0)
+    (export "_initialize" (func $_initialize))
+    (export "__wasm_apply_data_relocs" (func $__wasm_apply_data_relocs))
+    (export "foo" (func $foo))
+    (export "well" (global 4))
+    (data (;0;) (global.get $__memory_base) "\04\00\00\00")
+  )
+  (core module (;3;)
+    (@dylink.0
+      (mem-info (memory 20 4))
+      (needed "foo")
+    )
+    (type $.data (;0;) (func (param i32) (result i32)))
+    (type (;1;) (func))
+    (import "env" "memory" (memory (;0;) 1))
+    (import "env" "__indirect_function_table" (table (;0;) 0 funcref))
+    (import "env" "__memory_base" (global $__memory_base (;0;) i32))
+    (import "env" "__table_base" (global $__table_base (;1;) i32))
+    (import "env" "foo" (func $foo (;0;) (type $.data)))
+    (import "GOT.mem" "well" (global $well (;2;) (mut i32)))
+    (func $_initialize (;1;) (type 1))
+    (func $__wasm_apply_data_relocs (;2;) (type 1))
+    (func $bar (;3;) (type $.data) (param i32) (result i32)
+      local.get 0
+      call $foo
+      global.get $well
+      i32.load
+      i32.add
+    )
+    (global (;3;) i32 i32.const 0)
+    (export "_initialize" (func $_initialize))
+    (export "__wasm_apply_data_relocs" (func $__wasm_apply_data_relocs))
+    (export "test:test/test#bar" (func $bar))
+    (export "um" (global 3))
+    (data (;0;) (global.get $__memory_base) "\01\00\00\00\02\00\00\00\03\00\00\00\04\00\00\00\05\00\00\00")
+  )
+  (core module (;4;)
+    (type (;0;) (func))
+    (type (;1;) (func (param i32)))
+    (import "env" "memory" (memory (;0;) 0))
+    (import "env" "__indirect_function_table" (table (;0;) 0 funcref))
+    (import "bar" "__wasm_apply_data_relocs" (func (;0;) (type 0)))
+    (import "bar" "_initialize" (func (;1;) (type 0)))
+    (import "env" "foo:memory_base" (global (;0;) i32))
+    (import "foo" "well" (global (;1;) i32))
+    (import "env" "bar:well" (global (;2;) (mut i32)))
+    (import "foo" "__wasm_apply_data_relocs" (func (;2;) (type 0)))
+    (import "foo" "_initialize" (func (;3;) (type 0)))
+    (import "env" "bar:memory_base" (global (;3;) i32))
+    (import "bar" "um" (global (;4;) i32))
+    (import "env" "foo:um" (global (;5;) (mut i32)))
+    (func (;4;) (type 0)
+      global.get 0
+      global.get 1
+      i32.add
+      global.set 2
+      global.get 3
+      global.get 4
+      i32.add
+      global.set 5
+      call 0
+      call 2
+      call 1
+      call 3
+    )
+    (start 4)
+    (elem (;0;) (i32.const 0) func)
+    (elem (;1;) (i32.const 0) func)
+    (data (;0;) (i32.const 1048576) "\00\00\00\00\00\00\10\00")
+    (@producers
+      (processed-by "wit-component" "$CARGO_PKG_VERSION")
+    )
+  )
+  (core instance (;0;) (instantiate 0))
+  (alias core export 0 "memory" (core memory (;0;)))
+  (alias core export 0 "__heap_base" (core global (;0;)))
+  (alias core export 0 "__heap_end" (core global (;1;)))
+  (core instance (;1;)
+    (export "__heap_base" (global 0))
+    (export "__heap_end" (global 1))
+  )
+  (core instance (;2;))
+  (alias core export 0 "memory" (core memory (;1;)))
+  (alias core export 0 "__indirect_function_table" (core table (;0;)))
+  (alias core export 0 "__stack_pointer" (core global (;2;)))
+  (alias core export 0 "c:memory_base" (core global (;3;)))
+  (alias core export 0 "c:table_base" (core global (;4;)))
+  (core instance (;3;)
+    (export "memory" (memory 1))
+    (export "__indirect_function_table" (table 0))
+    (export "__stack_pointer" (global 2))
+    (export "__memory_base" (global 3))
+    (export "__table_base" (global 4))
+  )
+  (core instance (;4;) (instantiate 1
+      (with "GOT.mem" (instance 1))
+      (with "GOT.func" (instance 2))
+      (with "env" (instance 3))
+    )
+  )
+  (alias core export 0 "foo:um" (core global (;5;)))
+  (alias core export 0 "__heap_base" (core global (;6;)))
+  (alias core export 0 "__heap_end" (core global (;7;)))
+  (core instance (;5;)
+    (export "um" (global 5))
+    (export "__heap_base" (global 6))
+    (export "__heap_end" (global 7))
+  )
+  (core instance (;6;))
+  (alias core export 0 "memory" (core memory (;2;)))
+  (alias core export 0 "__indirect_function_table" (core table (;1;)))
+  (alias core export 0 "__stack_pointer" (core global (;8;)))
+  (alias core export 0 "foo:memory_base" (core global (;9;)))
+  (alias core export 0 "foo:table_base" (core global (;10;)))
+  (alias core export 4 "abort" (core func (;0;)))
+  (alias core export 4 "malloc" (core func (;1;)))
+  (core instance (;7;)
+    (export "memory" (memory 2))
+    (export "__indirect_function_table" (table 1))
+    (export "__stack_pointer" (global 8))
+    (export "__memory_base" (global 9))
+    (export "__table_base" (global 10))
+    (export "abort" (func 0))
+    (export "malloc" (func 1))
+  )
+  (alias export 0 "bar" (func (;0;)))
+  (core func (;2;) (canon lower (func 0)))
+  (core instance (;8;)
+    (export "bar" (func 2))
+  )
+  (core instance (;9;) (instantiate 2
+      (with "GOT.mem" (instance 5))
+      (with "GOT.func" (instance 6))
+      (with "env" (instance 7))
+      (with "test:test/test" (instance 8))
+    )
+  )
+  (alias core export 0 "bar:well" (core global (;11;)))
+  (alias core export 0 "__heap_base" (core global (;12;)))
+  (alias core export 0 "__heap_end" (core global (;13;)))
+  (core instance (;10;)
+    (export "well" (global 11))
+    (export "__heap_base" (global 12))
+    (export "__heap_end" (global 13))
+  )
+  (core instance (;11;))
+  (alias core export 0 "memory" (core memory (;3;)))
+  (alias core export 0 "__indirect_function_table" (core table (;2;)))
+  (alias core export 0 "__stack_pointer" (core global (;14;)))
+  (alias core export 0 "bar:memory_base" (core global (;15;)))
+  (alias core export 0 "bar:table_base" (core global (;16;)))
+  (alias core export 9 "foo" (core func (;3;)))
+  (core instance (;12;)
+    (export "memory" (memory 3))
+    (export "__indirect_function_table" (table 2))
+    (export "__stack_pointer" (global 14))
+    (export "__memory_base" (global 15))
+    (export "__table_base" (global 16))
+    (export "foo" (func 3))
+  )
+  (core instance (;13;) (instantiate 3
+      (with "GOT.mem" (instance 10))
+      (with "GOT.func" (instance 11))
+      (with "env" (instance 12))
+    )
+  )
+  (core instance (;14;) (instantiate 4
+      (with "env" (instance 0))
+      (with "bar" (instance 13))
+      (with "c" (instance 4))
+      (with "foo" (instance 9))
+    )
+  )
+  (type (;1;) (func (param "v" s32) (result s32)))
+  (alias core export 13 "test:test/test#bar" (core func (;4;)))
+  (func (;1;) (type 1) (canon lift (core func 4)))
+  (component (;0;)
+    (type (;0;) (func (param "v" s32) (result s32)))
+    (import "import-func-bar" (func (;0;) (type 0)))
+    (type (;1;) (func (param "v" s32) (result s32)))
+    (export (;1;) "bar" (func 0) (func (type 1)))
+  )
+  (instance (;1;) (instantiate 0
+      (with "import-func-bar" (func 1))
+    )
+  )
+  (export (;2;) (interface "test:test/test") (instance 1))
+  (@producers
+    (processed-by "wit-component" "$CARGO_PKG_VERSION")
+  )
+)
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/link-initialize/component.wit.print b/crates/wit-component/tests/components/link-initialize/component.wit.print
new file mode 100644
index 00000000..d02a685b
--- /dev/null
+++ b/crates/wit-component/tests/components/link-initialize/component.wit.print
@@ -0,0 +1,7 @@
+package root:component
+
+world root {
+  import test:test/test
+
+  export test:test/test
+}
diff --git a/crates/wit-component/tests/components/link-initialize/lib-bar.wat b/crates/wit-component/tests/components/link-initialize/lib-bar.wat
new file mode 100644
index 00000000..776295c2
--- /dev/null
+++ b/crates/wit-component/tests/components/link-initialize/lib-bar.wat
@@ -0,0 +1,29 @@
+(module
+  (@dylink.0
+    (mem-info (memory 20 4))
+    (needed "foo")
+  )
+  (type (func (param i32) (result i32)))
+  (type (func))
+  (import "env" "memory" (memory 1))
+  (import "env" "__indirect_function_table" (table 0 funcref))
+  (import "env" "__memory_base" (global $__memory_base i32))
+  (import "env" "__table_base" (global $__table_base i32))
+  (import "env" "foo" (func $foo (type 0)))
+  (import "GOT.mem" "well" (global $well (mut i32)))
+  (func $_initialize (type 1))
+  (func $__wasm_apply_data_relocs (type 1))
+  (func $bar (type 0) (param i32) (result i32)
+    local.get 0
+    call $foo
+    global.get $well
+    i32.load
+    i32.add
+  )
+  (global i32 i32.const 0)
+  (export "_initialize" (func $_initialize))
+  (export "__wasm_apply_data_relocs" (func $__wasm_apply_data_relocs))
+  (export "test:test/test#bar" (func $bar))
+  (export "um" (global 3))
+  (data $.data (global.get $__memory_base) "\01\00\00\00\02\00\00\00\03\00\00\00\04\00\00\00\05\00\00\00")
+)
diff --git a/crates/wit-component/tests/components/link-initialize/lib-bar.wit b/crates/wit-component/tests/components/link-initialize/lib-bar.wit
new file mode 100644
index 00000000..9cab0b5e
--- /dev/null
+++ b/crates/wit-component/tests/components/link-initialize/lib-bar.wit
@@ -0,0 +1,10 @@
+package test:test;
+
+interface test {
+   bar: func(v: s32) -> s32;
+}
+
+world lib-bar {
+    import test;
+    export test;
+}
diff --git a/crates/wit-component/tests/components/link-initialize/lib-c.wat b/crates/wit-component/tests/components/link-initialize/lib-c.wat
new file mode 100644
index 00000000..7798b7d6
--- /dev/null
+++ b/crates/wit-component/tests/components/link-initialize/lib-c.wat
@@ -0,0 +1,27 @@
+(module
+  (@dylink.0
+    (mem-info (memory 0 4))
+  )
+  (type (func))
+  (type (func (param i32) (result i32)))
+  (import "GOT.mem" "__heap_base" (global $__heap_base (mut i32)))
+  (import "GOT.mem" "__heap_end" (global $__heap_end (mut i32)))
+  (global $heap (mut i32) i32.const 0)
+  (func $start (type 0)
+    global.get $__heap_base
+    global.set $heap
+  )
+  (func $malloc (type 1) (param i32) (result i32)
+    global.get $heap
+    global.get $heap
+    local.get 0
+    i32.add
+    global.set $heap
+  )
+  (func $abort (type 0)
+    unreachable
+  )
+  (export "malloc" (func $malloc))
+  (export "abort" (func $abort))
+  (start $start)
+)
diff --git a/crates/wit-component/tests/components/link-initialize/lib-c.wit b/crates/wit-component/tests/components/link-initialize/lib-c.wit
new file mode 100644
index 00000000..262c853d
--- /dev/null
+++ b/crates/wit-component/tests/components/link-initialize/lib-c.wit
@@ -0,0 +1,3 @@
+package test:test;
+
+world lib-c { }
diff --git a/crates/wit-component/tests/components/link-initialize/lib-foo.wat b/crates/wit-component/tests/components/link-initialize/lib-foo.wat
new file mode 100644
index 00000000..4fdebe22
--- /dev/null
+++ b/crates/wit-component/tests/components/link-initialize/lib-foo.wat
@@ -0,0 +1,55 @@
+(module
+  (@dylink.0
+    (mem-info (memory 4 4))
+    (needed "c")
+  )
+  (type (func))
+  (type (func (param i32) (result i32)))
+  (import "env" "memory" (memory 1))
+  (import "env" "__indirect_function_table" (table 0 funcref))
+  (import "env" "__stack_pointer" (global $__stack_pointer (mut i32)))
+  (import "env" "__memory_base" (global $__memory_base i32))
+  (import "env" "__table_base" (global $__table_base i32))
+  (import "env" "malloc" (func $malloc (type 1)))
+  (import "env" "abort" (func $abort (type 0)))
+  (import "GOT.mem" "um" (global $um (mut i32)))
+  (import "test:test/test" "bar" (func $bar (type 1)))
+  (func $_initialize (type 0))
+  (func $__wasm_apply_data_relocs (type 0))
+  (func $foo (type 1) (param i32) (result i32)
+    global.get $__stack_pointer
+    i32.const 16
+    i32.sub
+    global.set $__stack_pointer
+
+    i32.const 4
+    call $malloc
+
+    i32.const 0
+    i32.eq
+    if
+      call $abort
+      unreachable
+    end
+
+    local.get 0
+    global.get $um
+    i32.load offset=16
+    i32.add
+    i32.const 42
+    i32.add
+
+    call $bar
+
+    global.get $__stack_pointer
+    i32.const 16
+    i32.add
+    global.set $__stack_pointer
+  )
+  (global i32 i32.const 0)
+  (export "_initialize" (func $_initialize))
+  (export "__wasm_apply_data_relocs" (func $__wasm_apply_data_relocs))
+  (export "foo" (func $foo))
+  (export "well" (global 4))
+  (data $.data (global.get $__memory_base) "\04\00\00\00")
+)
diff --git a/crates/wit-component/tests/components/link-initialize/lib-foo.wit b/crates/wit-component/tests/components/link-initialize/lib-foo.wit
new file mode 100644
index 00000000..3171fc99
--- /dev/null
+++ b/crates/wit-component/tests/components/link-initialize/lib-foo.wit
@@ -0,0 +1,3 @@
+package test:test;
+
+world lib-foo { }