Skip to content

Commit b98e460

Browse files
authored
rust: Define cabi_realloc as a weak symbol (#851)
* rust: Define `cabi_realloc` as a weak symbol This commit updates the `wit-bindgen` Rust crate to define the `cabi_realloc` symbol as a weak symbol. This is not easy because Rust does not offer a stable means by which to do this. Despite this through a combination of shell scripts and CI it should be possible to get this all working together by using C to define a weak symbol calling a known Rust symbol. Closes #849 * Fix typo in yml * More yml changes * Regenerate cabi_realloc files * Strip custom sections * Install wasm-tools on CI as well * Pin to slightly older wasm-tools * Fix native tests * Subvert rate limiting
1 parent c7f4fe7 commit b98e460

File tree

11 files changed

+224
-21
lines changed

11 files changed

+224
-21
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: 'Install the wasi-sdk'
2+
description: 'Install the wasi-sdk toolchain'
3+
4+
runs:
5+
using: composite
6+
steps:
7+
- run: |
8+
curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-linux.tar.gz -L | tar xzvf -
9+
echo "WASI_SDK_PATH=`pwd`/wasi-sdk-20.0" >> $GITHUB_ENV
10+
if: runner.os == 'Linux'
11+
shell: bash
12+
- run: |
13+
curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-macos.tar.gz -L | tar xzvf -
14+
echo "WASI_SDK_PATH=`pwd`/wasi-sdk-20.0" >> $GITHUB_ENV
15+
if: runner.os == 'macOS'
16+
shell: bash
17+
- run: |
18+
curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0.m-mingw.tar.gz -L | tar xzvf -
19+
echo "WASI_SDK_PATH=`pwd`/wasi-sdk-20.0+m" >> $GITHUB_ENV
20+
if: runner.os == 'Windows'
21+
shell: bash
22+
- name: Setup `wasm-tools`
23+
uses: bytecodealliance/actions/wasm-tools/setup@v1
24+
with:
25+
version: "1.0.60"
26+
github_token: ${{ github.token }}

.github/workflows/main.yml

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,7 @@ jobs:
6767
- name: Install wasm32-wasi target
6868
run: rustup target add wasm32-wasi
6969

70-
- run: |
71-
curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-linux.tar.gz -L | tar xzvf -
72-
echo "WASI_SDK_PATH=`pwd`/wasi-sdk-20.0" >> $GITHUB_ENV
73-
if : matrix.os == 'ubuntu-latest'
74-
- run: |
75-
curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-macos.tar.gz -L | tar xzvf -
76-
echo "WASI_SDK_PATH=`pwd`/wasi-sdk-20.0" >> $GITHUB_ENV
77-
if : matrix.os == 'macos-latest'
78-
- run: |
79-
curl https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0.m-mingw.tar.gz -L | tar xzvf -
80-
echo "WASI_SDK_PATH=`pwd`/wasi-sdk-20.0+m" >> $GITHUB_ENV
81-
if : matrix.os == 'windows-latest'
70+
- uses: ./.github/actions/install-wasi-sdk
8271

8372
- run: |
8473
curl.exe -LO https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1
@@ -119,6 +108,26 @@ jobs:
119108
run: |
120109
source ./emsdk-main/emsdk_env.sh
121110
cargo test --workspace
111+
112+
check:
113+
name: Check
114+
runs-on: ubuntu-latest
115+
steps:
116+
- uses: actions/checkout@v4
117+
- name: Install Rust
118+
run: rustup update stable --no-self-update && rustup default stable
119+
- name: Install wasm32-unknown-unknown target
120+
run: rustup target add wasm32-unknown-unknown
121+
- name: Install wasm32-wasi target
122+
run: rustup target add wasm32-wasi
123+
124+
# Verify the output of the `./ci/rebuild-libcabi-realloc.sh` script is
125+
# up-to-date.
126+
- uses: ./.github/actions/install-wasi-sdk
127+
- run: ./ci/rebuild-libcabi-realloc.sh
128+
- run: git diff --exit-code
129+
130+
# Test various feature combinations, make sure they all build
122131
- run: cargo build
123132
- run: cargo build --no-default-features
124133
- run: cargo build --no-default-features --features rust
@@ -128,7 +137,6 @@ jobs:
128137
- run: cargo build --no-default-features --features csharp
129138
- run: cargo build --no-default-features --features markdown
130139

131-
132140
rustfmt:
133141
name: Rustfmt
134142
runs-on: ubuntu-latest
@@ -162,6 +170,7 @@ jobs:
162170
- rustfmt
163171
- build
164172
- verify-publish
173+
- check
165174
if: always()
166175

167176
steps:

.github/workflows/release-process.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ jobs:
3434
- name: Bump version number
3535
run: ./publish ${{ github.event.inputs.action }}
3636

37+
# Regenerate precompiled artifacts in this repo as they're dependent on
38+
# the version number.
39+
- uses: ./.github/actions/install-wasi-sdk
40+
- run: ./ci/rebuild-libcabi-realloc.sh
41+
3742
- name: Prep PR metadata
3843
run: |
3944
set -ex

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ ace
77
*.wasm
88
!wasi_snapshot_preview1.reactor.wasm
99
__pycache__
10+
crates/guest-rust/src/cabi_realloc.o

ci/rebuild-libcabi-realloc.sh

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/bin/sh
2+
3+
# This script, and various infrastructure, is a hack to work around the lack of
4+
# stable support in Rust to generate a weak symbol.
5+
#
6+
# The basic problem here is that the Rust `wit-bindgen` crate wants to export
7+
# the `cabi_realloc` symbol from the final binary. This library, however,
8+
# is not stable which means that we're publishing new versions of `wit-bindgen`
9+
# over its development. This means that if `wit-bindgen`-the-crate were to
10+
# export a `#[no_mangle]` symbol of `cabi_realloc` then it wouldn't work to
11+
# have two versions of `wit-bindgen` in the same project. This can arise
12+
# relatively quickly, however, so this is something we want to solve.
13+
#
14+
# The general idea of the solution here is to ensure that the `cabi_realloc`
15+
# symbol itself is declared as a weak symbol. A weakly-defined symbol means
16+
# that if the linker sees multiple copies it can discard all but one. This is
17+
# the semantics we want where some `wit-bindgen` needs to define `cabi_realloc`
18+
# but it doesn't matter much which one.
19+
#
20+
# Stable Rust can't define weak symbols as of the time of this writing. C,
21+
# however, can. Unfortunately users of this crate do not always have a C
22+
# compiler on-hand for wasm, nor do we want to require one. That's where all
23+
# these hacks come into play. With that intro, the purpose of this script is to:
24+
#
25+
# * Generate a `cabi_realloc.rs` file with a "mangled" Rust symbol that's
26+
# unique per-major-version of the crate.
27+
# * Generate a `cabi_realloc.c` file that defines a weak `cabi_realloc` symbol
28+
# that calls the above Rust symbol
29+
# * Compile `cabi_realloc.c` into an object and place it into an archive and
30+
# check that archive into this repo.
31+
#
32+
# This all leads up to the point where we're distributing binary artifacts with
33+
# this crate. These artifacts are verified in CI to ensure what this script
34+
# generates.
35+
#
36+
# Overall this is intended to provide `cabi_realloc` as a weak symbol,
37+
# everything works on stable Rust, and users don't need a C compiler when they
38+
# use this crate.
39+
40+
set -ex
41+
42+
version=$(grep '^version =' ./Cargo.toml | sed 's/.*"\(.*\)"/\1/' | sed 's/\./_/g')
43+
44+
sym=cabi_realloc_wit_bindgen_$version
45+
46+
cat >./crates/guest-rust/src/cabi_realloc.rs <<-EOF
47+
// This file is generated by $0
48+
49+
#[no_mangle]
50+
pub unsafe extern "C" fn $sym(
51+
old_ptr: *mut u8,
52+
old_len: usize,
53+
align: usize,
54+
new_len: usize,
55+
) -> *mut u8 {
56+
crate::rt::cabi_realloc(old_ptr, old_len, align, new_len)
57+
}
58+
EOF
59+
60+
cat >./crates/guest-rust/src/cabi_realloc.c <<-EOF
61+
// This file is generated by $0
62+
63+
#include <stdint.h>
64+
65+
extern void *$sym(void *ptr, size_t old_size, size_t align, size_t new_size);
66+
67+
__attribute__((__weak__, __export_name__("cabi_realloc")))
68+
void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
69+
return $sym(ptr, old_size, align, new_size);
70+
}
71+
EOF
72+
73+
rm -f crates/guest-rust/src/cabi_realloc.o
74+
$WASI_SDK_PATH/bin/clang crates/guest-rust/src/cabi_realloc.c \
75+
-O -c -o crates/guest-rust/src/cabi_realloc.o
76+
77+
# Remove the `producers` section. This appears to differ whether the host for
78+
# clang is either macOS or Linux. Not needed here anyway, so discard it to help
79+
# either host produce the same object.
80+
wasm-tools strip -d producers ./crates/guest-rust/src/cabi_realloc.o \
81+
-o ./crates/guest-rust/src/cabi_realloc.o
82+
83+
rm -f crates/guest-rust/src/libwit_bindgen_cabi_realloc.a
84+
$WASI_SDK_PATH/bin/llvm-ar crus crates/guest-rust/src/libwit_bindgen_cabi_realloc.a \
85+
crates/guest-rust/src/cabi_realloc.o

