Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wasm32-unknown-unknown support #1010

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

trevyn
Copy link
Contributor

@trevyn trevyn commented Aug 6, 2021

All credit to @passchaos in #935 for the insight that SQLite only uses a very small subset of libc, that can easily be copied in-tree.

Significant changes from that PR:

  • Using pristine libc .c files from OpenBSD
  • Minimal libc headers
  • libsqlite3-sys/sqlite3/sqlite3.c kept pristine
  • Removed custom memvfs, replaced by a thin VFS that liberally returns SQLITE_IOERR. This target is intended to be used only with Connection::open_in_memory() or equivalent.
  • Builds and runs a basic integration test in CI, via wasm-pack.

The allocator's free() method is still a little weird, but it is documented that SQLite's default allocator stores the allocation length in the first 8 bytes: https://sqlite.org/malloc.html#the_default_memory_allocator . If we want to wrap it again, I'm happy to do that, just let me know.

To do:

  • Audit SQLite API for structures passed by value
  • Improve time-related and randomness implementations

Closes #488, closes #827, closes #935

@codecov
Copy link

codecov bot commented Aug 6, 2021

Codecov Report

Attention: Patch coverage is 4.30108% with 89 lines in your changes missing coverage. Please review.

Project coverage is 80.62%. Comparing base (a96f61b) to head (415cafe).
Report is 143 commits behind head on master.

Files Patch % Lines
tests/wasm_pack.rs 0.00% 45 Missing ⚠️
libsqlite3-sys/build.rs 6.38% 44 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1010      +/-   ##
==========================================
- Coverage   81.31%   80.62%   -0.69%     
==========================================
  Files          51       52       +1     
  Lines       10402    10493      +91     
==========================================
+ Hits         8458     8460       +2     
- Misses       1944     2033      +89     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@trevyn
Copy link
Contributor Author

trevyn commented Aug 24, 2021

I reviewed https://sqlite.org/capi3ref.html, and did not see any instances of structs being passed or returned by value.

@thomcc Does this PR look roughly acceptable, if I sort out the time and randomness-related bits?

@thomcc thomcc self-requested a review August 24, 2021 16:17
@thomcc
Copy link
Member

thomcc commented Aug 24, 2021

Give me some time to review it (probably sometime over the weekend), but thanks for the work.

Copy link
Contributor

@wngr wngr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great effort, hope it makes it into rusqlite soon!

Reading up on https://github.com/jlongster/absurd-sql, I wonder how feasible it would be to implement a pluggable persistence backend like jslongster did in sql-js/sql.js#481 for rusqlite.
Have you seen that? What are your thoughts on it?

@trevyn
Copy link
Contributor Author

trevyn commented Oct 4, 2021

@wngr Thanks! It does seem plausible to make a VFS that uses IndexedDB as a backend; that would be very cool. I don't expect to personally have time to work on it in the near future, but would love to see it happen.

@trevyn
Copy link
Contributor Author

trevyn commented Jan 21, 2022

A concern from the previous PR:

A lot of the declarations in the header don't have definitions. What's the deal there?

I believe what's happening (in the few cases of this that still exist) is that the amalgamation is relying on certain declarations existing, but never actually uses them in the ultimately-generated code. If they were used, the WASM file would fail to load with its cryptic version of an "undefined symbol" error, see for example rustwasm/wasm-bindgen#2570

This is essentially how I decided which libc files to include; provide the minimal declarations that allowed compilation, and then track down the symbols that needed to be added that would allow the WASM to load.

Edit: Also, it seems clang provides builtins for memcpy & friends.

@trevyn
Copy link
Contributor Author

trevyn commented Jan 21, 2022

@thomcc Ready for review.

  • Reimplemented the allocator functions to explicitly store allocation size in a header.
  • Implemented required time & randomness functions.

@a-cat-named-snowball
Copy link

@thomcc Any chance you could review this PR? wasm32-unknown-unknown support would be awesome 😄

@paulyoung
Copy link

I tried using this and ran into the following (click to expand)

error: failed to run custom build command for `libsqlite3-sys v0.23.2 (https://github.com/trevyn/rusqlite.git?branch=wasm32-unknown-unknown#004cf263)`
Caused by:
  process didn't exit successfully: `/Users/py/projects/codebase-labs/ic-sqlite/main/target/debug/build/libsqlite3-sys-5deb3527a4482ea4/build-script-build` (exit status: 1)
  --- stdout
  cargo:rerun-if-changed=sqlite3/sqlite3.c
  cargo:rerun-if-changed=sqlite3/wasm32-wasi-vfs.c
  cargo:rerun-if-env-changed=SQLITE_MAX_VARIABLE_NUMBER
  cargo:rerun-if-env-changed=SQLITE_MAX_EXPR_DEPTH
  cargo:rerun-if-env-changed=LIBSQLITE3_FLAGS
  TARGET = Some("wasm32-unknown-unknown")
  OPT_LEVEL = Some("0")
  HOST = Some("aarch64-apple-darwin")
  CC_wasm32-unknown-unknown = None
  CC_wasm32_unknown_unknown = None
  TARGET_CC = Some("cc")
  CFLAGS_wasm32-unknown-unknown = None
  CFLAGS_wasm32_unknown_unknown = None
  TARGET_CFLAGS = None
  CFLAGS = None
  CRATE_CC_NO_DEFAULTS = None
  DEBUG = Some("true")
  CARGO_CFG_TARGET_FEATURE = Some("llvm14-builtins-abi")
  running: "cc" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "-I" "sqlite3/wasm32-unknown-unknown/include" "-DSQLITE_CORE" "-DSQLITE_DEFAULT_FOREIGN_KEYS=1" "-DSQLITE_ENABLE_API_ARMOR" "-DSQLITE_ENABLE_COLUMN_METADATA" "-DSQLITE_ENABLE_DBSTAT_VTAB" "-DSQLITE_ENABLE_FTS3" "-DSQLITE_ENABLE_FTS3_PARENTHESIS" "-DSQLITE_ENABLE_FTS5" "-DSQLITE_ENABLE_JSON1" "-DSQLITE_ENABLE_LOAD_EXTENSION=1" "-DSQLITE_ENABLE_MEMORY_MANAGEMENT" "-DSQLITE_ENABLE_RTREE" "-DSQLITE_ENABLE_STAT2" "-DSQLITE_ENABLE_STAT4" "-DSQLITE_SOUNDEX" "-DSQLITE_THREADSAFE=1" "-DSQLITE_USE_URI" "-DHAVE_USLEEP=1" "-D_POSIX_THREAD_SAFE_FUNCTIONS" "-DHAVE_LOCALTIME_R" "-DSQLITE_OS_OTHER" "-DSQLITE_TEMP_STORE=3" "-DLONGDOUBLE_TYPE=double" "-DSQLITE_OMIT_LOCALTIME" "-o" "/Users/py/projects/codebase-labs/ic-sqlite/main/target/wasm32-unknown-unknown/debug/build/libsqlite3-sys-f5f455669bc46cf2/out/sqlite3/sqlite3.o" "-c" "sqlite3/sqlite3.c"
  cargo:warning=In file included from sqlite3/sqlite3.c:24572:
  cargo:warning=In file included from /nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/sysctl.h:83:
  cargo:warning=In file included from /nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/ucred.h:76:
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/bsm/audit.h:203:2: error: unknown type name 'dev_t'
  cargo:warning=        dev_t           port;
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/bsm/audit.h:209:2: error: unknown type name 'dev_t'
  cargo:warning=        dev_t           at_port;
  cargo:warning=        ^
  cargo:warning=In file included from sqlite3/sqlite3.c:24572:
  cargo:warning=In file included from /nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/sysctl.h:83:
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/ucred.h:101:2: error: unknown type name 'u_int'
  cargo:warning=        u_int   cr_version;             /* structure layout version */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/ucred.h:104:2: error: unknown type name 'gid_t'
  cargo:warning=        gid_t   cr_groups[NGROUPS];     /* advisory group list */
  cargo:warning=        ^
  cargo:warning=In file included from sqlite3/sqlite3.c:24572:
  cargo:warning=In file included from /nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/sysctl.h:84:
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:109:2: error: unknown type name 'caddr_t'
  cargo:warning=        caddr_t user_stack;     /* where user stack was allocated */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:114:2: error: unknown type name 'u_int'
  cargo:warning=        u_int   p_estcpu;        /* Time averaged value of p_cpticks. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:116:2: error: unknown type name 'fixpt_t'
  cargo:warning=        fixpt_t p_pctcpu;        /* %cpu for this process during p_swtime */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:119:2: error: unknown type name 'u_int'
  cargo:warning=        u_int   p_swtime;        /* Time swapped in or out. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:120:2: error: unknown type name 'u_int'
  cargo:warning=        u_int   p_slptime;       /* Time since last blocked. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:123:2: error: unknown type name 'u_quad_t'
  cargo:warning=        u_quad_t p_uticks;              /* Statclock hits in user mode. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:124:2: error: unknown type name 'u_quad_t'
  cargo:warning=        u_quad_t p_sticks;              /* Statclock hits in system mode. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:125:2: error: unknown type name 'u_quad_t'
  cargo:warning=        u_quad_t p_iticks;              /* Statclock hits processing intr. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:134:2: error: unknown type name 'u_char'; did you mean 'char'?
  cargo:warning=        u_char  p_priority;     /* Process priority. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:135:2: error: unknown type name 'u_char'; did you mean 'char'?
  cargo:warning=        u_char  p_usrpri;       /* User-priority based on p_cpu and p_nice. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:140:2: error: unknown type name 'u_short'; did you mean 'short'?
  cargo:warning=        u_short p_xstat;        /* Exit status for wait; also stop signal. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/proc.h:141:2: error: unknown type name 'u_short'; did you mean 'short'?
  cargo:warning=        u_short p_acflag;       /* Accounting flags. */
  cargo:warning=        ^
  cargo:warning=In file included from sqlite3/sqlite3.c:24572:
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/sysctl.h:453:2: error: unknown type name 'gid_t'
  cargo:warning=        gid_t   p_rgid;                 /* Real group id. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/sysctl.h:454:2: error: unknown type name 'gid_t'
  cargo:warning=        gid_t   p_svgid;                /* Saved effective group id. */
  cargo:warning=        ^
  cargo:warning=/nix/store/qzzicx3cjj00z0c4kx3v0919gqp40mnp-libSystem-11.0.0/include/sys/sysctl.h:462:2: error: unknown type name 'gid_t'
  cargo:warning=        gid_t   cr_groups[NGROUPS];     /* groups */
  cargo:warning=        ^
  cargo:warning=fatal error: too many errors emitted, stopping now [-ferror-limit=]
  cargo:warning=20 errors generated.
  exit status: 1

--- stderr

error occurred: Command "cc" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "-I" "sqlite3/wasm32-unknown-unknown/include" "-DSQLITE_CORE" "-DSQLITE_DEFAULT_FOREIGN_KEYS=1" "-DSQLITE_ENABLE_API_ARMOR" "-DSQLITE_ENABLE_COLUMN_METADATA" "-DSQLITE_ENABLE_DBSTAT_VTAB" "-DSQLITE_ENABLE_FTS3" "-DSQLITE_ENABLE_FTS3_PARENTHESIS" "-DSQLITE_ENABLE_FTS5" "-DSQLITE_ENABLE_JSON1" "-DSQLITE_ENABLE_LOAD_EXTENSION=1" "-DSQLITE_ENABLE_MEMORY_MANAGEMENT" "-DSQLITE_ENABLE_RTREE" "-DSQLITE_ENABLE_STAT2" "-DSQLITE_ENABLE_STAT4" "-DSQLITE_SOUNDEX" "-DSQLITE_THREADSAFE=1" "-DSQLITE_USE_URI" "-DHAVE_USLEEP=1" "-D_POSIX_THREAD_SAFE_FUNCTIONS" "-DHAVE_LOCALTIME_R" "-DSQLITE_OS_OTHER" "-DSQLITE_TEMP_STORE=3" "-DLONGDOUBLE_TYPE=double" "-DSQLITE_OMIT_LOCALTIME" "-o" "/Users/py/projects/codebase-labs/ic-sqlite/main/target/wasm32-unknown-unknown/debug/build/libsqlite3-sys-f5f455669bc46cf2/out/sqlite3/sqlite3.o" "-c" "sqlite3/sqlite3.c" with args "cc" did not execute successfully (status code exit status: 1).

I was previously trying to use @rkusa's branch and had to set TARGET_CC for that to build, but that's the only thing I'm explicitly setting here.

@paulyoung
Copy link

For what it's worth, this is what happens if I don't set TARGET_CC:

error: failed to run custom build command for `libsqlite3-sys v0.23.2 (https://github.com/trevyn/rusqlite.git?branch=wasm32-unknown-unknown#004cf263)`
Caused by:
  process didn't exit successfully: `/Users/py/projects/codebase-labs/ic-sqlite/main/target/debug/build/libsqlite3-sys-5deb3527a4482ea4/build-script-build` (exit status: 1)
  --- stdout
  cargo:rerun-if-changed=sqlite3/sqlite3.c
  cargo:rerun-if-changed=sqlite3/wasm32-wasi-vfs.c
  cargo:rerun-if-env-changed=SQLITE_MAX_VARIABLE_NUMBER
  cargo:rerun-if-env-changed=SQLITE_MAX_EXPR_DEPTH
  cargo:rerun-if-env-changed=LIBSQLITE3_FLAGS
  TARGET = Some("wasm32-unknown-unknown")
  OPT_LEVEL = Some("0")
  HOST = Some("aarch64-apple-darwin")
  CC_wasm32-unknown-unknown = None
  CC_wasm32_unknown_unknown = None
  TARGET_CC = None
  CC = Some("clang")
  CFLAGS_wasm32-unknown-unknown = None
  CFLAGS_wasm32_unknown_unknown = None
  TARGET_CFLAGS = None
  CFLAGS = None
  CRATE_CC_NO_DEFAULTS = None
  DEBUG = Some("true")
  running: "clang" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "--target=wasm32-unknown-unknown" "-I" "sqlite3/wasm32-unknown-unknown/include" "-DSQLITE_CORE" "-DSQLITE_DEFAULT_FOREIGN_KEYS=1" "-DSQLITE_ENABLE_API_ARMOR" "-DSQLITE_ENABLE_COLUMN_METADATA" "-DSQLITE_ENABLE_DBSTAT_VTAB" "-DSQLITE_ENABLE_FTS3" "-DSQLITE_ENABLE_FTS3_PARENTHESIS" "-DSQLITE_ENABLE_FTS5" "-DSQLITE_ENABLE_JSON1" "-DSQLITE_ENABLE_LOAD_EXTENSION=1" "-DSQLITE_ENABLE_MEMORY_MANAGEMENT" "-DSQLITE_ENABLE_RTREE" "-DSQLITE_ENABLE_STAT2" "-DSQLITE_ENABLE_STAT4" "-DSQLITE_SOUNDEX" "-DSQLITE_THREADSAFE=1" "-DSQLITE_USE_URI" "-DHAVE_USLEEP=1" "-D_POSIX_THREAD_SAFE_FUNCTIONS" "-DHAVE_LOCALTIME_R" "-DSQLITE_OS_OTHER" "-DSQLITE_TEMP_STORE=3" "-DLONGDOUBLE_TYPE=double" "-DSQLITE_OMIT_LOCALTIME" "-o" "/Users/py/projects/codebase-labs/ic-sqlite/main/target/wasm32-unknown-unknown/debug/build/libsqlite3-sys-f5f455669bc46cf2/out/sqlite3/sqlite3.o" "-c" "sqlite3/sqlite3.c"
  cargo:warning=clang-11: warning: argument unused during compilation: '-mmacos-version-min=11.0' [-Wunused-command-line-argument]
  cargo:warning=clang-11: warning: argument unused during compilation: '-arch arm64' [-Wunused-command-line-argument]
  cargo:warning=error: unknown target CPU 'apple-a13'
  cargo:warning=note: valid target CPU values are: mvp, bleeding-edge, generic
  exit status: 1

--- stderr

error occurred: Command "clang" "-O0" "-ffunction-sections" "-fdata-sections" "-fPIC" "-g" "-fno-omit-frame-pointer" "--target=wasm32-unknown-unknown" "-I" "sqlite3/wasm32-unknown-unknown/include" "-DSQLITE_CORE" "-DSQLITE_DEFAULT_FOREIGN_KEYS=1" "-DSQLITE_ENABLE_API_ARMOR" "-DSQLITE_ENABLE_COLUMN_METADATA" "-DSQLITE_ENABLE_DBSTAT_VTAB" "-DSQLITE_ENABLE_FTS3" "-DSQLITE_ENABLE_FTS3_PARENTHESIS" "-DSQLITE_ENABLE_FTS5" "-DSQLITE_ENABLE_JSON1" "-DSQLITE_ENABLE_LOAD_EXTENSION=1" "-DSQLITE_ENABLE_MEMORY_MANAGEMENT" "-DSQLITE_ENABLE_RTREE" "-DSQLITE_ENABLE_STAT2" "-DSQLITE_ENABLE_STAT4" "-DSQLITE_SOUNDEX" "-DSQLITE_THREADSAFE=1" "-DSQLITE_USE_URI" "-DHAVE_USLEEP=1" "-D_POSIX_THREAD_SAFE_FUNCTIONS" "-DHAVE_LOCALTIME_R" "-DSQLITE_OS_OTHER" "-DSQLITE_TEMP_STORE=3" "-DLONGDOUBLE_TYPE=double" "-DSQLITE_OMIT_LOCALTIME" "-o" "/Users/py/projects/codebase-labs/ic-sqlite/main/target/wasm32-unknown-unknown/debug/build/libsqlite3-sys-f5f455669bc46cf2/out/sqlite3/sqlite3.o" "-c" "sqlite3/sqlite3.c" with args "clang" did not execute successfully (status code exit status: 1).

