How-To: build lighttpd for iOS on arm64

Yesterday, I tweeted this:

Today, I will guide you through building lighttpd 1.4.59 (latest stable release as of writing) by cross-compiling it for iOS on the arm64 architecture from an x86_64 Macintosh.


Prerequisites

For this, you’ll need:

  • Any x86_64 Macintosh that can run macOS 11 Big Sur (untested on arm64e, tho it should work as well);
  • The latest stable Xcode.app with the MacOSX 11.3 and iPhoneOS 14.5 SDKs;
  • Xcode Command Line Tools (should be installed if you have Homebrew);
  • Homebrew.

Installing the required tools

Fire up a Terminal and install the required tools:

$ brew install autoconf automake libtool m4 pcre pkg-config

These are needed for the GNU Autoconf system that lighttpd uses.

Also, not required but nice to have if you wanna download a few files, is wget. Once again, Homebrew is really handy!

Prepping the buildroot

Still from your Terminal, change the working directory to somewhere safe and create two new directories:

$ mkdir ./buildroot && mkdir ./dstroot

Buildroot will be used for the actual building of lighttpd and dstroot will be used as the install destination during the make process.

Lighttpd includes a header file that’s not part of the public iOS SDK (netinet/tcp_fsm.h), but it is present in the macOS SDK so we’ll grab it from there. We’ll then ask the compiler to also look for this header in another location.

First, create this new include directory:

$ mkdir ./buildroot/extra_include

The missing header is in the netinet directory so create it as well, and copy the header:

$ mkdir ./buildroot/extra_include/netinet
$ cp /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/netinet/tcp_fsm.h ./buildroot/extra_include/netinet/

Now go to buildroot, get and unpack the lighttpd source code:

$ cd ./buildroot/
$ wget https://download.lighttpd.net/lighttpd/releases-1.4.x/lighttpd-1.4.59.tar.gz
$ tar xvf ./lighttpd-1.4.59.tar.gz

Wait for tar to finish. Now, let’s configure the building process!

Configure

Go to the lighttpd source directory and invoke autogen.sh:

$ cd ./lighttpd-1.4.59
$ ./autogen.sh

Autogen will launch Autoreconf and if all the tools are installed, it will output this once it’s done:

configure.ac:97: the top level
configure.ac:63: installing './compile'
configure.ac:24: installing './missing'
src/Makefile.am: installing './depcomp'
Now type './configure ...' and 'make' to compile.

Problem is: if you just call configure and make, lighttpd will be compiled for the current architecture and OS, which is macOS on x86_64 (if you’re doing this on an arm64e Mac, let me know how it went!).

Luckily, configure’s behavior can be customized through environment variables. We’ll use them to modify the used SDK as well as the target architecture.

To do so, export these variables (modify as needed):

# CC is the compiler to use, and we'll use clang
$ export CC="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang"
# CFLAGS are the Compiler Flags, we use it to instruct clang
# to build against the iOS SDK for arm64 and also look for
# header files in our extra_include directory
$ export CFLAGS="-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.5.sdk -I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.5.sdk/usr/include -I/path/to/buildroot/extra_include -F/path/to/buildroot/extra_include -arch arm64"
# CPPFLAGS are the Pre Processor Flags. Again, use the
# iOS SDK for arm64 and our extra_include directory
$ export CPPFLAGS="-DUSE_GETCWD -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.5.sdk -arch arm64 -I/path/to/buildroot/extra_include -F/path/to/buildroot/extra_include"
# LDFLAGS are the Linker Flags. You know what to do.
$ export LDFLAGS="-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.5.sdk -arch arm64"

Now, you can call ./configure and pass some arguments to customize the resulting build of lighttpd:

# --without-pcre: do not link against pcre (not present on iOS)
# --with-libxml: link against libxml
# --with-zlib: link against zlib (for webpage compression)
# --host arm-apple-darwin: lighttpd will run on an
#                          arm Apple Darwin-based OS
$ ./configure --without-pcre --with-libxml --with-zlib --host arm-apple-darwin

Configure will now poke around macOS to determine which arguments can be passed to the toolchain, which options are enabled or disabled, if the SDKs are correctly set up and create the makefiles accordingly.

You can easily see from output that it will do cross-compilation:

$ ./configure [...]
checking build system type... x86_64-apple-darwin20.5.0
checking host system type... arm-apple-darwin
[...]
checking whether we are cross compiling... yes
[...]

When it’s done, it’ll output the list of enabled/disabled modules and features.

Make

Now that configure created a makefile, it’s time to actually compile all the required source code and build the final product:

# install, but to our dstroot, macOS doesn't need
# lighttpd for iOS on arm64...
$ make install DESTDIR=/path/to/dstroot/

Once it’s done, and if you received no error from either clang or libtool, navigate to your dstroot and see the result by yourself:

$ cd /path/to/destroot && find .
 .
 ./sbin
 ./sbin/lighttpd
 ./sbin/lighttpd-angel
 ./lib
 ./lib/mod_ajp13.so
 ./lib/mod_vhostdb.so
 ./lib/mod_dirlisting.so
 ./lib/mod_staticfile.so
 ./lib/mod_ssi.so
 ./lib/mod_fastcgi.la
 ./lib/mod_expire.so
 ./lib/mod_rewrite.so
 ./lib/mod_extforward.so
 ./lib/mod_proxy.so
 ./lib/mod_redirect.so
 ./lib/mod_evasive.la
 ./lib/mod_status.la
 ./lib/mod_alias.la
 ./lib/mod_setenv.so
 ./lib/mod_accesslog.la
 ./lib/mod_authn_file.so
 ./lib/mod_usertrack.so
 ./lib/mod_indexfile.la
 ./lib/mod_proxy.la
 ./lib/mod_extforward.la
 ./lib/mod_redirect.la
 ./lib/mod_evasive.so
 ./lib/mod_ajp13.la
 ./lib/mod_vhostdb.la
 ./lib/mod_expire.la
 ./lib/mod_fastcgi.so
 ./lib/mod_rewrite.la
 ./lib/mod_staticfile.la
 ./lib/mod_dirlisting.la
 ./lib/mod_ssi.la
 ./lib/liblightcomp.dylib
 ./lib/mod_indexfile.so
 ./lib/mod_usertrack.la
 ./lib/mod_authn_file.la
 ./lib/mod_status.so
 ./lib/mod_setenv.la
 ./lib/mod_alias.so
 ./lib/mod_accesslog.so
 ./lib/mod_evhost.so
 ./lib/mod_rrdtool.so
 ./lib/mod_auth.so
 ./lib/mod_wstunnel.la
 ./lib/mod_secdownload.so
 ./lib/mod_sockproxy.la
 ./lib/mod_deflate.so
 ./lib/liblightcomp.la
 ./lib/mod_webdav.so
 ./lib/mod_simple_vhost.so
 ./lib/mod_uploadprogress.so
 ./lib/mod_userdir.la
 ./lib/mod_access.la
 ./lib/mod_scgi.la
 ./lib/mod_cgi.so
 ./lib/mod_flv_streaming.la
 ./lib/mod_webdav.la
 ./lib/mod_simple_vhost.la
 ./lib/mod_auth.la
 ./lib/mod_rrdtool.la
 ./lib/mod_evhost.la
 ./lib/mod_deflate.la
 ./lib/mod_sockproxy.so
 ./lib/mod_wstunnel.so
 ./lib/mod_secdownload.la
 ./lib/mod_cgi.la
 ./lib/mod_scgi.so
 ./lib/mod_access.so
 ./lib/mod_flv_streaming.so
 ./lib/mod_userdir.so
 ./lib/mod_uploadprogress.la
 ./share
 ./share/man
 ./share/man/man8
 ./share/man/man8/lighttpd-angel.8
 ./share/man/man8/lighttpd.8

Codesign

iOS wouldn’t be the iOS we love if we didn’t have to deal with a bit of code signing and entitlements 😛

For lighttpd to properly work on your iDevice with lowered security by any mean you might see fit, there’s two extra steps:

  • ad-hoc sign the libraries;
  • ad-hoc sign and add entitlements to the main binaries.

For that matter, here’s an example of entitlements you might want to use: entitlements.plist

These entitlements:

  • allow you to listen incoming and open outgoing network connections (useful for a server);
  • allow you to operate without a sandbox profile.

To code sign the libraries:

# -v: verbose output
# -f: force code signing, replace if necessary
# -s: code signing identity to use
# "-": ad-hoc sign, do not use a code signing certificate
# -i "identity": unique identifier embedded into the signature
$ codesign -v -f -s - -i "my.identity.lighttpd.libraries" ./usr/local/lib/*

To code sign the main binaries:

# --entitlements "ent.xml": entitlements to embed when signing
$ codesign -v -f -s - -i "my.identity.lighttpd" --entitlements /path/to/entitlements.plist ./usr/local/sbin/lighttpd
$ codesign -v -f -s - -i "my.identity.lighttpd-angel" --entitlements /path/to/entitlements.plist ./usr/local/sbin/lighttpd-angel

Use

Now, to make use of your freshly compiled lighttpd, copy the contents of dstroot to the filesystem of your iDevice.
If you don’t want to bother adding a new launchd job, you can manually launch it from a shell, as root:

# /usr/local/sbin/lighttpd -f /usr/local/etc/lighttpd.conf

This will launch a daemonized lighttpd with the configuration file at the specified path.

Here’s a working config file: lighttpd.conf

This config file will:

  • bind the process to port 80;
  • load a few core modules;
  • lower the process’ uid and gid to mobile;
  • define the access and error logs to /var/tmp/lighttpd (you’ll have to create it and chown it to mobile if it doesn’t exist);
  • enable directory listing and set document root to “/”.

Bonus

Thank you for reading all of this! I hope it helped you in some way.

Now, if you don’t actually want to compile lighttpd by yourself, here’s the arm64 build compiled against the iOS 14.5 SDK (works on iOS 15.0 beta 2) that I showed on Twitter:

lighttpd1.4.59_ios_arm64.zip

By Pierre Blazquez

I mess with computers, drink too much coffee and listen to music at max volume.

2 comments

Comments are closed.