From 98b3c27c76ecc5e9a978faafb2079517dd9d6386 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Fri, 10 Apr 2026 12:28:29 +0200 Subject: [PATCH] builder: fix SIGSEGV when stripping duplicate function definitions The previous approach of erasing basic blocks one-by-one from duplicate function definitions could crash with a segfault. When a function has complex control flow (branches, switches, PHI nodes), deleting a basic block that is still referenced by instructions in other blocks of the same function causes use-after-free in LLVM. LLVM's C++ Function::deleteBody() avoids this by calling dropAllReferences() on all blocks first, but that API is not exposed in the Go LLVM bindings. Fix this by using a completely different approach: instead of manually deleting the function body, set the duplicate function's linkage to LinkOnceODRLinkage. This tells the LLVM linker that the definition can be merged with another copy, causing it to prefer the already-linked ExternalLinkage definition in the destination module. The result is the same (the runtime's definition wins) but without any unsafe block manipulation. --- builder/build.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/builder/build.go b/builder/build.go index 48b8e2ec6..714a331a3 100644 --- a/builder/build.go +++ b/builder/build.go @@ -551,7 +551,8 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe // body in a standard library package that was previously // just a declaration provided by //go:linkname from the // runtime. In that case, keep the existing (runtime) - // definition and turn the new one into a declaration. + // definition by weakening the new one's linkage so the + // LLVM linker discards it in favor of the existing one. for fn := pkgMod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { if fn.IsDeclaration() { continue @@ -560,9 +561,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe if existing.IsNil() || existing.IsDeclaration() { continue } - for _, bb := range fn.BasicBlocks() { - bb.EraseFromParent() - } + fn.SetLinkage(llvm.LinkOnceODRLinkage) } err = llvm.LinkModules(mod, pkgMod) if err != nil {