Fil-C

Memory SafetyC/C++ CompatibilityModern Tooling

Pizlix: Memory Safe Linux From Scratch

Weston desktop built with Fil-C

Pizlix is LFS (Linux From Scratch) 12.2 with some added components, where userland is compiled with Fil-C. This means you get the most memory safe Linux-like OS currently available.

Caveats:

Pizlix is possible because Fil-C is so compatible with C and C++ that many packages in LFS need no changes, and the rest of them mostly just require small changes. That said, it's not as simple as just replacing LFS's compiler with the Fil-C compiler because:

This document starts with a description of how to install and build Pizlix. At the end of this document, I explain exactly how Fil-C is injected into LFS.

Supported Systems

Pizlix has been tested inside VMware and Hyper-V on X86_64.

I have confirmed that it's possible to build Pizlix on Ubuntu 24.

Installing Pizlix

First, clone the Fil-C GH repo:

git clone https://github.com/pizlonator/fil-c.git

Then go into the pizlix directory under fil-c.

Pizlix requires you to set up your machine thusly:

Once you have satisfied those requirements, and you're happy with the contents of /mnt/lfs being annihilated, just do:

sudo ./build.sh

From fil-c/pizlix. Then, edit your grub config to include the menuentry in etc/grub_custom and reboot into Pizlix!

If you run into trouble, see the Build Stages.

Using Pizlix

Pizlix by default has the following configuration:

Please change the passwords, or better yet, replace the pizlo user with some other user, if your Pizlix install will face the network!

Once you get your internet to work (it will "just work" if eth0 is DHCP capable), you'll need to run:

make-ca -g

As root. Without this, curl and wget will have problems with HTTPS.

To see what this thing is really capable of, log in as a non-root user (like pizlo) and do:

weston

And enjoy a totally memory safe GUI!

Build Stages

The Pizlix build proceeds in the following stages.

The Pizlix build snapshots after each successful stage so that it's possible to restart the build at that stage later. This is great for troubleshooting!

Pre-LC

This is the bootstrapping phase that uses a Yolo-C GCC to build a Yolo-C toolchain within the /mnt/lfs chroot environment.

If you want to just do this stage of the build and nothing more, do sudo ./build_prelc.sh.

If you want to start the build here, do sudo ./build.sh.

LC

This is the phase where the chroot environment is pizlonated with a Fil-C compiler. This builds the Fil-C compiler and slams it into /mnt/lfs.

If you want to just do this stage of the build and nothing more, do sudo ./build_lc.sh.

If you want to start the build here, do sudo ./build_with_recovered_prelc.sh.

The Injecting Fil-C Into LFS section describes the LC phase, and its relationship to Pre-LC and Post-LC.

Post-LC

This is the actual Linux From Scratch build (Chapters 8, 9, 10 of the LFS book) using the Fil-C toolchain. After this completes, the Yolo-C stuff produced in Pre-LC is mostly eliminated, except for what is necessary to run the Fil-C compiler and the GCC used for building the kernel.

If you want to just do this stage of the build and nothing more, do sudo ./build_postlc.sh.

If you want to start the build here, do sudo ./build_with_recovered_lc.sh.

Post-LC 2

This builds BLFS (Beyond Linux From Scratch) components that I like to have, such as openssh, emacs, dhcpcd, and cmake.

If you want to just do this stage of the build and nothing more, do sudo ./build_postlc2.sh.

If you want to start the build here, do sudo ./build_with_recovered_postlc.sh.

Post-LC 3

This builds the Wayland environment and Weston so that you can have a GUI.

If you want to just do this stage of the build and nothing more, do sudo ./build_postlc3.sh.

If you want to start the build here, do sudo ./build_with_recovered_postlc2.sh.

Other Tricks

You can mount the chroot's virtual filesystems with sudo ./build_mount.sh. You can unmount the chroot's virtual filesystems with sudo ./build_unmount.sh.

You can hop into the chroot with sudo ./enter_chroot.sh. However, if you're past Post-LC, then you'll need to do sudo ./enter_chroot_late.sh instead.

If you want to just rebuild libpizlo.so runtime and slam it into the chroot, do sudo ./rebuild_pas.sh.

If you want to just rebuild the compiler, libpizlo.so, and user glibc, do sudo ./rebuild_lc.sh.

Injecting Fil-C Into LFS

My process for building Pizlix started with writing shell scripts to automate the LFS build. This mostly involved copy-pasting the shell commands from the LFS book after manually validating that they worked for me.

