-
Notifications
You must be signed in to change notification settings - Fork 18k
cmd/link: Go 1.24.3 and 1.23.9 regression - duplicated definition of symbol dlopen #73617
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The same issue also happens to me natively on darwin/amd64 |
I'm guessing it may have been introduced here: https://go-review.googlesource.com/c/go/+/662335/3/src/cmd/link/internal/loader/loader.go Maybe a corner case where |
cc @golang/compiler @cherrymui |
Possibly this is an interaction between the |
Oh, it is probably between the assembly symbol and data symbol at https://github.com/ebitengine/purego/blob/9059adfae616e486aa4145d6f4d5fefaa1b47a61/dlfcn.go#L85-L86. |
I don't believe they have the same name though. One is Subject: [PATCH] rename assembly
---
Index: dlfcn.go
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/dlfcn.go b/dlfcn.go
--- a/dlfcn.go (revision 2c36c15debde48b37b30e9ae9dea32483ef79922)
+++ b/dlfcn.go (date 1746629059115)
@@ -82,18 +82,18 @@
// sadly, I do not know of anyway to remove the assembly stubs entirely because //go:linkname doesn't
// appear to work if you link directly to the C function on darwin arm64.
-//go:linkname dlopen dlopen
+//go:linkname dlopen go_dlopen
var dlopen uintptr
var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
-//go:linkname dlsym dlsym
+//go:linkname dlsym go_dlsym
var dlsym uintptr
var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
-//go:linkname dlclose dlclose
+//go:linkname dlclose go_dlclose
var dlclose uintptr
var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
-//go:linkname dlerror dlerror
+//go:linkname dlerror go_dlerror
var dlerror uintptr
var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))
Index: dlfcn_stubs.s
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/dlfcn_stubs.s b/dlfcn_stubs.s
--- a/dlfcn_stubs.s (revision 2c36c15debde48b37b30e9ae9dea32483ef79922)
+++ b/dlfcn_stubs.s (date 1746629059143)
@@ -6,21 +6,21 @@
#include "textflag.h"
// func dlopen(path *byte, mode int) (ret uintptr)
-TEXT dlopen(SB), NOSPLIT|NOFRAME, $0-0
+TEXT go_dlopen(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_dlopen(SB)
RET
// func dlsym(handle uintptr, symbol *byte) (ret uintptr)
-TEXT dlsym(SB), NOSPLIT|NOFRAME, $0-0
+TEXT go_dlsym(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_dlsym(SB)
RET
// func dlerror() (ret *byte)
-TEXT dlerror(SB), NOSPLIT|NOFRAME, $0-0
+TEXT go_dlerror(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_dlerror(SB)
RET
// func dlclose(handle uintptr) (ret int)
-TEXT dlclose(SB), NOSPLIT|NOFRAME, $0-0
+TEXT go_dlclose(SB), NOSPLIT|NOFRAME, $0-0
JMP purego_dlclose(SB)
RET |
Yes, it is the assembly (TEXT) symbol and the data (BSS) symbol. The assembly function is short (6 bytes), smaller than the data symbol (uintptr, 8 bytes). Previously we just pick the one with content, i.e. the text symbol. Now we error out if the non-content symbol is larger, in the sense that if the code actually reads the variable as a uintptr, it will read to the next symbol. In this case, the code doesn't read the variable, just takes its address, which is fine. For backward compatibility we probably want to still allow this case, at least if one side is TEXT symbol. There is no easy way in the linker to detect whether we read the content as a variable. For a quick fix, you can change the variable definition from uintptr to uint8, like |
They do. Both sides don't have the dot. The name of the assembly symbol is the one at the TEXT line, i.e. Your patch changes both from dlopen to go_dlopen, so they still have the same name. |
This patch to the linker may make it work. I haven't thoroughly tested it though. diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 128173b8cf..52d48912c7 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -455,29 +455,45 @@ func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind in
// If one is a DATA symbol (i.e. has content, DataSize != 0)
// and the other is BSS, the one with content wins.
// If both are BSS, the one with larger size wins.
- // Specifically, the "overwrite" variable and the final result are
+ //
+ // For a special case, we allow a TEXT symbol overwrites a BSS symbol
+ // even if the BSS symbol has larger size. This is because there is
+ // code like below to take the address of a function
+ //
+ // //go:linkname fn
+ // var fn uintptr
+ // var fnAddr = uintptr(unsafe.Pointer(&fn))
+ //
+ // TODO: maybe limit this case to just pointer sized variable?
+ //
+ // In summary, the "overwrite" variable and the final result are
//
// new sym old sym overwrite
// ---------------------------------------------
// DATA DATA true => ERROR
// DATA lg/eq BSS sm/eq true => new wins
// DATA small BSS large true => ERROR
+ // TEXT BSS true => new wins
// BSS large DATA small true => ERROR
// BSS large BSS small true => new wins
// BSS sm/eq D/B lg/eq false => old wins
- overwrite := r.DataSize(li) != 0 || oldsz < sz
+ // BSS TEXT false => old wins
+ overwrite := r.DataSize(li) != 0 || oldsz < sz ||
+ sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())].IsText()
if overwrite {
// new symbol overwrites old symbol.
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
- if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) || oldsz > sz {
- log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
+ newtyp := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+ if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) || (oldsz > sz && !newtyp.IsText()) {
+ log.Fatalf("duplicated definition of symbol %s, from %s (type %s size %d) and %s (type %s size %d)", name, r.unit.Lib.Pkg, newtyp, sz, oldr.unit.Lib.Pkg, oldtyp, oldsz)
}
l.objSyms[oldi] = objSym{r.objidx, li}
} else {
// old symbol overwrites new symbol.
- typ := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
- if !typ.IsData() { // only allow overwriting data symbol
- log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
+ newtyp := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+ if !newtyp.IsData() { // only allow overwriting data symbol
+ oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
+ log.Fatalf("duplicated definition of symbol %s, from %s (type %s size %d) and %s (type %s size %d)", name, r.unit.Lib.Pkg, newtyp, sz, oldr.unit.Lib.Pkg, oldtyp, oldsz)
}
}
return oldi |
This addresses the issue golang/go#73617. Closes #313
This addresses the issue golang/go#73617. Closes #313
The patch appears to be working for the builds that were failing for us. |
updating to github.com/ebitengine/purego v0.9.0-alpha.3.0.20250507171635-5047c08daa38 made it work, thank you! |
This works, though I recommend the stable branch version: go get github.com/ebitengine/purego@1638563e361522e5f63511d84c4541ae1c5fd704 |
## Which problem is this PR solving? - Our release #7107 is blocked by the build error `link: duplicated definition of symbol dlopen, from github.com/ebitengine/purego and github.com/ebitengine/purego` - Ongoing upstream issue golang/go#73617 ## Description of the changes - Pin to a pre-release of https://github.com/ebitengine/purego v0.8.3 that provides a patch ## How was this change tested? - CI --------- Signed-off-by: Yuri Shkuro <[email protected]>
Go version
go 1.24.3/1.23.9 (linux amd64 --> darwin amd64)
Output of
go env
in your module/workspace:What did you do?
I went to release Go 1.24.3 and Go 1.23.9 into our build system and some packages began failing when trying to cross-compile to darwin/amd64:
I've reproduced it down to a small example:
What did you see happen?
I received an error:
link: duplicated definition of symbol dlopen, from github.com/ebitengine/purego and github.com/ebitengine/purego
What did you expect to see?
I expected it to continue to compile, as it did in Go 1.24.2
The text was updated successfully, but these errors were encountered: