Tuesday, October 31, 2006

Fixing Cygwin & MingW

I'm a long-time Cygwin user (and a longer-time Unix and Linux user). It is handy to have the whole suite of Linux tools under Windows. Of course, one feature every Linux programmer uses constantly is gcc -- the Gnu C compiler. However, the Cygwin compiler by default creates binaries for use with Cygwin. That means you need a commercial license to distribute them and it also means you can't just send an executable to your buddy who doesn't use Cygwin.

There is a another project that allows gcc to run under Windows called MingW. In fact, you can use the Cygwin tools to install the MingW libraries and -- in theory -- a simple -mno-cygwin command line option will let you compile Ming binaries (that is, normal Windows executables that don't depend on Cygwin). Of course, you lose any "special" Cygwin features when you do this, but that's the price.

I've used this before, but unfortunately it seems to be broken in the current release of Cygwin. Here's what I did to fix it.

1) In /usr/lib/gcc there is a directory for i686-pc-mingw32/3.4.4. In that directory, the Cygwin installer put ordinary .lnk files linking back to /usr/lib/gcc/i686-pc-cygwin/3.4.4. None of these work, so the first step is to make hard or symbolic links to the correct files. For example, cc1.exe.lnk needs to be fixed (from the i686-pc-mingw32/3.4.4 directory):

ln ../i686-pc-cygwin/cc1.exe cc1.exe

Fix all the links in this way. Now gcc will run with -mno-cygwin, but it still won't link.

2) The problem is, gcc is a driver and the specs file is broken. In the same directory, edit the file named specs. The trick here is we need to change the references to crt2.o to refer to a specific path (/usr/lib/mingw/crt2.0). We also need to add -L/usr/lib/mingw to several places. Here's a diff between the standard specs file and mine (significant additions in red):



$ diff specs ../../i686-pc-cygwin/3.4.4/specs
51c51
< %{pg:-lgmon} %{!mno-cygwin:-lcygwin} %{mno-cygwin:%{mthreads:-lmingwthrd -L/usr/lib/mingw} -lmingw32} %{mwindows:-lgdi32 -lcomdlg32} -luser32 -lkernel32 -ladvapi32 -lshell32
---
> %{pg:-lgmon} %{!mno-cygwin:-lcygwin} %{mno-cygwin:%{mthreads:-lmingwthrd} -lmingw32} %{mwindows:-lgdi32 -lcomdlg32} -luser32 -lkernel32 -ladvapi32 -lshell32
54c54
< %{mno-cygwin: %{mthreads:-lmingwthrd} -lmingw32} -lgcc %{mno-cygwin:-lmoldname -lmingwex -lmsvcrt -L/usr/lib/mingw}
---
> %{mno-cygwin: %{mthreads:-lmingwthrd} -lmingw32} -lgcc %{mno-cygwin:-lmoldname -lmingwex -lmsvcrt}
57c57
< %{shared|mdll: %{mno-cygwin:dllcrt2%O%s}} %{!shared: %{!mdll: %{!mno-cygwin:crt0%O%s} %{mno-cygwin:/usr/lib/mingw/crt2%O%s} %{mno-cygwin:-L/usr/lib/mingw} %{pg:gcrt0%O%s}}}
---
> %{shared|mdll: %{mno-cygwin:dllcrt2%O%s}} %{!shared: %{!mdll: %{!mno-cygwin:crt0%O%s} %{mno-cygwin:crt2%O%s} %{pg:gcrt0%O%s}}}


Now you are done. Want to prove it? Try making a file howdy.c:

#include
void main() {
printf("Howdy!\n)";
}


Now compile it without using cygwin:
gcc -o howdy.exe -mno-cygwin howdy.c

Sure it works, but how do you know it isn't using Cygwin? Try this:
objdump -p howdy.exe | grep dll

You'll see:
$ objdump -p howdy.exe | grep dll
DLL Name: msvcrt.dll
DLL Name: KERNEL32.dll

If you recompile without the -mno-cygwin option and rerun objdump, you'll see the executable now relies on cygwin1.dll!

Notice that if you want to build DLLs you probably have to fix the dllcrt2.o in the spec file the same way. But from here you should be able to figure it out!

Labels: , , ,

Submit to:   book mark Fixing Cygwin & MingW in del.icio.usDel.icio.us  |   submit Fixing Cygwin & MingW to digg.comDigg  |   submit Fixing Cygwin & MingW to slashdot.comSlashdot  |  diigo itDiigo