crates/guest-rust/build.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
fn main() {
2+
if !cfg!(feature = "realloc") {
3+
return;
4+
}
5+
6+
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or(String::new());
7+
let target_family = std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or(String::new());
8+
9+
if target_family != "wasm" {
10+
return;
11+
}
12+
13+
if target_arch != "wasm32" {
14+
panic!("only wasm32 supports cabi-realloc right now");
15+
}
16+
17+
println!("cargo:rustc-link-lib=wit_bindgen_cabi_realloc");
18+
let cwd = std::env::current_dir().unwrap();
19+
let cwd = cwd.display();
20+
println!("cargo:rustc-link-search=native={cwd}/src");
21+
}

crates/guest-rust/src/cabi_realloc.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This file is generated by ./ci/rebuild-libcabi-realloc.sh
2+
3+
#include <stdint.h>
4+
5+
extern void *cabi_realloc_wit_bindgen_0_18_0(void *ptr, size_t old_size, size_t align, size_t new_size);
6+
7+
__attribute__((__weak__, __export_name__("cabi_realloc")))
8+
void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
9+
return cabi_realloc_wit_bindgen_0_18_0(ptr, old_size, align, new_size);
10+
}

crates/guest-rust/src/cabi_realloc.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// This file is generated by ./ci/rebuild-libcabi-realloc.sh
2+
3+
#[no_mangle]
4+
pub unsafe extern "C" fn cabi_realloc_wit_bindgen_0_18_0(
5+
old_ptr: *mut u8,
6+
old_len: usize,
7+
align: usize,
8+
new_len: usize,
9+
) -> *mut u8 {
10+
crate::rt::cabi_realloc(old_ptr, old_len, align, new_len)
11+
}

