Setting up a new Linux System From Scratch

Building the file structure and installing packages

As a gadget geek, I've always loved small embedded handheld computers. From the palm pilots, gameboys, and pocket pcs of my youth, to the Retroid Pocket, GPD and Steam Decks of today - I've always had a special place in my heart for pocket computing. Recently, I decided to see just exactly what my RG351mp could do if I targeted it natively, rather than running my games through an emulator. This is the story of that ill conceived notion.

Today we have the fun of actally building out the file structure for our custom linux system, and installing / building packages into it.

Update Environment Variables

Switch to your cross user (clfs if you were following from previous notes)

Update the .bashrc file for this user to include the following:

#
# CONFIGURATION ###############################################################
# 

# Disable bash's hash function.  This is an optimization to prevent
# bash needing to look up the location of an executable each time it
# is run.  However, as we will be incrementally building new tools
# as we progress - and want those that live in cross-tools to take
# precedence - we disable the hash lookup
set +h

# files and directories should be writable only by owner, but can
# be read and executed by anyone
umask 022

# location of the clfs installation
CLFS=/mnt/clfs

# controls the localization of sertain programs, failure to set this may
# cause some installation steps to fail.
LC_ALL=POSIX

# CFLAGS must not be set when building cross tools.  since that is all
# this profile will be doing, disable it by default so we don't forget
unset CFLAGS

#
# VARIABLES ###################################################################
# These are used when creating the cross toolchain.  This config is based on
# the RockChip RK3326 spec sheet
#

export CLFS_HOST=$(echo ${MACHTYPE} | sed "s/-[^-]*/cross/")
export CLFS_TARGET=aarch64-linux-musleabi
export CLFS_ARCH=aarch64
export CLFS_ARM_ARCH=armv8-a
export CLFS_FLOAT=hard
export CLFS_FPU=neon

#
# CROSS CONFIG ################################################################
# These are used to configure the actual cross compiling process (using the tools)
# Note: they were built prefixed with ${CLFS_TARGET} to further ensure that accidental
# use of the host library or executables are not used.
#
export CLFS_TARGET_FS="${CLFS}/targetfs/${CLFS_TARGET}"

CLFS_SYS_ROOT="--sysroot=${CLFS_TARGET_FS}"
export CC=\""${CLFS_TARGET}-gcc ${CLFS_SYS_ROOT}\""
export CXX=\""${CLFS_TARGET}-g++ ${CLFS_SYS_ROOT}\""
export AR=\""${CLFS_TARGET}-ar\""
export AS=\""${CLFS_TARGET}-as\""
export LD=\""${CLFS_TARGET}-ld ${CLFS_SYS_ROOT}\""
export RANLIB=\""${CLFS_TARGET}-ranlib\""
export READELF=\""${CLFS_TARGET}-readelf\""
export STRIP=\""${CLFS_TARGET}-strip\""

#
# PATH ########################################################################
#
export PATH=${CLFS}/cross-tools/${CLFS_TARGET}/bin:/bin:/usr/bin

Folder Structure

We will build our folder structure based on the FHS (Filesystem Heirarchy Standard) which can be found here. We will be building our folder structure into the ${CLFS_TARGET_FS} directory. Eventually we will image this folder structure onto a bootable drive to run our custom linux system.

mkdir -pv ${CLFS_TARGET_FS}/{bin,boot,dev,etc,home,lib/{firmware,modules}}
mkdir -pv ${CLFS_TARGET_FS}/{mnt,opt,proc/{,mounts},sbin,srv,sys}
mkdir -pv ${CLFS_TARGET_FS}/var/{cache,lib,local,lock,log,opt,run,spool}
install -dv -m 0750 ${CLFS_TARGET_FS}/root
install -dv -m 1777 ${CLFS_TARGET_FS}/{var/,}tmp
mkdir -pv ${CLFS_TARGET_FS}/usr/{,local/}{bin,include,lib,sbin,share,src}

Users and Groups

/etc/mtab

Linux system uses /etc/mtab to track the mounted file systems. We will soft link it to /proc/mounts

