Possible to build LFS on win32?

Richard Jenniss RichardJenniss at TelusPlanet.net
Tue Mar 19 18:22:13 PST 2002

I'm sure people have reasons, I found this howto I thought I'd share.

%%howto-version: 1.0
%%title: Building Cygwin hosted Linux toolchain
%%url: http://www.nanotech.wisc.edu/~khan/software/gnu-win32/
%%category: cygwin
%%filename: cygwin-to-linux-cross-howto
%%author: Mumit Khan

This howto provides a roadmap to building a Linux development toolchain
(ix86-pc-linux-gnu) hosted on Cygwin (ix86-pc-cygwin) platform. Shows
how to build Linux 2.4.0 kernel as an example. And before you ask, no, I
don't know why someone would want to do that ;-)

  - Background
  - Preliminaries
  - Build steps
  - Postscript

Created: Tue Aug  3 17:34:57 CDT 1999
Last Modified: Thu Jan 25 11:10:11 CST 2001


When it comes to cross-compiling (the simple kind or the canadian kind),
three terms are very important -- host, target and build. The host is
the machine that the resulting toolchain will run on, the build is the
machine that the resulting toolchain are being built on, and target is
the machine that resulting toolchain will create binaries for. The most
usual case is where host == build == target (eg., if you're using a Linux
compiler on a Linux box that was created on a Linux box); in the case of
most cross-compilers, host == build, target is different (eg., host and
build could be say Linux and target could be say i686-pc-cygwin, so that
when you compile/link on Linux box using this toolchain, you create
binaries that will run on i686-pc-cygwin); in the case of building a
canadian cross compiler, host, build and target may all be different
(I'll refrain from expounding on this one, and leave it to your

Ok, so let's say we have a PC running Win2k and Cygwin, and we want to
able to build Linux binaries on that PC. Yuck, I know, but there are
those who seem to want it, and I just did it to satisfy some perverse
need to see if it could be done trivially. FYI, you can then easily
build a Linux kernel on a Cygwin machine.

CrossGCC folks use various schemes, and I personally find those too
complicated, but do it my way mostly because I'm too lazy to read the

Here're the basic steps: (Preliminaries) Decide on where you want to
install and so on, (1) Gather all the source packages you need, move
these over to the Cygwin host, (2) Get the Linux runtime from a Linux
box and move that over as well, (3) Build and install Binutils, and
finally (4) Build and install GCC. Postscript shows a simple example,
and shows how to avoid GCC from always adding .exe to the executable
name (if you want to avoid that, read that before step 3). Also shows
how you can build the Linux 2.4.0 kernel on a Cygwin machine using
your freshly built cross-development toolchain.

The only complicated step is (2), but good news is that you *only* need
to do this once. You only need to redo this when you want to upgrade
glibc2 for the cross-compiler.

For the purposes of this HOWTO, I've used the following packages:

1. Cygwin   -- 1.1.7 (with all updates applied to date)
2. GCC      -- 2.95.2-6 (part of Cygwin source distribution)
3. Binutils -- 2.10.1 (standard GNU distribution, you may however prefer
               to use whatever Linux/GNU folks use)
4. glibc2   -- 2.1.3-21 (from RedHat 6.2, with all updates applied to date)


Scattered throughout this article are user commands that you'll type
in, and I've used either ``cygwin$'' or ``linux$'' as the shell prompt
to denote the host machine you're supposed to be on for that particular

For the Cygwin host, let's say we'll be using the following (I'm using
bash, so if you're using a csh/tcsh type shell, do the right thing):

  cygwin$ host=i686-pc-cygwin
  cygwin$ build=i686-pc-cygwin
  cygwin$ target=i686-pc-linux-gnu
  cygwin$ prefix=/usr/local/linux
  cygwin$ src_root=/usr/local/src/gnu

Feel free to change $prefix and/or $src_root to match your environment,
but please don't mess with $host, $build and $target. Do make sure that
$src_root directory does exist. You don't have to set these shell
variables of course, but it saves me some typing and saves you from my
inevitable typos.

You should also add $prefix/bin to your PATH, but you can wait till you
have installed binutils for that (ie., end of Step 3).

Step 1:
Gather all the source packages you need. The minimal set is the compiler
sources, binary utilities.

Let's say we get the following from a GNU mirror:


Since we're building on a Cygwin host, the safe bet is to get a version
of gcc-2.95.2 from the Cygwin distribution instead which may have some
Cygwin specific fixes, which is what I use. See Cygwin home page at
http://sources.redhat.com/cygwin/ to see how to download the GCC source
package using the network "setup" utility (or just ftp it over from one
the mirrors). At the time of this writing, the latest one is gcc-2.95.2-6.

Download these on your Cygwin box and unpack:

  cygwin$ cd $src_root
  cygwin$ tar zxf /tmp/gcc-2.95.2.tar.gz
  cygwin$ tar zxf /tmp/binutils-2.10.1.tar.gz

Step 2:

Gather the Linux target runtime. This is easier than having to build
glibc2 using your cross-tools, believe me. Once you've gone through
this process, you should be able to do without much trouble, provided
you're familiar with glibc2 configuration and build process.

Now comes the first slightly tricky job of deciding what the minimal set
to get from a Linux box that will serve as our runtime. For the rest of
this article, when I refer to GNU/Linux, I'm referring to a RedHat 6.2
installation, so please take note if you're using some other distribution.
This at first would seem like a trivial task:

** On a Linux box now **

  linux$ mkdir -p /tmp/linux-target-runtime
  linux$ cd /tmp/linux-target-runtime
  linux$ (cd /usr; tar cf - include) | tar xf -
  linux$ ln -s include sys-include
  [ Please don't ask why I add the sys-include symlink; if you do, I'll
    simply ask you look at GCC configuration files. In fact, we could
    have just renamed include to sys-include, but there are packages
    that get confused if the include directory is missing altogether. ]
  [ Also see note below on using --dereference tar option to avoid
    certain messy steps ]
  linux$ (cd /usr; tar cf - lib) | tar xf -
  linux$ (cd /; tar cf - lib) | tar xf -
  [ See below on avoiding copying the whole /lib and /usr/lib and only
    getting what you need. ]
  linux$ ls
  include lib sys-include

But there's a kink. Let's start with the problem with the includes after
we copied it over. If you look in /usr/include, you'll notice that there
are two symbolic links that are rather important:

  /usr/include/linux --> ../src/linux/include/linux
  /usr/include/asm   --> ../src/linux/include/asm

Since we didn't use --dereference tar option, these remain as links, and
copying the tree over will miss the actual files, ie., these will remain
as dangling symbolic links.

The ../src/linux points to /usr/src/linux, where your kernel headers and
possibly the source tree is kept. So, that means that either you get your
whole kernel header tree as well, maintaining the correct relative paths,
or just copy those over instead of the symbolic link. But there's another
slight kink -- you'll note that the symbolic link /usr/include/asm points
to is itself a symbolic link -- points to asm-i386. Oh joy.

  /usr/src/linux/include/asm --> asm-i386

So, now we fix the links:

  linux$ cd /tmp/linux-target-runtime/include
  linux$ rm linux asm
  linux$ (cd /usr/src/linux/include; tar cf - linux asm-i386) | tar xf -
  linux$ ln -s asm-i386 asm

Whew, done with the includes. Before you ask, yes, you could have just
as easily used --dereference option to tar when copying the include tree
and avoided this hassle, but I didn't want all the other linked include

Now, let's take a look at the library scenario: The following are the
most basic, and absolutely needed in the lib directory:

  linux$ cd /tmp/linux-target-runtime
  linux$ ls -F lib
  Mcrt1.o  gcrt1.o         libc.a            libc_stubs.a     libdl.so.2@
  crt1.o   ld-2.1.3.so*    libc.so           libdl-2.1.3.so*  libm-2.1.3.so*
  crti.o   ld-linux.so.2@  libc.so.6@        libdl.so.1@      libm.a
  crtn.o   libc-2.1.3.so*  libc_nonshared.a  libdl.so.1.9.5*  libm.so.6@

[ '*' after the name denotes executable, and '@' denotes symlink ]

Note that some of these files such as ld-linux.so and libc.so live in /lib,
not in /usr/lib, and you'll have to copy those out. You can copy the whole
tree of course.

The only kink in the libraries is that you'll need to fix libc.so, which
is just a linker script (and that's why it's only a few hundred bytes!).
If you look inside, you'll see something like the following:

  linux$ cat lib/libc.so
  /* GNU ld script
     Use the shared library, but some functions are only in
     the static library, so try that secondarily.  */
  GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )

Note the last line -- obviously these paths are not going to be the
same on your Cygwin host, so both the paths need to changed to reflect
your Cygwin installation. If you choose say /usr/local/linux as the
prefix when building your Linux-targeted Cygwin-hosted toolchain on
Cygwin, then change /lib to $prefix/i686-pc-linux-gnu/lib instead.

Here's mine:

  cygwin$ cat /usr/local/linux/i686-pc-linux-gnu/lib/libc.so
  /* GNU ld script
     Use the shared library, but some functions are only in
     the static library, so try that secondarily.  */
  GROUP ( /usr/local/linux/i686-pc-linux-gnu/lib/libc.so.6
/usr/local/linux/i686-pc-linux-gnu/lib//libc_nonshared.a )

The /usr/local/linux/ in /usr/local/linux/i686-pc-linux-gnu comes from
our $prefix setting and the i686-pc-linux-gnu comes from our $target
setting in *Preliminaries*.

So, let's package it up and send it over to Cygwin box.

  linux$ cd /tmp/linux-target-runtime
  linux$ tar -zcvf /tmp/linux-target-runtime.tar.gz .
  linux$ scp /tmp/linux-target-runtime.tar.gz user at cygwin_hostname:/tmp/
  [ or use whatever method to copy it over to the cygwin box ]

** Back on the Cygwin box now **

Unpack the linux-target-runtime.

  cygwin$ mkdir -p $prefix/$target
  cygwin$ tar zxvf /tmp/linux-target-runtime.tar.gz
  cygwin$ ls
  include lib sys-include

Ok, we're done with Step 1. It's painful, but you only do this once every
few months, just like going to the dentist.

It's smooth sailing from now on.

Step 3:

Build and install binutils. Never build in the source tree, so I'll just
arbitrarily pick $src_root/BUILD as the top build directory, under which
I'll build both binutils and gcc.

  cygwin$ mkdir -p $src_root/BUILD/binutils
  cygwin$ cd $src_root/BUILD/binutils
  cygwin$ $src_root/binutils-2.10.1/configure \
    --with-included-gettext \
    --target=$target --host=$host --build=$build \
    --prefix=$prefix -v
  cygwin$ make > make.log 2>&1

If all goes well, install.

  cygwin$ make install > install.log 2>&1

*IMPORTANT* Add $prefix/bin to your PATH Before going forward.

  cygwin$ export PATH=$PATH:$prefix/bin


  cygwin$ $target-ld --version
  GNU ld 2.10.1
  Copyright 2000 Free Software Foundation, Inc.
  This program is free software; you may redistribute it under the terms of
  the GNU General Public License.  This program has absolutely no warranty.
    Supported emulations:

Done. Smooth sailing.

Step 4:

Build and install GCC.

  cygwin$ mkdir -p $src_root/BUILD/gcc
  cygwin$ cd $src_root/BUILD/gcc
  cygwin$ $src_root/gcc-2.95.2/configure \
    --enable-languages=c,c++ \
    --with-included-gettext --enable-shared --enable-threads \
    --target=$target --host=$host --build=$build \
    --prefix=$prefix -v

Feel free to change the arguments to --enable-languages, or leave it
out altogether if you want to build all available languages. Also,
do as you wish with --enable-shared and --enable-threads parameters.
Don't mess with the rest.

  cygwin$ make > make.log 2>&1

If all goes well, install.

  cygwin$ make install > install.log 2>&1


Now that you're run, you should be able to create binaries and ship
those over to a Linux/GNU machine to run. If all goes well:

  $ cat > hello.c
  #include <stdio.h>
    printf("hello world\n");
    return 0;
  $ $target-gcc -o hello hello.c
  $ ls -l hello*
  hello.c hello.exe

Huh, why the .exe extension? Well, it has to do with gcc hosted on
Cygwin, and you can always patch gcc sources to get rid of the automatic
.exe addition. Just comment out the EXECUTABLE_SUFFIX macro in
$src_root/gcc-2.95.2-6/gcc/config/i386/xm-cygwin.h and rebuild gcc.

This is capable of building the Linux kernel. Of course, you'll be
missing /sbin/depmod and /sbin/genksyms, but that's a different
problem. You'll need a trivial patch to get around Cygwin limitation
in command line argument length and a bug in mmap implementation, but
other than that, it's the usual `make menuconfig; make dep; make clean;
make bzImage' that everyone is used to. Do keep in mind that just
because the kernel builds with gcc-2.95.2 does not mean that it's safe;
see kernel documentation for more info.

=== Linux 2.4.0 kernel patch for Cygwin cross build:

The following patch allows you to build the Linux 2.4.0 kernel on a Cygwin
machine using a cross-compiler. Tested only on Win2k running Cygwin 1.1.7,
gcc-2.95.2-6 and binutils-2.10.1. YMMV.

The patch to <top>/Makefile avoids too long a command line error, and the
patch to scripts/mkdep.c avoids a Cygwin mmap bug.

Once patched, you can do the following:

  $ make CROSS_COMPILE=i686-pc-linux-gnu- dep
  $ make CROSS_COMPILE=i686-pc-linux-gnu- clean
  $ make CROSS_COMPILE=i686-pc-linux-gnu- bzImage

and get a kernel. May even work! You can always set the CROSS_COMPILE
variable in the <top>/Makefile and skip the CROSS_COMPILE setting when
running make every time.

2001-01-24  Mumit Khan  <khan at nanotech.wisc.edu>

 * Makefile (dep-files): Use xargs to reduce command line length.
 * scripts/mkdep.c (do_depend): Workaround for Cygwin mmap bug.

--- Makefile.~1 Wed Jan 24 22:22:16 2001
+++ Makefile Wed Jan 24 22:23:19 2001
@@ -442,7 +442,7 @@ sums:

 dep-files: scripts/mkdep archdep include/linux/version.h
  scripts/mkdep init/*.c > .depend
- scripts/mkdep `find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h
! -name modversions.h -print` > .hdepend
+ find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h ! -name
modversions.h -print | xargs scripts/mkdep | cat > .hdepend
  $(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS))
  $(MAKE) update-modverfile
--- scripts/mkdep.c.~1 Wed Jan 24 17:41:19 2001
+++ scripts/mkdep.c Wed Jan 24 17:41:50 2001
@@ -471,7 +471,9 @@ cee_CONFIG_word:
 void do_depend(const char * filename, const char * command)
  int mapsize;
+#ifndef __CYGWIN__
  int pagesizem1 = getpagesize()-1;
  int fd;
  struct stat st;
  char * map;
@@ -490,7 +492,9 @@ void do_depend(const char * filename, co

  mapsize = st.st_size;
+#ifndef __CYGWIN__
  mapsize = (mapsize+pagesizem1) & ~pagesizem1;
  map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
  if ((long) map == -1) {
   perror("mkdep: mmap");

=== End of Linux 2.4.0 kernel patch for Cygwin cross build.


For more information on GNU Compiler Collection (GCC), see GCC home

For more information on Cygwin, see Cygnus's cygwin project page:

For more information on Crossgcc, see:

For more information on binutils, see:

Latest version of this documentation, and other information related to
GNU tools on various types of windows32 system, is available from my
gnu-win32 page:

Created: Tue Aug  3 17:34:57 CDT 1999
Last Modified: Thu Jan 25 11:10:11 CST 2001
Mumit Khan <khan at nanotech.wisc.edu>

Good luck.


Unsubscribe: send email to listar at linuxfromscratch.org
and put 'unsubscribe lfs-support' in the subject header of the message

More information about the lfs-support mailing list