1) Introduction
   ============

The following note describes the cygwin modifications that are necessary for 
being able to link cygwin1.dll as a static library. Using a  static cygwin 
link, without any dependencies on a cygwin1.dll will result in 'native',
standalone executables that can be used in a pure DOS environment, and will not 
interfere with a potentially different version of Cygwin that may also be
installed on the machine on which such statically linked executables run. 

Without some modifications, static linking of the CygWin kernel is not immediately
possible, because the implementation contains a few hard assumptions that this kernel 
is running as a separately linked dll. Also, there are other ways in which the different 
versions of CygWin on the same machine might interfere, such as information held by 
shared memory partitions by which the CygWin kernel transfers information between different
process spaces.

The change list below fixes this. As far as I can see, most of these changes will not
break normal operation of CygWin as a dll, and hence can potentially be rolled
back in the main source base.

For obtaining a CVS checkout of the Cygwin sources, see www.cygwin.com/cvs.html.
The following modifications are made on the source base as of August 8, 2006.


2) Cygwin source patches
   =====================

a) Separate CygWin's and the application's static constructor and destructor sections.

      ===================================================================
      RCS file: /cvs/src/src/winsup/cygwin/winsup.h,v
      retrieving revision 1.190
      diff -r1.190 winsup.h
      331a332,333
      > extern void (*__CYGWIN_CTOR_LIST__) (void);
      > extern void (*__CYGWIN_DTOR_LIST__) (void);
      ===================================================================
      RCS file: /cvs/src/src/winsup/cygwin/dcrt0.cc,v
      retrieving revision 1.311
      diff -r1.311 dcrt0.cc
      743c743
      <   do_global_ctors (&__CTOR_LIST__, 1);
      ---
      >   do_global_ctors (&__CYGWIN_CTOR_LIST__, 1);
      ===================================================================
      RCS file: /cvs/src/src/winsup/cygwin/cygwin.sc,v
      retrieving revision 1.20
      diff -r1.20 cygwin.sc
      11c11
      <      ___CTOR_LIST__ = .; __CTOR_LIST__ = .;
      ---
      >      ___CYGWIN_CTOR_LIST__ = .; __CYGWIN_CTOR_LIST__ = .;
      13c13
      <      ___DTOR_LIST__ = .; __DTOR_LIST__ = .;
      ---
      >      ___CYGWIN_DTOR_LIST__ = .; __CYGWIN_DTOR_LIST__ = .;



b) Avoid interference between any installed, 'real' CygWin and our static version.
   This change will allow a 'real' CygWin application to spawn 'static' CygWin applications,
   and vice versa. Note that the 'zero' arrays are only used as magic pattern, that is,
   they are only used in the magic check in dcrt0.cc, and nobody assumes that the contens
   are actually zero. Any magic pattern will do, and the change below uses an existing
   macro CURR_CHILD_INFO_MAGIC (child_info.h) which is entirely unused in the current 
   mainstream CVS source base.
   
      ===================================================================
      RCS file: /cvs/src/src/winsup/cygwin/dcrt0.cc,v
      retrieving revision 1.311
      diff -r1.311 dcrt0.cc
      593c593
      <   char zeros[sizeof (child_proc_info->zero)] = {0};
      ---
      >   DWORD zeros[sizeof (child_proc_info->zero)/sizeof(DWORD)] = {CURR_CHILD_INFO_MAGIC,0};
      ===================================================================
      RCS file: /cvs/src/src/winsup/cygwin/sigproc.cc,v
      retrieving revision 1.297
      diff -r1.297 sigproc.cc
      788a789
      >   zero[0] = CURR_CHILD_INFO_MAGIC;



c) The following appears necessary because the NT header for executables
   still claims that it has a nonempty export table, causing a crash
   in hook_or_detect_cygwin, while walking the export table of the module that 
   contains CygWin (which in our case is an executable):
   
      ===================================================================
      RCS file: /cvs/src/src/winsup/cygwin/hookapi.cc,v
      retrieving revision 1.12
      diff -r1.12 hookapi.cc
      127a137,138
      >   if (!(pnt->FileHeader.Characteristics&IMAGE_FILE_DLL))
      >     return;

   ... and for statically linked executables we detect whether they
   use cygwin by checking whether they use the cygheap:

      ===================================================================
      RCS file: /cvs/src/src/winsup/cygwin/hookapi.cc,v
      retrieving revision 1.12
      diff -r1.12 hookapi.cc
      50a51,59
      > static bool
      > has_cygheap (PIMAGE_NT_HEADERS pnt)
      > {
      >   PIMAGE_SECTION_HEADER section = (PIMAGE_SECTION_HEADER) (pnt + 1);
      >   for (int i = 0; i < pnt->FileHeader.NumberOfSections; i++)
      >     if (strncmp(".cygheap",(char*)section[i].Name,8)==0) return true;
      >   return false;
      > }
      >
      207c218,222
      <   return fh.origfn;
      ---
      >   if (fh.origfn)
      >     return fh.origfn;
      >   if (has_cygheap(pExeNTHdr))
      >     return (void *) "static cygwin link";
      >   return false;



