Statically compiled Mosh with musl

June 14, 2021

Categories: Technical Tags: linux

Small maintenance becomes fact of life when you stick with your tool of choice through passage of time but also want taste of modernity here and there. "True colour" (24 bit) support in terminal emulators is my new fancy. Though it's more of a necessity than a luxury, A good light (neo)vim colour scheme that agrees with my eyes proved to be impossible to come by without true colour support.

Now, I can only ever tolerate terminal emulators through urxvt (because of its daemon/client mode, customisability and perl plugins but that's another story). It does support true colour (though I am given to understand that it's more of an approximation than really 24 bit, it doesn't matter in practice though). However the last release (which is what distros package) came in 2016, this support was added after that. So in short I had to be packaging the HEAD for my personal use which can be funky sometimes (it uses Subversion!). Thankfully just a month ago they finally made a new release (after over 5 years!).

But now I picked up another tool: Mosh. I am late to the party I suppose. Thing is, while internet speed and reliability has improved, latency still isn't imperceptible when ssh-ing to remote machines, and aren't we doing this close to speed of light anyway? It probably doesn't really get better than this. Unless you cheat in the software side that is, which Mosh does. I am not privy to all the details, but Mosh client does "predictive typing" in the sense that it figures out locally whether an input would just be an echo or big change, and thus can just render basic input without waiting for echo confirmation, this actually helps with perceived latency. Aside from that there are some other nice properties like "roaming" support where connection holds despite IP address change, or adaptive frame rate which Mosh achieves by literally running a terminal emulator in the server side. It's this insanity that however breaks your client side terminal emulator features like 24 bit colour, because Mosh doesn't support it.

Actually Mosh does, but last release was nearly 4 years ago (déjà vu!). Since then development has waned but they did land true colour support. I can compile this locally again, except remember Mosh has a server side component. These days obviously servers are more of a cattle than pet. Unlike at home/work (where clients run), they come with different OS and architecture. So how do you deploy then? Just like any other average software?

Well deploying average software was a pain though until we had containers. Can you just wrap up Mosh in a container? I mean why not, but containers run in all kinds of isolated namespaces. I would have to know exact interplay between SSH and Mosh, how the handover goes etc. Sounds fascinating, but for another day. Today I will just statically compile Mosh binary. Let's see:

$ ldd /usr/bin/mosh-server

	linux-vdso.so.1 (0x0000fffc85c90000)
	libtinfo.so.6 => /lib64/libtinfo.so.6 (0x0000fffc85ba0000)
	libprotobuf.so.15 => /lib64/libprotobuf.so.15 (0x0000fffc858c0000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x0000fffc85880000)
	libssl.so.1.1 => /lib64/libssl.so.1.1 (0x0000fffc857d0000)
	libcrypto.so.1.1 => /lib64/libcrypto.so.1.1 (0x0000fffc85510000)
	libutil.so.1 => /lib64/libutil.so.1 (0x0000fffc854e0000)
	libz.so.1 => /lib64/libz.so.1 (0x0000fffc854a0000)
	libutempter.so.0 => /lib64/libutempter.so.0 (0x0000fffc85470000)
	libstdc++.so.6 => /lib64/libstdc++.so.6 (0x0000fffc852c0000)
	libm.so.6 => /lib64/libm.so.6 (0x0000fffc851f0000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000fffc851b0000)
	libc.so.6 => /lib64/libc.so.6 (0x0000fffc85030000)
	/lib/ld-linux-aarch64.so.1 (0x0000fffc85ca0000)
	libdl.so.2 => /lib64/libdl.so.2 (0x0000fffc85000000)

Recently Mosh added a configure option called --enable-static-libraries which enables static linking for everything here except for libc, because typically people use glibc and it's really not a good idea to do that for glibc. And if we don't include glibc, however the problem is target machines vary in terms of glibc version, so ABI compatibility isn't guaranteed.

Or we can just use musl. A lot of times passing --static to the linker is sufficient. Also easiest would be to use building infrastructure of a distro with first class musl support. I will go with Void, but Alpine would do too. Here is the relevant template:

# Template file for 'mosh'
pkgname=mosh
version=1.3.2
revision=1
_commit=03087e7a761df300c2d8cd6e072890f8e1059dfa
wrksrc=mosh-${_commit}
build_style=gnu-configure
hostmakedepends="autoconf automake pkg-config protobuf"
makedepends="ncurses-devel protobuf-devel libutempter-devel openssl-devel"
depends="perl-IO-Tty"
short_desc="Mobile shell, remote terminal application that allows roaming"
maintainer="your name <[email protected]>"
license="GPL-3.0-or-later"
homepage="https://mosh.org/"
distfiles="https://github.com/mobile-shell/mosh/archive/${_commit}.tar.gz"
checksum=1f5138398a426b797feb4eba14eb1b3da9741ae9d9e9c4cafbf1d9250da458ec

pre_configure() {
        ./autogen.sh
        export LDFLAGS+=" -static"
}

(credit to original packager)

Anyway, just appending to LDFLAGS does the trick most of the time with sane build system. And now:

$ file /usr/bin/mosh-server
/usr/bin/mosh-server: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, stripped

$ ldd /usr/bin/mosh-server
	not a dynamic executable

Cool, now just deploy single binary everywhere without hassle.