No, really, they do. The whole point of an object file is to serialize an object to disk so that it can be loaded again later; but the tradition has sadly been to throw away a lot of useful information to save memory. As a result, linkers and debuggers rely on a lot of really absurd hacks to work.

If you wanted to dynamically load an object, you would ideally want to know:

  • the location(s) in memory that the object was last loaded at. these days, address-space layout randomization is used by pretty much all operating systems, but we at least need to know where the members of the object are loaded relative to each other.
  • how to relocate the object in memory.
  • the entry point of the object, if it's invocable.
  • the language-specific syntax trees of the methods of the object and it's invocation, to allow for code regeneration and debugging (because stepping through code requires disabling certain kinds of optimizations)
  • the machine-code and/or bytecode for the methods of the object and it's invocation. this can be regenerated from the syntax trees, but you typically don't want to distribute the release version of your object files with their syntax trees embedded inside. you may also distribute an object file with pre-generated machine-code for several different machines.
  • the members (symbols) of the object. this includes pre-computed hashcodes of their names, the actual names, the types, the values, and the scopes.
  • type information for every type used in the syntax trees and symbols of the object and it's methods. this includes the structure of records/classes, the prototypes of subroutines, and attributes (especially for things like calling conventions). having this information makes header files completely unnecessary.

Most of this information can be stripped for obfuscation when the object is ready for distribution. You could probably hack all these things into the ELF or PE/COFF format, but to be honest, these formats were never really designed to store objects in the literal sense. They were designed to load assembly programs into memory and dynamic loading was sort of haphazardly tacked on later. I'm not saying that we should try to replace ELF or PE/COFF (as nice as that would be); just that having a better intermediate representation would greatly benefit the development of better tools.