Hi all, I'm trying to better understand the component model and how it relates to "modules". I have some questions:
I'm a bit confused by the statement of "modules are shared libraries, components are executables". Do we expect some runtimes to provide some shared functionality using modules, like how shared libraries work on Linux? Or do we expect them to only provide such functionality using components?
Hi. @Merlijn Sebrechts.
I'm not sure what describes modules as shared libraries and components as executables, as that's not quite apt
This blog post makes that analogy: https://www.fermyon.com/blog/webassembly-component-model
Regarding #2: you can now also compose application-defined modules in a component:
https://github.com/WebAssembly/component-model/blob/main/design/mvp/examples/SharedEverythingDynamicLinking.md
https://github.com/bytecodealliance/wasm-tools/pull/1133
I think the confusing term there is "shared"; unlike shared libraries, module instances can't be shared between components (though module code can be)
Merlijn Sebrechts said:
This blog post makes that analogy: https://www.fermyon.com/blog/webassembly-component-model
Ha, I wrote that blog. Like all analogies, it's imprecise, but I _think_ it provides a decent intuition.
So does that mean that in the future, "modules" will become more or less a hidden implementation details for app developers (like object files are nowadays)? So devs in the future will mostly interact with components instead?
Merlijn Sebrechts said:
So does that mean that in the future, "modules" will become more or less a hidden implementation details for app developers (like object files are nowadays)? So devs in the future will mostly interact with components instead?
I think that's the hope, at least; interacting with components provides interoperability and composability that's just not possible when dealing with core modules.
Situations where module composition can be desirable are e.g. porting existing libraries and FFI systems. For example: supporting Python native extensions, which has been one of my recent projects.
Joel Dice said:
Regarding #2: you can now also compose application-defined modules in a component:
https://github.com/WebAssembly/component-model/blob/main/design/mvp/examples/SharedEverythingDynamicLinking.md
https://github.com/bytecodealliance/wasm-tools/pull/1133
I have to admit, I've already read through the shared everything dynamic linking doc, and I'm still having a hard time to understand it. Is it something like this?
libc
, and libc
is a module instead of a component, it will be copied across multiple components (by the compiler).libc
module to the component. Instead, the runtime does this.Am I understanding this correctly?
That sounds right to me. When you link libc.a
into your app when building a core module (which may or may not end up wrapped in a component), it can't be shared with other modules or components. When you link libc.so
using shared-everything linking, it can be used by multiple modules and even multiple subcomponents within the resulting component, which results in less code duplication.
That becomes a big deal when the library is big, like libpython3.11.so
or the .NET runtime.
Oh, so the "shared" means "two components share the same module" instead of "two modules share the same address space"?
yeah, kind of like how two native processes can use the same .so, but not share memory
ok, makes sense!
but to be clear, you can _also_ have two modules share the same address space (i.e. when one imports a memory from the other)
Indeed
Right now, wasmtime seems to have two linkers; one for modules and one for components. Is this expected to be the case in the future? Or will we end up in a situation where there is one conceptual "linker" that links both components and modules?
@Alex Crichton is probably the one to answer that. I suspect we'll have both kinds of linker for a while, at least, since they each have unique capabilities and abstractions.
Ok, another question about the component model itself:
I haven't found much documentation about "worlds". I understand the use-case for the command
/CLI world, for example. App and compiler devs can target this world, and know their app will work in any runtime that supports it.
But does a guest component need to explicitly declare what world they target? Or can they just declare to use a set of interfaces without defining a world?
Generally speaking, we can refer to the union of interfaces which a component imports and exports as the "world" it targets. That world could be defined in a single .wit file like the CLI world or the wasi-http one, for example -- or it could be a combination of several worlds (e.g. both CLI and wasi-http, in which case the either the host would need to support both or parts would need to be virtualized in terms of the world(s) the host _does_ support).
But since wit-bindgen
and related tools require a world as input, you generally start with at least one world defined in a .wit file (which you might have written yourself or else downloaded from somewhere else).
How are guest worlds matched to host worlds? If the interfaces in a guest world are a subset of the interfaces in the host world, but they have different (world) names. Would that give any issues?
No, that wouldn't be a problem. The name(s) of the world(s) are not included in the component type -- just the name(s) of the interfaces.
Oh, I think I misread your comment.
Yes, the names of the interfaces _do_ matter.
I think your first interpretation of my comment was correct
I meant different world names, not interface names
Okay, yeah, world names don't matter (to my knowledge).
Ok, that makes sense, thanks!
Joel Dice said:
Generally speaking, we can refer to the union of interfaces which a component imports and exports as the "world" it targets. That world could be defined in a single .wit file like the CLI world or the wasi-http one, for example -- or it could be a combination of several worlds (e.g. both CLI and wasi-http, in which case the either the host would need to support both or parts would need to be virtualized in terms of the world(s) the host _does_ support).
How would such virtualization happen? Could the runtime add a component that bridges the gap between the guest world and runtime world?
(like a polyfill for wasm)
Yes, the runtime _could_ do it, but usually the app developer would do it ahead of time using e.g. wasm-tools compose
to combine the original component with a virtualizing component. See https://github.com/bytecodealliance/WASI-Virt for example.
Another example where this is useful: the host _does_ support the required interfaces, but you're using a third-party subcomponent which you want to quarantine off and not give access to system resources.
Ah, that's interesting!
I'm not clear on how access rights are supposed to work in the component model in general. Can a runtime run a component that declares to use interfaces which the runtime disallows?
A host runtime can do whatever it wants :smile:, which could include inserting a virtualizing shim, providing no-op (or trapping) stubs as native host functions, or refusing to instantiate the component.
Ok, cool! Do I understand it correctly then that the permissions system is currently ad-hoc and runtime-specific? Like, one runtime might support giving an app granular access to files, while another only supports binary all-or-nothing full filesystem access?
Yes, that sounds correct.
Awesome, thanks a lot for helping me through this! :)
Last updated: Dec 23 2024 at 12:05 UTC