After separate compilation
One file can define a function while another contains a symbolic reference to it. A linker reads object files and archives, connects definitions with references, and relocates locations that depend on the final layout.
Successful compilation of each source file therefore does not guarantee successful linking. Missing definitions or incompatible input formats can prevent the separately produced pieces from becoming a usable output.
Controlling the output layout
GNU ld scripts can specify how input sections enter output sections. Its simple example gathers .text sections into code and separately places .data and .bss, using a location counter to establish addresses.
Linking is consequently more than concatenating files. Symbols, sections, addresses and the target format determine the result; a script can express a required memory layout.
References: [2]
Formats, drivers and optimization
LLD provides linkers for formats including ELF, PE/COFF, Mach-O and WebAssembly. Compiler drivers normally invoke it, bringing the target platform’s startup files and runtime libraries into the invocation.
With link-time optimization, compiler intermediate code can reach the link stage and expose cross-file optimization opportunities. This does not make arbitrary source languages or incompatible binary interfaces interchangeable.
References: [3]