Linking sounds like a clean solution, but it really, really isn't. It's messy as all hell.
I think maybe, if I was to design a solution from scratch, I'd make object files basically a binary encoding of assembly code rather than true machine code; notably, I would keep jumps as abstract "jump to this label" instructions rather than proper machine code. This would put the assembler in the linker, where we actually have enough information to do instruction selection, rather than in the assembler where we don't.
We could also just put straight-up compiler IR in the object files, which puts a whole compiler back-end in the linker. This opens the door for LTO. Obviously, there are performance implications of this; if most of the hard work happens in the linker, linking can't be a stupid single-threaded serial operation anymore. But we could just make multi-threaded linkers and we get back our parallelism. We do lose some of the incrementalness of incremental compilation though.
Linkers actually don't change instruction lengths. If they need to jump further away than what the instruction allows, they add trampolines, which are chunks of code with the appropriate instruction for the long jump, and place the trampoline at a good distance of the relocatable short jump.
Edit: well, except in the proposed case of relaxation, where they'd want to shorten jump instructions.
Just a reminder that we're talking about LLVM here, whose whole point from the beginning was exactly what you described ;)
https://en.wikipedia.org/wiki/Architecture_Neutral_Distribut...
Being able to link pre-compiled object files to a new program is certainly a bit more than "a kind of incremental compilation", in my opinion. It's also a fairly clean solution to the problem of not recompiling code that hasn't changed.