Skip to content

Instantly share code, notes, and snippets.

@misterdjules
Last active October 25, 2017 22:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save misterdjules/eae9ec70dc1d91fb8dd1 to your computer and use it in GitHub Desktop.
Save misterdjules/eae9ec70dc1d91fb8dd1 to your computer and use it in GitHub Desktop.

Summary of the current issues with Solaris binaries

TL;DR

SmartOS' C and C++ runtimes have diverged from Solaris' C and C++ runtime. The SmartOS ABI is not compatible with the Solaris ABI. For that reason, node binaries should not be ran on anything else than the SmartOS platform on which they've been built, or a newer SmartOS platform. As a consequence, Solaris users should not run SmartOS binaries on Solaris, and it is expected that they would not run anyway.

Node v4.x binaries do not run on Solaris

The original problem has been reported in nodejs/node#3349: Solaris 11.2 cannot run any of the node v4.x binaries.

All v4.x binaries do not fail for the same reason though. From node v4.0.0 to node v4.2.1, the failure is the same, but the problem is different with node v4.2.2:

jgilli@solaris:~$ uname -a
SunOS solaris 5.11 11.2 i86pc i386 i86pc
jgilli@solaris:~$ ./node-v4.0.0-sunos-x86/bin/node 
ld.so.1: node: fatal: libstdc++.so.6: version 'GLIBCXX_3.4.20' not found (required by file node-v4.0.0-sunos-x86/bin/node)
ld.so.1: node: fatal: libstdc++.so.6: open failed: No such file or directory
Killed
jgilli@solaris:~$ ./node-v4.2.1-sunos-x86/bin/node 
ld.so.1: node: fatal: libstdc++.so.6: version 'GLIBCXX_3.4.20' not found (required by file node-v4.2.1-sunos-x86/bin/node)
ld.so.1: node: fatal: libstdc++.so.6: open failed: No such file or directory
Killed
jgilli@solaris:~$ ./node-v4.2.2-sunos-x86/bin/node 
ld.so.1: node: fatal: libc.so.1: version 'ILLUMOS_0.5' not found (required by file node-v4.2.2-sunos-x86/bin/node)
ld.so.1: node: fatal: libc.so.1: open failed: No such file or directory
Killed
jgilli@solaris:~$

Basically, node versions v4.0.0 to v4.2.1 require a C++ runtime at version GLIBCXX_3.4.20 that cannot be found on Solaris 11.2. For node v4.2.2, the problem is that the node binary requires a runtime at version ILLUMOS_0.5, which obviously should not be found on a Solaris system, as Illumos is a fork of Solaris and this version number is likely to indicate a requirement on Illumos-specific features.

node v4.0.0. to v4.2.1

node binaries between version v4.0.0 and v4.2.1 are built using g++ 4.9. When examining their runtime requirements, we can determine that they require a C++ runtime at version 'GLIBCXX_3.4.20' and CXXABI_1.3:

jgilli@solaris:~$ ldd -v ./node-v4.0.0-sunos-x86/bin/node  | grep CXX
        libstdc++.so.6 (GLIBCXX_3.4.20) =>       (version not found)
        libstdc++.so.6 (CXXABI_1.3) =>   /usr/lib/libstdc++.so.6
jgilli@solaris:~$ 

ldd indicates that it can't find a C++ runtime that satisfies the requirement on version GLIBCXX_3.4.20, and this corresponds to the error message outputed when running that node binary.

When running these node binaries, here's how the runtime linker determines which C++ runtime to load:

jgilli@solaris:~$ LD_DEBUG=all ./node-v4.0.0-sunos-x86/bin/node 2>&1 | grep c++
04455: file=libstdc++.so.6;  needed by node-v4.0.0-sunos-x86/bin/node
04455: find object=libstdc++.so.6; searching
04455:  trying path=/opt/local/gcc49/i386-sun-solaris2.11/lib/./libstdc++.so.6
04455:  trying path=/opt/local/gcc49/lib/./libstdc++.so.6
04455:  trying path=/lib/libstdc++.so.6
04455:  trying path=/usr/lib/libstdc++.so.6
04455: file=/usr/lib/libstdc++.so.6  [ ELF ]; generating link map
04455:             libstdc++.so.6              GLIBCXX_3.4.20
ld.so.1: node: fatal: libstdc++.so.6: version 'GLIBCXX_3.4.20' not found (required by file node-v4.0.0-sunos-x86/bin/node)
04455: ld.so.1: node: fatal: libstdc++.so.6: version 'GLIBCXX_3.4.20' not found (required by file node-v4.0.0-sunos-x86/bin/node)
04455: file=/usr/lib/libstdc++.so.6;  deleting
ld.so.1: node: fatal: libstdc++.so.6: open failed: No such file or directory
04455: ld.so.1: node: fatal: libstdc++.so.6: open failed: No such file or directory
jgilli@solaris:~$

We can see that the runtime linker first tries to use the C++ runtime from g++ 4.9:

04455:  trying path=/opt/local/gcc49/i386-sun-solaris2.11/lib/./libstdc++.so.6
04455:  trying path=/opt/local/gcc49/lib/./libstdc++.so.6

The reason for that is that the node binary has a path embedded that points to the C++ runtime to use (/opt/local/gcc49/i386-sun- solaris2.11/lib/./libstdc++.so.6). This path was set by the g++ 4.9 compiler used to build that binary. The runtime linker first tries to load a C++ runtime at that path but fails, because g++ 4.9 runtime libraries are not installed by default on Solaris 11.2 (and they may not be installed at that location anyway, since the g++ 4.9 compiler that was used to produce these binaries come from a SmartOS package).

Then the runtime linker tries to load a C++ runtime from other locations and eventually finds a C++ runtime at one of these locations:

04455:  trying path=/lib/libstdc++.so.6
04455:  trying path=/usr/lib/libstdc++.so.6
04455: file=/usr/lib/libstdc++.so.6  [ ELF ]; generating link map

but that C++ runtime fails to satisfy the version requirements on the C++ ABI:

04455:             libstdc++.so.6              GLIBCXX_3.4.20
ld.so.1: node: fatal: libstdc++.so.6: version 'GLIBCXX_3.4.20' not found (required by file node-v4.0.0-sunos-x86/bin/node)
04455: ld.so.1: node: fatal: libstdc++.so.6: version 'GLIBCXX_3.4.20' not found (required by file node-v4.0.0-sunos-x86/bin/node)
04455: file=/usr/lib/libstdc++.so.6;  deleting
ld.so.1: node: fatal: libstdc++.so.6: open failed: No such file or directory
04455: ld.so.1: node: fatal: libstdc++.so.6: open failed: No such file or directory

Looking at the C++ runtime loaded on this Solaris system, it is clear why it doesn't match that version requirement:

jgilli@solaris:~$ ls -l /usr/lib/libstdc++.so
lrwxrwxrwx   1 root     root          19 Oct 14 12:13 /usr/lib/libstdc++.so -> libstdc++.so.6.0.18
jgilli@solaris:~$ nm /usr/lib/libstdc++.so | grep CXX
[1744]  |         0|         0|OBJT |LOCL |2    |ABS    |.force_WEAK_off_GLIBCXX_3.4.12
[2041]  |         0|         0|OBJT |LOCL |2    |ABS    |.force_WEAK_off_GLIBCXX_3.4.3
[2042]  |         0|         0|OBJT |LOCL |2    |ABS    |.force_WEAK_off_GLIBCXX_3.4.8
[9146]  |         0|         0|OBJT |GLOB |0    |ABS    |CXXABI_1.3
[8015]  |         0|         0|OBJT |GLOB |0    |ABS    |CXXABI_1.3.1
[8017]  |         0|         0|OBJT |GLOB |0    |ABS    |CXXABI_1.3.2
[8018]  |         0|         0|OBJT |GLOB |0    |ABS    |CXXABI_1.3.3
[8020]  |         0|         0|OBJT |GLOB |0    |ABS    |CXXABI_1.3.4
[8024]  |         0|         0|OBJT |GLOB |0    |ABS    |CXXABI_1.3.5
[8025]  |         0|         0|OBJT |GLOB |0    |ABS    |CXXABI_1.3.6
[8026]  |         0|         0|OBJT |GLOB |0    |ABS    |CXXABI_1.3.7
[6948]  |         0|         0|OBJT |GLOB |0    |ABS    |CXXABI_TM_1
[7763]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4
[7621]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.1
[8847]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.10
[8852]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.11
[8853]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.12
[8856]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.13
[8860]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.14
[8861]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.15
[8862]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.16
[8864]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.17
[8867]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.18
[8871]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.19
[7622]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.2
[7623]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.3
[7625]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.4
[7627]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.5
[7631]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.6
[7632]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.7
[7635]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.8
[7636]  |         0|         0|OBJT |GLOB |0    |ABS    |GLIBCXX_3.4.9
jgilli@solaris:~$

