ptrcnull.me/content/posts/spotify-on-musl.md

7.8 KiB

title date draft
WIP: Running Spotify on musl 2021-11-19T16:22:02+01:00 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. 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 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 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, but __mbrlen and __close were still missing, so I cloned gcompat and added them in.

Note: gcompat seems to be temporarily no longer maintained as A. Wilcox has retired, 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, and readlink uses the syscall, right?

Nope, gcompat replaces 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 in savestate_r () from /lib/libgcompat.so.0. We could try to fix this or we can replace it with a fair dice roll:

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

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 - 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 or send an e-mail.