Anaconda/Stage1DevelopmentGuide

From FedoraProject

Jump to: navigation, search

[[TableOfContents()]

Contents

Stage 1 Development Guide

The stage 1 portion of anaconda is almost always called the loader. The main reason for this is the actual executable is called 'loader'. The other reason is the primary task of stage 1 is to find the stage 2 portion of the installer (which is the actual installer) and run it.

The Loader Environment

Loader is a small static executable that is spawned by our own init process that loads from the initial ramdisk. The reason for the static executable is to ensure the initial ramdisk is as small as it can be. The same initrd is used for booting from removable media and the network. If it were too large, it might exceed the limitations imposed by various boot loaders out there.

To start, we need an initrd to look at. The initrd is built during tree composition by release engineering. The scripts in the anaconda/scripts subdirectory handle creating the initrd. For this example, we will take the built initrd from RHEL 5.1 Server on i386. We take it from the images/pxelinux subdirectory in the tree.

Looking at the initrd.img for the RHEL 5.1 Server i386 release, we see a standard directory tree with some modifications:

./   .buildstamp  bin@  etc/   modules/  sbin/     sys/  var/
../  .profile     dev/  init@  proc/     selinux/  tmp/

The init symlink points to /sbin/init. The bin symlink points to /sbin. The /sys, /dev, /proc, /tmp, and /selinux are all empty on the initrd. The /var directory contains a number of empty subdirectories necessary for lock files and control files. Let's examine the contents more closely starting with the /etc directory:

./   arch        lang-table  mtab@   screenfont.gz
../  keymaps.gz  loader.tr   passwd  terminfo/

Fairly simple. The scripts in anaconda/scripts/ are how these files are created or copied in place. The mtab symlink points to /proc/mounts. The terminfo subdirectory contains only a subset of terminfo files we need for the loader environment (we use the newt screen widget library, which requires certain terminal types).

Looking at the /modules directory, we see:

./  ../  module-info  modules.alias  modules.cgz  modules.dep  pci.ids

All of the loadable modules are stored in modules.cgz, which is a gzipped cpio archive. The loader will probe for devices using kudzu and load appropriate modules. The module-info file is part of the loader source code and is managed by hand.

The last directory of importance is the /sbin one, which contains init and loader. It also contains 3 symlinks to loader: insmod, modprobe, and rmmod. There are also 3 symlinks to init: halt, poweroff, and reboot. The only actual executables in the initrd are init and loader.

There are two files of importance at the main level. The .buildstamp and .profile files.

Debugging A SIGSEGV From Loader

"Loader received SIGSEGV" is a popular bug report title. If loader crashes, users are left with no other options. There's no recovery. All they can do is report the stacktrace. With a little debugging, we can fix the problem relatively easily.

When loader receives SIGSEGV, it will look like this:

loader received SIGABRT!  Backtrace:
/sbin/loader(loaderSegvHandler+0xa0)[0x804fcb0] 
[0x130400] 
/lib/libc.so.6(gsignal+0x46)[0x44c746] 
/lib/libc.so.6(abort+0x188)[0x44e1b8] 
/lib/libc.so.6[0x490cc3] 
/lib/libc.so.6(cfree+0x35)[0x492d35] 
/sbin/loader[0x805ea40] 
/sbin/loader[0x805efb9] 
/sbin/loader(mountHardDrive+0x4c7)[0x805f5c7] 
/sbin/loader(main+0xfce)[0x80512de] 
/lib/libc.so.6(__libc_start_main+0xe6)[0x4375d6] 
/sbin/loader[0x804dd51] 
install exited abnormally [1/1] 
disabling swap...
unmounting filesystems...
/proc umount failed (16)
/dev/pts done
/sys done
/tmp/ramfs done
you may safely reboot your system

Assuming the user copied this information in to the bug report, you can debug it. There is no way for the user to save this data. They have to write it down by hand or take a picture of the screen. You may have to ask them to go back and collect the backtrace, but be prepared for users who won't really be interested in that.

The tools you'll need to debug this SIGSEGV are eu-addr2line and a copy of the anaconda-debuginfo for that tree. The bug report should indicate the release it happened on. Get the anaconda-debuginfo package for that release and install it on your system. Next, make sure you have elfutils installed.

NOTE: If the bug reported is for a RHEL nightly build or rawhide and it's been a while since the bug was reported, chances are you won't be able to get debuginfo for that release. In those cases, you either need to recreate the SIGSEGV again using a current tree or ask the user to test on a later tree and then grab the debuginfo when you can.

With anaconda-debuginfo installed, run eu-addr2line on the debug loader binary:

eu-addr2line -e /usr/lib/debug/usr/lib/anaconda-runtime/loader/loader.debug

Type in each of the addresses given in the traceback. eu-addr2line will give you file names and line numbers so you know where to go in the code.

Testing A Loader Fix

Once you've debugged a problem in loader and have a fix in the code, you'll want to test it. That involves compiling a new loader and init binary, updating the initrd.img, and finally booting it.

First, compile the new executables. Make sure you have all of the BuildRequires installed on your system. They are listed in the anaconda.spec file. Next, run a 'make clean' to clear out any stale object files. And finally run 'make' to build a new loader and init binary. After you run make once, you'll only have to run 'make -C loader2 loader init' to rebuild just the loader and init binaries.

With the new binaries built, you'll want to update the initrd.img file you were using to boot the tree. The following script can be run from the loader2 subdirectory passing the path to the initrd.img to update. It assumes you have new loader and init binaries already built.

#!/bin/sh

if [ $# -ne 1 ] ; then
echo "Usage: $0 <initrd>"
exit 1
fi

CWD=$(pwd)

INITRD=$(readlink -f $1)
TMPINITRD=initrd.img-$$

if [ ! -f $CWD/loader ] ; then
echo "Missing loader"
exit 1
fi

if [ ! -f $CWD/init ] ; then
echo "Missing init"
exit 1
fi

tmpdir=$(mktemp -d)
pushd $tmpdir
gzip -dc $INITRD |cpio -id
strip -s -o sbin/loader $CWD/loader
strip -s -o sbin/init $CWD/init
(find . |cpio -c -o |gzip -9) > $CWD/$TMPINITRD
mv $CWD/$TMPINITRD $INITRD
popd
rm -rf $tmpdir

The trick to testing a new loader is to make sure you have a matching installation tree and initrd.img file. Each time you use a new tree, you'll need to make sure you are using the correct initrd. The .buildstamp file is what matches the tree to the initrd.

See the stage2 document for more information on testing changes to an installable tree.