That C++ runtime is at version 6.0.18, which means it comes from G++ 4.8, and the symbols in that shared object show that the highest version supported is GLIBCXX_3.4.19, so it doesn't support GLIBCXX_3.4.20.

Whhy do node binaries at versions between v4.0.0 and v4.2.1 have a requirement on a C++ runtime at version GLIBCXX_3.4.20? These versions have been built with g++ 4.9, and g++ 4.9 uses __throw_out_of_range_fmt when throwingexceptions instead of __throw_out_of_range.

That change adds a dependency on version 3.4.20 of the C++runtime. Since Solaris binaries don't have symbol versions attached to them, here's the output of nm on Linux that demonstrates that:

jgilli@ubuntu:~/dev/node$ nm ~/node-4.x-g++-4.9 | grep 3.4.20 | c++filt 
00000000013a4420 D TLSv1_enc_data
00000000013b4f20 D v8::internal::FLAG_turbo_osr
00000000013b4e20 D v8::internal::FLAG_log_instruction_period
                 U std::__throw_out_of_range_fmt(char const*, ...)@@GLIBCXX_3.4.20
00000000013c4120 b no_eventfd.9277
00000000013c4220 b process_title
00000000013a4220 d scsv.19260
jgilli@ubuntu:~/dev/node$ nm ~/node-4.x-g++-4.8 | c++filt | grep throw_out_of_range
                 U std::__throw_out_of_range(char const*)@@GLIBCXX_3.4
jgilli@ubuntu:~/dev/node$

node v4.2.2

The C++ runtime on this Solaris system comes from G++ 4.8, node's v4.2.2 SmartOS binary is built with g++ 4.8, does that mean node v4.2.2 work on Solaris 11.2?

No. node v4.2.2 fails because it doesn't meet the requirement on version ILLUMOS_0.5 of the runtime:

jgilli@solaris:~$ ./node-v4.2.2-sunos-x86/bin/node 
ld.so.1: node: fatal: libc.so.1: version 'ILLUMOS_0.5' not found (required by file node-v4.2.2-sunos-x86/bin/node)
ld.so.1: node: fatal: libc.so.1: open failed: No such file or directory
Killed
jgilli@solaris:~$

Why would an additional requirement be present in a binary built with an older compiler? The ILLUMOS_0.5 requirement is added if a binary depends on any of the following symbols:

  1. __cxa_atexit
  2. __cxa_finalize

Let's see how the v4.2.1 and v4.2.2 differ with regards to their dependency on these symbols:

jgilli@solaris:~$ nm ./node-v4.2.1-sunos-x86/bin/node | grep atexit
[92162] | 138167740|         0|FUNC |GLOB |0    |UNDEF  |atexit
jgilli@solaris:~$ nm ./node-v4.2.2-sunos-x86/bin/node | grep atexit
[74401] | 138064320|         0|FUNC |GLOB |0    |UNDEF  |__cxa_atexit
[62271] | 138061664|         0|FUNC |GLOB |0    |UNDEF  |atexit
jgilli@solaris:~$ nm ./node-v4.2.1-sunos-x86/bin/node | grep finalize
[98923] | 140221904|       249|FUNC |GLOB |0    |3878   |_ZN6icu_5616CollationBuilder11finalizeCEsER10UErrorCode
jgilli@solaris:~$ nm ./node-v4.2.2-sunos-x86/bin/node | grep finalize
[58510] | 140218352|       236|FUNC |GLOB |0    |1526   |_ZN6icu_5616CollationBuilder11finalizeCEsER10UErrorCode
jgilli@solaris:~$

So clearly, the dependency of node v4.2.2 on __cxa_atexit is responsible for the new requirement on ILLUMOS_0.5. __cxa_atexit is used to implement destructors for static objects. The --enable-__cxa_atexit can be used when building g++ to enable this feature.