@trevyn
Copy link
Contributor Author

trevyn commented Sep 15, 2022

@paulyoung For an aarch64-apple-darwin host, this relies on a Homebrew clang install, it looks like yours might be out of date? brew install llvm might help.

Anyway, this still works for me with:

cargo install cargo-generate
cargo generate --git https://github.com/rustwasm/wasm-pack-template
rusqlite = {git = "https://github.com/trevyn/rusqlite", branch = "wasm32-unknown-unknown", features = ["bundled"]}
wasm-pack build

Does this work for you? Are you able to link to the project that you're actually building?

@paulyoung
Copy link

@trevyn I manage my projects and their dependencies using nix (not homebrew) so it's probably a matter of figuring out what versions are needed and making sure they're being used.

I could create a minimal example project that exhibits this issue if it would help.

@paulyoung
Copy link

@trevyn
Copy link
Contributor Author

trevyn commented Sep 16, 2022

Fancy! In that case, libsqlite3-sys/build.rs is looking for the CC and AR env variables to be set to a clang and llvm-ar that support wasm32-unknown-unknown. I know Homebrew's [email protected] works, not sure what the minimum requirements are otherwise.

https://github.com/trevyn/rusqlite/blob/004cf263143e038f01e6022d8d5b81b9c621f89f/libsqlite3-sys/build.rs#L253-L261

@BlueHotDog
Copy link

hey, is this waiting for something? can it be merged?

@stan-irl
Copy link

This built fine on my m1 mac, then it failed on docker: rust:1.69.0-bookworm until i installed clang after which it built fine.

Would love to see this merged into master 🎉

@zone117x
Copy link

If merged this would be a pretty big win for the Rust ecosystem imo

@stan-irl
Copy link

Absolutely massive. Especially since sled is dead.

@trevyn trevyn marked this pull request as ready for review January 8, 2024 22:36
@jorgenpt
Copy link

@trevyn I am trying to get this branch working with wasm-pack and I've been hitting some issues. I've reached out to the Rusqlite Discord, but I don't think you're in it, and your fork doesn't have any options to open issues or discussions. Is there any appropriate place to ask for help with this from someone more experienced than me? (Specifically I'm encountering rust-lld: error: --shared-memory is disallowed by sqlite3.o because it was not compiled with 'atomics' or 'bulk-memory' features. when building.)

I don't want to hijack your pull request discussion for a support request, I was just hoping there was somewhere you could point me that would be a good place to discuss this. Thanks! (And thanks for this work, this is very exciting -- I'm hoping to use flutter_rust_bridge to get the same Rust sqlite code running on my server as in native mobile apps and a web app, which would be really helpful for not having to duplicate database logic!)

@carlsverre
Copy link

I suggest moving that line of questioning to wasm-pack land. Here is a similar issue: rustwasm/wasm-pack#1328

You'll need to figure out how to convince wasm-pack + cargo + rustc to recompile everything with the atomics and bulk-memory features in order to support shared memory. I did a bunch of investigation into this recently and it's a huge PITA (for now). I also suggest taking a look at the new wasm32-wasi-preview1-threads target which may make this easier (or harder): https://doc.rust-lang.org/rustc/platform-support/wasm32-wasi-preview1-threads.html

@jorgenpt
Copy link

@carlsverre After investigating a bit, this seems to be distinct from the wasm-pack issue -- it's not about rust code, but specifically about sqlite3.o, which is produced by libsqlite3-sys. The issue seems to be that the target feature flags set for rust are not being forwarded to clang when building sqlite3.c.

I proposed a fix to @trevyn in a pull request to his fork's pull request branch (😅), not sure if that's the best approach.

jorgenpt and others added 3 commits February 17, 2024 13:44
This allows code that e.g. relies on
`-Ctarget-feature=+atomics,+bulk-memory` to build libsqlite3-sys.
@jorgenpt
Copy link

FWIW, the list of supported features was pulled from https://github.com/rust-lang/rust/blob/master/compiler/rustc_target/src/target_features.rs#L312 -- I'm not sure if there's a clean way to depend on rustc_target to avoid hard coding this list? (I also don't know if all wasm32 target features will map 1:1 to clang options, though the current list does -- I compared it to the clang command line documentation.)

@ec2
Copy link

ec2 commented Apr 11, 2024

I just wrote a test to init a DB connection and INSERT then SELECT using this branch (with latest master pulled into it).

