Needs a (2012), this was written with the Xcode 4.4 release.
And these days ld64 is on the way toward being obsoleted by LLVM’s lld, although the latter still uses the same basic model.
Inside the Linker (2012)
11–20 of 30 posts
Re: Inside the Linker (2012)
#12Earlier quoted context omitted.
You'll need some more explanation for your claim. Second sentence: It is not "section" based like traditional linkers which mostly just interlace sections from multiple object files into the output file.
As input to a linker, Mach-O and ELF files look pretty similar: they both split their contents into named "sections", and then there's a symbol table, which is basically a list of (name, address) pairs. In C code, each symbol generally represents (the start of) a separate function or variable. All references between them are explicitly marked as relocations; thus, the linker should be free to reorder them, and any fu…
Re: Inside the Linker (2012)
#13I work on C++ code at a large tech company. My workflow is such that I compile and run tests often after I made incremental changes. I feel like I often wait for the linker for ages. I am wondering if linking could be sped up. I feel like a lot of the steps mentioned here could be cached for instance.
Re: Inside the Linker (2012)
#14Its "atom"-based concept carried over into LLVM's experimental ATOM-based ld: https://lld.llvm.org/AtomLLD.html
However as AndyKelley notes elsewhere in these comments, that variant of LLD hasn't seen progress in years.
That said, that AtomLLD appears to be a side-project of LLD, LLVM's LD project, which appears well-supported on Linux (ELF) and experimental on Windows (PE/COFF).
I wonder what Apple's plans are.
Re: Inside the Linker (2012)
#15I work on C++ code at a large tech company. My workflow is such that I compile and run tests often after I made incremental changes. I feel like I often wait for the linker for ages. I am wondering if linking could be sped up. I feel like a lot of the steps mentioned here could be cached for instance.
Re: Inside the Linker (2012)
#16Needs a (2012), this was written with the Xcode 4.4 release.
Re: Inside the Linker (2012)
#17I work on C++ code at a large tech company. My workflow is such that I compile and run tests often after I made incremental changes. I feel like I often wait for the linker for ages. I am wondering if linking could be sped up. I feel like a lot of the steps mentioned here could be cached for instance.
Visual C++ supports incremental compilation and linking.
https://blogs.msdn.microsoft.com/vcblog/2018/03/14/build-tim...
https://blogs.msdn.microsoft.com/vcblog/2014/11/12/speeding-...
https://blogs.msdn.microsoft.com/vcblog/2018/01/04/visual-st...
https://blogs.msdn.microsoft.com/vcblog/2016/10/05/faster-c-...
Re: Inside the Linker (2012)
#18Earlier quoted context omitted.
As input to a linker, Mach-O and ELF files look pretty similar: they both split their contents into named "sections", and then there's a symbol table, which is basically a list of (name, address) pairs. In C code, each symbol generally represents (the start of) a separate function or variable. All references between them are explicitly marked as relocations; thus, the linker should be free to reorder them, and any fu…
Can you say more about the last paragraph? I have often wondered if you could avoid identifier mangling with a better designed object file format
foo::bar(int,int)
…and most likely, everything that deals with binaries would have no problem with it.A bigger obstacle might be the assembler, whose input is text. Assembly files usually write symbol names without any escaping or quoting, so non-alphanumeric characters could be misinterpreted. But in fact, it seems that both GNU as and LLVM's assembler (currently used on macOS) allow optionally surrounding symbol names in quotes, allowing those characters to be used:
"foo::bar(int,int)":
jmp "foo::bar(int,int)"
Also, it seems that Clang will use this syntax where necessary when generating assembly files. This compiles: int bar(int a, int b) asm("foo::bar(int,int)");
int bar(int a, int b) {
return a + b;
}
…but GCC apparently doesn't use it; I just tried it on the latest version of GCC (8.1.0) and it produces assembly that uses the name unquoted, which then makes the assembler spit out errors.However, I've left one thing out. I think C++ symbol mangling mainly originated as a hack to support existing assemblers, but it also achieves a basic form of compression. For example, keywords are represented by a single character, and there's a "substitution" syntax for reusing the same token sequence more than once. C++ symbol names already tend to be crazy long, and having a less succinct mangling would make them even longer, which would make binaries larger and might make dynamic linking slower – though to be honest, I have no idea how much (if at all) this would be noticeable in the relative scheme of things.
Also, there has to be a single canonical mangling of any given declaration, so even if a platform decided to use C++ syntax directly in symbol names, it would probably omit spaces, unnecessary parentheses, etc., and the result might be harder to read than what you get after demangling. So a demangler might still be desirable. Still, it would certainly be more readable than the current mangling!
But that's all assuming that the overall compilation scheme would still look like today, with a 'dumb' linker that only knows about symbols and assembly code, not types or anything about C++ semantics.
You could go a step further and design an object format with native support for C++, even things like templates. Imagine being able to define a template in one .cpp file, link it into a library, and then instantiate that template from another executable! That would be enormously cool. In fact, the C++ spec used to define an 'export template' syntax that was supposed to do this, but essentially no compilers implemented it, and it was removed in C++11. (C++ modules are also kind of a form of this, but they're meant to be compiler-specific, private build artifacts rather than something defined at the system level.)
I can think of three distinct drawbacks, though:
1. C++ template semantics are very tightly bound to its syntax; there's little you can say about a template definition without knowing what it's instantiated with. Indeed, if you're going to encode template definitions in object files, the format would probably be nothing more complicated than pre-tokenized source code. More modern languages do this somewhat better – in fact, Swift actually plans to have a stable ABI for generics.
2. Similarly, C++ template semantics are very C++-specific; other languages would probably require separate support in the format rather than being able to reuse the C++ functionality. In comparison, existing 'dumb' object formats are basically language-agnostic.
3. The biggest problem: If you allowed templates to be exported from dynamic libraries, the dynamic linker's functionality would have to be transformed from a series of quick name lookups and fixups that can be done every time a binary is launched, to a full-fledged C++ compiler with an expensive code generation step. Even if you cached the output it would still be slow on first launch, especially on low-powered platforms like mobile devices…
And yet despite all those drawbacks, I still dream of a system that has… at least some form of this. (I've thought a bit about possible designs: perhaps it could be designed as a component of the package manager rather than of the linker directly.) Why?
Well, Debian just started packaging Rust code, and look at how that's going. Each library package ("crate") gets a libfoo-dev that just contains a copy of the package source code, with no libfoo binary package; each executable is statically linked, and a new package version will be released whenever any of its dependencies update. Which is going to mean a lot of redundant upgrades. That's Rust, not C++, but to the extent C++ libraries avoid this problem, it's usually by eschewing templates altogether for anything that's meant to have a stable ABI. If the API does include templates (at least ones that clients instantiate with their own parameters), then clients have to be rebuilt whenever the library changes, same as Rust. I find this quite annoying, since I think the future should be full of ergonomic, strongly-typed APIs taking full advantage of the features of modern languages… yet I don't want to burden sysadmins with pointless upgrades. :) And it just feels wrong that linkers have basically never progressed beyond C.
Re: Inside the Linker (2012)
#19Earlier quoted context omitted.
You'll need some more explanation for your claim. Second sentence: It is not "section" based like traditional linkers which mostly just interlace sections from multiple object files into the output file.
As input to a linker, Mach-O and ELF files look pretty similar: they both split their contents into named "sections", and then there's a symbol table, which is basically a list of (name, address) pairs. In C code, each symbol generally represents (the start of) a separate function or variable. All references between them are explicitly marked as relocations; thus, the linker should be free to reorder them, and any fu…
My guess would be to facilitate debugging, where you might want to invoke a symbol that wouldn't otherwise be used at runtime (e.g. some sort of debug print).
Re: Inside the Linker (2012)
#20I work on C++ code at a large tech company. My workflow is such that I compile and run tests often after I made incremental changes. I feel like I often wait for the linker for ages. I am wondering if linking could be sped up. I feel like a lot of the steps mentioned here could be cached for instance.