Thursday, April 12, 2007

Unleash the Ramdisk Within [Ubuntu/Debian]

If you check out your grub configuration file (menu.1st) you'll see something similar to this:

title Ubuntu, kernel 2.6.15-28-686
root (hd0,1)
kernel /boot/vmlinuz-2.6.15-28-686 root=/dev/sda2 ro quiet splash
initrd /boot/initrd.img-2.6.15-28-686
savedefault
boot
Now initrd's (initial ramdisks) were the bread and butter technology I used to build Trinux. Although it says initrd it is actually an initrams, a kernel feature introduced in late 2.5 . meaning it is available in the 2.6 kernel you are probably running.

What is this file and how is it created?

root@gx620:/boot# file initrd.img-2.6.15-28-686
initrd.img-2.6.15-28-686: gzip compressed data, from Unix, max compression

So it can be gunzipped and un-cpio'd (-vid are the options, do an "info cpio") and you can get whats inside. This file is created by /usr/sbin/mkinitramfs and you can create you own. Let's say you wanted to create a new one with the latest kernel (2.6.20.2, right now) to check out some new features you would do "mkinitramfs -o my-initramfs 2.6.20.2" based on:

Usage: /usr/sbin/mkinitramfs [OPTION]... <-o outfile> [version]

Options:
-d confdir Specify an alternative configuration directory.
-k Keep temporary directory used to make the image.
-o outfile Write to outfile.
-r root Override ROOT setting in mkinitrd.conf.


One of the really cool things about this script is the copy_exec function which adds files to the new ramdisk image, automatically populating /lib and /usr/lib (or whatever) which the required dynamic libraries.


copy_exec /sbin/modprobe /sbin
copy_exec /sbin/depmod /sbin
copy_exec /sbin/rmmod /sbin
mkdir -p ${DESTDIR}/etc/modprobe.d
cp -a /etc/modprobe.d/* ${DESTDIR}/etc/modprobe.d

/sbin/rmmod /sbinmkdir -p ${DESTDIR}/etc/modprobe.dcp -a /etc/modprobe.d/* ${DESTDIR}/etc/modprobe.d


As you can see, the module utilities aren't terribly interesting, but even a simple tool like wget depends on a quite a few libraries:


root@gx620:/usr/share/initramfs-tools# ldd /sbin/insmod
linux-gate.so.1 => (0xffffe000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7dac000)
/lib/ld-linux.so.2 (0xb7eed000)
root@gx620:/usr/share/initramfs-tools# ldd /usr/bin/wget
linux-gate.so.1 => (0xffffe000)
libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0xb7fce000)
librt.so.1 => /lib/tls/i686/cmov/librt.so.1 (0xb7fc6000)
libssl.so.0.9.8 => /usr/lib/i686/cmov/libssl.so.0.9.8 (0xb7f88000)
libcrypto.so.0.9.8 => /usr/lib/i686/cmov/libcrypto.so.0.9.8 (0xb7e59000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7d2a000)
/lib/ld-linux.so.2 (0xb7fe3000)
libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb7d18000)
libz.so.1 => /usr/lib/libz.so.1 (0xb7d04000)

This took me ages to do with Trinux.

But how does the new system boot? Instead of a good old linuxrc (used by initrd) initramfs uses an init shell script which you can see in /usr/share/initramfs-tools/init. If you look near the end, you can see where the "real" init is called


root@gx620:/usr/share/initramfs-tools# tail -f init
while [ ! -x ${rootmnt}${init} ]; do
panic "Target filesystem doesn't have ${init}"
done
maybe_break init

# Unset the problematic debug variable and chain to real filesystem
unset debug
exec run-init ${rootmnt} ${init} "$@" <${rootmnt}/dev/console >${rootmnt}/dev/console


If you comment out these last few lines and add a /bin/sh you could (and then rebuild with mkinitramfs.

I'll be documenting more of this on my wiki and as part of ubuntutrinux, so stay tuned!

No comments: