From: wirzeniu@klaava.Helsinki.FI (Lars Wirzenius)
Subject: Re: Followup on size of executables.
Organization: University of Helsinki
Date: Mon, 4 Jan 1993 12:29:07 GMT

<RVK@psuvm.psu.edu> writes:
[ assembly program is 548 bytes, the equivalent C program is 6762
  bytes ]

>This brings us to another question: Who cares for a few K's
>more or less?

Plenty of people, including for myself, when there are hundreds of
small programs, each using a few extra kilobytes.  Wastes disk space,
which is a scarce enough resource for me (gotta fit those c.l.c
archives on the same disk :-), so 100 utilities using each 5 kB extra
waste half a megabyte.  Luckily, I don't quite have to suffer that
much, see below.

Then again, there are plenty of people who don't care, and with good
reasons: _they_ have the disk space so they don't have to care about
the size that much, and for most larger programs, an overhead of 5 kB
is, well, like a galaxy in the universe.

>why do I have to link with everything that's there in stdio.h to
>produce an executable?

You don't.  A header is (or should be) just a lot of _declarations_,
and declarations don't take up space in the compiled program, only in
the compiler's symbol table (the part of the compiler that keeps track
of all declared and defined variables, functions, etc).

What happens is that when you use any of the standard I/O functions in
your program, not only that function, but also a lot of supporting
functions also get linked into the program.  These functions include
stuff for buffer management, conversion for text-mode files, etc.
This can be a lot of stuff, and might take several kilobytes.  On top
of that, printf is usually one of the biggest functions of those
declared in the stdio library, because it so much more complex than,
for instance, puts.

>This brings me to my final question: Is there no way other than
>programming in assembly, to produce a really small executable?
>I would like to use C, but I don't know how (if at all possible)
>to do that.

It depends on the operating system and C compiler.  On _my_ machine, a
386 running Linux, using the same source code you used,

>#include <stdio.h>
>int main() { printf("Hello, World\n"); return 0;}

I get an executable that is 9216 bytes.  It is actually smaller, but
it is padded to two full 4 kB pages (one for data, one for code) so
that Linux can dynamically load any part of it while running the
program without having to parse the file to find the thousand bytes or
so it actually needs.  (This means that the operating system will only
load those parts of the program that are actually used when running
it, and can re-load them if it has to discard them because it runs out
of memory.  The wonders of good operating systems.)  The extra
kilobyte is a header that contains information about the layout of
various parts in the program file; it has to be a full disk block 
(1 kB) so that the 4 kB pages are aligned on disk block boundaries.

Now, for small programs that run quickly, there is actually no need to
be able to reload pages from the executable.  If I link the program so
that it uses an executable format that cannot be loaded, I can get rid
of the 1 kB header and don't have to make the code and data segments
occupy full 4 kB blocks.  The result is an executable of 1228 bytes.

Another useful feature of Linux is that it has shared libraries, which
means that the library routines are not actually linked into the
program, but are loaded only once for the whole system and all
programs using the library share the same copy in memory of it.  This
reduced both the size of the disk file and the amount of memory it
uses while running.  (It is possible to link "statically", so that the
shared library is not used; hello linked this way is 37888 bytes, or
31076 bytes if dynamic loading is disabled.)

Yet another thing which helps is that none of the above executables
contains symbol table information, which is useful mostly for
debugging, nor any other information not necessary for running the
program, such as support for source level debuggers.

Dynamic linking (and disabling it) and shared libraries (or dynamic
libraries, which are slightly different) are available on most modern
versions of Unix, and, I assume, most other modern operating systems.

Under DOS, with at least some versions of Borland's compilers, you can
try similar tricks by using a non-standard startup module, the c0x.obj
file that is linked before any of your own modules and which takes
care of all the stuff that has to be done before calling main.  Stuff
like breaking the command line to an argv array, and initializing the
standard I/O library.

The source code for c0x.obj is provided, and you can try hacking it so
that you can link the program without including any of the standard
libraries, if you can write the program so that it doesn't need any
functions thereof.  This means that you have to write everything in
terms of builtin functions, such as those for generating interrupts,
and this can be a pain in the parts below your back, but it might be
worth it if every byte counts.  I believe there is an example of doing
this somewhere in the files that come with the compiler.

Note that trying to write a program with no support from the standard
libraries (and that means _any_ support, not even string functions)
will result in a program that is, um, somewhat unportable.

--
Lars.Wirzenius@helsinki.fi  (finger wirzeniu@klaava.helsinki.fi)
   MS-DOS, you can't live with it, you can live without it.