Build TOR on FreeBSD

Pre-intro

For this article I used an AWS EC2 instance, running a FreeBSD AMI based on FreeBSD 13. I don’t recommend using this AMI because it has an issue with ncurses (not a huge problem, easily fixed), but instead go for the stable release, eg an AMI based on FreeBSD 12.
This means that I already had the binary pkg system in place and to become root I had to only type su -.

Introduction

Since you are going to be building software on FreeBSD, it’s fair to assume you are going to have some knowledge about the underlying platform, like installing packages and… becoming root.

Becoming root

If you are running FreeBSD on a machine you administer, then you already know the root password, so you can use su - or you installed sudo. Details on how to install sudo can be found in the handbook.

Installing packages

There are 2 ways to install software on FreeBSD that are more or less painless: ports and binary packages. Keep in mind that if you add a package via pkg, then all deps not already present on the system will be added via the same method and if you add a package via the ports system, eg by building, then all not installed deps will be added via the ports system, eg by building them. On your machine. You don’t want that. Unless you have access to huge amounts of computing and disk power and you don’t care about the carbon footprint. Carbon Aware Software should be a thing.

Hacking TOR on FreeBSD

I am going to use security/tor as an example.

Primer on FreeBSD Ports System

The FreeBSD Ports System is one of the best build system I ever worked with. It needs a local database to know how to build things, with files in PORTSDIR (default /usr/ports) and PORT_DBDIR (default /var/db/ports). As a developer you should check the Porter’s Handbook.

PORTSDIR is the directory where the ports database itself resides, along with a set of utilities. More detailed info. For each port it contains information about how to build the port (and consequently the binary package), such as port Makefile and whatever patches are required to make the thing work on FreeBSD. This database is managed by portsnap.

PORT_DBDIR is the directory where the OPTIONS for each port are stored. Eg, use libzstd, install the docs etc. More detailed info.

DISTDIR (default PORTSDIR/distfiles), where the source archives are downloaded at build time.

WRKDIRPREFIX (default PORTSDIR/path/to/port/work), where the build artefacts and what not are written.

There is a way to have a partial PORTSDIR and still be able to build things, but is not supported by FreeBSD and might not even save you that much time.

0. Get/update the ports db and install deps


Most likely you need to do this only every so often, maybe once a week? See portsnap man page for more info. As root, fetch a new snapshot of the ports database and extract it:

portsnap fetch && portsnap auto

As root, install deps (both build and run):

pkg install `make -C /usr/ports/security/tor run-depends-list build-depends-list | awk -F '/' '{ print $NF }'`

Done.

From now on, all the commands should be run in ~/building_tor or similar, as your regular user.


1. Configure the port


Let’s assume we want to hack on tor with lib zstd, both the shared build and static.

make -C /usr/ports/security/tor PORT_DBDIR=${PWD}/options/non-static config

and uncheck everything apart ZSTD.

make -C /usr/ports/security/tor PORT_DBDIR=${PWD}/options/static config

and uncheck everything apart STATIC_TOR and ZSTD.

2. Prepare the sources

make -C /usr/ports/security/tor DISTDIR=${PWD}/distfiles WRKDIRPREFIX=${PWD}/work extract

This will download the source tarball in DISTDIR and unpack it in WRKDIRPREFIX/PORTSDIR/<path_to_port>/work/port_name-version, eg in my case “${PWD}work/usr/ports/security/tor/work/tor-0.4.3.5/“.

3. Build (the package)

make -C /usr/ports/security/tor DISTDIR=${PWD}/distfiles WRKDIRPREFIX=${PWD}/work PORT_DBDIR=${PWD}/options/<variant>/

where <variant> is static or non-static in our case.

The output in this case will be ${PWD}/work/usr/ports/security/tor/work/stage (eg the binary itself being ${PWD}/work/usr/ports/security/tor/work/stage/usr/local/bin/tor).

Further reading (search for Default targets and their behaviors)

Closing

I think a jail setup will make things a bit easier, you won’t need to use so many make variables, but that’s for another time.