Then, I studied the build process to identify the best injection point for Fil-C. That is, where to either compile or binary-drop the Fil-C compiler and have all subsequent build steps use that compiler. After some trial and error, I found that the second glibc build step (the one at start of Chapter 8) is the perfect point to transition from Yolo-C to Fil-C:

This is why the build stages have the names that they do:

Here's how the various build stages are modified to support Fil-C.

Modifying Pre-LC

In normal LFS, most of the Pre-LC tools are built with --prefix=/usr even though these are not the final versions of the tools. Post-LC overwrites the tools build in this bootstraping phase.

It turns out that I cannot quite do this for Fil-C, because Fil-C does not share ABI with Yolo-C. So, we do not want situations like:

  1. Pre-LC builds library libfoo.so.

  2. Pre-LC builds a binary bar that dynamically links libfoo.so.

  3. Post-LC builds library libfoo.so and overwrites the version from Pre-LC.

If we did that, then bar would stop functioning, since bar is a Yolo executable and libfoo.so is now a Fil library. The same kind of problem would occur if in step 3, Post-LC built bar with Fil-C - we'd have a Fil binary dynamically linking to a Yolo library. It wouldn't work!

So, Pre-LC takes the following approach:

At the end of Pre-LC, all libraries and binaries are actually installed in /yolo, but they function as if they were installed in /usr in the sense that the glibc loader is going to look for libraries in /lib and lots of software (like lots of shell scripts) point at /bin or /usr/bin directly.

Modifying LC

The LC phase is where most of the magic happens. LC has multiple substages: Fil-C build, yoloify, yolo glibc build, Fil-C binary drop, user glibc build, and finally libc++ binary drop. Note that the need for two glibcs - yolo and user - is due to the libc sandwich runtime.

Fil-C Build

The first stage of LC is to build Fil-C in a way that is almost identical to ./build_all_fast_glibc.sh except that we force the use of the kernel headers from the version of the kernel that Pizlix uses. We will use the clang compiler binary, libpizlo.so, libc++abi, and libc++ libraries as binary drops into Pizlix in later parts of LC.

This also packages up yolo glibc and user glibc so that they can be built inside the Pizlix chroot.

Yoloify

Now we break the symlinks from /usr to /yolo. To do this without breaking the bootstrap binaries, we also patchelf all of the binaries so that:

Also, all scripts that use absolute paths to their interpreter (i.e. #!) is in /yolo/bin.

Yolo glibc Build

Now we build yolo glibc (i.e. the version of glibc 2.40 hacked to be the Yolo libc in the Fil-C sandwich runtime) inside the Pizfix chroot. This is a very hacked glibc build:

Now, the yolo glibc is installed in such a way that libpizlo.so and the Fil-C compiler know how to find it. And, most importantly, it's installed in such a way that it stays out of the way of the user glibc (which will claim the libc and libm library namespace).

Fil-C Binary Drop

This part is very hackish! I haven't ported libpizlo.so to build with GCC. So, rather than porting it or having Yolo clang in Pre-LC, this step just binary drops the libpizlo.so we've already built into /usr/lib. This also drops the Fil-C headers into /usr/include.

This step also binary drops the Fil-C compiler - a clang binary that only dynamically links glibc ABI version 6 - into /usr/bin and sets up the symlinks (so gcc, g++, cc, c++, clang, and clang++ all point to this compiler).

User glibc Build

Now we build user glibc (i.e. the version of glibc 2.40 that has been ported to Fil-C) inside the Pizfix chroot. This is almost exactly the build process used in Section 8.5 of LFS, except we're using a version of glibc 2.40 that has been ported to Fil-C and we're compiling with the Fil-C compiler rather than GCC.

libc++ Binary Drop

Ideally, we'd build libc++abi and libc++ within the Pizfix chroot, but for now I'm being lazy so I just binary drop the versions I've already built.

This concludes the LC phase! Now it's possible to proceed with the rest of the LFS build. That build will end up using the Fil-C compiler even if the build scripts explicitly ask for gcc or g++, and they will get a Fil-C version of glibc, and libc++abi/libc++ as their C++ libraries.

Modifying Post-LC

Post-LC is almost exactly the LFS chapters 8-10 except for the glibc step (which we already did in the LC phase). The only changes are:

Pizlix also includes a Post-LC 2 and Post-LC 3 phases that build some parts of BLFS 12.2 plus enough to run Weston.