https://cr.yp.to/2025/fil-c.html
Notes by djb on using Fil-C (2025)
I'm impressed with the level of compatibility of the new memory-safe
C/C++ compiler Fil-C (filcc, fil++). Many libraries and applications
that I've tried work under Fil-C without changes, and the exceptions
haven't been hard to get working.
I've started accumulating miscellaneous notes on this page regarding
usage of Fil-C. My selfish objective here is to protect various
machines that I manage by switching them over to code compiled with
Fil-C, but maybe you'll find something useful here too.
Timings below are from a mini-PC named phoenix except where otherwise
mentioned. This mini-PC has a 6-core (12-thread) AMD Ryzen 5 7640HS
(Zen 4) CPU, 12GB RAM, and 36GB swap. The OS is Debian 13. (I
normally run LTS software, periodically upgrading from software
that's 4-5 years old such as Debian 11 today to software that's 2-3
years old such as Debian 12 today; but some of the packages included
in Fil-C expect newer utilities to be available.)
Related:
* I've posted a script to help auditors see how Fil-C differs from
upstream sources (clang, glibc, ...).
* I've posted a self-contained filian-install-compiler script
(replacing the 20251029 version) to download+compile+install
Fil-C on Debian 13 in what I think are Debian-appropriate
locations, along with glibc and binutils compiled with Fil-C. A
run took 86 minutes real time (for 477 minutes user time and 52
minutes system time).
* I've posted the start of a filian-install-packages script to
download+compile+install Debian source packages, using Fil-C as
the compiler (after filian-install-compiler has finished). This
script has various limitations that need fixing, but it does work
for a few packages already (e.g., ./filian-install-packages
bzip2), after the installation of dh-exec etc. described below.
* I've posted a graph showing nearly 9000 microbenchmarks of Fil-C
vs. clang on cryptographic software (each run pinned to 1 core on
the same Zen 4). Typically code compiled with Fil-C takes between
1x and 4x as many cycles as the same code compiled with clang.
Another way to run Fil-C is via Filnix from Mikael Brockman. For
example, an unprivileged user under Debian 12 with about 10GB of free
disk space can download, compile, and install Fil-C, and run a
Fil-C-compiled Nethack, as follows:
unshare --user --pid echo YES # just to test
git clone https://github.com/nix-community/nix-user-chroot
cd nix-user-chroot
cargo build --release
mkdir -m 0755 ~/nix
~/nix-user-chroot/target/release/nix-user-chroot ~/nix \
bash -c 'curl -L https://nixos.org/nix/install | sh'
env TERM=vt102 \
~/nix-user-chroot/target/release/nix-user-chroot ~/nix \
~/nix/store/*-nix-2*/bin/nix \
--extra-experimental-features 'nix-command flakes' \
run 'github:mbrock/filnix#nethack'
Compiler and C library
Current recommendations for things to do at the beginning as root:
mkdir -p /var/empty
apt install \
autoconf-dickey build-essential bison clang cmake flex gawk \
gettext ninja-build patchelf quilt ruby texinfo time
I created an unprivileged filc user. Everything else is as that user.
I downloaded the Fil-C source package:
git clone https://github.com/pizlonator/fil-c.git
cd fil-c
This isn't just the compiler; there's also glibc and quite a few
higher-level libraries and applications. There are also binary Fil-C
packages, but I've worked primarily with the source package at this
point.
I compiled Fil-C and glibc:
time ./build_all_fast_glibc.sh
There are also options to use musl instead of glibc, but musl is
incompatible with some of the packages shipped with Fil-C: attr needs
basename, elfutils needs argp_parse, sed's test suite needs the glibc
variant of calloc, and vim's build needs iconv to be able to convert
from CP932 to UTF-8.
I had originally configured the server phoenix with only 12GB swap. I
then had to restart ./build_all_fast_glibc.sh a few times because the
Fil-C compilation ran out of memory. Switching to 36GB swap made
everything work with no restarts; monitoring showed that almost 19GB
swap (plus 12GB RAM) was used at one point. A larger server, 128
cores with 512GB RAM, took 8 minutes for Fil-C plus 6 minutes for
musl, with no restarts needed.
More libraries and applications included with Fil-C
Fil-C includes a ./build_all_slow.sh that builds many more libraries
and applications (sometimes with patches from the Fil-C author). I
wrote a replacement script https://cr.yp.to/2025/
build-parallel-20251023.py with the following differences:
* The build-parallel script runs through everything, putting
results into build/logs/*, whereas build_all_slow stops at the
first error.
* The build-parallel script tries to use multiple cores, while
build_all_slow runs through targets serially, parallelizing only
to the extent that each target's build mechanism is parallelized.
* The build-parallel script might produce wrong results if it's
parallelizing things that shouldn't be parallelized. But this
should be easy to fix via tweaking the dependencies listed in the
script.
On phoenix, running time PATH="$HOME/bin:$HOME/fil-c/build/bin:$HOME/
fil-c/pizfix/bin:$PATH" ./build-parallel.py went through 61 targets
in 101 minutes real time (467 minutes user time, 55 minutes system
time), successfully compiling 60 of them.
libcap. This is the one that didn't compile: /home/filc/fil-c/pizfix/
bin/ld: /usr/libexec/gcc/x86_64-linux-gnu/14/liblto_plugin.so: error
loading plugin: libc.so.6: cannot open shared object file: No such
file or directory
util-linux. I skipped this one. It does compile, but the compiled
taskset utility needs to be patched to use sched_getaffinity and
sched_setaffinity as library functions rather than via syscall, or
Fil-C needs to be patched for those syscalls. This is an issue for
build-parallel since build-parallel relies on taskset; maybe
build-parallel should instead use Python's affinity functions.
attr, bash, benchmarks, binutils, bison, brotli, bzip2, bzip3, check,
cmake, coreutils, cpython, curl, dash, diffutils, elfutils, emacs,
expat, ffi, gettext, git, gmp, grep, icu, jpeg-6b, libarchive,
libcap, libedit, libevent, libpipeline, libuev, libuv, lua, lz4, m4,
make, mg, ncurses, nghttp2, openssh, openssl, pcre2, pcre, perl,
pkgconf, procps, quickjs, sed, shadow, simdutf, sqlite, tcl, tmux,
toybox, vim, wg14_signals, xml_parser, xz, zlib, zsh, zstd. No
problems encountered so far (given whatever patches were already
applied from the Fil-C author!). The benchmarks package is supplied
with Fil-C and does a few miscellaneous measurements.
Further libraries and applications
I did export PATH="$HOME/bin:$HOME/fil-c/build/bin:$HOME/fil-c/pizfix
/bin:$PATH" before these.
boost 1.89.0: Seems to mostly work. Most of the package is
header-only; a few simple tests worked fine.
I also looked a bit at the compiled parts. Running ./bootstrap.sh
--with-toolset=clang --prefix=$HOME ran into vfork, which Fil-C
doesn't support, but editing tools/build/src/engine/execunix.cpp to
use defined(__APPLE__) || defined(__FILC__) for the no-fork test got
past this.
Running ./b2 install --prefix=$HOME toolset=clang address-model=64
architecture=x86_64 binary-format=elf produced an error message since
I should have said x86 instead of x86_64; Fil-C said it caught a
safety issue in the b2 program after the error message: filc safety
error: argument size mismatch (actual = 8, expected = 16). I didn't
compile with debugging so Fil-C didn't say where this is in b2.
cdb-20251021: Seems to work. One regression test, an artificial
out-of-memory regression test, currently produces a different error
message with Fil-C: filc panic: src/libpas/
pas_compact_heap_reservation.c:65: pas_aligned_allocation_result
pas_compact_heap_reservation_try_allocate(size_t, size_t): assertion
page_result.result failed.
libcpucycles-20250925: Seems to work. I commented out the first three
lines of cpucycles/options.
libgc: I replaced this with a small gcshim package (https://cr.yp.to/
2025/gcshim-20251022.tar.gz) that simply calls malloc etc. So far
this seems to be an adequate replacement. (Fil-C includes a garbage
collector.)
libntruprime-20241021: Seems to work after a few tweaks but I didn't
collect full notes yet. chmod +t crypto_hashblocks/sha512/avx2
disables assembly and makes things compile; configured with
--no-valgrind since Fil-C doesn't support valgrind; did a bit more
tweaking to make cpuid work.
lpeg-1.1.0: Compiles, maybe works (depends on lua, dependency of
neovim):
cd
PREFIX=$(dirname $(dirname $(which lua)))
wget https://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.1.0.tar.gz
tar -xf lpeg-1.1.0.tar.gz
cd lpeg-1.1.0
make CC=`which filcc` DLLFLAGS='-shared -fPIC' test
cp lpeg.so $PREFIX/lib
luv-1.51.0: Compiles, maybe works (depends on lua, dependency of
neovim):
cd
PREFIX=$(dirname $(dirname $(which lua)))
wget https://github.com/luvit/luv/releases/download/1.51.0-1/luv-1.51.0-1.tar.gz
tar -xf luv-1.51.0-1.tar.gz
cd luv-1.51.0-1
mkdir build
cd build
LUA_DIR=$HOME/fil-c/projects/lua-5.4.7
# lua install should probably do this:
cp $LUA_DIR/lua.h $PREFIX/include/
cp $LUA_DIR/lauxlib.h $PREFIX/include/
cp $LUA_DIR/luaconf.h $PREFIX/include/
cp $LUA_DIR/lualib.h $PREFIX/include/
# and then:
cmake -DCMAKE_C_COMPILER=`which filcc` -DCMAKE_INSTALL_PREFIX=$PREFIX -DWITH_LUA_ENGINE=Lua -DLUA_DIR=$HOME/fil-c/projects/lua-5.4.7/ ..
make test
make install
mutt-2-2-15-rel (depends on ncurses):
wget https://github.com/muttmua/mutt/archive/refs/tags/mutt-2-2-15-rel.tar.gz
tar -xf mutt-2-2-15-rel.tar.gz
cd mutt-mutt-2-2-15-rel
CC=`which clang` ./prepare --prefix=$HOME/fil-c/pizfix --with-homespool
make -j12 install
Seems to work, at least for reading email.
tig (depends on ncurses and maybe more):
wget https://github.com/jonas/tig/releases/download/tig-2.6.0/tig-2.6.0.tar.gz
tar -xf tig-2.6.0.tar.gz
cd tig-2.6.0
CC=`which filcc` ./configure --prefix=$(dirname $(dirname $(which git)))
make -j12
make test
make -j12 install
Seems to work, at least for viewing the Fil-C repo.
w3m (depends on gcshim and ncurses): Seems to work. I tried the
Debian version: git clone https://salsa.debian.org/debian/w3m.git. I
used CFLAGS=-Wno-incompatible-function-pointer-types (which is
probably needed for clang anyway even without Fil-C).
Debian using Fil-C (Filian?)
I've built and installed some replacement Debian packages using Fil-C
as the compiler on a Debian 13 machine, as explained below. Hopefully
this can rapidly scale to many packages, taking advantage of the
basic compile-install-test knowledge already built into Debian source
packages, although some packages will take more work because they
need extra patches to work with Fil-C.
Structure. Debian already understands how to have packages for
multiple architectures (ABIs; Debian "ports") installed at once. For
example, dpkg --add-architecture i386; apt update; apt install
bash:i386 installs a 32-bit version of bash, replacing the usual
64-bit version; you can do apt install bash:amd64 to revert to the
64-bit version. Meanwhile the 32-bit libraries and 64-bit libraries
are installed in separate locations, basically /lib/i386-linux-gnu or
/usr/lib/i386-linux-gnu vs. /lib/x86_64-linux-gnu or /usr/lib/
x86_64-linux-gnu. (On Debian 11 and newer, and on Ubuntu 22.04 and
newer, /lib is symlinked to /usr/lib.)
I'm following this model for plugging Fil-C into Debian: the goal is
for apt install bash:amd64fil0 to install a Fil-C-compiled
(amd64fil0) version of bash, replacing the usual (amd64) version of
bash, while the amd64 and amd64fil0 libraries are installed in
separate locations.
The include-file complication. Debian expects library packages
compiled for multiple ABIs to all provide the same include files: for
example, /usr/include/ncurses.h is provided by libncurses-dev:i386,
libncurses-dev:amd64, etc. This is safe because Debian forces
libncurses-dev:i386 and libncurses-dev:amd64 and so on to all have
the same version. An occasional package with ABI-dependent include
files can still use /usr/include/x86_64-linux-gnu etc.
Fil-C instead omits /usr/include in favor of a Fil-C-specific
directory (which will typically be different from /usr/include: even
if Fil-C is compiled with glibc, probably the glibc version won't be
the same as in /usr/include). This difference is the top source of
messiness below. I'm planning to tweak the Fil-C driver to use /usr/
include on Debian. [This is done in the filian-install-compiler
script.]
Something else I'm planning to tweak is Fil-C's glibc compilation, so
that it uses the final system prefix. [This is also done in the
filian-install-compiler script.] The approach described below instead
requires /home/filian/fil-c to stay in place for compiling and
running programs.
Building Debian packages. How does Debian package building work?
First, more packages to install as root:
apt install dpkg-dev devscripts docbook2x \
dh-exec dh-python python3-setuptools fakeroot \
sbuild mmdebstrap uidmap piuparts
Debian has multiple options for building a package. The option that
has the best isolation, and that Debian uses to continually build new
packages for distribution, is sbuild, but for fast development I'll
focus on directly using the lower-level dpkg-buildpackage.
Baseline 1: using sbuild without Fil-C. In case you do want to try
sbuild, here's the basic setup, and then an example of building a
small package (tinycdb):
mkdir -p ~/shared/sbuild
time mmdebstrap --include=ca-certificates --skip=output/dev --variant=buildd unstable ~/shared/sbuild/unstable-amd64.tar.zst https://deb.debian.org/debian
mkdir -p ~/.config/sbuild
cat << "EOF" > ~/.config/sbuild/config.pl
$chroot_mode = 'unshare';
$external_commands = { "build-failed-commands" => [ [ '%SBUILD_SHELL' ] ] };
$build_arch_all = 1;
$build_source = 1;
$source_only_changes = 1;
$run_lintian = 1;
$lintian_opts = ['--display-info', '--verbose', '--fail-on', 'error,warning', '--info'];
$run_autopkgtest = 1;
$run_piuparts = 1;
$piuparts_opts = ['--no-eatmydata', '--distribution=%r', '--fake-essential-packages=systemd-sysv'];
EOF
mkdir -p ~/shared/packages
cd ~/shared/packages
apt source tinycdb
cd tinycdb-*/
time sbuild
Baseline 2: using dpkg-buildpackage without Fil-C. Here's what it
looks like compiling the same small package with dpkg-buildpackage:
mkdir -p ~/shared/packages
cd ~/shared/packages
apt source tinycdb
cd tinycdb-*/
time dpkg-buildpackage -us -uc -b
The goal: Using dpkg-buildpackage with Fil-C. As root, teach dpkg
basic features of the new architecture, imitating the current line
amd64 x86_64 (amd64|x86_64) 64 little in the same file:
echo amd64fil0 x86_64+fil0 amd64fil0 64 little >> /usr/share/dpkg/cputable
Also, allow apt to install packages compiled for this architecture
(beware that this will also later make apt update look for that
architecture on servers, and whimper a bit for not finding it, but
nothing breaks):
dpkg --add-architecture amd64fil0
Also, teach autoconf to accept amd64fil0 (the third of these lines is
what's critical for Debian builds):
sed -i '/| x86_64 / a| x86_64+fil0 \\' /usr/share/autoconf/build-aux/config.sub
sed -i '/| x86_64 / a| x86_64+fil0 \\' /usr/share/libtool/build-aux/config.sub
sed -i '/| x86_64 / a| x86_64+fil0 \\' /usr/share/misc/config.sub
[Not necessary if you've used filian-install-compiler:] As a filian
user, compile Fil-C and its standard library:
cd
git clone https://github.com/pizlonator/fil-c.git
cd fil-c
time ./build_all_fast_glibc.sh
[Not necessary if you've used filian-install-compiler:] As root, copy
Fil-C and its standard library into system locations:
mkdir -p /usr/libexec/fil/amd64/compiler
time cp -r /home/filian/fil-c/pizfix /usr/libexec/fil/amd64/
rm -rf /usr/lib/x86_64+fil0-linux-gnu
mv /usr/libexec/fil/amd64/pizfix/lib /usr/lib/x86_64+fil0-linux-gnu
ln -s /usr/lib/x86_64+fil0-linux-gnu /usr/libexec/fil/amd64/pizfix/lib
rm -rf /usr/include/x86_64+fil0-linux-gnu
mv /usr/libexec/fil/amd64/pizfix/include /usr/include/x86_64+fil0-linux-gnu
ln -s /usr/include/x86_64+fil0-linux-gnu /usr/libexec/fil/amd64/pizfix/include
time cp -r /home/filian/fil-c/build/bin /usr/libexec/fil/amd64/compiler/
time cp -r /home/filian/fil-c/build/include /usr/libexec/fil/amd64/compiler/
time cp -r /home/filian/fil-c/build/lib /usr/libexec/fil/amd64/compiler/
( echo '#!/bin/sh'
echo 'exec /usr/libexec/fil/amd64/compiler/bin/filcc "$@"' ) > /usr/bin/x86_64+fil0-linux-gnu-gcc
chmod 755 /usr/bin/x86_64+fil0-linux-gnu-gcc
( echo '#!/bin/sh'
echo 'exec /usr/libexec/fil/amd64/compiler/bin/fil++ "$@"' ) > /usr/bin/x86_64+fil0-linux-gnu-g++
chmod 755 /usr/bin/x86_64+fil0-linux-gnu-g++
ln -s /usr/libexec/fil/amd64/compiler/bin/llvm-objdump /usr/bin/x86_64+fil0-linux-gnu-objdump
ln -s x86_64+fil0-linux-gnu-gcc /usr/bin/filcc
ln -s x86_64+fil0-linux-gnu-g++ /usr/bin/fil++
Now, as user filian (or whichever other user), let's make a little
helper script to adjust a Debian source package:
mkdir -p $HOME/bin
( echo '#!/bin/sh'
echo 'sed -i '\''s/^ \([^"]*\)$/ pizlonated_\1/'\'' debian/*.symbols'
echo 'find . -name '\''*.map'\'' | while read fn'
echo 'do'
echo ' awk '\''{'
echo ' if ($1 == "local:") global = 0'
echo ' if ($1 == "}") global = 0'
echo ' if (global && NF > 0 && !index($0,"c++")) $1 = "pizlonated_"$1'
echo ' if ($1 == "global:") global = 1'
echo ' print'
echo ' }'\'' < $fn > $fn.tmp'
echo ' mv $fn.tmp $fn'
echo 'done'
echo 'find debian -name '\''*.install'\'' | while read fn'
echo 'do'
echo ' awk '\''{'
echo ' if (NF == 2 && $2 == "usr/include") $2 = $2"/${DEB_HOST_MULTIARCH}"'
echo ' if (NF == 1 && $1 == "usr/include") { $2 = $1"/${DEB_HOST_MULTIARCH}"; $1 = $1"/*" }'
echo ' print'
echo ' }'\'' < $fn > $fn.tmp'
echo ' mv $fn.tmp $fn'
echo 'done'
) > $HOME/bin/fillet
chmod 755 $HOME/bin/fillet
And now let's try building a small package:
mkdir -p ~/shared/packages
cd ~/shared/packages
apt source tinycdb
cd tinycdb-*/
$HOME/bin/fillet
time env DPKG_GENSYMBOLS_CHECK_LEVEL=0 \
DEB_BUILD_OPTIONS='crossbuildcanrunhostbinaries nostrip' \
dpkg-buildpackage -d -us -uc -b -a amd64fil0
Explanation of the differences from a normal build:
* DEB_BUILD_OPTIONS=nostrip skips removing symbols from binaries,
in part because Debian's dh_dwz utility doesn't currently work
with Fil-C, in part because we'll look at symbols later.
* DEB_BUILD_OPTIONS=crossbuildcanrunhostbinaries silences a warning
about cross-compilation not necessarily working with tests.
* -a amd64fil0 specifies the amd64fil0 ABI, which triggers using
the x86_64+fil0-linux-gnu-gcc compiler.
* -d stops dpkg-buildpackage from checking for dependencies. (This
doesn't matter for this package.)
* [Superseded by the binutils patch in in filian-install-compiler
starting with version 20251030:] The tweak to *.map in the helper
script tries to stop Debian from hiding the shared-library
symbols after Fil-C mangling.
* The tweak to *.symbols in the helper script tries to notify
Debian regarding the expected shared-library symbols after Fil-C
mangling. [Not necessary given DPKG_GENSYMBOLS_CHECK_LEVEL=0.]
* DPKG_GENSYMBOLS_CHECK_LEVEL=0 should stop Debian from complaining
that there are wrong symbols.
* [Not necessary if you've used filian-install-compiler:] The tweak
to *.install in the helper script installs /usr/include/
x86_64+fil0/cdb.h (where Fil-C will see it for programs using
this library) instead of /usr/include/cdb.h.
For me this worked and produced three ../*.deb packages. Installing
them as root also worked:
apt install /home/filian/shared/packages/*.deb
# some sanity checks:
apt list | grep tinycdb
# prints "tinycdb/stable 0.81-2 amd64" (available package)
# and prints "tinycdb/now 0.81-2 amd64fil0 [installed,local]"
dpkg -L tinycdb:amd64fil0
# lists various files such as /usr/bin/cdb
nm /usr/bin/cdb
# shows various symbols including "pizlonated" (Fil-C) symbols
ldd /usr/bin/cdb
# shows dependence on libraries in /usr/libexec/fil
/usr/bin/cdb -h
# prints a help message: "cdb: Constant DataBase" etc.
Compiling a deliberately wrong test program with the newly installed
library also works, and triggers Fil-C's run-time protection:
cd /root
( echo '#include '
echo 'int main() { cdb_init(0,0); return 0; }' ) > usecdb.c
filcc -o usecdb usecdb.c -lcdb
./usecdb < /bin/bash
# ... "filc panic: thwarted a futile attempt to violate memory safety."
More Debian packages
libc-dev. Some packages depend on libc-dev, so let's build a fake
libc-dev package (probably there's an easier way to do this):
FAKEPACKAGE=libc-dev
mkdir -p ~/shared/packages/$FAKEPACKAGE/debian
cd ~/shared/packages/$FAKEPACKAGE
( echo $FAKEPACKAGE' (0.0) unstable; urgency=medium'
echo ''
echo ' * Initial Release.'
echo ''
echo ' -- djb Sun, 26 Oct 2025 16:05:17 +0000'
) > debian/changelog
( echo 'Source: '$FAKEPACKAGE
echo 'Build-Depends: debhelper-compat (= 13)'
echo 'Maintainer: djb '
echo ''
echo 'Package: '$FAKEPACKAGE
echo 'Architecture: any'
echo 'Multi-Arch: same'
echo 'Description: fake '$FAKEPACKAGE
) > debian/control
( echo '#!/usr/bin/make -f'
echo ''
echo 'build-arch build-indep build \'
echo 'install-arch install-indep install \'
echo 'binary-arch binary-indep binary \'
echo ':'
echo 'Xdh $@' | tr X '\011'
echo ''
echo 'clean:'
echo 'Xdh_clean' | tr X '\011'
) > debian/rules
time env DPKG_GENSYMBOLS_CHECK_LEVEL=0 \
DEB_BUILD_OPTIONS='crossbuildcanrunhostbinaries nostrip' \
dpkg-buildpackage -d -us -uc -b -a amd64fil0
ncurses.
mkdir -p ~/shared/packages
cd ~/shared/packages
apt source ncurses
cd ncurses-*/
$HOME/bin/fillet
time env DPKG_GENSYMBOLS_CHECK_LEVEL=0 \
DEB_BUILD_OPTIONS='crossbuildcanrunhostbinaries nostrip' \
dpkg-buildpackage -d -us -uc -b -a amd64fil0
rm ../ncurses-*deb # apt won't let us touch the binaries
As root, install the above libraries:
apt install /home/filian/shared/packages/lib*.deb
libmd. Seems to work. At first this didn't install since the compiled
version (for amd64fil0) was 1.1.0-2 while the installed version (for
amd64) was 1.1.0-2+b1. Debian requires the same version number across
architectures (see above regarding include-file compatibility), so
apt said that 1.1.0-2+b1 breaks 1.1.0-2. I resolved this by compiling
and installing 1.1.0-2 for both amd64 and amd64fil0. This is a
downgrade since "+b" refers to a "binNMU", a "binary-only
non-maintainer upload", a patch beyond the official source; I don't
know what the patch is.
readline. Needs ln -s /usr/include/readline /usr/include/
x86_64+fil0-linux-gnu/readline after installation. Could have tweaks
in debian/rules (which seems to predate *.install), but this is in
any case an example of the messiness that I'm planning to get rid of.
lua5.4. Seems to work. Depends on readline.