d) Replace thread munging by letting created threads start with threadfunc_fe directly.
   Move process initialization code from the dll entry function into a new function process_init, 
   which is now called by dll_entry, as well as by dll_crt0 when it detects that it is in a statically
   linked cygwin.
   Removed function munge_threadfunc, and global variables search_for, _my_oldfunc, threadfunc_ix

        ===================================================================
        RCS file: /cvs/src/src/winsup/cygwin/cygthread.cc,v
        retrieving revision 1.76
        diff -r1.76 cygthread.cc
        191a192,193
        > DWORD WINAPI threadfunc_fe (VOID *arg);
        > 
        211c213
        <       htobe = CreateThread (&sec_none_nih, 0, is_freerange ? simplestub : stub,
        ---
        >       htobe = CreateThread (&sec_none_nih, 0, is_freerange ? simplestub : threadfunc_fe,



        ===================================================================
        RCS file: /cvs/src/src/winsup/cygwin/dcrt0.cc,v
        retrieving revision 1.311
        diff -r1.311 dcrt0.cc
        867,869d866
        <   extern DWORD threadfunc_ix;
        <   if (!threadfunc_ix)
        <     system_printf ("internal error: couldn't determine location of thread function on stack.  Expect signal problems.");
        974a972,974
        > 
        > extern void process_init (HMODULE module, bool dynload);
        > 
        977a978,981
        >  /* If the cygwin dll entry function has not called process_init yet,
        >     we are in a statically linked cygwin. Do process_init here: */
        >   if (!cygwin_hmodule) { process_init(GetModuleHandle(0),false); }
        > 



        ===================================================================
        RCS file: /cvs/src/src/winsup/cygwin/init.cc,v
        retrieving revision 1.69
        diff -r1.69 init.cc
        21,22d20
        < static DWORD _my_oldfunc;
        < 
        24,25d21
        < static char NO_COPY *search_for = (char *) cygthread::stub;
        < unsigned threadfunc_ix[8] __attribute__((section (".cygwin_dll_common"), shared));
        30c26
        < static void WINAPI
        ---
        > DWORD WINAPI
        33,35c29,36
        <   (void)__builtin_return_address(1);
        <   asm volatile ("andl $-16,%%esp" ::: "%esp");
        <   _cygtls::call ((DWORD (*)  (void *, void *)) TlsGetValue (_my_oldfunc), arg);
        ---
        >   if (!hwait_sig) {
        >       cygthread::stub(arg);
        >   } else {
        >       (void)__builtin_return_address(1);
        >       asm volatile ("andl $-16,%%esp" ::: "%esp");
        >       _cygtls::call ((DWORD (*)  (void *, void *)) cygthread::stub, arg);
        >   }
        >   return 0;
        38,70d38
        < /* If possible, redirect the thread entry point to a cygwin routine which
        <    adds tls stuff to the stack. */
        < static void
        < munge_threadfunc ()
        < {
        <   int i;
        <   char **ebp = (char **) __builtin_frame_address (0);
        <   if (!threadfunc_ix[0])
        <     {
        <       char **peb;
        <       char **top = (char **) _tlsbase;
        <       for (peb = ebp, i = 0; peb < top && i < 7; peb++)
        <       if (*peb == search_for)
        <         threadfunc_ix[i++] = peb - ebp;
        <       if (0 && !threadfunc_ix[0])
        <       {
        <         try_to_debug ();
        <         return;
        <       }
        <     }
        < 
        <   if (threadfunc_ix[0])
        <     {
        <       char *threadfunc = ebp[threadfunc_ix[0]];
        <       if (!search_for || threadfunc == search_for)
        <       {
        <         search_for = NULL;
        <         for (i = 0; threadfunc_ix[i]; i++)
        <           ebp[threadfunc_ix[i]] = (char *) threadfunc_fe;
        <         TlsSetValue (_my_oldfunc, threadfunc);
        <       }
        <     }
        < }
        117a86
        > HMODULE NO_COPY cygwin_hmodule;
        120c89,113
        < HMODULE NO_COPY cygwin_hmodule;
        ---
        > void
        > process_init (HMODULE module, bool dynload)
        > {
        >   BOOL wow64_test_stack_marker;
        > 
        >   wincap.init ();
        >   init_console_handler (false);
        > 
        >   cygwin_hmodule     = module;
        >   dynamically_loaded = dynload;
        > 
        >   /* Is the stack at an unusual address?  This is, an address which
        >      is in the usual space occupied by the process image, but below
        >      the auto load address of DLLs?
        >      Check if we're running in WOW64 on a 64 bit machine *and* are
        >      spawned by a genuine 64 bit process.  If so, respawn. */
        >   if (wincap.is_wow64 ()
        >       && &wow64_test_stack_marker >= (PBOOL) 0x400000
        >       && &wow64_test_stack_marker <= (PBOOL) 0x10000000)
        >     respawn_wow64_process ();
        > 
        >   dll_crt0_0 ();
        > }
        > 
        > 
        127d119
        < 
        133,150c125
        <       wincap.init ();
        <       init_console_handler (false);
        < 
        <       cygwin_hmodule = (HMODULE) h;
        <       dynamically_loaded = (static_load == NULL);
        < 
        <       /* Is the stack at an unusual address?  This is, an address which
        <        is in the usual space occupied by the process image, but below
        <        the auto load address of DLLs?
        <        Check if we're running in WOW64 on a 64 bit machine *and* are
        <        spawned by a genuine 64 bit process.  If so, respawn. */
        <       if (wincap.is_wow64 ()
        <         && &wow64_test_stack_marker >= (PBOOL) 0x400000
        <         && &wow64_test_stack_marker <= (PBOOL) 0x10000000)
        <       respawn_wow64_process ();
        < 
        <       dll_crt0_0 ();
        <       _my_oldfunc = TlsAlloc ();
        ---
        >       process_init ((HMODULE)h,static_load == NULL);
        155,156d129
        <       if (hwait_sig)
        <       munge_threadfunc ();



d) Place the following patch to avoid interference of shared memory partitions of the different cygwins.
   This is a hack, so it is *not* a good idea to submit this in this form to the main source tree.

        ===================================================================
        RCS file: /cvs/src/src/winsup/cygwin/sec_helper.cc,v
        retrieving revision 1.58
        diff -r1.58 sec_helper.cc
        118a119,122
        > #define SID_PREFIX "sl-"
        >
        127,128c131,132
        <   strcpy (nsidstr, "S-1-");
        <   t = nsidstr + sizeof ("S-1-") - 1;
        ---
        >   strcpy (nsidstr, SID_PREFIX"S-1-");
        >   t = nsidstr + sizeof (SID_PREFIX"S-1-") - 1;



3) Obtaining cygwin1.o
   ===================

