rust, WebAssembly, and passing arguments for increased total memory - rust

I have a rust project I am compiling to webasm per http://asquera.de/blog/2017-04-10/the-path-to-rust-on-the-web/
Project compiles. When I run it in Chrome Canary, it runs out of memory and gives me a very helpful error message:
abort("Cannot enlarge memory arrays. Either (1) compile with -s
TOTAL_MEMORY=X with X higher than the current value 16777216, (2) compile
with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime,
...
Problem is, its not clear how to pass those flags to rustc / the build tool chain.
Neither setting EMMAKEN_CFLAGS or the following work:
cargo rustc -v --target=wasm32-unknown-emscripten --release -- -Clink-args="-s TOTAL_MEMORY=33554432"

This blog post offers a solution that I think can be applied in your case too:
As best as I can tell there is no way to pass most linker arguments through cargo. Instead, hack around the limitation by specifying a custom linker that is actually a shell script wrapping the real linker.
Create a shell script like emcc_link that calls emscripten with the appropriate options:
emcc "-s" "TOTAL_MEMORY=33554432" $#
(You may need other options to make it work. Check the blog post for details.)
And specify to use it for your project by editing/creating .cargo/config:
[target.wasm32-unknown-emscripten]
linker = "/your/project/dir/emcc_sdl"
[target.asmjs-unknown-emscripten]
linker = "/your/project/dir/emcc_sdl"
I ruthlessly assumed the build environment is Linux or the like. On Windows the shell script should probably be a batch script and I'm not sure if there are any differences in .cargo/config.
Disclaimer: I have not tried any of this.

Related

Remove default linker flags in Rust

Is there a way to remove problematic linker flags in the cargo build command? I'm trying to cross-compile a binary on windows for x86_64-unknown-linux-gnu but linking fails with the message: ld unrecognized option: --eh-frame-hdr. Is there a way to remove this argument from the linking stage without having to create a custom target specification for linux builds?
I only found the rustflags key in config.toml, but as far as I know it only allows you to add additional flags and not to strip existing ones.
Start by making sure that you are passing the right linker-flavor. rustc might have misidentified what linker you are using, and specifying that might help. Also ensure you are using the right linker. But if you really want to remove that argument, it is possible using some unstable features.
There isn't a general way to remove individual linker arguments. The eh_frame_header target attribute indicates if the linker should insert a EH (exception handling) frame header. Unstable rustc does have support for changing individual target options, but it is a bit complicated and can only handle #![no_std] binaries on some platforms. You might want to consider just calling the linker manually.
First, dump the JSON target metadata (using a nightly compiler) for the target you are compiling to, replacing [target] with a target triple like thumbv7neon-unknown-linux-musleabihf:
$ rustc -Z unstable-options --print target-spec-json --target [target] > target.json
target.json now has target-specific compilation information. On some targets (such as WASM), EH frame headers are disabled by default. If you see a line like this in target.json, you are already done (and shouldn't be getting that error):
"eh-frame-header": false,
If you don't see that line, then add it to target.json right after opening brace, like
{
"eh-frame-header": false,
...
You can now specify the path to target.json wherever a target triple would normally be. However, you now need to recompile the standard libraries (such as core and std) with the new target information. Otherwise, the compiler will tell you that the `target-17676080659170949227` target may not be installed. On unstable Cargo, you can use -Z build-std
to do that:
$ cargo +nightly run -Z build-std=core --target target.json
It will take a bit longer than normal, since it needs to recompile the core standard library. Not all platforms support compiling the fuller std library this way, but you can change the build-std=core to just build-std if you want to try to build the whole std standard library as well.

Add linker flag during conan install

I'm working in a project that uses a number of external libraries. These libraries are included using Conan. The project is primarily written for Unix, but it also need to compile and run on Windows.
My current problem is that Windows defaults fopen() to be O_TEXT, while Unix expects this to be O_BINARY. I have a fix that works for my own code, simply include binmode.obj when linking to change this default to O_BINARY.
The problem is that this does not affect my third party libraries. Googling for this didn't turn up much, most suggestions seems to be based on where you are creating your own package and want flags added, rather than how to add flags when using other's packages.
What I have tried so far:
Make binmode.obj come before libraries, in case the linking order matters. Made no difference.
Added --env 'CL=link binmode.obj' to conan install, but this flag did not end up as part of the compile flags nor link flags.
Any suggestions for what I could try?
EDIT: I was wrong about "CL" taking no effect. This was caused by confusing output. But I did observe that CL seems to be applied for both compiler and linker, which makes it somewhat challenging what flags to give. Using "/link" prefix makes it work with compiler, but does not work with linker.
EDIT 2: More confusions... I didn't realize that the syntax of the CL value was: "<compile flags> /link <link flags>". It affected compile, but not link, however. So this environment variable apparently can't be used to make Conan add a linker flag for autotools based packages.
Hi Mats L welcome to our community!
I once had a similar problem and what I end up doing was quite hacky but quite simple also:
On my conan profile located at ~/.conan/profiles/default or any other profile actually I added an enviromental variable as such:
CXX=/usr/bin/clang++ -fms-compatibility. This one made me compile all the c++ sources with this flag (that can understand windows specific code).
So in your case you can run which c++ to find the location of your compiler
and edit the CXX environmental variable in the conan profile you use your final file will probably look like :
[settings]
os=Macos
os_build=Macos
arch=x86_64
arch_build=x86_64
compiler=clang
compiler.version=11
compiler.libcxx=libc++
build_type=Release
[options]
[build_requires]
[env]
CXX=c++ --required_flag
Some additional notes: You might also want this flag set on your CC enviromental variable .
It's preferable to not change the default profile but copy it (lets say on a file named default_edited_copy) and then invoke the other profile with conan ... --profile default_edited_copy

How to set LD_LIBRARY_PATH from a CMake toolchain file?

On Linux, I want to create a CMake toolchain file for cross-compilation.
The compiler needs some shared libraries that are located in non-standard directories, so I have to set LD_LIBRARY_PATH before invoking it. That worked when calling the compiler from the command line, but not when calling it from CMake.
I tried to set LD_LIBRARY_PATH via set(ENV{LD_LIBRARY_PATH} "${CMAKE_CURRENT_LIST_DIR}/<shared library directory>") from the toolchain file. However the compiler complained that it couldn't find the shared libraries.
Table of contents:
Setting it in a toolchain file isn't going to do what you want.
I'm not sure why setting LD_LIBRARY_PATH before invoking the buildsystem isn't working for you.
There's a more idiomatic way in CMake to do what you want.
Setting it in a toolchain file isn't going to do what you want
I'm pretty sure the approach you are asking how to take won't work because set(ENV) just sets an environment variable that will only be known to CMake at the configure stage (not the build stage). Here's a minimal reproducible example of that:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.23)
project(Foo)
set(ENV{FOO} "hello world!")
message("\$ENV{FOO} at configure time: $ENV{FOO}")
add_custom_target(echo
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_SOURCE_DIR}/echo.cmake"
VERBATIM
)
echo.cmake:
cmake_minimum_required(VERSION 3.23)
message("\$ENV{FOO} at build time: $ENV{FOO}")
run the following:
$ cmake -S . -B build --fresh
<...>
$ENV{FOO} at configure time: hello world!
<...>
$ cmake --build build/ --target echo
$ENV{FOO} at build time:
Since environment variables are "passed downward" to child processes and not upward, when you do set(ENV), that's just setting it in the CMake process that performs the configure step (the one invoked by the cmake -S ... command in the example above). As the example shows, CMake doesn't do anything fancy to make those environment variables known at its configure time to the generated buildsystem at build time.
I'm not sure why setting LD_LIBRARY_PATH before invoking the buildsystem isn't working for you
So I have to set LD_LIBRARY_PATH before invoking it. It works when calling the compiler from the command line, but not from CMake.
As long as you're exporting the variable for it to be made available to the shell's child processes (using the correct mechanism for whichever shell you're using), that should work. A minimal reproducible example would help here.
There's a more idiomatic way in CMake to do what you want
Try using the find_library() command. It does what its name says it does at configuration time (instead of build time). If you use it, you'll also get the benefit of your buildsystems being more cross-platform.
It has several ways of tweaking how it searches for libraries. You can read about exactly how it works in the official docs.
For your case here, one of the suitable configuration points to guide the library search would be the CMAKE_LIBRARY_PATH variable, although as you'll read about, there are other configuration points you could use as well.

LLVM debug output through rust

I am looking for a way to read the LLVM debug output by invoking the rust compiler (through cargo). I am especially interested in output of LLVMs ASAN.
To run a build with ASAN I can do:
cargo clean && RUSTFLAGS="-Zsanitizer=address" cargo build
but I don't know the command to get the debug log ouput.
With clang I think one can add
-mllvm -debug-only=asan
as compiler-flag.
How can I supply something like this flag through rustc?
I am using the current rust source (https://github.com/rust-lang/rust/ commit 31f5d69) to build rustc myself.
The equivalent compiler argument for rustc is
-C llvm-args=-debug-only=<LLVM DEBUG_TYPE>
The -C part gives access to the options for code generation of rust.
The llvm-args part tells rustc to forward every subsequent flag to LLVM. If I understand correctly, you can specify every LLVM option that is evaluated with cl::opt() in LLVM source code.
So the full command to build a rust application with ASAN and see only the log output of ASAN would be:
cargo clean && RUSTFLAGS="-C llvm-args=-debug-only=asan -Zsanitizer=address" cargo build
Side note: I think debug mode for LLVM must be enabled in config.toml when building rust to see the respective log output.

How to I tell Rust where to look for a static library?

Is there any way to tell Rust where to look for my static library? Example code
#[link(name = "/this/is/the/path/libfoo.a", kind = "static")]
If not, what config change can I make or what folder do I place my library in so that I can use it?
rustc invokes the system linker which looks for all libraries specified in #[link(...)] in library directories. There are usually several default library directories (like /lib and /usr/lib on Linux), and more can be specified via linker flags (rustc accepts -L options which it then passes through to the linker).
If you invoke rustc directly, you can use the -L option to add additional library directories which will be then passed through to the linker. If you use Cargo, however, you've got a few more options:
Cargo adds the /target/<profile>/deps directory as a library source directory.
You can use cargo rustc
cargo rustc -- -L /path/to/library/directory
You can specify the RUSTFLAGS environment variable:
RUSTFLAGS='-L /path/to/library/directory' cargo build
You can use a build script to output more linker options
println!("cargo:rustc-link-lib=static=foo");
println!("cargo:rustc-link-search=native=/path/to/foo");
The easiest way for you, I think, is to add a custom build script which will copy or create a symlink to your library in the corresponding /target/<profile>/deps directory.
To add on to the accepted answer, what worked for me is as following:
under debug build, putting dependency files under target/debug/deps worked; but putting files under target/debug/native/* did not seem to work.
Cargo seems to only look under target/debug/deps by default.
You can run with cargo build --verbose to see the verbose rustc commands and the options used. -L option specifies additional link dependency directory.

Resources