Back in the dim, dark past, there was simple hardware. This simple hardware supported a simple, small system. a.out was completely adequate for the job of representing binaries on this simple system (a PDP-11). As people ported unix from this simple system, they retained the a.out format because it was sufficient for the early ports of unix to architectures like the Motorola 68k, VAXen, etc.
Then some bright hardware engineer decided that if he could
force software to do some sleazy tricks, then he'd be able to
shave a few gates off the design and allow his CPU core to run
faster. While it was made to work with this new kind of
hardware (known these days as RISC), a.out
was ill-suited
for this hardware, so many formats were developed to get to a
better performance from this hardware than the limited, simple
a.out
format could offer. Things like COFF
,
ECOFF
, and a few obscure others were invented and their
limitations explored before things seemed to settle on ELF
.
In addition, program sizes were getting huge and disks (and
physical memory) were still relatively small so the concept of a
shared library was born. The VM system also became more
sophisticated. While each one of these advancements was done
using the a.out
format, its usefulness was stretched more
and more with each new feature. In addition, people wanted to
dynamically load things at run time, or to junk parts of their
program after the init code had run to save in core memory
and/or swap space. Languages became more sophistocated and
people wanted code called before main automatically. Lots of
hacks were done to the a.out
format to allow all of these
things to happen, and they basically worked for a time. In
time, a.out
wasn't up to handling all these problems
without an ever increasing overhead in code and complexity.
While ELF
solved many of these problems, it would be
painful to switch from the system that basically worked. So
ELF
had to wait until it was more painful to remain with
a.out
than it was to migrate to ELF
.
However, as time passed, the build tools that FreeBSD derived
their build tools from (the assembler and loader especially)
evolved in two parallel trees. The FreeBSD tree added shared
libraries and fixed some bugs. The GNU folks that originally
write these programs rewrote them and added simpler support for
building cross compilers, plugging in different formats at will,
etc. Since many people wanted to build cross compilers
targeting FreeBSD, they were out of luck since the older sources
that FreeBSD had for as and ld weren't up to the task. The new
gnu tools chain (binutils) does support cross compiling,
ELF
, shared libraries, C++ extnensions, etc. In addition,
many vendors are releasing ELF
binaries, and it is a good
thing for FreeBSD to run them. And if it is running ELF
binaries, why bother having a.out
any more? It is a tired
old horse that has proven useful for a long time, but it is time
to turn him out to pasture for his long, faithful years of
service.
ELF
is more expressive than a.out and will allow more
extensibility in the base system. The ELF
tools are better
maintained, and offer cross compilation support, which is
important to many people. ELF
may be a little slower than
a.out, but trying to measure it can be difficult. There are
also numerous details that are different between the two in how
they map pages, handle init code, etc. None of these are very
important, but they are differences. In time support for
a.out
will be moved out of the GENERIC kernel, and
eventually removed from the kernel once the need to run legacy
a.out
programs is past.