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.
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 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$
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:
- __cxa_atexit
- __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.
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.
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.
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.
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]#
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:
- V8 explicitly states that it requires g++ 4.8 and later to build.
- 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.
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 ...