From 2d477e069dc7f2fe6c6ab910a48d2de885069238 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Fri, 10 Apr 2026 09:47:47 +0200 Subject: [PATCH] builder, runtime: fix duplicate symbol error with Go 1.26+ on Windows Go 1.26 changed syscall.loadlibrary, syscall.loadsystemlibrary, and syscall.getprocaddress from declarations to definitions in syscall/dll_windows.go. TinyGo's runtime also defines these via //go:linkname, causing "symbol multiply defined!" during LLVM module linking. Resolve this by detecting duplicate function definitions before calling llvm.LinkModules and turning the incoming duplicate into a declaration, so the runtime's version wins. TinyGo's implementations must take precedence because Go 1.26's versions depend on //go:cgo_import_dynamic, which TinyGo does not support. Also fix the signature of syscall_loadsystemlibrary to match the standard library (remove unused absoluteFilepath parameter). Signed-off-by: deadprogram --- builder/build.go | 18 ++++++++++++++++++ src/runtime/runtime_windows.go | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/builder/build.go b/builder/build.go index 44f41eb23..48b8e2ec6 100644 --- a/builder/build.go +++ b/builder/build.go @@ -546,6 +546,24 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe if err != nil { return fmt.Errorf("failed to load bitcode file: %w", err) } + // Resolve duplicate function definitions before linking. + // This can happen when a newer Go version adds a function + // 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. + for fn := pkgMod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) { + if fn.IsDeclaration() { + continue + } + existing := mod.NamedFunction(fn.Name()) + if existing.IsNil() || existing.IsDeclaration() { + continue + } + for _, bb := range fn.BasicBlocks() { + bb.EraseFromParent() + } + } err = llvm.LinkModules(mod, pkgMod) if err != nil { return fmt.Errorf("failed to link module: %w", err) diff --git a/src/runtime/runtime_windows.go b/src/runtime/runtime_windows.go index c4df8dec3..8419fb4f8 100644 --- a/src/runtime/runtime_windows.go +++ b/src/runtime/runtime_windows.go @@ -247,7 +247,7 @@ func growHeap() bool { } //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary -func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { +func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) { handle = _LoadLibraryExW(filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) if handle == 0 { panic("todo: get error")