It runs fine on non-wasm but when I ran it on wasm it throws: RuntimeError: indirect call to null. I could repro this on firefox and safari. The full stacktrace is here:

    JS exception that was thrown:
        RuntimeError: indirect call to null
        sqlite3OsFileControl@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[3403]:0x3340f3
        sqlite3_file_control@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[761]:0x20d9e1
        sqlite3Pragma@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[61]:0x26e6d
        yy_reduce@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[60]:0x2454c
        sqlite3Parser@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[971]:0x23e22e
        sqlite3RunParser@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[301]:0x15b901
        sqlite3Prepare@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[277]:0x14cafc
        sqlite3LockAndPrepare@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[1029]:0x249b15
        sqlite3_prepare_v3@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[3523]:0x339c56
        rusqlite::inner_connection::InnerConnection::prepare_::hc2f0a88ee06fb549@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[4084]:0x34f87a
        rusqlite::inner_connection::InnerConnection::prepare::h94bf9f3edeb66287@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[427]:0x19d8a3
        rusqlite::Connection::prepare_with_flags::h09f3557df09b0fc9@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[3308]:0x32f292
        rusqlite::Connection::prepare::h310e550dfb17c830@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[4451]:0x35a312
        rusqlite::Connection::execute_batch::hea1042c16c0cd981@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[294]:0x1570c0
        rusqlite::pragma::<impl rusqlite::Connection>::pragma_update::hdec1666d01ace9c7@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[621]:0x1e5e75
        rusqlite_migration::set_user_version::h982001b4ab2782a6@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[1417]:0x28b38a
        rusqlite_migration::Migrations::goto_up::h727ad997790a6cb9@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[154]:0xe7555
        rusqlite_migration::Migrations::goto::h1a94cc5b0cbd3f29@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[207]:0x119bcf
        rusqlite_migration::Migrations::to_latest::hcecaa0df0427a54b@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[705]:0x1febfe
        db::lib_test::test::hc7c7c3310ec12cbf@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[628]:0x1e7f23
        core::ops::function::FnOnce::call_once::h8dd7fba8693501e0@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[5668]:0x370078
        wasm_bindgen_test::__rt::Context::execute_sync::{{closure}}::he71cc68df0d3c2a7@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[3323]:0x32ff13
        <wasm_bindgen_test::__rt::TestFuture<F> as core::future::future::Future>::poll::{{closure}}::{{closure}}::hc25b673a04f73bb8@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[3518]:0x33987e
        wasm_bindgen::convert::closures::invoke0_mut::h6e1e9c6b868d2ee7@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[4063]:0x34ed5a
        __wbg_adapter_41@http://127.0.0.1:38265/wasm-bindgen-test:337:10
        cb0@http://127.0.0.1:38265/wasm-bindgen-test:487:28
        window.__wbg_test_invoke@http://127.0.0.1:38265/:38:38
        __wbg_get_imports/imports.wbg.__wbg_wbgtestinvoke_7de5d5374a833823/<@http://127.0.0.1:38265/wasm-bindgen-test:492:30
        handleError@http://127.0.0.1:38265/wasm-bindgen-test:342:18
        __wbg_get_imports/imports.wbg.__wbg_wbgtestinvoke_7de5d5374a833823@http://127.0.0.1:38265/wasm-bindgen-test:480:76
        wasm_bindgen_test::__rt::__wbg_test_invoke::hb58221eb1bd2a29f@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[2517]:0x2fdf69
        <wasm_bindgen_test::__rt::TestFuture<F> as core::future::future::Future>::poll::{{closure}}::hf3411908db1aeefa@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[3839]:0x346df8
        scoped_tls::ScopedKey<T>::set::h73e8dcd9e422f126@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[3629]:0x33e690
        <wasm_bindgen_test::__rt::TestFuture<F> as core::future::future::Future>::poll::h4925da01429b92e6@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[653]:0x1ef992
        <wasm_bindgen_test::__rt::ExecuteTests as core::future::future::Future>::poll::h086a35e5c805ca10@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[184]:0x104fcd
        wasm_bindgen_test::__rt::Context::run::{{closure}}::hc9ef03a3fb10a54c@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[1576]:0x2a0982
        wasm_bindgen_futures::future_to_promise::{{closure}}::{{closure}}::h32d6024e9975e335@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[714]:0x20109e
        wasm_bindgen_futures::task::singlethread::Task::run::h184e97cc0ef1e77e@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[1510]:0x297fe8
        wasm_bindgen_futures::queue::QueueState::run_all::h4285a3205cd25e58@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[1151]:0x260bb3
        wasm_bindgen_futures::queue::Queue::new::{{closure}}::hc990aef658832399@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[4229]:0x35407f
        <dyn core::ops::function::FnMut<(A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h17f3b0b306d2221f@http://127.0.0.1:38265/wasm-bindgen-test_bg.wasm:wasm-function[3349]:0x33156b
        __wbg_adapter_28@http://127.0.0.1:38265/wasm-bindgen-test:240:10
        real@http://127.0.0.1:38265/wasm-bindgen-test:203:20

where the test is:

use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
wasm_bindgen_test_configure!(run_in_browser);

#[derive(Debug, Eq, PartialEq, Clone)]
struct Example {
    pub id: i64,
    pub name: String,
}

#[wasm_bindgen_test]
#[test]
fn test() {
    let migrations = rusqlite_migration::Migrations::new(vec![rusqlite_migration::M::up(
        "CREATE TABLE example (id INT, name TEXT);",
    )]);
    let mut conn = rusqlite::Connection::open_in_memory().unwrap();
    migrations.to_latest(&mut conn).unwrap();

    let val = Example {
        id: 1,
        name: "test".into(),
    };
    conn.execute(
        "INSERT INTO example (id, name) VALUES (?, ?)",
        (val.id, val.name.clone()),
    )
    .expect("Failed to set");
    let res = conn
        .query_row(
            "SELECT id, name FROM example WHERE (id=?)",
            [val.id],
            |row| {
                Ok(Example {
                    id: row.get(0).unwrap(),
                    name: row.get(1).unwrap(),
                })
            },
        )
        .unwrap();
    assert_eq!(res, val);
}

I'm seeing this same issue as well on the latest commit of this branch. Upon some investigation, it seems like I run into this if I am executing statements via Transactions. Specifically it looks like it happens when I try to execute more than 2 statements in a batch_execute via a Transaction. Specifically I'm doing 2+ create tables in a batch.

@ec2
Copy link

ec2 commented Apr 16, 2024

Sorry I'm not deep enough into understanding sqlite3 to propose a fix, but I have found out that this happens when journaling is enabled. When a transaction is started a MemJournal gets opened and when step calls xFileControl, it is null because that's how sqlite3_io_methods MemJournalMethods is defined.

@GyulyVGC
Copy link

GyulyVGC commented Jun 4, 2024

Hey @trevyn thanks for the PR, this branch correctly compiles to wasm32-unknown-unknown for me as well!

However, I get a disk I/O error when trying to open a connection to a file db (rusqlite::Connection::open(SQLITE_PATH)).
Did someone encountered this problem before?

I've also tried older commits from this branch but the problem persists...

@ec2
Copy link

ec2 commented Jun 4, 2024

Hey @trevyn thanks for the PR, this branch correctly compiles to wasm32-unknown-unknown for me as well!

However, I get a disk I/O error when trying to open a connection to a file db (rusqlite::Connection::open(SQLITE_PATH)). Did someone encountered this problem before?

I've also tried older commits from this branch but the problem persists...

IIUC, this PR only supports an in memory db.

@GyulyVGC
Copy link

GyulyVGC commented Jun 5, 2024

IIUC, this PR only supports an in memory db.

Yeah, I've now found out that in order to access the filesystem you need to target wasm32-wasi.

Copy link

@complexspaces complexspaces left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there,

This PR came up for discussion at $EMPLOYER today so I thought I would take a cursory look at what was here so far. I am not the maintainer but I tried being leaving some helpful comments on things I would have noticed if this fork was checked-in internally.

@@ -37,6 +37,12 @@ wasm32-wasi-vfs = []
[dependencies]
openssl-sys = { version = "0.9", optional = true }

[target.wasm32-unknown-unknown.dependencies]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this include a not(unix) condition here because js-sys is a web-only dependency? wasm32-unknown-unknown doesn't always mean web. I did something similar, along with a js feature and it's worked pretty well for quite a few downstream WASM users.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like you might be conflating two different things here:

  1. To my understanding, wasm32-unknown-unknown will never report itself as unix, so your proposed condition is unnecessary. unix is set by wasm32-unknown-emscripten, which is explicitly not matched by [target.wasm32-unknown-unknown.dependencies], and is outside the scope of this PR.

  2. The other thing is that wasm32-unknown-unknown doesn't always mean web, which is true. I think you actually meant to say that wasm32-unknown-unknown doesn't always mean a Javascript environment, which is also true. PR welcome for supporting that use case.

@@ -261,6 +261,57 @@ mod build_bundled {
cfg.file("sqlite3/wasm32-wasi-vfs.c");
}
}
if env::var("TARGET") == Ok("wasm32-unknown-unknown".to_string()) {
// Apple clang doesn't support wasm32, so use Homebrew clang by default.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, this block should be reverted and be replaced with documentation stating how to setup a functional cross-compiling environment on Apple's hosts. Not everyone uses Homebrew (this wouldn't work with Nix, for example) and it would result in different errors about the path not existing vs clang saying "unknown target".

I see it in wasm32-unknown-unknown-openbsd-libc's build script as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely welcome any improvements to documentation or error messages. I think that it is reasonable to find and use a Homebrew installation by default if available.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's pretty weird for us to automatically do this for the user, seconding the suggestion to instead document how to set things up appropriately.

- run: npm install -g wasm-pack

- name: Test wasm-pack ${{ matrix.host }}
run: wasm-pack test --node --features bundled

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a browser-bound test as well, no? Otherwise there's no way to know in CI if the js_sys dependency or another browser-specific one breaks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A browser test is certainly reasonable to add, but note that there are no web dependencies, only Javascript dependencies, which node fulfills just as well.

@@ -37,6 +37,12 @@ wasm32-wasi-vfs = []
[dependencies]
openssl-sys = { version = "0.9", optional = true }

[target.wasm32-unknown-unknown.dependencies]
compiler-rt-builtins = "0.1"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it documented which part of the requirements compiler-rt-builtins provides vs the stripped down OpenBSD libc source?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1010 (comment)

Also, I haven't tested things since rust-lang/compiler-builtins#566 got merged, it's possible that might already provide the required functions or get us closer to a cleaner solution in some way.

Comment on lines +160 to +161
let target_date = js_sys::Date::now() + (microseconds as f64 / 1000.0);
while js_sys::Date::now() < target_date {}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this PR supports being built with the atomics WASM feature (and people are intending to), maybe allow this to be optimized with memory_atomic_wait32 instead of a spinloop? This is what the standard library does when built with atomics support too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good suggestion, thanks.

Copy link
Member

@thomcc thomcc Aug 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth noting that atomic waits don't work on the browser's main thread (only in workers). So IDK if this is a good idea.

OTOH, there are JS environments where the timestamp will never advance (due to concerns around Spectre). This at least used to be the case for Cloudflare workers, and may still be the case there.

@thomcc
Copy link
Member

thomcc commented Aug 5, 2024

Not for nothing, this PR is requested more frequently than any other from people in the Discord (not that that means it's requested that frequently, but I did just get another request about it). It doesn't appear to pull in a big chunk of libc anymore, so the previous main objection I had might be fixed now.

@@ -261,6 +261,57 @@ mod build_bundled {
cfg.file("sqlite3/wasm32-wasi-vfs.c");
}
}
if env::var("TARGET") == Ok("wasm32-unknown-unknown".to_string()) {
// Apple clang doesn't support wasm32, so use Homebrew clang by default.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's pretty weird for us to automatically do this for the user, seconding the suggestion to instead document how to set things up appropriately.

[a, b][(a < b) as usize]
}

const ALIGN: usize = max(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is subtly wrong (too low) for targets other than wasm32-unknown-unknown (in particular, 64 bit targets).

For example, I could imagine this code not getting updated if wasm64-unknown-unknown support is added.

It's probably better to use 16 instead of 8 for the hard-coded alignment bound, which is a somewhat safer value to hard-code (it's a very common max_align_t value for 64bit targets).

Also add a comment about this, please.

}

const fn max(a: usize, b: usize) -> usize {
[a, b][(a < b) as usize]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This goofy way of implementing max shouldn't be needed anymore.

return null_mut();
}

*(ptr as *mut usize) = size;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we don't need to provide posix_memalign, only storing size is fine (otherwise we'd need to store both size/align for free).

Comment on lines +160 to +161
let target_date = js_sys::Date::now() + (microseconds as f64 / 1000.0);
while js_sys::Date::now() < target_date {}
Copy link
Member

@thomcc thomcc Aug 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth noting that atomic waits don't work on the browser's main thread (only in workers). So IDK if this is a good idea.

OTOH, there are JS environments where the timestamp will never advance (due to concerns around Spectre). This at least used to be the case for Cloudflare workers, and may still be the case there.

@istankovic
Copy link

@thomcc is there any news regarding this? What's the plan here? Is there help needed to get this over the finish line?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Can't build for wasm target wasm build fails import sqlite3_* functions