WebAssembly / WebAssembly/tool-conventions

Referencing symbols accross the DLL boundary

Open
#69 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
WebAssembly
Stars
372
Forks
75
PR merge metrics
No merged PRs in 30d

Description

The current state of dynamic linking is described https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md.

However this doesn't detail how imported symbols can referenced. Looking at the current implementation in emscripten it seems that all references to external symbols current go through JS functions:

Function Imports

Imported functions point JS thunks, which start empty and get updates as that various DLL are loaded. This works a little bit like PLT written in JS. Because all the complexity is moved JS all such functions are called with the regular call instruction and codegen doesn't need to know if a function is internal or external to the DLL.

Address Imports

For address that are external the current module, getters are used to retrieve the absolute symbol address. These are JS functions of the form g$<symbol_name> which return the absolute address of the given dynamic symbol. This means the codegen for -fPIC code needs to treat internal at external symbols very differently.

  • internal: get_global $memory_base + add known offset of <symbol_name>.
  • external call g$<symbol_name>.

The circular nature of imports and exports between shared libraries means that we can't directly import functions from other modules. However, I think we can do better than forcing all references to external functions and address to go via JS.

I'm proposing the following scheme:

Function Imports

All functions imported from other DLLs go via call_indirect. The loader is responsible for allocating table slots for every function. This makes the table act as out PLT. Functions are imported as immutable globals that represent the table offset so each external call would looks like:

get_global $foo
call_indirect 

Address Imports

We can use mutable globals for this. The loader can allocate one mutable global for each data address. A load from external address would then look like:

get_global $bar
i32.load

Mutable globals are needed because the addresses all global won't be known until all DLLs have been loaded, and which point the loader and update the mutable globals.

Its possible that only code compiled with -fPIC would need this extra indirection. We could treat the main executable as unique and always load it last, at which point all data address and wasm function will be available to the loader and external functions can be imported directly, avoiding the call_indirect.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading DynamicLinking.md and comparing its description with the current Emscripten implementation outlined in the issue. Evaluate the proposed call_indirect and mutable-global schemes for function and address imports, including the -fPIC and main-executable cases. Done requires an agreed convention that resolves how imported symbols are represented and loaded across DLLs.

Written by the indexing model from the issue text.

Assessment

Tech stack
wasm
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.