wasi-sdk icon indicating copy to clipboard operation
wasi-sdk copied to clipboard

`std::cout` is not initialized when used in the constructor of a global static variable.

Open j-jorge opened this issue 4 years ago • 11 comments

When compiling and running the followig code:

// global_ctor.cpp
#include <iostream>

struct S
{
  S()
  {
    std::cout << "Go.\n";
  }
};

static S s;

int main() {}
clang++ --target=wasm32-unknown-wasi --sysroot=/path/to/wasi/sysroot/ global_ctor.cpp -o global_ctor-wasi
wasmtime -- ./global_ctor-wasi

The following error occurs:

Error: failed to run main module `./global_ctor-wasi`

Caused by:
    0: failed to invoke command default
    1: wasm trap: out of bounds memory access
       wasm backtrace:
         0: 0x52e9 - <unknown>!std::__2::basic_ostream<char, std::__2::char_traits<char> >::sentry::sentry(std::__2::basic_ostream<char, std::__2::char_traits<char> >&)
         1:  0xd40 - <unknown>!std::__2::basic_ostream<char, std::__2::char_traits<char> >& std::__2::__put_character_sequence<char, std::__2::char_traits<char> >(std::__2::basic_ostream<char, std::__2::char_traits<char> >&, char const*, unsigned long)
         2:  0xc78 - <unknown>!std::__2::basic_ostream<char, std::__2::char_traits<char> >& std::__2::operator<<<std::__2::char_traits<char> >(std::__2::basic_ostream<char, std::__2::char_traits<char> >&, char const*)
         3:  0xc08 - <unknown>!S::S()
         4:  0xbbe - <unknown>!__cxx_global_var_init
         5: 0x1f60 - <unknown>!_GLOBAL__sub_I_global_ctor.cpp
         6:  0xb7c - <unknown>!__wasm_call_ctors
         7:  0xb8a - <unknown>!_start

It seems that std::cout is not initialized at the time it is used, when constructing static S s.

According to the C++ standard, paragraph 3 of [iostream.objects.overview] (where "the objects" refers to std::cout and co., emphasis of mine):

The objects are constructed and the associations are established at some time prior to or during the first time an object of class ios_­base​::​Init is constructed, and in any case before the body of main begins execution. The objects are not destroyed during program execution. The results of including <iostream> in a translation unit shall be as if <iostream> defined an instance of ios_­base​::​Init with static storage duration.

And according to [basic.start.dynamic], objects with static storage duration in the same compilation unit should be initialized in the order they appear; thus ios_base::Init before S s.

A workaround is to explicitly declare an std::ios_base::Init before using std::cout:

S()
{
  std::ios_base::Init init;
  std::cout << "Go.\n";
}

j-jorge avatar Sep 15 '20 09:09 j-jorge

Thanks for the report, this is indeed a bug! It appears that this is an instance of https://bugs.llvm.org/show_bug.cgi?id=28954 .

Another workaround is to add -lc++ to the command-line, before any of your own source/object files.

sunfishcode avatar Sep 15 '20 15:09 sunfishcode

I also have a patch to upstream the fix we made in emscripten: https://reviews.llvm.org/D74885

sbc100 avatar Sep 15 '20 15:09 sbc100

@sbc100 Is there an update on the status of that patch?

sunfishcode avatar Sep 15 '20 23:09 sunfishcode

No really, I guess we should push on it?

sbc100 avatar Sep 16 '20 01:09 sbc100

Looks like we are moving forward with https://reviews.llvm.org/D31413. I'm hoping we even get it into llvm 11 which is about to be released.

sbc100 avatar Sep 16 '20 16:09 sbc100

Awesome, thanks!

sunfishcode avatar Sep 16 '20 16:09 sunfishcode

This issue should be fixed with https://reviews.llvm.org/D31413 (by commit https://github.com/llvm/llvm-project/commit/39faf428164a28f3652370958ce893d9200927c8). I'll see if we can cherry-pick to LLVM 11.

ldionne avatar Sep 16 '20 17:09 ldionne

This issue is still present in wasi-sdk 14d7b78. Confirming the effectiveness of the @j-jorge's workaround.

michaelfranzl avatar Apr 19 '21 07:04 michaelfranzl

@ldionne Do you know if https://reviews.llvm.org/D31413 was cherry-picked into LLVM 11?

Either way, https://github.com/WebAssembly/wasi-sdk/pull/178 updates wasi-sdk to LLVM 12, which will hopefully resolve this.

sunfishcode avatar Apr 19 '21 17:04 sunfishcode

@sunfishcode The commit is:

commit 39faf428164a28f3652370958ce893d9200927c8
Author: Louis Dionne <[email protected]>
Date:   Thu May 14 09:56:35 2020 -0400

    [libc++] Ensure streams are initialized early

    When statically linking libc++ on some systems, the streams are not
    initialized early enough, which causes all kinds of issues. This was
    reported e.g. in http://llvm.org/PR28954, but also in various open
    source projects that use libc++.

    Fixes http://llvm.org/PR28954.

    Differential Revision: https://reviews.llvm.org/D31413

I think it was only included in LLVM 12.

ldionne avatar Apr 19 '21 17:04 ldionne

Either way, #178 updates wasi-sdk to LLVM 12, which will hopefully resolve this.

I can confirm that it is resolved in LLVM v12.

michaelfranzl avatar Aug 12 '21 06:08 michaelfranzl