# from ${CLFS_TARGET_FS}
ln -svf proc/mounts etc/mtab`

Users

We will need to create a root user for our system. Each user has corresponding entries in /etc/passwd. While we are doing this, we should also add an unpriviledged user for running background tasks called daemon.

Create a file called ${CLFS_TARGET_FS}/etc/passwd and add the following line to it:

root::0:0:root:/root:/bin/ash
daemon:x:2:6:daemon:/sbin:/bin/false

Note: we do not put in a password yet. That will be one of the first things we do when we chroot into this system.

Each entry in the file is a : separated list, where each field corresponds to the following:

mark:x:1001:1001:mark,,,:/home/mark:/bin/bash
[--] - [--] [--] [-----] [--------] [--------]
|    |   |    |     |         |        |
|    |   |    |     |         |        +-> 7. Login shell
|    |   |    |     |         +----------> 6. Home directory
|    |   |    |     +--------------------> 5. GECOS
|    |   |    +--------------------------> 4. GID
|    |   +-------------------------------> 3. UID
|    +-----------------------------------> 2. Password
+----------------------------------------> 1. Username
  1. The user name
  2. The password (encrypted)
  3. A unique identifier for this user
  4. A unique identifier for the users primary group
  5. A comma separated list information for the user like a full name, phone number, etc
  6. The path to the users home directory
  7. Which login shell to use. Generally set to /bin/bash

Groups

We are going to need a few more groups than users, based on the needs of some programs we know we will be adding, as well as some conventions established by the Linux Standard Base. It's possible that we could omit some of these, but I haven't bothered stripping this list down any further.

Create a file in ${CLFS_TARGET_FS}/var/etc/group and populate it with the following items:

root:x:0:
bin:x:1:
sys:x:2:
kmem:x:3:
tty:x:4:
tape:x:5:
daemon:x:6:
floppy:x:7:
disk:x:8:
lp:x:9:
dialout:x:10:
audio:x:11:
video:x:12:
utmp:x:13:
usb:x:14:
cdrom:x:15:

Similar to the user, a group entry is a : separated list with the following fields:

root:x:0:,,,
[--] | | [-]
|    | |  +---> 4. Group List
|    | +------> 3. Group ID
|    +--------> 2. Password
+-------------> 1. Group Name
  1. Group Name - a name for the group.
  2. Group ID - a unique group id for this group
  3. Password - generally unused group password (I don't know if I've ever seen this used)
  4. Group List - A comma separated list of user names who are members of this group

lastlog file

A few programs we will be running make use of a file called the lastlog which tracks information about who was logged into the system and when. We should create this file and make sure they will have permissions to read and write it:

touch ${CLFS_TARGET_FS}/var/log/lastlog
chmod -v 664 ${CLFS_TARGET_FS}/var/log/lastlog

LibGCC

When we built musl, we created the libgcc library binaries. These ended up in ${CLFS}/cross-tools because this is where our cross tools exist. However, we will also need to make the shared library available to programs that run on our LFS system. To do so, all we have to do is copy the shared library definition from our ${CLFS}/cross-tools library into our ${CLFS_TARGET_FS}/lib

While we are doing it, we can make use of the ${STRIP} utility to remove any unnecessary sections for the runtime library so it's smaller.

cp -v ${CLFS}/cross-tools/${CLFS_TARGET}/${CLFS_TARGET}/lib/libgcc_s.so.1 \
      ${CLFS_TARGET_FS}/lib/
${STRIP} ${CLFS_TARGET_FS}/lib/libgcc_s.so.1

BusyBox

Since we are going to be running on a relatively small device, rather than installing full sized fully featured unix GNU utliity apps - instead we use a a suite of tools called BusyBox which provides the essential functions for many of the utilities we are used to (ls for example), with a lighter feature set but much smaller in size.

Building

Download the busybox source into the ${CLFS}/sources directory, unzip it, and enter it.

wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2
tar xvf busybox-1.35.0.tar.gz2
cd busybox-1.35.0

Create a default "sane" configuration.

make ARCH=${CLFS_ARCH} defconfig

Then build

make ARCH=${CLFS_ARCH} CROSS_COMPILE="${CLFS_TARGET}-"

Note how we pass a prefix to CROSS_COMPILE such that instead of using gcc it would use ${CLFS_TARGET}-gcc which should use our cross compile tools.

Install busy box to our targetfs

make ARCH=${CLFS_ARCH} CROSS_COMPILE="${CLFS_TARGET}-" CONFIG_PREFIX=${CLFS_TARGET_FS} install

One last detail is that we are going to need the depmod.pl script from the busy box source tree in our cross compiling bin directory - we'll end up using it later.

# from ${CLFS}/sources/bosybox-1.35.0
cp -v ${CLFS}/sources/busybox-1.35.0/examples/depmod.pl ${CLFS}/cross-tools/${CLFS_TARGET}/bin
# set appropriate permissions
chmod -v 755 ${CLFS}/cross-tools/${CLFS_TARGET}/bin/depmod.pl

iana-etc

This library is used for network related protocols and services.

Grab the latest package from

https://github.com/Mic92/iana-etc/releases

This article is using 20220414

Unzip the package and copy the protocols file to ${CLFS_TARGET_FS}/etc/protocols and copy the services file to ${CLFS_TARGET_FS}/etc/services

Conclusion

We have now put together the core filesystem necessary to run our custom system, but there remain a few important steps before we can deploy it to our device. A big requirement is building the linux kernel targeting our architecture, and after that we need to configure our boot scripts so we can copy our filesystem onto an SD Card that can be booted when our device starts.