7 ms·
Understanding the Go Compiler: The Linker
- Surac 8mo agoI can see no difference to an ordinary linker. Anyone care to explain it to me.?
- jenoer 8mo agoWhat is there to explain? The author did not claim there is a difference in the article.
- cloudhead 8mo agoThe title is misleading
- jespino 8mo agoMisleading in what way? This is the linker part of a serie of posts about understanding the go compiler. I think there is no much space to be misleading.
- pjmlp 8mo agoWhy should it be one?
- gregwebs 8mo agoThe difference is that Go has its own linker rather than using a system linker. Another article could explain the benefits of tighter integration and the drawbacks of this approach. Having its own toolchain I assume is part of what enables the easy cross compilation of Go.
- jrockway 8mo agoYou can actually make go spit out .o files and link it with your favorite linker. Bazel does this, if you ask it to. I played a lot with experimental linkers when I was trying to get build time down for our (well, $JOB-1's) large Go binary, but they didn't help that much. The toolchain that comes with Go is quite good.
- jespino 8mo agoYes, it is not specially different from other linkers. It has some tasks building the final binary including special sections in the binary, and is more aware about the specifics of the go language. But there is nothing that is extremely different from other linkers. The whole point of the series is to explain a real compiler, but in general, most of the parts of the go compiler are very widely used in other languages, like ssa, ast, escape analysis, inlining...
- froh 8mo agowhen does golang create the final dynamic dispatch tables? isn't that the one thing that in golang needs real compute at final link time, beyond what a C linker would do? and where C++ has all information at compile time, while golang can only create the dispatch tables at link time?
- jespino 8mo agoYes, there is some information that is written by the linker in the final data section of the binary, the itab, that is the interface table for the dynamic dispatching. AFAIK, it is done there because you need to know other packages structs and interfaces to have the whole picture and build that table, and that happens using the build cache.
- froh 8mo agoyes, the interface tables! that was the word I didn't remember. and that is some computation going on there not "just" merging sections, and, in a normal static linker, wiring exports to imports, and not pulling in unneeded definitions (dead code elimination). the interface table computation is a golang speciality, a fascinating one. and the implementation of interface magic is disturbingly not mentioned in the article.
- hbogert 8mo agoI always have the unfounded feeling that the go compiler/linker does not remove dead code. Go binaries have large minimal size. Tinygo in contrast can make awesome small binaries
- gethly 8mo agoGo has a runtime. That alone is over a megabyte. Tinygo on the other hand has very limited(smaller) runtime. In other words, you don't know what you're talking about.
- clktmr 8mo agoIt's pretty good at dead code elimination. The size of Go binaries is in large part because of the runtime implementation. Remove a bunch of the runtime's features (profiling, stacktraces, sysmon, optimizations that avoid allocations, maybe even multithreading...) and you'll end up with much smaller binaries. I would love if there was a build tag like "runtime_tiny", that provides such an implementation.
- teeray 8mo agoThere is Tiny Go, which is similar to what you seek
- jrockway 8mo agoI think it depends on the codebase. There are some reflection calls that you can make that can cause dead code elimination to fail, thought I believe it's less easy to run into than it was a few years ago. One common dependency, at least in my line of work, is the Kubernetes API and it manages to both be gigantic and trigger this edge case (last I looked), so yeah, the binaries end up pretty big. Another thing that people run into is big binaries = slow container startup times. This time is mostly spent in gzip. If you use Zstandard layers instead of gzip layers, startup time is improved. gzip decompression is actually very slow, and the OCI spec no longer mandates it.
- jjcm 8mo agoThis is entirely tangential to the article, but I’ve been coding in golang now going on 5 years. For four of those years, I was a reluctant user. In the last year I’ve grown to love golang for backend web work. I find it to be one of the most bulletproof languages for agentic coding. I have a two main hypotheses as to why: - very solid corpus of well-written code for training data. Compare this to vanilla js or php - I find agents do a very poor job with both of these due to what I suspect is poorly written code that it’s been trained on. - extremely self documenting, due to structs giving agents really solid context on what the shape of the data is In any file an agent is making edits in, it has all the context it needs in the file, and it has training data that shows how to edit it with great best practices. My main gripe with go used to be that it was overly verbose, but now I actually find that to be a benefit as it greatly helps agents. Would recommend trying it out for your next project if you haven’t given it a spin.
- IhateAI_2 8mo ago[dead]
- tejinderss 8mo agoI wonder how is the experience writing Rust or Zig with LLMs. I suspect zig might not have enough training data and rust might struggle with compile times and extra context required for borrow checker.
- embedding-shape 8mo ago> I wonder how is the experience writing Rust or Zig with LLMs I've had no issues with Rust, mostly (99% of the time) using codex with gpt-5.2 xhigh and does as well as any other language. Not sure why you think compile times would be an issue, the LLM doesn't really care if it takes 1 minute or 1 hour to compile, it's more of a "your hardware + project" issue than about the LLMs. Also haven't found it to struggle with borrow checker, if it screw up it sees the compilation errors, fixes it, just like with any other languages I've tried to use with LLMs.
- jwxz 8mo ago
- vlinx 8mo agoIt's always fascinating to dive into the internals of the Go linker. One aspect I've found particularly clever is how it handles static linking by default, bundling everything into a single binary
- MisterTea 8mo agoThe Go tooling is heavily based on the Inferno tool chain which was based off the highly portable Plan 9 tool chain. Plan 9 by default is statically linked as dynamic libraries are supported but not implemented anywhere. The idea was that librarys should instead be implemented as a service that runs local or on a remote machine.
- KingOfCoders 8mo agoPerfectly happy with Go, my "Go should do X" / "Go should have Y" days are over. But if I could have a little wish, "cargo check" would be it.
- 12345hn6789 8mo agoEnums is mine. Going on year 4 working at $DAY_JOB and just last week we had a case where enums and also union types would have made things simpler.
- piinbinary 8mo agoI'm impressed with how approachable the explanation is!
- high_na_euv 8mo agoWhy not skip linker at all and generate single optimized exe file?
- jespino 8mo agoIn fact it generate single optimized exe files, but it does in multiple steps for multiple reasons, one of them is separation of concerns, but also, one of the main reasons is speed. The linker is linking (normally statically linking) different already build cached libraries, including the runtime. Without the linking ability, you would need to compile everything every time. Not only that, the linker has other responsibilities, like building some metadata that goes into the binary, for example the dynamic dispatch table.
- yuritomanek 8mo agoI don't use Go as much as I probably should but when I do I thoroughly enjoy it.