--- title: "WIP: Running Spotify on musl" date: 2021-11-19T16:22:02+01:00 draft: false --- Note: this post is not finished yet (and might never be) - it's more of a collection of notes. When trying to run Spotify on Alpine, most answers on the internet pointed to [Flatpak](https://flatpak.org/). And don't get me wrong - Flatpak is great and allows for running glibc-based apps with minimal setup, but... I'd really like to get Spotify to run natively, mostly because containers a bit heavier in terms of disk space and interoperability is . Let's start by downloading Spotify's deb package from [the official repository](https://repository-origin.spotify.com/pool/non-free/s/spotify-client/) and unpacking it into downloads. Most of the files are in `usr/share/spotify`, so we'll try to just go there and run it. ``` $ cd usr/share/spotify $ ./spotify zsh: no such file or directory: ./spotify ``` Oh right, it's missing some stuff. What stuff though? ``` $ ldd spotify (114 lines skipped...) Error relocating ./libcef.so: __libc_realloc: symbol not found Error relocating ./libcef.so: __libc_free: symbol not found Error relocating ./libcef.so: __fdelt_chk: symbol not found Error relocating ./libcef.so: __fprintf_chk: symbol not found Error relocating ./libcef.so: __sprintf_chk: symbol not found Error relocating ./libcef.so: initstate_r: symbol not found Error relocating ./libcef.so: random_r: symbol not found Error relocating ./libcef.so: gnu_get_libc_version: symbol not found Error relocating ./libcef.so: __register_atfork: symbol not found Error relocating ./libcef.so: __longjmp_chk: symbol not found Error relocating ./libcef.so: __libc_stack_end: symbol not found Error relocating ./spotify: strtoll_l: symbol not found Error relocating ./spotify: strtoull_l: symbol not found Error relocating ./spotify: __isnanf: symbol not found Error relocating ./spotify: __isinff: symbol not found Error relocating ./spotify: __isnan: symbol not found Error relocating ./spotify: __isinf: symbol not found Error relocating ./spotify: __cxa_thread_atexit_impl: symbol not found ``` It seems that it uses a lot of glibc-specific stuff, but we can use Adelie Linux's [gcompat](https://git.adelielinux.org/adelie/gcompat) to make it work. ``` $ apk add gcompat (1/3) Installing musl-obstack (1.2.2-r0) (2/3) Installing libucontext (1.1-r0) (3/3) Installing gcompat (1.0.0-r2) OK: 11425 MiB in 1571 packages $ patchelf --set-interpreter /lib/ld-musl-x86_64.so.1 spotify $ patchelf --add-needed libgcompat.so.0 spotify $ ldd spotify (97 lines skipped...) Error relocating ./libcef.so: __mbrlen: symbol not found Error relocating ./libcef.so: __close: symbol not found Error relocating ./libcef.so: initstate_r: symbol not found Error relocating ./libcef.so: random_r: symbol not found ``` That's much better, but there are still a few things missing. `random_r` was added [a few months ago](https://git.adelielinux.org/adelie/gcompat/-/commit/59e99e9e6db4fd4d33786fbe3ffd3fabf4a49dd7), but `__mbrlen` and `__close` were still missing, so I cloned gcompat and [added them in](https://github.com/ptrcnull/gcompat). Note: gcompat seems to be temporarily no longer maintained as A. Wilcox has [retired](https://catfox.life/2020/10/21/leaving-the-linux-distribution-community/), so I pushed my changed to a fork on GitHub instead of creating a merge request to the original repo. After recompiling gcompat and trying to run Spotify again, it still complains about missing glibc: ``` $ ./spotify Error loading shared library ld-linux-x86-64.so.2: No such file or directory (needed by libcef.so) ``` Fortunately, another `patchelf`, this time to `libcef.so`, makes the error go away: ``` $ patchelf --remove-needed ld-linux-x86-64.so.2 libcef.so $ ./spotify ``` Aaand... it crashes with `boost::filesystem::read_symlink: No such file or directory: "/proc/self/exe"`. That's weird, `/proc/self/exe` should be present, `read_symlink` just calls [https://github.com/steinwurf/boost/blob/1c73b071b4c458de71923072be2a7ee8e4c87799/libs/filesystem/src/operations.cpp#L1586](readlink), and `readlink` [uses the syscall](https://github.com/bminor/musl/blob/cfdfd5ea3ce14c6abf7fb22a531f3d99518b5a1b/src/unistd/readlink.c#L5), right? Nope, gcompat [replaces](https://git.adelielinux.org/adelie/gcompat/-/blob/current/libgcompat/readlink.c) readlink with its own function to intercept `/proc/self/exe` (ugh), but as we don't use the loader, we can remove it from the Makefile and build again. Another recompile of gcompat later, maybe now it would launch? ``` $ ./spotify [1] 26559 segmentation fault (core dumped) ./spotify ``` Whoops, it doesn't look good... After running `gdb spotify core`, it looks like it [segfaults](https://git.adelielinux.org/adelie/gcompat/-/issues/344) in `savestate_r () from /lib/libgcompat.so.0`. We could try to fix this _or_ we can replace it with a [fair dice roll](https://xkcd.com/221/): ```diff diff --git a/libgcompat/random_r.c b/libgcompat/random_r.c index 5bc7053..f6e7071 100644 --- a/libgcompat/random_r.c +++ b/libgcompat/random_r.c @@ -141,4 +141,7 @@ int random_r(struct random_data *restrict buf, int32_t *restrict result) { } + *result = 4; + return 0; + if (buf->n == 0) { k = buf->x[0] = lcg31(buf->x[0]); ``` (and also add `return 0;` before first `savestate_r` in `initstate_r`) Aaand... it shows a window! ![Empty white window on gray background, with title Spotify](/posts/spotify-on-musl/white_window.png) Sadly, after a while, the enthusiasm goes away - the window stays white and doesn't display any content. Command line argument `--show-console` doesn't give us any helpful info either: ``` $ ./spotify --show-console 14:54:32.302 I [f:156 ] Access allowance changed from online: 0 stream: 0, sync: 0, persistent conn: 0 to online: 1 stream: 1, sync: 0, persistent conn: 1 14:54:32.334 I [f:77 ] Connectivity policy is initially allow_all 14:54:32.334 I [f:79 ] Connection type is initially unknown 14:54:32.334 I [f:96 ] Enabling all persistent connections 14:54:32.353 I [f:150 ] D-Bus name 'org.freedesktop.NetworkManager' does not exist on system bus ``` Okay, but A. Wilcox [has the same issue](https://catfox.life/2020/07/06/live-from-adelie-streaming-spotify-on-musl/) - maybe we can use that? ``` $ ldd spotify /lib/ld-musl-x86_64.so.1 (0x7f0b5dbf5000) libgcompat.so.0 => /lib/libgcompat.so.0 (0x7f0b5dbe1000) libm.so.6 => /lib/ld-musl-x86_64.so.1 (0x7f0b5dbf5000) libdl.so.2 => /lib/ld-musl-x86_64.so.1 (0x7f0b5dbf5000) libasound.so.2 => /usr/lib/libasound.so.2 (0x7f0b5daf6000) libatomic.so.1 => /usr/lib/libatomic.so.1 (0x7f0b5daed000) libcurl-gnutls.so.4 => /usr/lib/libcurl-gnutls.so.4 (0x7f0b5da73000) libcef.so => ./libcef.so (0x7f0b52887000) (86 lines skipped...) ``` It seems to match (libm/libdl before libcef), so let's try removing them and see what happens: ``` $ patchelf --remove-needed libm.so.6 libcef.so $ patchelf --remove-needed libdl.so.2 libcef.so $ ./spotify --show-console 15:02:04.445 I [f:156 ] Access allowance changed from online: 0 stream: 0, sync: 0, persistent conn: 0 to online: 1 stream: 1, sync: 0, persistent conn: 1 15:02:04.491 I [f:77 ] Connectivity policy is initially allow_all 15:02:04.491 I [f:79 ] Connection type is initially unknown 15:02:04.492 I [f:96 ] Enabling all persistent connections 15:02:04.500 I [f:150 ] D-Bus name 'org.freedesktop.NetworkManager' does not exist on system bus ``` Unfortunately, it still shows the white window and `strace` reveals that it still segfaults: ``` 28567 +++ killed by SIGSEGV (core dumped) +++ 28566 +++ killed by SIGSEGV (core dumped) +++ 28565 +++ killed by SIGSEGV (core dumped) +++ 28564 +++ killed by SIGSEGV (core dumped) +++ 28563 +++ killed by SIGSEGV (core dumped) +++ ``` This is where the post ends, at least for now (2021-11-19). If you manage to make it further than I did, please DM me on [Telegram](https://t.me/ptrcnull) or send an [e-mail](mailto:hello@ptrcnull.me).