After obtaining the Cygwin sources, and applying the above described patches,
start a configure and build as usual:

  cd $CYGSRC
  ./configure
  make
  
This should complete normally.

Then, copy all files from cw_static_link into the top of the source directory,
so that we can link the cygwin static 'library' using the provided script:

  sh staticlink
  
This shell script is a brute force script that runs a link command modified
from the command by which the normal build linked cygwin1.dll. The
crt0 sources (which normally are linked against the application) are
recompiled in order to get rid of the cygwin1.dll references.

The resulting cygwin1.o contains cygwin's windows glue layer (winsup), plus the entire
libc and libm libraries. This monolithic link is similar to how the cygwin dll is
linked together. It makes sense for the cygwin dll, but in our statically linked case 
this adds 1.5M of code and data to each executable. However, repartitioning cygwin1.o into
a common core plus a libc.a and libm.a is a later cleanup step, and not the goal of this 
exercise (our goal was to link busybox in a 'native' DOS mode that is independent 
of any particular CygWin installation, and busybox requires a large part of libc anyway).

The script staticlink also defines wrapper functions for certain system calls that are 
renamed in libcygwin.a on 'real' cygwin installations. An example of such a function
is fstat, which libcygwin.a maps to fstat64.



4) Linking applications
   ====================
   
Under the assumption that the libc library ABI has not changed, application sources can
be compiled on any recent Cygwin distribution. However, they must be linked using a modified
link command, in order to:

    - include our created libcygwin1.o
    - omit the crt0 startup code that is normally added by gcc
    - use a modified link script, in order to call wrapper functions for translated
        system calls, and also for defining the cygheap markers. Defining these markers
        in the linker script used by staticlink somehow failed due to a reported 'forward'
        symbol definition (did not track this further).
    - always use static linking
    
Also, they must be linked against an empty libcygwin.a, in order to prevent pulling in
unwanted definitions of some bss symbols (did not track this further).
    
For instance:

 gcc -c x.c
 
 ar cr ./libcygwin.a
 gcc -L. `cat swapsyscalls` -static -nostartfiles -Tapplink.sc cygwin1.o x.o -o x.out
 
If this executable x.out is to be spawned by a different executable that is also
linked to the static cygwin kernel, then it is essential that all sections in cygwin1.o
occur at the same address in both executables.
I could not achieve this, because adapting the linker script cygwin1.o.sc for grouping 
all text, all data, all rdata and all bss sections together ran into a linker bug (see below).


 
5) OPEN ISSUES

Need to cluster and rename all cygwin sections to get them to link first in the executable.
otherwise e.g. cygwin data will come after application text, giving them a nonpredictable
location in different applications. If our only application out there is busybox this 
is not a problem, but it needs be solved if we want to link other applications with a
static cygwin. 

For instance, I tried to modify in cygwin1.o.sc (because cygwin1.o still contains many text sections)

     ...
       .text  :
       {
         *(.text)
     ...    
    
into

     ...
       .text  :
       {
         *(.text*)
     ...    

This gave the following error on objdump:

    $ objdump -h cygwin1.o
    BFD: BFD 2.17.50 20060709 internal error, aborting at /netrel/src/binutils-20060709-1/bfd/coffcode.h line 841 in handle_COMDAT

    BFD: Please report this bug.

So I gave up on this. Will send in a bug report




