How to build a custom Red Hat kernel RPM set with Snare functionality --------------------------------------------------------------------- I. Getting and meeting the pieces --------------------------------- The first step is to start by retrieving the following pair of RPM's from a Red Hat site: kernel-2.4.20-31.9.src.rpm kernel-source-2.4.20-31.9.i386.rpm The kernel-source-2.4.20-31.9.i386.rpm file, when installed, creates a tree under /usr/src/linux-2.4.20-31.9 containing the kernel in its patched, ready to build form. The kernel-2.4.20-31.9.src.rpm file, on the other hand, contains all of the pieces required to build the Red Hat kernel RPMs the way that Red Hat themselves do it. Installing this RPM will put all of the constituent elements necessary to build the kernel RPMs under "/usr/src/redhat". /usr/src/redhat is the traditional directory used by Red Hat for processing RPM builds. Just about any Red Hat RPM that you build from a src.rpm file will put stuff under /usr/src/redhat, so you will probably want to clean things up before you install your kernel src.rpm file. If you are *sure* that you don't need any of the detritus from previous RPM processing, go ahead and do rm /usr/src/redhat/SOURCES/* rm /usr/src/redhat/SPECS/* rm /usr/src/redhat/SRPMS/* rm /usr/src/redhat/RPMS/* rm -r /usr/src/redhat/BUILD/* to clean things up, then do rpm -ivh kernel-2.4.20-31.9.src.rpm to install the RPM build pieces for the kernel. Once the src.rpm is installed, /usr/src/redhat contains the following subdirectories: BUILD/ RPMS/ SOURCES/ SPECS/ SRPMS/ The ones we care about right now are the SOURCES and SPECS directories. Ia. The SOURCES --------------- The SOURCES directory contains the original Linus Torvalds kernel source tree that Red Hat based their distribution on.. in this case, that's linux-2.4.20.tar.bz2 In addition, the SOURCES directory contains all of the patches that Red Hat has applied to their version of the Linux kernel. Many of these patches bring about major changes, relative to the base Torvaldsian kernel.. even to the point of back-porting features from the 2.5 development tree, in some cases. Each of these patches exists as a separate file in the SOURCES directory. Also in the SOURCES directory are the config files which contain the configuration options used in the normal Linux kernel build process, according to the variety of kernel being built. When you download a Red Hat kernel RPM built optimized for the i686 processor family with SMP enabled, you're getting a build configured from the kernel-2.4.20-i686-smp.config file in the SOURCES directory. Anyway. The SOURCES directory contains the raw elements for building your customized kernel RPMs. The SPECS directory contains the building plan. Ib. The SPECS ------------- When you installed kernel-2.4.20-31.9.src.rpm, it placed a file under /usr/src/redhat/SPECS called kernel-2.4.spec You'll notice that this name is absolutely generic. You'll need to look inside this file to see what version of Red Hat's kernel it came from. # Polite request for people who spin their own kernel rpms: # please modify the "release" field in a way that identifies # that the kernel isn't the stock RHL kernel, for example by # adding some text to the end of the version number. # %define release 31.9 %define sublevel 20 %define kversion 2.4.%{sublevel} The details of how these definitions are used in the rest of the .spec file don't really concern us, but we'll be coming back to that %define release directive before too long. The bulk of the .spec file consists of directions to the rpmbuild tool on how to build the kernel RPMs.. this includes specifying that the build starts with the linux-2.4.20.tar.bz2 file: Source0: ftp://ftp.kernel.org/pub/linux/kernel/v2.4/linux-%{kversion}.tar.bz2 Note that rpmbuild only cares about the last segment of that URL.. the rest is purely there to inform and entertain any humans reading the .spec file. The rpmbuild process, which knows how to unpack and extract files from compressed tar archives, will look for linux-2.4.20.tar.bz2 under SOURCES, just like everything else that it uses. After this master source file comes a number of other pieces that also get assembled into the working tree during the actual build. These include various shell scripts and the config files. The rpmbuild process copies all of these into the working tree. Then come all of the patches. Each patch is listed in the .spec file in the order in which it is meant to be applied. Different patches are written to be applied at different points during the process of transforming the stock Torvalds kernel sources into the sources that result from installation of the kernel-source-2.4.20-31.9.i386.rpm file. Since the patch format provides for the naming of files to be patched, each patch file applies to a different subset of files in the working directory. In the .spec file, first come the patch declarations: Patch1: patch-2.4.21-pre3.bz2 Patch2: linux-2.4.20-selected-ac-bits.patch Patch3: linux-2.4.18-unselected-ac-bits.patch Patch4: linux-2.4.18-noramfs.patch Patch5: linux-2.4.20-later-ac-updates.patch and then, much further down in the file, in a special section of the .spec file, after all of the patches are defined, they are applied: # # Patches 0 through 100 are meant for core subsystem upgrades # # update to 2.4.20-ac1 %patch1 -p1 %patch2 -p1 # and the bits we don't want removed again %patch4 -p1 %patch5 -p1 II. What We Want To Accomplish ------------------------------ Our goal in this process is to produce a new patch that we can fold into a new .src.rpm file, just like all of the other patches that Red Hat includes in their .src.rpm file. The way we'll tackle this is to take our source tree that we installed from the kernel-source-2.4.20-31.9.i386.rpm file under /usr/src/linux-2.4.20-31.9, bodily copy it to a new tree that we'll work with called /usr/src/linux-2.4.20-31.9-ARLUT so that we have the original to use as a reference. I'm old fashioned and stuck in my ways enough to use tar for this: # cd /usr/src # mkdir linux-2.4.20-31.9-ARLUT # tar cf - linux-2.4.20-31.9 | (cd linux-2.4.20-31.9-ARLUT/; tar xf -) # cd linux-2.4.20-31.9-ARLUT # mv linux-2.4.20-31.9/* . # rmdir linux-2.4.20-31.9 This leaves us with /usr/src/linux-2.4.20-31.9-ARLUT as an exact duplicate of the original /usr/src/linux-2.4.20-31.9 directory. Now we'll need to apply the Snare 0.9.6 kernel patch to it and see what breaks. If something breaks, we'll attempt to fix the patch, copy the original /usr/src/linux-2.4.20-31.9 tree to /usr/src/linux-2.4.20-31.9-ARLUT again, and re-try the patch. Once we get a seemingly clean and trustworthy patch, we'll incorporate it into the SOURCES directory as a named patch, we'll define it with its own patch number, and we'll be sure and apply it in the proper sequence. Since we're going to be generating the diff against the contents of /usr/src/linux-2.4.20-31.9 (which corresponds to the kernel tree after all of the patches in the .src.rpm file are applied), we'll need to apply the patch we make last. For now, though, we need to patch. Here's how we'll go about trying that. # cd /usr/src/ # wget http://www.intersectalliance.com/projects/Snare/Download/linux-2.4.20-24.9.patch # cd linux-2.4.20-31.9-ARLUT # patch -p1 < ../linux-2.4.20-24.9.patch And here's what we get: . . . patching file init/main.c patching file kernel/exit.c Hunk #2 succeeded at 718 (offset 1 line). patching file kernel/fork.c patching file kernel/module.c patching file kernel/sys.c patching file Makefile Hunk #1 FAILED at 1. 1 out of 4 hunks FAILED -- saving rejects to file Makefile.rej patching file net/socket.c patching file README-Snare Hm, everything appears on first glance to have worked, except for the Makefile patch. That's not a big surprise, as the Makefile usually contains a reference to the version number of the kernel being built. Let's have a look at that Makefile.rej file, shall we? *************** *** 1,7 **** VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 20 - EXTRAVERSION = -24.9custom KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) --- 1,7 ---- VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 20 + EXTRAVERSION = -24.9snare KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) ah, just so. The Snare 0.9.6 kernel patch was expecting -24.9.custom, but we are actually building from Red Hat's very last RH 9 kernel spin, so on ours the line in question is actually EXTRAVERSION = -31.9custom note that in Red Hat's usage, the .9 indicates that this is a kernel for RH9, and the 31 indicates that it is the 31st kernel that they have (built? released?) in the RH9 series So we'll just manually tweak the Makefile ourselves, in lieu of fixing the patch and re-patching.. EXTRAVERSION = -31.9ARLSNARE There, that's better. Now we've got what appears (on first glance, at least) to be a cleanly patched kernel, we'll need to reconstruct a patch that we can insert into the .src.rpm build spin, including the bit we had to tweak by hand. We can do this by simply regen'ing the patch, taking the final state of our linux-2.4.20-31.9-ARLUT directory against the original linux-2.4.20-31.9 directory. First, though, we have to make sure we've got a clean kernel tree.. when I did the patch, I got a stray kernel/exit.c.orig laying around for some reason. This .orig file will break things when we use the patch we're about to generate later, so to be sure: # cd /usr/src/linux-2.4.20-31.9-ARLUT # find . -name \*.orig -print # find . -name \*.rej -print we really shouldn't find any .rej's here.. the only reason we got the kernel/exit.c.orig is because patch wanted to warn us that the preconditions were very slightly different from what it was expecting in our patch (though not different enough for the patch not to apply) So.. # cd /usr/src/linux-2.4.20-31.9-ARLUT # find . -name \*.orig -exec rm {} \; -print will take care of that for us. Now to create our patch: # cd /usr/src # diff -NrU 3 linux-2.4.20-31.9 linux-2.4.20-31.9-ARLUT > arlsnare.patch Now we've got a patch that seems to do what we want.. it transforms the source tree built from the kernel-source-2.4.20-31.9.i386.rpm file into the source tree that we want. Now all we have to do is convince the RPM build process to incorporate this patch and build us some RPMs. III. Modifying the RPM build ---------------------------- Because we have installed the kernel-2.4.20-31.9.src.rpm file, /usr/src/redhat/SOURCES contains all of the pieces for building the Red Hat kernels, and /usr/src/redhat/SPECS contains the kernel-2.4.spec file which orchestrates the build. We'll proceed to do the kernel build by copying arlsnare.patch into /usr/src/redhat/SOURCES: # cd /usr/src # cp arlsnare.patch ./redhat/SOURCES and by creating our own variant of the .spec file for us to use: # cd /usr/src/redhat/SPECS # cp kernel-2.4.spec kernel-2.4-ARLSNARE.spec this gives us our own copy to work with. Now we'll edit the kernel-2.4.spec kernel-2.4-ARLSNARE.spec file in a couple of ways. First, we'll find the line that says %define release 31.9 and change it to say %define release 31.9.SNARE096 This will percolate through the entire build process and affect the name of the RPMs generated when we're done. Second, we need to tell the .spec file about our patch, and when to apply it. Telling it about the patch is easy, we just add the following line at the end of the section where the patches are defined: Patch19991: arlsnare.patch The patch number is arbitrary, I chose it because it's nice and high and because that number wasn't taken. Now that the .spec file knows that our patch exists, we have to tell it when to apply it during the RPM build. If you look at the .spec file, you may notice that all patches must be applied after the %prep %setup -q -n %{name}-%{version} -c cd linux-%{version} instructions are run. Again, if you look at Red Hat's file, you'll see that all the patches are applied in one great sequence, with certain patches opted out with conditionals such as Red Hat's patch721, which is meant to be applied only on x86 and AMD64 systems.. # # kksymoops - automatically decode oopses/backtraces from within the kernel %patch720 -p1 %ifarch %{all_x86} x86_64 %patch721 -p1 %endif Now, looking at the Red Hat .spec file, we'll see that once the patches are applied, we see the following: . . . # update -rmap VM to the latest %patch807 -p1 # x86-64.. when done needs to move to the right place # %patch220 -p1 # END OF PATCH APPLICATIONS cp %{SOURCE10} Documentation/ chmod +x arch/sparc*/kernel/check_asm.sh mkdir configs %ifarch %{all_x86} x86_64 cp -fv $RPM_SOURCE_DIR/kernel-%{kversion}-i?86*.config configs cp -fv $RPM_SOURCE_DIR/kernel-%{kversion}-athlon*.config configs cp -fv $RPM_SOURCE_DIR/kernel-%{kversion}-x86_64*.config configs %else %ifarch sparc sparc64 cp -fv $RPM_SOURCE_DIR/kernel-%{kversion}-sparc*.config configs %else cp -fv $RPM_SOURCE_DIR/kernel-%{kversion}-%{_target_cpu}*.config configs %endif %endif # make sure the kernel has the sublevel we know it has... perl -p -i -e "s/^SUBLEVEL.*/SUBLEVEL = %{sublevel}/" Makefile that is, that the patches are applied before the config files are copied into the kernel build tree's configs directory. Since we spun our arlsnare.patch file against a source tree that already had the config files copied into configs, we'll need to apply our patch after this section dealing with configs, since we need to patch the Red Hat config files to enable C2_AUDIT. So, right after the above %endif's, and right before the # make sure the kernel has the sublevel we know it has... we'll insert our patch. # now apply the ARL patch, after we have the config files in place %patch19991 -p1 With that done, our spec file knows about the patch, and we're ready to try and give it a spin. IV. Spinning the build ----------------------- To make the build go, just do: # cd /usr/src/redhat/SPECS # rpmbuild -ba kernel-2.4.spec kernel-2.4-ARLSNARE.spec > build.log 2>&1 & # tail -f build.log that will send all of the build output to the build.log file, which we'll monitor with the tail -f command. There's far too many things that can go wrong during an RPM spin to let all that yummy diagnostic output get lost, after all. Once we get the tail running, we'll see the rpmbuild at its work. First it removes the old /usr/src/redhat/BUILD/kernel-2.4.20 (in the event that we had previously attempted the build), and then it extracts everything from the original linux-2.4.20.tar.bz2 file into that same directory. Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.2053 + umask 022 + cd /usr/src/redhat/BUILD + LANG=C + export LANG + cd /usr/src/redhat/BUILD + rm -rf kernel-2.4.20 + /bin/mkdir -p kernel-2.4.20 + cd kernel-2.4.20 + /usr/bin/bzip2 -dc /usr/src/redhat/SOURCES/linux-2.4.20.tar.bz2 + tar -xf - Then come the application of the patches: + STATUS=0 + '[' 0 -ne 0 ']' ++ /usr/bin/id -u + '[' 0 = 0 ']' + /bin/chown -Rhf root . ++ /usr/bin/id -u + '[' 0 = 0 ']' + /bin/chgrp -Rhf root . + /bin/chmod -Rf a+rX,g-w,o-w . + cd linux-2.4.20 + echo 'Patch #1 (patch-2.4.21-pre3.bz2):' Patch #1 (patch-2.4.21-pre3.bz2): + patch -p1 -s + /usr/bin/bzip2 -d + STATUS=0 + '[' 0 -ne 0 ']' + echo 'Patch #2 (linux-2.4.20-selected-ac-bits.patch):' Patch #2 (linux-2.4.20-selected-ac-bits.patch): + patch -p1 -s + echo 'Patch #4 (linux-2.4.18-noramfs.patch):' Patch #4 (linux-2.4.18-noramfs.patch): + patch -p1 -s This will run for awhile, until it gets around to our patch at the very end: + echo 'Patch #19991 (arlsnare.patch):' Patch #19991 (arlsnare.patch): + patch -p1 -s 1 out of 4 hunks FAILED -- saving rejects to file Makefile.rej error: Bad exit status from /var/tmp/rpm-tmp.2053 (%prep) oops! Something went wrong with our patch to the Makefile! Let's see what went wrong: # cd /usr/src/BUILD/kernel-2.4.20 # find . -name \*.rej -print and we see ./linux-2.4.20/Makefile.rej which contains: *************** *** 1,7 **** VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 20 - EXTRAVERSION = -31.9custom KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) --- 1,7 ---- VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 20 + EXTRAVERSION = -31.9ARLSNARE KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) Hm! It looks like it didn't like appreciate our identification of the kernel as a 2.4.20-31.9custom one.. let's look at ./linux-2.4.20/Makefile.orig to see what it was expecting.. VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 21 EXTRAVERSION = -pre3 Hmm.. looks like Red Hat's doing something a bit strange in their build process.. that certainly isn't the Makefile we saw after installing kernel-source-2.4.20-31.9.i386.rpm .. perhaps they tweaked the Makefile that went into the kernel-source RPM by hand? No matter, we can easily generate a patch which doesn't cause us this sort of problem. Back to the original source tree: # cd /usr/src/linux-2.4.20-31.9 # vi Makefile and we'll want to change the third and fourth line of Makefile from SUBLEVEL = 20 EXTRAVERSION = -31.9custom to SUBLEVEL = 21 EXTRAVERSION = -pre3 Now when we regen the patch, it'll properly anticipate the state of the Makefile when it is applied during the RPM spin. Once again: # cd /usr/src # diff -NrU 3 linux-2.4.20-31.9 linux-2.4.20-31.9-ARLUT > arlsnare.patch and # cd /usr/src # cp arlsnare.patch redhat/SOURCES and once again # cd /usr/src/redhat/SPECS # rpmbuild -ba kernel-2.4.spec kernel-2.4-ARLSNARE.spec > build.log 2>&1 & # tail -f build.log and after the clean and rebuild starts, and the patches start to be applied, we eventually see: + echo 'Patch #19991 (arlsnare.patch):' Patch #19991 (arlsnare.patch): + patch -p1 -s + perl -p -i -e 's/^SUBLEVEL.*/SUBLEVEL = 20/' Makefile + find . -name '*.orig' -exec rm -fv '{}' ';' No problems reported with the patch! Hurrah! Now all we have to do is wait however many hours for the build to nproceed. I'm going to let it run while I go to dinner, I think! ;-) Well, wait, I can give you a preview of what's next.. V. Spin it again, Sam ---------------------- Note that this is just the first step. Once you've got an rpmbuild -ba to go with the default targets, you need to do the following to build the other RPMs that come from Red Hat: # cd /usr/src/redhat/SPECS # rpmbuild -ba --target i586 kernel-2.4-ARLSNARE.spec > build2.log 2>&1 & # tail -f build2.log # cd /usr/src/redhat/SPECS # rpmbuild -ba --target i686 kernel-2.4-ARLSNARE.spec > build3.log 2>&1 & # tail -f build3.log # cd /usr/src/redhat/SPECS # rpmbuild -ba --target athlon kernel-2.4-ARLSNARE.spec > build4.log 2>&1 & # tail -f build4.log VI. The final product --------------------- In the end, you should find the RPMs you seek in /usr/src/redhat/RPMS, and a .src.rpm that can be used to start the process all over again in /usr/src/redhat/SRPMS. Once you've got your hands on the appropriate kernel rpm, just follow the instructions at ftp://ftp.arlut.utexas.edu/pub/snare/redhat9/INSTALL in order to put it in place and see how it goes. -- Jonathan Abbey jonabbey@ Applied Research Laboratories The University of Texas at Austin GPG Key: 71767586 at keyserver pgp.mit.edu, http://www.ganymeta.org/workkey.gpg