一个程序员的辩白

31 Jul 2017

Compile shadowsocks-libev statically

(Update 2021-01-01: this article has been revised to adopt latest ss-libev)

Previously I’ve discussed how to compile and run an user-mode Linux.

In this post, I’ll talk about how to compile a statically-linked shadowsocks-libev.

 

Preparation

Before we do anything, we need to install basic build packages.

Following are common package group you may use:

CentOS/Fedora: sudo yum groupinstall "Development Tools"

Debian/Ubuntu: sudo apt-get install build-essential

Arch Linux/Manjaro: sudo pacman -Sy base-devel

You may happen to install git, autoconf, automake, libtool, gettext, asciidoc, xmlto manually.

 

To compile shadowsocks-libev(ss-libev for short) statically, its dependencies must be compiled(also static) first.

ss-libev depends on several packages: mbedTLS, libsodium, pcre, libev, c-ares.

And assuming current directory is /tmp/foo and statically-linked packages will be installed in $SS_DIR(using --prefix option in configure file).

# Replace to yours
SS_DIR=$HOME/ss-libev
mkdir -p "$SS_DIR"

 

Compile dependencies

mbedTLS

set -euf

VER=$(curl -sL https://api.github.com/repos/ARMmbed/mbedtls/releases/latest | grep '"tag_name":' | cut -d'"' -f4 | tr -d 'v')
wget https://github.com/ARMmbed/mbedtls/archive/v$VER.tar.gz -O mbedtls-$VER.tar.gz
tar xf mbedtls-$VER.tar.gz

pushd mbedtls-$VER
# Install into $SS_DIR
sed -i "s|DESTDIR=/usr/local|DESTDIR=$SS_DIR/mbedtls|" Makefile
CC=gcc AR=ar LD=ld LDFLAGS=-static make -j$(nproc) && make install

# Back to /tmp/foo
popd

libsodium

set -euf

VER=$(curl -sL https://download.libsodium.org/libsodium/releases/ | cut -d'"' -f2 | grep "^libsodium-" | grep "tar\.gz$" | grep "\-stable\." | sort -V | tail -1 | grep -Eo "[0-9]+(\.[0-9]+)+")
wget https://download.libsodium.org/libsodium/releases/libsodium-$VER-stable.tar.gz
tar xf libsodium-$VER-stable.tar.gz

pushd libsodium-stable
./configure --host=$(uname -m)-linux --prefix=$SS_DIR/libsodium --disable-ssp --disable-shared
make -j$(nproc) && make install

popd

pcre

set -euf

VER=$(curl -sL https://ftp.pcre.org/pub/pcre/ | cut -d'"' -f2 | grep "^pcre-" | grep "tar\.gz$" | sort -V | grep -Eo "[0-9]+\.[0-9]+" | tail -1)
wget https://ftp.pcre.org/pub/pcre/pcre-$VER.tar.gz
tar xf pcre-$VER.tar.gz

pushd pcre-$VER
./configure --host=$(uname -m)-linux --prefix=$SS_DIR/pcre --disable-shared --enable-utf8 --enable-unicode-properties
make -j$(nproc) && make install

popd

libev

set -euf

VER=$(curl -sL http://dist.schmorp.de/libev/ | cut -d'"' -f2 | grep "^libev-" | grep "\.tar.gz$" | grep -Eo "[0-9]+\.([0-9]+)+")
wget http://dist.schmorp.de/libev/libev-$VER.tar.gz
tar xf libev-$VER.tar.gz

pushd libev-$VER
./configure --host=$(uname -m)-linux --prefix=$SS_DIR/libev --disable-shared
make -j$(nproc) && make install

popd

c-ares

set -euf

VER=$(curl -sL https://api.github.com/repos/c-ares/c-ares/releases/latest | grep '"name":' | cut -d'"' -f4 | grep -E "^[0-9]+(\.[0-9]+)+$")
URL=$(curl -sL https://api.github.com/repos/c-ares/c-ares/releases/latest | grep browser_download_url | cut -d'"' -f4 | grep "\.tar\.gz$")
wget $URL
tar xf c-ares-$VER.tar.gz

pushd c-ares-$VER
./configure --host=$(uname -m)-linux --prefix=$SS_DIR/c-ares --disable-shared
make -j$(nproc) && make install

popd

 

Compile shadowsocks-libev statically

You can download latest ss-libev from GitHub API:

VER=$(curl -sL https://api.github.com/repos/shadowsocks/shadowsocks-libev/releases/latest | grep '"tag_name"' | grep -Eo "[0-9]+(\.[0-9]+)+")
wget https://github.com/shadowsocks/shadowsocks-libev/releases/download/v$VER/shadowsocks-libev-$VER.tar.gz
tar xf shadowsocks-libev-$VER.tar.gz

pushd shadowsocks-libev-$VER
# Run the same commands listed below...

Or simply git clone it:

# Build upon HEAD, which functionalities may unstable.
git clone --depth 1 https://github.com/shadowsocks/shadowsocks-libev

pushd shadowsocks-libev
git submodule init
git submodule update

./autogen.sh

LIBS="-lpthread -lm" \
    LDFLAGS="-Wl,-static -static -static-libgcc" \
    ./configure --host=$(uname -m)-linux \
        --prefix=$SS_DIR/shadowsocks-libev \
        --disable-documentation \
        --enable-static \
        --with-mbedtls=$SS_DIR/mbedtls \
        --with-sodium=$SS_DIR/libsodium \
        --with-pcre=$SS_DIR/pcre \
        --with-ev=$SS_DIR/libev \
        --with-cares=$SS_DIR/c-ares
make -j$(nproc) && make install

popd

 

Compile simple-obfs

simple-obfs is a simple obfuscating tool(plugin) used in ss-libev, which also can be statically compiled.

git clone --depth 1 https://github.com/shadowsocks/simple-obfs

pushd simple-obfs
git submodule init
git submodule update

./autogen.sh

LIBS="-lpthread -lm" LDFLAGS="-Wl,-static -static -static-libgcc -L$SS_DIR/libsodium/lib -L$SS_DIR/libudns/lib -L$SS_DIR/libev/lib" CFLAGS="-I$SS_DIR/libsodium/include -I$SS_DIR/libudns/include -I$SS_DIR/libev/include" ./configure --host=$(uname -m)-linux --prefix=$SS_DIR/shadowsocks-libev --disable-ssp --disable-documentation
make -j$(nproc) && make install

popd

 

After all things done, you should have a copy of statically-linked simple-obfs and ss-libev:

[$SS_DIR/shadowsocks-libev/bin]# ls -l
total 19344
-rwxr-xr-x 1 root root 1617280 Aug  2 14:25 obfs-local
-rwxr-xr-x 1 root root 1765240 Aug  2 14:25 obfs-server
-rwxr-xr-x 1 root root 3801592 Aug  2 14:21 ss-local
-rwxr-xr-x 1 root root 1628344 Aug  2 14:21 ss-manager
-rwxr-xr-x 1 root root    5388 Aug  2 14:21 ss-nat
-rwxr-xr-x 1 root root 3737000 Aug  2 14:21 ss-redir
-rwxr-xr-x 1 root root 3965760 Aug  2 14:21 ss-server
-rwxr-xr-x 1 root root 3271912 Aug  2 14:21 ss-tunnel

If you trying detect one of those file, say, ss-local, it’ll shows:

$ file ss-local
ss-local: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=6584349d54fbf0f9e1c389c57ae1394dcbec2543, not stripped

$ ldd ss-local
    not a dynamic executable

Which indicates it’s statically-linked(ss-nat is an exception, it’s a shell script).

 

You can take further steps to strip all symbols, or even pack them up, which can decrease file size significantly.

Before you do anything to the final production, remember to back them up.

# Strip out all symbols
[$SS_DIR/shadowsocks-libev/bin]# find . ! -name 'ss-nat' -type f | xargs strip -s

# NOTE: This should be optional, may make them unstable even erroneous.
[$SS_DIR/shadowsocks-libev/bin]# find . ! -name 'ss-nat' -type f | xargs upx -9
# Also remember to test them
[$SS_DIR/shadowsocks-libev/bin]# find . ! -name 'ss-nat' -type f | xargs upx -t

 

We’re done, you can now use ss-libev.

FYI, I’ve built ss-libev successfully under Debian 8 x64, its kernel is 3.16.0-4-amd64.

 

References

静态编译shadowsocks libev

交叉编译shadowsocks-libev

cross_and_static_compile_shadowsocks-libev.sh - gist