Now, on SmartOS g++ 4.8 and 4.9 are built with different options:

[root@dev ~]# uname -a
SunOS dev 5.11 joyent_20150219T102159Z i86pc i386 i86pc Solaris
[root@dev ~]# cat /etc/pkgsrc_version 
release: 2014Q4
architecture: x86_64
[root@dev ~]# /opt/local/gcc49/bin/g++ -v
Using built-in specs.
COLLECT_GCC=/opt/local/gcc49/bin/g++
COLLECT_LTO_WRAPPER=/opt/local/gcc49/libexec/gcc/x86_64-sun-solaris2.11/4.9.2/lto-wrapper
Target: x86_64-sun-solaris2.11
Configured with: ../gcc-4.9.2/configure --enable-languages='c obj-c++ objc go fortran c++' --enable-shared --enable-long-long --with-local-prefix=/opt/local/gcc49 --enable-libssp --enable-threads=posix --with-boot-ldflags='-static-libstdc++ -static-libgcc -Wl,-R/opt/local/lib ' --disable-nls --with-gxx-include-dir=/opt/local/gcc49/include/c++/ --without-gnu-ld --with-ld=/usr/bin/ld --with-gnu-as --with-as=/opt/local/bin/gas --prefix=/opt/local/gcc49 --build=x86_64-sun-solaris2.11 --host=x86_64-sun-solaris2.11 --infodir=/opt/local/gcc49/info --mandir=/opt/local/gcc49/man
Thread model: posix
gcc version 4.9.2 (GCC) 
[root@dev ~]# /opt/local/gcc48/bin/g++ -v
Using built-in specs.
COLLECT_GCC=/opt/local/gcc48/bin/g++
COLLECT_LTO_WRAPPER=/opt/local/gcc48/libexec/gcc/x86_64-sun-solaris2.11/4.8.3/lto-wrapper
Target: x86_64-sun-solaris2.11
Configured with: ../gcc-4.8.3/configure --enable-languages='c obj-c++ objc fortran c++' --enable-shared --enable-long-long --with-local-prefix=/opt/local/gcc48 --enable-libssp --enable-threads=posix --with-boot-ldflags='-static-libstdc++ -static-libgcc -Wl,-R/opt/local/lib ' --disable-nls --with-cloog=/opt/local --enable-cloog-backend=isl --enable-__cxa_atexit --with-gxx-include-dir=/opt/local/gcc48/include/c++/ --without-gnu-ld --with-ld=/usr/bin/ld --with-gnu-as --with-as=/opt/local/bin/gas --prefix=/opt/local/gcc48 --build=x86_64-sun-solaris2.11 --host=x86_64-sun-solaris2.11 --infodir=/opt/local/gcc48/info --mandir=/opt/local/gcc48/man
Thread model: posix
gcc version 4.8.3 (GCC) 
[root@dev ~]#

So g++ 4.8 uses __cxa_atexit but not g++ 4.9. This explains why node v4.2.2, which is built with g++ 4.8, has a requirement on ILLUMOS_0.5 and not node versions v4.0.0 to v4.2.1, which are built with g++ 4.9.

Now, even if g++ 4.8 wasn't using __cxa_atexit, it would still make node have a requirement on ILLUMOS_0.8, like for node v4.2.1:

[root@dev ~]# ldd -v node-v4.2.2-sunos-x86/bin/node | grep ILLUMOS
        libc.so.1 (ILLUMOS_0.5) =>       /lib/libc.so.1
        libc.so.1 (ILLUMOS_0.8) =>       /lib/libc.so.1
[root@dev ~]# ldd -v node-v4.2.1-sunos-x86/bin/node | grep ILLUMOS
        libc.so.1 (ILLUMOS_0.8) =>       /lib/libc.so.1
[root@dev ~]# 

and running that binary would fail with the following error message:

ld.so.1: node: fatal: libc.so.1: version 'ILLUMOS_0.8' not found (required by file node-v4.2.2-sunos-x86/bin/node)
ld.so.1: node: fatal: libc.so.1: open failed: No such file or directory

What that means is that SmartOS has its own ABI versions, and these versions are not compatible with the Solaris ABI. For that reason, node binaries should not be ran on anything else than the SmartOS platform on which they've been built, or a newer SmartOS platform. As a consequence, Solaris users should not run SmartOS binaries, and it is expected that they would not run anyway.

Why would older node binaries (v0.10.x and v0.12.x) run on Solaris 11.2 then?

node v0.12.x and v0.10.x versions downloaded from nodejs.org run on Solaris.

Here are the dependencies of a SmartOS node v0.12.7 binary as displayed by ldd on Solaris:

jgilli@solaris:~$ uname -a
SunOS solaris 5.11 11.2 i86pc i386 i86pc
jgilli@solaris:~$ ldd -v node-v0.12.7-sunos-x86/bin/node | grep CXX
        libstdc++.so.6 (GLIBCXX_3.4) =>  /usr/lib/libstdc++.so.6
        libstdc++.so.6 (CXXABI_1.3) =>   /usr/lib/libstdc++.so.6
jgilli@solaris:~$ ldd -v node-v0.12.7-sunos-x86/bin/node | grep ILLUMOS
jgilli@solaris:~$

ldd tells us that that binary depends on a C++ runtime that supports ABI at versions GLIBCXX_3.4 and CXXABI_1.3. According to GCC's ABI Policy and Guidelines, that corresponds to gcc 3.4.0, which is much older than 4.8.0, the default C++ runtime present on Solaris 11.2.

Does that mean it's safe to run node v0.12.x on Solaris? No, as any build time assumption made when these binaries are built on SmartOS could break on Solaris. Basically, any code along the lines of:

#if defined(SMARTOS)
doSomethingThatWouldBreakOnSolaris
#endif

would definitely be a problem when running a SmartOS binary on Solaris.

Other issues for users of SmartOS

While we now understand better why some SmartOS node binaries work on Solaris, and why some others don't work, this investigation process actually raised some concerns about issues with running SmartOS binaries on SmartOS itself.

node v4.x.y binaries

node v4.2.2

Let's take the example of a node v4.2.2 binary running on a SmartOS with using a 2014Q4 pkgsrc repository (the current LTS version) and a 14.4.2 image (also the current LTS version):

[root@dev ~/node-1]# uname -a
SunOS dev 5.11 joyent_20150219T102159Z i86pc i386 i86pc Solaris
[root@dev ~/node-1]# cat /etc/motd 
   __        .                   .
 _|  |_      | .-. .  . .-. :--. |-