crates/guest-rust/src/lib.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ pub use wit_bindgen_rust_macro::generate;
305305
#[doc(hidden)]
306306
pub use bitflags;
307307

308+
/// For more information about this see `./ci/rebuild-libcabi-realloc.sh`.
309+
#[cfg(feature = "realloc")]
310+
mod cabi_realloc;
311+
308312
#[doc(hidden)]
309313
pub mod rt {
310314
use crate::alloc::string::String;
@@ -346,9 +350,36 @@ pub mod rt {
346350
// Re-export things from liballoc for convenient use.
347351
pub use super::alloc::{alloc, boxed, string, vec};
348352

353+
/// This function is called from generated bindings and will be deleted by
354+
/// the linker. The purpose of this function is to force a reference to the
355+
/// symbol `cabi_realloc` to make its way through to the final linker
356+
/// command line. That way `wasm-ld` will pick it up, see it needs to be
357+
/// exported, and then export it.
358+
///
359+
/// For more information about this see `./ci/rebuild-libcabi-realloc.sh`.
360+
pub fn maybe_link_cabi_realloc() {
361+
#[cfg(target_family = "wasm")]
362+
{
363+
#[cfg(feature = "realloc")]
364+
extern "C" {
365+
fn cabi_realloc(
366+
old_ptr: *mut u8,
367+
old_len: usize,
368+
align: usize,
369+
new_len: usize,
370+
) -> *mut u8;
371+
}
372+
#[cfg(feature = "realloc")]
373+
static _X: unsafe extern "C" fn(*mut u8, usize, usize, usize) -> *mut u8 = cabi_realloc;
374+
}
375+
}
376+
377+
/// NB: this function is called by a generated function in the
378+
/// `cabi_realloc` module above. It's otherwise never explicitly called.
379+
///
380+
/// For more information about this see `./ci/rebuild-libcabi-realloc.sh`.
349381
#[cfg(feature = "realloc")]
350-
#[no_mangle]
351-
unsafe extern "C" fn cabi_realloc(
382+
pub unsafe fn cabi_realloc(
352383
old_ptr: *mut u8,
353384
old_len: usize,
354385
align: usize,
412 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)