VirtualHere is closed-source software. It it distributed as two components: a server (which exposes USB devices) and a client (which connects to a server and gains the ability to use USB devices).
The server is a static executable and thus executes just fine on NixOS:
$ file vhusbdx86_64
vhusbdx86_64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, no section header
$ sudo ./vhusbdx86_64
VirtualHere USB Server is running...press CTRL-C to stop
The client is also statically linked, but it is a “pie executable” and does not work:
$ file ./vhuit64
./vhuit64: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked, no section header
$ ./vhuit64
Could not start dynamically linked executable: ./vhuit64
NixOS cannot run dynamically linked executables intended for generic
linux environments out of the box. For more information, see:
https://nix.dev/permalink/stub-ld
How do I fix the client to work on NixOS?
In general, I understand why pre-compiled dynamic executables do not run on NixOS, and I am familiar with usage of patchelf. However, this is the first time that I encounter a strange static-yet-not-static executable and I can’t find anything regarding “PIE”, “PIC”, “position” or “independent” in patchelf’s manpage.
$ uname -a
Linux kuuro 6.6.33 #1-NixOS SMP PREEMPT_DYNAMIC Wed Jun 12 09:13:03 UTC 2024 x86_64 GNU/Linux
Sadly, no luck with patchelf.
$ patchelf --print-interpreter vhuit64
patchelf: no section headers. The input file is probably a statically linked, self-decompressing binary
$ patchelf --print-needed vhuit64
patchelf: no section headers. The input file is probably a statically linked, self-decompressing binary
Evidently, it is not merely a static executable but attempts to invoke the dynamic loader.
If this is a GUI application, then it is clear why because rendering a GUI in a modern desktop cannot be done in a static binary to my knowledge. It’s likely dlopening at least a graphics driver such as mesa.
You could strace it to figure out which shared library it’s trying to load but you’ll need to patch it or run it inside of an FHS environment either way.
PIE is a red herring; it just means that the code is independent from its location in memory. It has nothing to do with whether the binary makes use of dynamic loading or not. (It does facilitate dynamic loading of a binary however.)
Thanks, you put me on the right track. Turns out, both executables come packed with UPX:
$ strings vhusbdx86_64 | grep UPX
UPX!
$Info: This file is packed with the UPX executable packer http://upx.sf.net $
$Id: UPX 4.23 Copyright (C) 1996-2024 the UPX Team. All Rights Reserved. $
UPX!
UPX!
After unpacking them with upx -d, the difference is obvious: the underlying executable is static in one case, but dynamic in the other:
$ file vh*
vhuit64: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8821360dbdbe75548f2deebd130ff6936323b968, for GNU/Linux 3.2.0, stripped
vhusbdx86_64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, no section header
$ ldd vhuit64
linux-vdso.so.1 (0x00007ffe749f0000)
libGL.so.1 => not found
libEGL.so.1 => not found
libwayland-egl.so.1 => not found
libwayland-client.so.0 => not found
libX11.so.6 => not found
libSM.so.6 => not found
libxkbcommon.so.0 => not found
libgtk-3.so.0 => not found
libgdk-3.so.0 => not found
libpangocairo-1.0.so.0 => not found
libcairo.so.2 => not found
libgdk_pixbuf-2.0.so.0 => not found
libgio-2.0.so.0 => not found
libpangoft2-1.0.so.0 => not found
libpango-1.0.so.0 => not found
libgobject-2.0.so.0 => not found
libglib-2.0.so.0 => not found
libfontconfig.so.1 => not found
libdl.so.2 => /nix/store/dbcw19dshdwnxdv5q2g6wldj6syyvq7l-glibc-2.39-52/lib/libdl.so.2 (0x00007fa781edb000)
libz.so.1 => not found
libstdc++.so.6 => not found
libm.so.6 => /nix/store/dbcw19dshdwnxdv5q2g6wldj6syyvq7l-glibc-2.39-52/lib/libm.so.6 (0x00007fa77ff1d000)
libgcc_s.so.1 => /nix/store/nda0h04bakn2damsd06vkscwi5ds4qjd-xgcc-13.2.0-libgcc/lib/libgcc_s.so.1 (0x00007fa781eb4000)
libpthread.so.0 => /nix/store/dbcw19dshdwnxdv5q2g6wldj6syyvq7l-glibc-2.39-52/lib/libpthread.so.0 (0x00007fa781eaf000)
libc.so.6 => /nix/store/dbcw19dshdwnxdv5q2g6wldj6syyvq7l-glibc-2.39-52/lib/libc.so.6 (0x00007fa77fd30000)
/lib64/ld-linux-x86-64.so.2 => /nix/store/dbcw19dshdwnxdv5q2g6wldj6syyvq7l-glibc-2.39-52/lib64/ld-linux-x86-64.so.2 (0x00007fa781ee8000)
So at least now it’s obvious what’s happening, and I can proceed with patchelf.