Richard W.M. Jones
2016-Feb-17 13:31 UTC
[Libguestfs] [PATCH supermin 0/2] Allow an alternate libc to be used for init.
Allow an alternate libc, such as dietlibc, to be used for the init binary in the supermin appliance. Rich.
Richard W.M. Jones
2016-Feb-17 13:31 UTC
[Libguestfs] [PATCH supermin 1/2] init: Uncompress modules before adding them to the mini initrd.
When building the mini initrd, previously we copied the modules into the initrd as-is, so for example if the module was xz-compressed, we copied the foo.ko.xz file to the initrd. This requires that the mini init binary is linked to zlib & lzma, so that it knows how to uncompress these modules when insmoding them at boot time. Also since the init is statically linked, it required _static_ versions of these libraries. This changes things so that the modules are uncompressed in the mini initrd, so they are a little bit larger, but the init binary no longer needs to be statically linked to zlib & lzma. The init binary is smaller (966K -> 837K), but because we are storing uncompressed modules in the mini initrd, the initrd as a whole becomes larger (1.4M -> 2.6M) However there are benefits to this change: - The code in the init binary is much simpler. - Removes the dependency on static zlib & lzma. - We can use an alternate libc to make a much smaller init binary (see next commit). --- README | 4 +- configure.ac | 74 ++------------------------- src/Makefile.am | 1 - src/config.ml.in | 2 + src/ext2_initrd.ml | 28 ++++++++++- src/init.c | 145 ----------------------------------------------------- 6 files changed, 34 insertions(+), 220 deletions(-) diff --git a/README b/README index 34949a2..56a28f6 100644 --- a/README +++ b/README @@ -97,9 +97,9 @@ are building: qemu >= 0.13 kernel >= 2.6.36 - zlib (statically linked) - if your kernel uses gzipped modules + gunzip (command) - if your kernel uses gzipped modules - xz (statically linked) - if your kernel uses xz-compressed modules + unxz (command) - if your kernel uses xz-compressed modules Building and installing ----------------------- diff --git a/configure.ac b/configure.ac index 126366b..0fe88c7 100644 --- a/configure.ac +++ b/configure.ac @@ -127,77 +127,11 @@ dnl Check for fakeroot, only used a few drivers where the host package dnl manager contains broken/unnecessary tests for root privs. AC_PATH_PROG(FAKEROOT,[fakeroot],[no]) -dnl Support for compressed input files, gzipped kernel modules. -AC_CHECK_HEADER([zlib.h],[ - AC_CHECK_LIB([z],[gzopen],[ - zlib=yes - ZLIB_LIBS=-lz +dnl Check for gunzip, only needed if you have gzip-compressed kernel modules. +AC_PATH_PROG(GUNZIP,[gunzip],[no]) - AC_MSG_CHECKING([for gzip static library]) - old_CFLAGS="$CFLAGS" - old_LDFLAGS="$LDFLAGS" - old_LIBS="$LIBS" - CFLAGS="$CFLAGS -static" - LDFLAGS="$LDFLAGS -static" - LIBS="$LIBS -lz" - AC_LINK_IFELSE([ - #include <stdio.h> - #include <stdlib.h> - #include <zlib.h> - int main () { gzFile g = gzopen ("test", "rb"); exit (g ? 1 : 0); } - ],[ - zlib_static=yes - ZLIB_STATIC_LIBS="$ZLIB_LIBS" - AC_MSG_RESULT([yes]) - ],[ - AC_MSG_RESULT([no]) - ]) - CFLAGS="$old_CFLAGS" - LDFLAGS="$old_LDFLAGS" - LIBS="$old_LIBS" - ]) -]) -if test "x$zlib" = "xyes"; then - AC_DEFINE([HAVE_ZLIB],[1],[Define if you have zlib]) - AC_SUBST([ZLIB_LIBS]) -fi -if test "x$zlib_static" = "xyes"; then - AC_DEFINE([HAVE_ZLIB_STATIC],[1],[Define if you have static zlib]) - AC_SUBST([ZLIB_STATIC_LIBS]) -fi - -dnl Support for xzed kernel modules. -AC_CHECK_HEADER([lzma.h],[ - AC_CHECK_LIB([lzma],[lzma_code],[ - AC_MSG_CHECKING([for xz static library]) - old_CFLAGS="$CFLAGS" - old_LDFLAGS="$LDFLAGS" - old_LIBS="$LIBS" - CFLAGS="$CFLAGS -static" - LDFLAGS="$LDFLAGS -static" - LIBS="$LIBS -llzma" - AC_LINK_IFELSE([ - #include <stdio.h> - #include <stdlib.h> - #include <lzma.h> - int main () { lzma_stream s = LZMA_STREAM_INIT; - exit (s.next_in == NULL ? 1 : 0); } - ],[ - lzma_static=yes - LZMA_STATIC_LIBS="-llzma" - AC_MSG_RESULT([yes]) - ],[ - AC_MSG_RESULT([no]) - ]) - CFLAGS="$old_CFLAGS" - LDFLAGS="$old_LDFLAGS" - LIBS="$old_LIBS" - ]) -]) -if test "x$lzma_static" = "xyes"; then - AC_DEFINE([HAVE_LZMA_STATIC],[1],[Define if you have static lzma]) - AC_SUBST([LZMA_STATIC_LIBS]) -fi +dnl Check for unxz, only needed if you have xz-compressed kernel modules. +AC_PATH_PROG(UNXZ,[unxz],[no]) dnl mke2fs. AC_PATH_PROG([MKE2FS],[mke2fs],[no], diff --git a/src/Makefile.am b/src/Makefile.am index 6261c86..5a601fe 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -143,7 +143,6 @@ noinst_PROGRAMS = init init_SOURCES = init.c init_CFLAGS = -static init_LDFLAGS = -static -init_LDADD = $(ZLIB_STATIC_LIBS) $(LZMA_STATIC_LIBS) CLEANFILES += ext2init-bin.S diff --git a/src/config.ml.in b/src/config.ml.in index 42cf833..19545b6 100644 --- a/src/config.ml.in +++ b/src/config.ml.in @@ -29,12 +29,14 @@ let dpkg_deb = "@DPKG_DEB@" let dpkg_query = "@DPKG_QUERY@" let dpkg_divert = "@DPKG_DIVERT@" let fakeroot = "@FAKEROOT@" +let gunzip = "@GUNZIP@" let makepkg = "@MAKEPKG@" let pacman = "@PACMAN@" let pactree = "@PACTREE@" let pacman_g2 = "@PACMAN_G2@" let rpm = "@RPM@" let rpm2cpio = "@RPM2CPIO@" +let unxz = "@UNXZ@" let urpmi = "@URPMI@" let yumdownloader = "@YUMDOWNLOADER@" let zypper = "@ZYPPER@" diff --git a/src/ext2_initrd.ml b/src/ext2_initrd.ml index b34f0e6..e49a19d 100644 --- a/src/ext2_initrd.ml +++ b/src/ext2_initrd.ml @@ -1,5 +1,5 @@ (* supermin 5 - * Copyright (C) 2009-2014 Red Hat Inc. + * Copyright (C) 2009-2016 Red Hat Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -105,8 +105,32 @@ let rec build_initrd debug tmpdir modpath initrd sprintf "cp -t %s %s" (quote initdir) (quote (modpath // modl)) in run_command cmd; + (* Uncompress the module, if the name ends in .xz or .gz. *) + let basename = Filename.basename modl in + let basename + let len = String.length basename in + if Config.unxz <> "no" && Filename.check_suffix basename ".xz" + then ( + let cmd = sprintf "%s %s" + (quote Config.unxz) + (quote (initdir // basename)) in + run_command cmd; + String.sub basename 0 (len-3) + ) + else if Config.gunzip <> "no" && + Filename.check_suffix basename ".gz" + then ( + let cmd = sprintf "%s %s" + (quote Config.gunzip) + (quote (initdir // basename)) in + run_command cmd; + String.sub basename 0 (len-3) + ) + else + basename in + (* Write module name to 'modules' file. *) - fprintf chan "%s\n" (Filename.basename modl); + fprintf chan "%s\n" basename; incr loaded ) ) set diff --git a/src/init.c b/src/init.c index 814243a..25d6bc6 100644 --- a/src/init.c +++ b/src/init.c @@ -42,14 +42,6 @@ #include <asm/unistd.h> -#ifdef HAVE_ZLIB_STATIC -#include <zlib.h> -#endif - -#ifdef HAVE_LZMA_STATIC -#include <lzma.h> -#endif - /* Maximum time to wait for the root device to appear (seconds). * * On slow machines with lots of disks (Koji running the 255 disk test @@ -102,12 +94,6 @@ main () print_uptime (); fprintf (stderr, "supermin: ext2 mini initrd starting up: " PACKAGE_VERSION -#ifdef HAVE_ZLIB_STATIC - " zlib" -#endif -#ifdef HAVE_LZMA_STATIC - " xz" -#endif "\n"); read_cmdline (); @@ -283,20 +269,6 @@ main () exit (EXIT_FAILURE); } -#if HAVE_LZMA_STATIC -static int -ends_with (const char *str, const char *suffix) -{ - if (!str || !suffix) - return 0; - size_t lenstr = strlen (str); - size_t lensuffix = strlen (suffix); - if (lensuffix > lenstr) - return 0; - return strncmp (str + lenstr - lensuffix, suffix, lensuffix) == 0; -} -#endif - static void insmod (const char *filename) { @@ -305,118 +277,6 @@ insmod (const char *filename) if (verbose) fprintf (stderr, "supermin: internal insmod %s\n", filename); -#ifdef HAVE_ZLIB_STATIC - int capacity = 64*1024; - char *buf = (char *) malloc (capacity); - int tmpsize = 8 * 1024; - char tmp[tmpsize]; - int num; - - errno = 0; - size = 0; - - if (!buf) { - perror("malloc"); - exit (EXIT_FAILURE); - } - -#ifdef HAVE_LZMA_STATIC - if (ends_with(filename, ".xz")) { - lzma_stream strm = LZMA_STREAM_INIT; - lzma_ret ret = lzma_stream_decoder(&strm, UINT64_MAX, - LZMA_CONCATENATED); - if (verbose) - fprintf (stderr, "supermin: running xz\n"); - FILE *fd = fopen (filename, "r"); - if (!fd) { - perror("popen failed"); - exit (EXIT_FAILURE); - } - char tmp_out[tmpsize]; - strm.avail_in = 0; - strm.next_out = tmp_out; - strm.avail_out = tmpsize; - - lzma_action action = LZMA_RUN; - - while (1) { - if (strm.avail_in == 0) { - strm.next_in = tmp; - strm.avail_in = fread(tmp, 1, tmpsize, fd); - - if (ferror(fd)) { - // POSIX says that fread() sets errno if - // an error occurred. ferror() doesn't - // touch errno. - perror("Error reading input file"); - exit (EXIT_FAILURE); - } - if (feof(fd)) action = LZMA_FINISH; - } - - ret = lzma_code(&strm, action); - - // Write and check write error before checking decoder error. - // This way as much data as possible gets written to output - // even if decoder detected an error. - if (strm.avail_out == 0 || ret != LZMA_OK) { - const size_t num = tmpsize - strm.avail_out; - if (num > capacity) { - buf = (char*) realloc (buf, size*2); - if (!buf) { - perror("realloc"); - exit (EXIT_FAILURE); - } - capacity = size; - } - memcpy (buf+size, tmp_out, num); - capacity -= num; - size += num; - strm.next_out = tmp_out; - strm.avail_out = tmpsize; - } - if (ret != LZMA_OK) { - if (ret == LZMA_STREAM_END) { - break; - } else { - perror("internal error"); - exit(EXIT_FAILURE); - } - } - } - fclose (fd); - if (verbose) - fprintf (stderr, "done with xz %d read\n", size); - } else { -#endif - gzFile gzfp = gzopen (filename, "rb"); - if (gzfp == NULL) { - fprintf (stderr, "insmod: gzopen failed: %s", filename); - exit (EXIT_FAILURE); - } - while ((num = gzread (gzfp, tmp, tmpsize)) > 0) { - if (num > capacity) { - buf = (char*) realloc (buf, size*2); - if (!buf) { - perror("realloc"); - exit (EXIT_FAILURE); - } - capacity = size; - } - memcpy (buf+size, tmp, num); - capacity -= num; - size += num; - } - if (num == -1) { - perror ("insmod: gzread"); - exit (EXIT_FAILURE); - } - gzclose (gzfp); -#ifdef HAVE_LZMA_STATIC -} -#endif - -#else int fd = open (filename, O_RDONLY); if (fd == -1) { fprintf (stderr, "insmod: open: %s: %m\n", filename); @@ -439,7 +299,6 @@ insmod (const char *filename) offset += rc; } while (offset < size); close (fd); -#endif if (init_module (buf, size, "") != 0) { fprintf (stderr, "insmod: init_module: %s: %s\n", filename, moderror (errno)); @@ -447,10 +306,6 @@ insmod (const char *filename) * of a missing device. */ } - -#ifdef HAVE_ZLIB_STATIC - free (buf); -#endif } /* Mount /proc unless it's mounted already. */ -- 2.5.0
Richard W.M. Jones
2016-Feb-17 13:31 UTC
[Libguestfs] [PATCH supermin 2/2] docs: Document how to link the init binary with an alternate libc.
Because of the previous commit, it is now possible to link the init binary using alternate libc's. This updates the documentation to describe how to link it using dietlibc. Using dietlibc reduces the overall size of the initrd substantially. For libguestfs, 2.6M -> 1.8M. Though this is still slightly larger than the initrd using compressed modules (1.4M). --- README | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README b/README index 56a28f6..fc6689f 100644 --- a/README +++ b/README @@ -40,6 +40,8 @@ Requirements - This is just used to generate the manpage. static libc + - Can be replaced with dietlibc (and maybe other alternate libc). + See section ``Alternate libc'' below. bash @@ -144,3 +146,25 @@ Feedback and bugs Send feedback to libguestfs@redhat.com. You can file bugs in https://bugzilla.redhat.com/ (under "Fedora", "supermin") +Alternate libc +-------------- + +Supermin uses a small, statically linked "init" binary. Normally this +is linked to static glibc, but static glibc produces enormous binaries +(800KB+). You can use an alternate libc if you prefer. For example, +using dietlibc, I can build a 22K init, about 1/40th of the size. + +$ ls -l src/init +-rwxrwxr-x. 1 rjones rjones 21736 Feb 17 13:10 src/init + +- Dietlibc + +For dietlibc, build supermin as normal, and then do: + + cd src + rm init init-init.o + make init CC="diet gcc" + make + +which rebuilds the init using dietlibc, and then rebuilds supermin +with the dietlibc-using init binary. -- 2.5.0
Apparently Analagous Threads
- [PATCH supermin 0/2] Allow an alternate libc to be used for init.
- [PATCH v2 0/3 supermin] URPMI & xz support.
- [PATCH 0/2] supermin: improve handling of memory
- [PATCH supermin v2 1/4] init: Uncompress modules before adding them to the mini initrd.
- [PATCH supermin v4] Supermin 5 rewrite.