/joh'liks/ n.,adj. 386BSD

Porting Unix to the 386: A Practical Approach



William & Lynne Jolitz







Validating GCC for Use in a Cross-Environment
We found GCC to have many fine qualities--unfortunately, cross-support operation was not one of them. From its inception, GCC has traditionally been run on the host on which it was compiled, and little thought has been put into preserving its ability to run on a machine vastly different from that host. In addition, some architectures supported under GCC relied to some degree on the presence of a preexisting native compiler to compile GCC and parts of its own compiler support libraries. To be fair, the compiler itself is quite capable of compiling and supporting itself. However, as originally configured, both cross-support and compiler bootstrapping are not very satisfying.

Other hurdles which we had to surmount included locating host compiler bugs upon compiling GCC. Unlike other compiler writers who attempt to minimize the use of arbitrary C features in their code, GCC's creators revel in it. As a result, compiling GCC itself constitutes an excellent test of a compiler because of its rich use of the language, and the impressive demands (macros, pointer dereferencing) it places on the said compiler. While this style of implementation goes loggerheads with practical portability in our compromised "real" world, we must admit that the creators of GCC show fearless, if not reckless, faith in their compiler. No one else so completely exploits the C language, at the price of providing faultless support for such an extensive use of the language. The intellectual honesty required for such an implementation has received its fair portion of praise.

In the course of attempting to qualify a cross-host, we attempted to compile GCC on many machines. One less than serious attempt was made to compile portions of GCC on MS-DOS using various common PC C compilers. As expected, we got dismal results. We found that to compile GCC on MS-DOS, we would have to extensively rewrite the code, and also use some manner of MS-DOS extender--an effort not compatible with our specification goals. We did consider using the standalone library (see "The Standalone System" in DDJ, March 1991) to run GCC in native mode after compiling GCC on a borrowed 386 system elsewhere, but gave up on this when our cross-host version of GCC stabilized. We worried that these two PC-hosted approaches would not only require a great deal of additional work, but also require us to maintain them in the future for avid users. Perhaps a fate worse than death?

Our intended cross-host, a UNIX machine, had many problems in compiling GCC, even though the compiler has been part of a stable production system for many years. However, consistency checks within GCC itself allowed us to locate the nature of the problem to within a few thousand instructions, whereupon we would tediously single-step to the problem with a debugger. Since we could not fix the cross-host's native compiler (frequently this would mean exchanging the bug you know for the bugs generated by the fix that you don't know), we mauled GCC itself and defeated portions of the compiler in a successful attempt to avoid code that the native compiler would mishandle. Due to the nature of the native compiler bug (an obscure pointer aliasing problem), this was the only way we could convince ourselves that we were not just migrating the bug. As you might expect from our mention above, one of the best tests of our then-generated cross-compiler was GCC itself.

Another aspect of running GCC in a cross-environment is dealing with an internal support library known as gnulib.a. GCC is arranged so that portions of machine-dependent operations not implemented by the compiler itself with issued assembly code will instead be implemented by a subroutine call to a gnulib.a entry point. To cleverly implement these missing areas within the compiler, one creates gnulib.a by compiling source code encapsulating the missing feature with the native host's compiler (not GCC), relying on it to implement the missing feature as it sees fit. Here's an example. Suppose we have the C expression:

  if(a != b)....


Let's assume the compiler does not know how to handle !=. It could generate code to call a gnulib entry point:

      ...
    pushl _a
    pushl _b
    call noteqsi2
      ...
The gnulib would contain code compiled with a different native compiler than GCC, one that can deal with a != expression:

  noteqsi2(n,m) {
                return(n != m);
  }
This is a sneaky way to leverage an existing native compiler to fill out voids in GCC. Surprisingly, this works with our cross-host in most cases. We implemented a replacement for gnulib only as needed (few are ever called).

We ran into an entertaining problem when we first moved the compiler onto the 386. Because we no longer needed our cross-host modifications to GCC, we started recompiling the stock version of GCC, including gnulib, with the only compiler we had on our nascent BSD UNIX system, namely GCC. GCC generated code that would call the support library, which in turn would then call itself to implement the same support, and so on ad infinitum! This is another minor example on the lack of native support for GCC in the then standard release. It is expected that GCC 2.0 and later versions will better address these and other cross-support issues.


<<BACK NEXT >>



Copyright 1990, 2006 TeleMuse Partners, William Jolitz and Lynne Jolitz