Según recuerdo, uno de los problemas originales con el formato a.out es que solo admitía tres secciones:texto, datos y bss. ELF permite cualquier número (o al menos muchos más). El formato del encabezado a.out era muy simple, algo así como:
word <magic>
word <text size>
word <data size>
word <bss size>
El formato ELF, en cambio, tiene encabezados de sección, con nombres, tamaños, etc.
Tener más secciones permite las secciones estándar, pero también nos da secciones constantes, secciones de constructor e incluso una sección por función, si lo queremos.
El a.out
El formato obligaba a las bibliotecas compartidas a ocupar un lugar fijo en la memoria. Si quería distribuir una biblioteca compartida a.out, tenía que registrar su espacio de direcciones. Esto fue bueno para el rendimiento, pero no escaló en absoluto. Comprueba por ti mismo lo complicado que fue (linuxjournal).
Por el contrario, en ELF, las bibliotecas compartidas se pueden cargar en cualquier lugar de la memoria e incluso pueden parecer que están en diferentes direcciones para diferentes aplicaciones que se ejecutan en la misma computadora (¡con el código todavía cargado efectivamente en un solo lugar en la memoria física)! Para lograr esto, en la arquitectura IA-32, se debe sacrificar un registro (%ebx). Una referencia más completa que muestra que las bibliotecas compartidas se volvieron más complicadas en ELF, pero esa era la complejidad del lado del compilador, a diferencia del lado del programador.