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

Using C++ string results in fd_close, fd_seek & fd_write, even for BAREMETAL compile #220

Open
ArjaanBuijk opened this issue Jan 25, 2022 · 2 comments

Comments

@ArjaanBuijk
Copy link

ArjaanBuijk commented Jan 25, 2022

I am writing C++ code for the internet computer, and I am a bit stuck at the moment trying to use strings.

I studied the discussion in this issue in great detail, and I made good progress after I edited the WASI SDK’s Makefile to disable assertions while opting into baremetal mode:

LIBCXXABI_CMAKE_FLAGS = \
    # ... snip ...
    -DUNIX:BOOL=ON \
+   -DLIBCXXABI_BAREMETAL=ON \
+   -DLIBCXXABI_ENABLE_ASSERTIONS=OFF \
    --debug-trycompile

With this patch, I could get the following to work:

// file: ok.cpp
#include <stdint.h>
#include <string.h>

// See: https://lld.llvm.org/WebAssembly.html#imports
#define WASM_SYMBOL_IMPORTED(module, name) \
    __attribute__((import_module(module))) __attribute__((import_name(name)));

// See: https://lld.llvm.org/WebAssembly.html#exports
#define WASM_SYMBOL_EXPORTED(name) asm(name) __attribute__((visibility("default")));

namespace ic0
{
    void debug_print(uint32_t src, uint32_t size)
        WASM_SYMBOL_IMPORTED("ic0", "debug_print");
}

void debug_print(const char *message)
{
    ic0::debug_print((uint32_t)message, (uint32_t)strlen(message));
}

void api_empty_to_empty() WASM_SYMBOL_EXPORTED("canister_query api_empty_to_empty");
void api_empty_to_empty()
{

    // Verify that debug logs are working (only on local network)
    const char *pmessage = "Using const char* - Hello from C++ api_empty_to_empty!";
    debug_print(pmessage);

    debug_print("Using directly - Hello from C++ api_empty_to_empty!");
}

I compile it with:

<...>/build/wasi-sdk-14.<xxx>/bin/clang++ --target=wasm32-wasi -O3 -flto -fcxx-exceptions --sysroot <...>/build/wasi-sdk-14.<xxx>/share/wasi-sysroot -std=c++20 -nostartfiles -Wl,--no-entry -Wl,--lto-O3 -Wl,--strip-all -Wl,--strip-debug -Wl,--stack-first -Wl,--export-dynamic ok.cpp -o ok.wasm

When converting the ok.wat using wasm2wat, the only import function is the one I specify, and the file ok.wat is really tiny:

(module
  (type (;0;) (func (param i32 i32)))
  (type (;1;) (func))
  (import "ic0" "debug_print" (func (;0;) (type 0)))
  (func (;1;) (type 1))
  (func (;2;) (type 1)
    call 1
    call 1)
  (func (;3;) (type 1)
    i32.const 65588
    i32.const 54
    call 0
    i32.const 65536
    i32.const 51
    call 0)
  (func (;4;) (type 1)
    call 3
    call 2)
  (table (;0;) 1 1 funcref)
  (memory (;0;) 2)
  (global (;0;) (mut i32) (i32.const 65536))
  (export "memory" (memory 0))
  (export "canister_query api_empty_to_empty" (func 4))
  (data (;0;) (i32.const 65536) "Using directly - Hello from C++ api_empty_to_empty!\00Using const char* - Hello from C++ api_empty_to_empty!\00"))

This file ok.wasm could be deployed without any issues.

Then I tried to use a string, and that did not work:

// file: nok.cpp
#include <stdint.h>
#include <string>

// See: https://lld.llvm.org/WebAssembly.html#imports
#define WASM_SYMBOL_IMPORTED(module, name) \
    __attribute__((import_module(module))) __attribute__((import_name(name)));

// See: https://lld.llvm.org/WebAssembly.html#exports
#define WASM_SYMBOL_EXPORTED(name) asm(name) __attribute__((visibility("default")));

namespace ic0
{
    void debug_print(uint32_t src, uint32_t size)
        WASM_SYMBOL_IMPORTED("ic0", "debug_print");
}

void debug_print(const std::string message)
{
    ic0::debug_print((uint32_t)message.c_str(), (uint32_t)message.size());
}

void api_empty_to_empty() WASM_SYMBOL_EXPORTED("canister_query api_empty_to_empty");
void api_empty_to_empty()
{
    /*
   * This does NOT work, it creates the imports:
   * (import "wasi_snapshot_preview1" "fd_close" (func (;1;) (type 1)))
   * (import "wasi_snapshot_preview1" "fd_seek" (func (;2;) (type 12)))
   * (import "wasi_snapshot_preview1" "fd_write" (func (;3;) (type 7)))
   */
    std::string message1 = "Create string first - Hello from C++ api_empty_to_empty!";
    debug_print(message1);
}

I compile it in the same way, but now the file nok.wat is much bigger and contains imports for fd_close, fd_seek & fd_write:

(module
  (type (;0;) (func (param i32 i32) (result i32)))
  ...
  (type (;42;) (func (param i32 i64 i64 i64 i64)))
  (import "ic0" "debug_print" (func (;0;) (type 10)))
  (import "wasi_snapshot_preview1" "fd_close" (func (;1;) (type 1)))
  (import "wasi_snapshot_preview1" "fd_seek" (func (;2;) (type 11)))
  (import "wasi_snapshot_preview1" "fd_write" (func (;3;) (type 7)))

I tried to apply the techniques explained in the other issue to hunt down the reason and then patch it, but was not able to.

Any tips how to get rid of these imports would be greatly appreciated.

@sbc100
Copy link
Member

sbc100 commented Jan 26, 2022

I still think -Wl,--trace-symbol=__wasi_fd_close is going to be your best starting point. It should tell you which object file is causing that symbol to be include.

If you build with -g should also easily be able to see where in the resulting .wat file those imports are used/needed. You can also use wasm-objdump -d -x foo.wasm to inspect the resulting binary without using wat format.

@sbc100
Copy link
Member

sbc100 commented Jan 26, 2022

Out of interest is there some fundamental reason why you don't use those wasi_snapshot_preview1 imports? It is just code size or are you trying to target an environment that doesn't support WASI? If its the latter, it seems rather odd to be using WASI SDK at all. Obviously we always want to generate the smallest possible binaries but targeting non-WASI host environments is probably out of scope of wasi-sdk.

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

No branches or pull requests

2 participants