|_    _|     ;|   ||  |(.-' |  | |
  |__|   `--'  `-' `;-| `-' '  ' `-'
                   /  ; Instance (base-64-lts 14.4.2)
                   `-'  https://docs.joyent.com/images/smartos/base

[root@dev ~/node-1]# cat /etc/pkgsrc_version 
release: 2014Q4
architecture: x86_64
[root@dev ~/node-1]# ldd -v ~/node-v4.2.2-sunos-x86/bin/node | grep CXX
        libstdc++.so.6 (GLIBCXX_3.4.15) =>       (version not found)
        libstdc++.so.6 (CXXABI_1.3) =>   (version not found)
[root@dev ~/node-1]#

ldd indicates that the node v4.2.2 binary cannot find a C++ runtime that matches its requirements. However, that binary runs fine:

[root@dev ~/node-1]# ~/node-v4.2.2-sunos-x86/bin/node -e 'console.log(process.versions)'
{ http_parser: '2.5.0',
  node: '4.2.2',
  v8: '4.5.103.35',
  uv: '1.7.5',
  zlib: '1.2.8',
  ares: '1.10.1-DEV',
  icu: '56.1',
  modules: '46',
  openssl: '1.0.2d' }
[root@dev ~/node-1]#

Why is that? Running that binary with LD_DEBUG=all:

55472: file=libstdc++.so.6;  needed by /root/node-v4.2.2-sunos-x86/bin/node
55472: find object=libstdc++.so.6; searching
55472:  trying path=/opt/local/gcc48/i486-sun-solaris2.11/lib/./libstdc++.so.6
55472:  trying path=/opt/local/gcc48/lib/./libstdc++.so.6
55472:  trying path=/lib/libstdc++.so.6
55472:  trying path=/usr/lib/libstdc++.so.6
55472: file=/usr/lib/libstdc++.so.6  [ ELF ]; generating link map
55472:             libstdc++.so.6              GLIBCXX_3.4.15
55472:             libstdc++.so.6              CXXABI_1.3

we find that it uses the C++ runtime at /usr/lib/libstdc++.so.6. What version is that runtime?

[root@dev ~/node-1]# ls -l /usr/lib/libstdc++.so.6
lrwxrwxrwx 1 root root 19 Feb 19  2015 /usr/lib/libstdc++.so.6 -> libstdc++.so.6.0.13
[root@dev ~/node-1]#

That runtime is at version 6.0.13, which corresponds, according to GCC's ABI Policy and Guidelines, to GCC 4.4.2.

This runtime is clearly older than a runtime at version GLIBCXX_3.4.15, which corresponds according to that same document to GCC 4.6.0. It's not clear why, in these conditions, a v4.2.2 binary would run on such a platform. It is very likely due to a bug in the linker/loader that causes node v4.4.2 to accidentally run fine. Running node v4.2.2 should actually fail with an error message indicating that the requirement on version GLIBCXX_3.4.15 of the C++ runtime is not met.

node v4.0.0 to node v4.2.1

The situation is different for node versions between v4.0.0 and v4.2.1. These versions have been built with g++ 4.9, whereas node v4.2.2 was built with g++ 4.8. What does it change in practice?

[root@dev ~/node-1]# ldd -v ~/node-v4.2.1-sunos-x86/bin/node | grep CXX
        libstdc++.so.6 (GLIBCXX_3.4.20) =>       (version not found)
        libstdc++.so.6 (CXXABI_1.3) =>   (version not found)
[root@dev ~/node-1]#

These node binaries depends on even newer versions of the C++ runtime: GLIBCXX_3.4.20 instead of GLIBCXX_3.4.15`.

The reason for that is [g++ 4.9 uses __throw_out_of_range_fmt when throwing exceptions instead of __throw_out_of_range](https://github.com/gcc- mirror/gcc/commit/b473f47fc414a1e6014f6fed59098f22f85bee6b), and that [adds a dependency on version 3.4.20 of the C++ runtime](https://github.com/gcc- mirror/gcc/commit/b473f47fc414a1e6014f6fed59098f22f85bee6b#diff- 9a0ac54b1f1551c4f4d6a02f4629fa21R1368). Here's the output of nm on Linux that demonstrates that:

jgilli@ubuntu:~/dev/node$ nm ~/node-4.x-g++-4.9 | grep 3.4.20 | c++filt 
00000000013a4420 D TLSv1_enc_data
00000000013b4f20 D v8::internal::FLAG_turbo_osr
00000000013b4e20 D v8::internal::FLAG_log_instruction_period
                 U std::__throw_out_of_range_fmt(char const*, ...)@@GLIBCXX_3.4.20
00000000013c4120 b no_eventfd.9277
00000000013c4220 b process_title
00000000013a4220 d scsv.19260
jgilli@ubuntu:~/dev/node$ nm ~/node-4.x-g++-4.8 | c++filt | grep throw_out_of_range
                 U std::__throw_out_of_range(char const*)@@GLIBCXX_3.4
jgilli@ubuntu:~/dev/node$

So node SmartOS binaries at versions between v4.0.0 and v4.2.1 should also not be able to run on a SmartOS 14.4.2 image and using a 2014Q4 pkgsrc repository, as indicated by ldd's output (note the version not found messages):

[root@dev ~/node-1]# ldd -v ~/node-v4.2.1-sunos-x86/bin/node | grep CXX
        libstdc++.so.6 (GLIBCXX_3.4.20) =>       (version not found)
        libstdc++.so.6 (CXXABI_1.3) =>   (version not found)
[root@dev ~/node-1]#

However, they do run, probably for the same reason that node v4.2.2 runs: by accident and due to a bug in the linker/loaded:

[root@dev ~/node-1]# ~/node-v4.2.1-sunos-x86/bin/node -p 'process.versions'
{ http_parser: '2.5.0',
  node: '4.2.1',
  v8: '4.5.103.35',
  uv: '1.7.5',
  zlib: '1.2.8',
  ares: '1.10.1-DEV',
  icu: '56.1',
  modules: '46',
  openssl: '1.0.2d' }
[root@dev ~/node-1]#

How to fix the problem on SmartOS?

It's not clear yet what the linker/loader bug to which this document alludes is. Fixing that bug could cause more harm than good, as it would probably make node v4.x binaries that do run today on some SmartOS platforms not run anymore.

Versions of node packaged in SmartOS pkgsrc repositories do not have that problem as they depend on the appropriate specific C++ runtime versions. So for instance, the nodejs-v4.2.1 package includes a node binary built by g++ 4.8 and it depends on g++ 4.8's runtime itself. As a result, all dependencies and requirement are met:

[root@dev ~/node-1]# pkgin install nodejs-4.2.1
calculating dependencies... done.

nothing to upgrade.
3 packages to be installed (0B to download, 94M to install):

gcc48-libs-4.8.3nb3 nodejs-4.2.1

proceed ? [Y/n] 
downloading packages...
installing packages...
installing gcc48-libs-4.8.3nb3...
installing nodejs-4.2.1...
pkg_install warnings: 0, errors: 0
reading local summary...
processing local summary...
updating database: 100%
marking nodejs-4.2.1 as non auto-removable
[root@dev ~/node-1]# node --version
v4.2.1
[root@dev ~/node-1]# which node
/opt/local/bin/node
[root@dev ~/node-1]# ldd `which node`
        libz.so.1 =>     /opt/local/lib/libz.so.1
        libkstat.so.1 =>         /lib/64/libkstat.so.1
        libumem.so.1 =>  /lib/64/libumem.so.1
        libsocket.so.1 =>        /lib/64/libsocket.so.1
        libnsl.so.1 =>   /lib/64/libnsl.so.1
        librt.so.1 =>    /lib/64/librt.so.1
        libsendfile.so.1 =>      /lib/64/libsendfile.so.1
        libstdc++.so.6 =>        /opt/local/gcc48/x86_64-sun-solaris2.11/lib/amd64/libstdc++.so.6
        libm.so.2 =>     /lib/64/libm.so.2
        libgcc_s.so.1 =>         /opt/local/gcc48/x86_64-sun-solaris2.11/lib/amd64/libgcc_s.so.1
        libpthread.so.1 =>       /lib/64/libpthread.so.1
        libc.so.1 =>     /lib/64/libc.so.1
        libgcc_s.so.1 =>         /opt/local/gcc47/x86_64-sun-solaris2.11/lib/amd64/libgcc_s.so.1
        libmp.so.2 =>    /lib/64/libmp.so.2
        libmd.so.1 =>    /lib/64/libmd.so.1
[root@dev ~/node-1]#

As we can see from ldd's output, the C++ runtime used by that node binary is at /opt/local/gcc48/x86_64-sun-solaris2.11/lib/amd64/libstdc++.so.6, which is not the default C++ runtime that comes with the platform. So it seems that providing a g++ 4.8 runtime on SmartOS machines that need to run node v4.x.y and later would be a potential solution.

Another potential solution would be to build node v4.x.y with g++ 4.7. However, it doesn't seem to be an avenue that the Node.js project likely to consider since:

  1. V8 explicitly states that it requires g++ 4.8 and later to build.
  2. Some previous experiments to build recent versions of node with g++ 4.7 failed.

So it seems that the only possible approach to solve that problem is to provide a g++ 4.8 runtime on SmartOS machines.

How to upgrade the C++ runtime on SmartOS machines?

Install all gcc runtime libraries in images

Evaluate gcc version upgraded in the platform's /usr/lib

Evaluate a way to get things like Red Hat Developer's Toolset

@jelmd
Copy link

jelmd commented Nov 19, 2015

Hmmm, nice analysis - misses the mediator part (e.g. on 11.3 on may set 4.5.2 or 4.8.2 as its default), so one still can't reliably say, which libstdc++.so.6 will be used by default. So one needs possibly hardcode the path (IIRC w/o tweaking g++ on Solaris does this by default) and tell the user, that gcc-c++-runtime@4.8 packages are required. Installing another "external" libstdc++.so.6 + stuff (even under an obscure /opt path) would be a no-GO for me.

So, I think the time has come to realize, that illumos and solaris have divereged enough, so that it is worth to give them its own container, i.e. its own native environment, like BSD or MacOSX to save time and avoid further trouble ...

@misterdjules
Copy link
Author

@jelmd Absolutely, I added a TL;DR section that clarifies the fact that SmartOS binaries should not be ran on Solaris.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment