Skip to main content
Engineering, Backend

Fixing Go’s Linker: An Unexpected Journey into ARM64, DWARF, and Linker Internals

16 February 2023 / Global
Featured image for Fixing Go’s Linker: An Unexpected Journey into ARM64, DWARF, and Linker Internals
Image
Figure 1: Interactive debug of main function of fooService compiled for AMD64
Image
Figure 2: Interactive debug of main function of fooService compiled for ARM64
Image
Figure 3: Dumping LPT of main function of fooService compiled for ARM64
Image
Figure 4: Dumping LPT of main function of fooService compiled for AMD64
Image
Figure 5: The process of building helloworld
Image
Figure 6: Verbose build of helloworld on ARM64
Image
Figure 7: Disassembly of the main function of helloworld ARM64 compiled object file 
Image
Figure 8: Disassembly of the main function of helloworld ARM64 linked executable file
Image
Figure 9: Side by side comparison of the ARM64 vs AMD64 calls to fx.New in main() of fooService linked executable
Image
Figure 10: Disassembly of the fx.New+0-tramp0 code in the ARM64 linked executable file of fooService
Image
Figure 11: Interactive debug step through of the call to fx.New+0-tramp0 of the ARM64 linked executable file of fooService
Image
Figure 12: Instructions the compiler uses to make the call to fx.New for ARM64 and AMD64 in executable fooService
Image
Figure 13: The limitations in call distance of AMD64 vs ARM64
Image
Figure 14: Why the ARM64 version of fooService needs a trampoline from main to fx.New
Image
Figure 15: The high level process the Go linker (as of Go 1.19) uses to insert DWARF LPT
Image
Figure 16: Portions of the relevant code used to insert the DWARF LPT inside the Go linker
Image
Figure 17: The high-level process the Go linker (as of Go 1.19) uses to assign function addresses
Image
Figure 18: Reproducing the issue on any trivial ARM64 Go binary (helloworld)
Image
Image
Image
Figure 19A, 19B, 19C: Portions of the relevant code used to insert trampolines inside the Go linker
Image
Figure 20: A simple change to the DWARF LPT generator that fixes the bug.
Jeremy Quirke

Jeremy Quirke

Jeremy Quirke is a Senior Staff Engineer at Uber working primarily on the underlying technology behind Earner Upfront Pricing, and is passionate about improving engineering tooling.

Posted by Jeremy Quirke