mirror of
https://github.com/MichielDerhaeg/build-linux.git
synced 2025-09-03 21:22:36 +02:00
71
README.md
71
README.md
@@ -87,8 +87,9 @@ Busybox Userspace
|
|||||||
All these tools you know and love like ``ls``, ``echo``, ``cat`` ``mv``, and
|
All these tools you know and love like ``ls``, ``echo``, ``cat`` ``mv``, and
|
||||||
``rm`` and so on are commonly referred to as the 'coreutils'. Busybox has that
|
``rm`` and so on are commonly referred to as the 'coreutils'. Busybox has that
|
||||||
and alot more, like utilities from ``util-linux`` so we can do stuff like
|
and alot more, like utilities from ``util-linux`` so we can do stuff like
|
||||||
``mount`` and even a complete init system. Basicly most tools to expect to be
|
``mount`` and even a complete init system. Basically, it contains most tools
|
||||||
present on a Linux system only are these somewhat simplified.
|
you expect to be present on a Linux system, except they are a slightly
|
||||||
|
simplified version of the regular ones.
|
||||||
|
|
||||||
You can get the source from [busybox.net](https://busybox.net/). They also
|
You can get the source from [busybox.net](https://busybox.net/). They also
|
||||||
provide prebuilt binaries which will do just fine for most use-cases. But just
|
provide prebuilt binaries which will do just fine for most use-cases. But just
|
||||||
@@ -110,7 +111,7 @@ The C standard library is more important to the operating system than you might
|
|||||||
think. It provides some useful functions and an interface to the kernel. But it
|
think. It provides some useful functions and an interface to the kernel. But it
|
||||||
also handles DNS requests and provides a dynamic linker. We don't really have to
|
also handles DNS requests and provides a dynamic linker. We don't really have to
|
||||||
pay attention to any of this, we can just statically link the one we are using
|
pay attention to any of this, we can just statically link the one we are using
|
||||||
right know which is probably 'glibc'. This means the following part is optional.
|
right now which is probably 'glibc'. This means the following part is optional.
|
||||||
But I thought this would make it more interesting and it also makes us able to
|
But I thought this would make it more interesting and it also makes us able to
|
||||||
build smaller binaries.
|
build smaller binaries.
|
||||||
|
|
||||||
@@ -120,33 +121,33 @@ on Ubuntu or simply ``musl`` on Arch Linux. Now we can link binaries to musl
|
|||||||
instead of glibc by using ``musl-gcc`` instead of ``gcc``.
|
instead of glibc by using ``musl-gcc`` instead of ``gcc``.
|
||||||
|
|
||||||
Before we can build busybox with musl, we need sanitized kernel headers for use
|
Before we can build busybox with musl, we need sanitized kernel headers for use
|
||||||
with musl. You get get that from [this github
|
with musl. You get that from [this github
|
||||||
repo](https://github.com/sabotage-linux/kernel-headers). And set
|
repo](https://github.com/sabotage-linux/kernel-headers). And set
|
||||||
``CONFIG_EXTRA_CFLAGS`` in your busybox config to
|
``CONFIG_EXTRA_CFLAGS`` in your busybox config to
|
||||||
``CONFIG_EXTRA_CFLAGS="-I/path/to/kernel-headers/x86_64/include"`` to use them.
|
``CONFIG_EXTRA_CFLAGS="-I/path/to/kernel-headers/x86_64/include"`` to use them.
|
||||||
Obviously change ``/path/to`` to the location where you put the headers repo,
|
Obviously change ``/path/to`` to the location where you put the headers repo,
|
||||||
can be relative from within the busybox source directory.
|
which can be relative from within the busybox source directory.
|
||||||
|
|
||||||
If you run ``make CC=musl-gcc`` now, the busybox executable will be
|
If you run ``make CC=musl-gcc`` now, the busybox executable will be
|
||||||
significantly smaller because we are statically linking a much smaller libc.
|
significantly smaller because we are statically linking a much smaller libc.
|
||||||
|
|
||||||
Be aware that even though there is a libc standard, musl is not always a
|
Be aware that even though there is a libc standard, musl is not always a
|
||||||
drop-in replacement from glibc if the application you're compiling uses glibc
|
drop-in replacement for glibc if the application you're compiling uses glibc
|
||||||
specific things.
|
specific things.
|
||||||
|
|
||||||
Building the Disk Image
|
Building the Disk Image
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Installing a OS on a file instead of a real disk complicates things but this
|
Installing an OS on a file instead of a real disk complicates things but this
|
||||||
makes development and testing easier.
|
makes development and testing easier.
|
||||||
|
|
||||||
So let's start by allocating a new file of size 100M by doing ``fallocate -l100M
|
So let's start by allocating a new file of size 100M by doing ``fallocate -l100M
|
||||||
image``(some distro's don't have ``fallocate`` so you can do ``dd if=/dev/zero
|
image``(some distro's don't have ``fallocate`` so you can do ``dd if=/dev/zero
|
||||||
of=image bs=1M count=100`` instead). And then we format it like we would format
|
of=image bs=1M count=100`` instead). And then we format it like we would format
|
||||||
a disk with ``fdisk image``. It automatically creates a MBR partition table for
|
a disk with ``fdisk image``. It automatically creates an MBR partition table for
|
||||||
us and we'll create just one partition filling the whole image by pressing 'n' and
|
us and we'll create just one partition filling the whole image by pressing 'n' and
|
||||||
afterwards just use the default options for everything and keep spamming 'enter'
|
afterwards just use the default options for everything and keep spamming 'enter'
|
||||||
untill you're done. Finally press 'w' exit and to write the changes to the
|
until you're done. Finally press 'w' exit and to write the changes to the
|
||||||
image.
|
image.
|
||||||
```bash
|
```bash
|
||||||
$ fdisk image
|
$ fdisk image
|
||||||
@@ -191,8 +192,8 @@ my case. Let's make a filesystem on it.
|
|||||||
```bash
|
```bash
|
||||||
$ mkfs.ext4 /dev/loop0p1
|
$ mkfs.ext4 /dev/loop0p1
|
||||||
```
|
```
|
||||||
If you want to use something else than ext4, be sure to enable it when
|
If you want to use something other than ext4, be sure to enable it when
|
||||||
configuring your kernel. Now that we have done that, we can mount it start
|
configuring your kernel. Now that we have done that, we can mount it and start
|
||||||
putting everything in place.
|
putting everything in place.
|
||||||
```bash
|
```bash
|
||||||
$ mkdir image_root
|
$ mkdir image_root
|
||||||
@@ -217,7 +218,7 @@ We'll copy our binaries over.
|
|||||||
$ cp /path/to/busybox usr/bin/busybox
|
$ cp /path/to/busybox usr/bin/busybox
|
||||||
$ cp /path/to/bzImage boot/bzImage
|
$ cp /path/to/bzImage boot/bzImage
|
||||||
```
|
```
|
||||||
You can call every busybox utility by supplying the utility as argument, like
|
You can call every busybox utility by supplying the utility as an argument, like
|
||||||
so: ``busybox ls --help``. But busybox also detects by what name it is called
|
so: ``busybox ls --help``. But busybox also detects by what name it is called
|
||||||
and then executes that utility. So you can put symlinks for each utility and
|
and then executes that utility. So you can put symlinks for each utility and
|
||||||
busybox can figure out which utility you want by the symlink's name.
|
busybox can figure out which utility you want by the symlink's name.
|
||||||
@@ -231,7 +232,7 @@ These symlinks might be incorrect from outside the system because of the
|
|||||||
absolute path, but they work just fine from within the booted system.
|
absolute path, but they work just fine from within the booted system.
|
||||||
|
|
||||||
Lastly, we'll copy some files from ``../filesystem`` to the image that will be
|
Lastly, we'll copy some files from ``../filesystem`` to the image that will be
|
||||||
some use to us later.
|
of some use to us later.
|
||||||
```bash
|
```bash
|
||||||
$ cp ../filesystem/{passwd,shadow,group,issue,profile,locale.sh,hosts,fstab} etc
|
$ cp ../filesystem/{passwd,shadow,group,issue,profile,locale.sh,hosts,fstab} etc
|
||||||
$ install -Dm755 ../filesystem/simple.script usr/share/udhcpc/default.script
|
$ install -Dm755 ../filesystem/simple.script usr/share/udhcpc/default.script
|
||||||
@@ -245,7 +246,7 @@ a binary keymap file I use for belgian azerty.
|
|||||||
The Boot Loader
|
The Boot Loader
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The next step is to install the bootloader, the program that loads our kernel in
|
The next step is to install the bootloader - the program that loads our kernel in
|
||||||
memory and starts it. For this we use GRUB, one of the most widely used
|
memory and starts it. For this we use GRUB, one of the most widely used
|
||||||
bootloaders. It has a ton of features but we are going to keep it very simple.
|
bootloaders. It has a ton of features but we are going to keep it very simple.
|
||||||
Installing it is very simple, we just do this:
|
Installing it is very simple, we just do this:
|
||||||
@@ -256,7 +257,7 @@ grub-install --modules=part_msdos \
|
|||||||
/dev/loop0
|
/dev/loop0
|
||||||
```
|
```
|
||||||
The ``--target=i386-pc`` tells grub to use the simple msdos MBR bootloader. This
|
The ``--target=i386-pc`` tells grub to use the simple msdos MBR bootloader. This
|
||||||
is often the default but this can vary from machine to machine so you better
|
is often the default, but this can vary from machine to machine so you better
|
||||||
specify it here. The ``--boot-directory`` options tells grub to install the grub
|
specify it here. The ``--boot-directory`` options tells grub to install the grub
|
||||||
files in /boot inside the image instead of the /boot of your current system.
|
files in /boot inside the image instead of the /boot of your current system.
|
||||||
``--modules=part_msdos`` is a workaround for a bug in Ubuntu's grub. When you
|
``--modules=part_msdos`` is a workaround for a bug in Ubuntu's grub. When you
|
||||||
@@ -265,7 +266,7 @@ think it needs to support msdos partition tables and won't be able to find the
|
|||||||
root partition.
|
root partition.
|
||||||
|
|
||||||
Now we just have to configure grub and then our system should be able to boot.
|
Now we just have to configure grub and then our system should be able to boot.
|
||||||
This basicly means telling grub how to load the kernel. This config is located
|
This basically means telling grub how to load the kernel. This config is located
|
||||||
at ``boot/grub/grub.cfg`` (some distro's use ``/boot/grub2``). This file needs
|
at ``boot/grub/grub.cfg`` (some distro's use ``/boot/grub2``). This file needs
|
||||||
to be created first, but before we do that, we need to figure something out
|
to be created first, but before we do that, we need to figure something out
|
||||||
first. If you look at ``/proc/cmdline`` on your own machine you might see
|
first. If you look at ``/proc/cmdline`` on your own machine you might see
|
||||||
@@ -277,14 +278,14 @@ BOOT_IMAGE=/boot/vmlinuz-4.4.0-71-generic root=UUID=83066fa6-cf94-4de3-9803-ace8
|
|||||||
These are the arguments passed to your kernel when it's booted. The 'root'
|
These are the arguments passed to your kernel when it's booted. The 'root'
|
||||||
option tells our kernel which device holds the root filesystem that needs to be
|
option tells our kernel which device holds the root filesystem that needs to be
|
||||||
mounted at '/'. The kernel needs to know this or it won't be able to boot. There
|
mounted at '/'. The kernel needs to know this or it won't be able to boot. There
|
||||||
are different ways of identifying your the root filesystem. Using a UUID is a
|
are different ways of identifying your root filesystem. Using a UUID is a
|
||||||
good way because it is a unique identifier for the filesystem generated when you
|
good way because it is a unique identifier for the filesystem generated when you
|
||||||
do ``mkfs``. The issue with using this is that the kernel doesn't really
|
do ``mkfs``. The issue with using this is that the kernel doesn't really
|
||||||
support it because it depends on the implementation of the filesystem. This
|
support it because it depends on the implementation of the filesystem. This
|
||||||
works on your system because it uses an initramfs. But we can't use it now. We
|
works on your system because it uses an initramfs, but we can't use it now. We
|
||||||
could do ``root=/dev/sda1``, this will probably work but it has some other problems.
|
could do ``root=/dev/sda1``, this will probably work but it has some other problems.
|
||||||
The 'a' in 'sda' is can depend on the order the bios will load the disk and this
|
The 'a' in 'sda' depends on the order the bios will load the disk and this
|
||||||
can change when you add a new disk or sometimes the order can change randomly.
|
can change when you add a new disk, or for a variety of other reasons.
|
||||||
Or when you use a different type of interface/disk it can be something entirely
|
Or when you use a different type of interface/disk it can be something entirely
|
||||||
different. So we need something more robust. I suggest we use the PARTUUID. It's
|
different. So we need something more robust. I suggest we use the PARTUUID. It's
|
||||||
a unique id for the partition (and not the filesystem like UUID) and this is a
|
a unique id for the partition (and not the filesystem like UUID) and this is a
|
||||||
@@ -294,9 +295,9 @@ a GPT thing). We'll find the id like this:
|
|||||||
$ fdisk -l ../image | grep "Disk identifier"
|
$ fdisk -l ../image | grep "Disk identifier"
|
||||||
Disk identifier: 0x4f4abda5
|
Disk identifier: 0x4f4abda5
|
||||||
```
|
```
|
||||||
Then we drop the 0x and append the partition number as two digit hexidecimal. A
|
Then we drop the 0x and append the partition number as two digit hexidecimal. An
|
||||||
MBR only has 4 partitions max so that it's hexidecimal or decimal doesn't really
|
MBR only has 4 partitions max so that it's hexidecimal or decimal doesn't really
|
||||||
matter but that's what the standard says. So the grub.cfg should look like this:
|
matter, but that's what the standard says. So the grub.cfg should look like this:
|
||||||
```
|
```
|
||||||
linux /boot/bzImage quiet init=/bin/sh root=PARTUUID=4f4abda5-01
|
linux /boot/bzImage quiet init=/bin/sh root=PARTUUID=4f4abda5-01
|
||||||
boot
|
boot
|
||||||
@@ -352,14 +353,14 @@ everything down because it's the first and last process to live.
|
|||||||
|
|
||||||
This also makes this ``init`` process very suitable to start and manage services
|
This also makes this ``init`` process very suitable to start and manage services
|
||||||
as is the case with the very common ``sysvinit`` and the more modern
|
as is the case with the very common ``sysvinit`` and the more modern
|
||||||
``systemd``. But this isn't strictly necessary and some other process can cary
|
``systemd``. But this isn't strictly necessary and some other process can carry
|
||||||
the burden of service supervision, which is the case with the
|
the burden of service supervision, which is the case with the
|
||||||
[runit](http://smarden.org/runit/)-like ``init`` that is included with
|
[runit](http://smarden.org/runit/)-like ``init`` that is included with
|
||||||
``busybox``.
|
``busybox``.
|
||||||
|
|
||||||
Unless you passed the ``rw`` kernel parameter the root filesystem is mounted as
|
Unless you passed the ``rw`` kernel parameter the root filesystem is mounted as
|
||||||
read-only. So before we can make changes to our running system we have to
|
read-only. So before we can make changes to our running system we have to
|
||||||
remount it as read-write first. And before we can do any mounting at all we have
|
remount it as read-write first. Before we can do any mounting at all we have
|
||||||
to mount the ``proc`` pseudo filesystem that serves as an interface to kernel.
|
to mount the ``proc`` pseudo filesystem that serves as an interface to kernel.
|
||||||
```bash
|
```bash
|
||||||
$ mount -t proc proc /proc
|
$ mount -t proc proc /proc
|
||||||
@@ -370,14 +371,14 @@ $ mount / -o remount,rw
|
|||||||
are not confortable using either of those you could always shutdown the VM,
|
are not confortable using either of those you could always shutdown the VM,
|
||||||
mount the image again, and use your favorite text editor on your host machine.
|
mount the image again, and use your favorite text editor on your host machine.
|
||||||
|
|
||||||
If you don't use an qwerty keyboard you might have noticed that the VM uses a
|
If you don't use a qwerty keyboard, you might have noticed that the VM uses a
|
||||||
qwerty layout which is the default, you might want to change it to azerty with
|
qwerty layout as this is the default. You might want to change it to azerty with
|
||||||
``loadkmap < /usr/share/keymaps/be-latin1.bmap``. You can dump the layout you
|
``loadkmap < /usr/share/keymaps/be-latin1.bmap``. You can dump the layout you
|
||||||
are using on your host machine with ``busybox dumpkmap > keymap.bmap`` in a
|
are using on your host machine with ``busybox dumpkmap > keymap.bmap`` in a
|
||||||
virtual console (not in X) and put this on your image instead.
|
virtual console (not in X) and put this on your image instead.
|
||||||
|
|
||||||
First, we'll create a script that handles the initialisation of the system
|
First, we'll create a script that handles the initialisation of the system
|
||||||
itself like mounting filesystems and configuring devices, etc. You could call it
|
itself (like mounting filesystems and configuring devices, etc). You could call it
|
||||||
``startup`` and put it in the ``/etc/init.d`` directory (create this first).
|
``startup`` and put it in the ``/etc/init.d`` directory (create this first).
|
||||||
Don't forget to ``chmod +x`` this file when you're done.
|
Don't forget to ``chmod +x`` this file when you're done.
|
||||||
```bash
|
```bash
|
||||||
@@ -457,17 +458,17 @@ after ``sysinit`` and will be restarted when they exit. We'll put some
|
|||||||
it's correct. If you don't care for user login and passwords, you could instead
|
it's correct. If you don't care for user login and passwords, you could instead
|
||||||
of the ``getty``'s do ``::askfirst:-/bin/sh``. ``askfirst`` does the same as
|
of the ``getty``'s do ``::askfirst:-/bin/sh``. ``askfirst`` does the same as
|
||||||
``respawn`` but asks you to press enter first. If no tty is specified it will
|
``respawn`` but asks you to press enter first. If no tty is specified it will
|
||||||
figure out what the console is. And the ``-`` infront of ``-/bin/sh`` means that
|
figure out what the console is. The ``-`` infront of ``-/bin/sh`` means that
|
||||||
the shell is started as a login shell. ``/bin/login`` usually does this for us
|
the shell is started as a login shell. ``/bin/login`` usually does this for us
|
||||||
but we have to specify it here. Starting the shell as a login shell means that
|
but we have to specify it here. Starting the shell as a login shell means that
|
||||||
it configures certain things it otherwise assumes already to be configured. E.g.
|
it configures certain things it otherwise assumes already to be configured. E.g.
|
||||||
it sources ``/etc/profile``.
|
it sources ``/etc/profile``.
|
||||||
|
|
||||||
We can now start our system with ``init``. You can remove the ``init=/bin/sh``
|
We can now start our system with ``init``. You can remove the ``init=/bin/sh``
|
||||||
entry in ``/boot/grub/grub.cfg`` because it defaults to ``/sbin/init``. And if
|
entry in ``/boot/grub/grub.cfg`` because it defaults to ``/sbin/init``. If
|
||||||
you reboot the system you should see a login screen. But if you run ``reboot``,
|
you reboot the system you should see a login screen. But if you run ``reboot``,
|
||||||
you'll notice it won't do anything. This happens because normally ``reboot``
|
you'll notice it won't do anything. This happens because normally ``reboot``
|
||||||
tells the running ``init`` to reboot. You know, the ``init`` that isn't running
|
tells the running ``init`` to reboot. You know - the ``init`` that isn't running
|
||||||
right now. So we have two options, we could run ``reboot -f`` which skips the
|
right now. So we have two options, we could run ``reboot -f`` which skips the
|
||||||
``init``, or we could do this:
|
``init``, or we could do this:
|
||||||
```bash
|
```bash
|
||||||
@@ -489,10 +490,10 @@ from how the more common ``sysvinit`` does things but it'll give you a
|
|||||||
feel for which problems it's supposed to solve and how.
|
feel for which problems it's supposed to solve and how.
|
||||||
|
|
||||||
A basic service consists of a directory containing a ``run`` executable, usually
|
A basic service consists of a directory containing a ``run`` executable, usually
|
||||||
a script. This ``run`` script usually starts the daemon and doesn't exit untill
|
a script. This ``run`` script usually starts the daemon and doesn't exit until
|
||||||
the daemon does. If ``run`` exits ``runit`` will thinks the service itself has
|
the daemon does. If ``run`` exits ``runit`` will think the service itself has
|
||||||
stopped and if it wasn't supposed to stop, ``runit`` will try to restart it. So
|
stopped and if it wasn't supposed to stop, ``runit`` will try to restart it. So
|
||||||
be careful with forking daemons. Startin the service is done with ``runsv``.
|
be careful with forking daemons. Starting the service is done with ``runsv``.
|
||||||
This is the process that actually monitors the service and restarts it if
|
This is the process that actually monitors the service and restarts it if
|
||||||
necessary. Usually you won't run it manually but doing so is useful for testing
|
necessary. Usually you won't run it manually but doing so is useful for testing
|
||||||
services.
|
services.
|
||||||
@@ -580,7 +581,7 @@ exec udhcpc -f -S
|
|||||||
$ chmod +x /etc/init.d/udhcpc/run
|
$ chmod +x /etc/init.d/udhcpc/run
|
||||||
$ ln -s /etc/init.d/udhcpc /etc/rc.d
|
$ ln -s /etc/init.d/udhcpc /etc/rc.d
|
||||||
```
|
```
|
||||||
And we're done. Yes it's that simple. Note that udhcpc just asks for a lease
|
Now we're done. Yes - it's that simple. Note that udhcpc just asks for a lease
|
||||||
from the DHCP server and that's it. When it has a lease it executes
|
from the DHCP server and that's it. When it has a lease it executes
|
||||||
``/usr/share/udhcpc/default.script`` to configure the system. We already copied
|
``/usr/share/udhcpc/default.script`` to configure the system. We already copied
|
||||||
this script to this location. This script is included with the busybox source.
|
this script to this location. This script is included with the busybox source.
|
||||||
@@ -592,4 +593,4 @@ Epilogue
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
That's it! We're done for now. Thanks for reading. I hope you learned something
|
That's it! We're done for now. Thanks for reading. I hope you learned something
|
||||||
useful, I certainly did while making this.
|
useful. I certainly did while making this.
|
||||||
|
Reference in New Issue
Block a user