Skip to content

Commit 8cf7a0b

Browse files
committed
cmd/link: support weak binding on darwin
Symbols loaded from host files can have the N_WEAK_REF bit set, which is used to instruct the loader to not fail if that symbol can't be resolved. The Go internal linker should honor this information by setting the BIND_SYMBOL_FLAGS_WEAK_IMPORT flag in the corresponding bind table entry. Fixes #76023 Cq-Include-Trybots: luci.golang.try:gotip-darwin-amd64-longtest,gotip-darwin-amd64_12,gotip-darwin-arm64_12,gotip-darwin-arm64_15,gotip-darwin-arm64-longtest,gotip-darwin-amd64_14 Change-Id: Id2cef247ec7a9cb08455844f3c30ff874772bb7b Reviewed-on: https://go-review.googlesource.com/c/go/+/713760 Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent 2dd7e94 commit 8cf7a0b

File tree

5 files changed

+62
-1
lines changed

5 files changed

+62
-1
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build cgo && darwin
6+
7+
package cgotest
8+
9+
import "testing"
10+
11+
func TestIssue76023(t *testing.T) { issue76023(t) }
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build darwin
6+
7+
package cgotest
8+
9+
/*
10+
#cgo LDFLAGS: -Wl,-undefined,dynamic_lookup
11+
12+
extern void __gotest_cgo_null_api(void) __attribute__((weak_import));
13+
14+
int issue76023(void) {
15+
if (__gotest_cgo_null_api) return 1;
16+
return 0;
17+
}
18+
*/
19+
import "C"
20+
import "testing"
21+
22+
func issue76023(t *testing.T) {
23+
r := C.issue76023()
24+
if r != 0 {
25+
t.Error("found __gotest_cgo_null_api")
26+
}
27+
}

src/cmd/link/internal/ld/macho.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ const (
180180
BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2
181181
BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3
182182

183+
BIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1
184+
183185
BIND_OPCODE_MASK = 0xF0
184186
BIND_IMMEDIATE_MASK = 0x0F
185187
BIND_OPCODE_DONE = 0x00
@@ -1429,7 +1431,11 @@ func machoDyldInfo(ctxt *Link) {
14291431
bind.AddUint8(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | uint8(d)&0xf)
14301432
}
14311433

1432-
bind.AddUint8(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
1434+
flags := uint8(0)
1435+
if ldr.SymWeakBinding(r.targ) {
1436+
flags |= BIND_SYMBOL_FLAGS_WEAK_IMPORT
1437+
}
1438+
bind.AddUint8(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM | flags)
14331439
// target symbol name as a C string, with _ prefix
14341440
bind.AddUint8('_')
14351441
bind.Addstring(ldr.SymExtname(r.targ))

src/cmd/link/internal/loader/loader.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ type Loader struct {
241241
plt map[Sym]int32 // stores dynimport for pe objects
242242
got map[Sym]int32 // stores got for pe objects
243243
dynid map[Sym]int32 // stores Dynid for symbol
244+
weakBinding map[Sym]bool // stores whether a symbol has a weak binding
244245

245246
relocVariant map[relocId]sym.RelocVariant // stores variant relocs
246247

@@ -326,6 +327,7 @@ func NewLoader(flags uint32, reporter *ErrorReporter) *Loader {
326327
plt: make(map[Sym]int32),
327328
got: make(map[Sym]int32),
328329
dynid: make(map[Sym]int32),
330+
weakBinding: make(map[Sym]bool),
329331
attrCgoExportDynamic: make(map[Sym]struct{}),
330332
attrCgoExportStatic: make(map[Sym]struct{}),
331333
deferReturnTramp: make(map[Sym]bool),
@@ -1447,6 +1449,18 @@ func (l *Loader) SetSymExtname(i Sym, value string) {
14471449
}
14481450
}
14491451

1452+
func (l *Loader) SymWeakBinding(i Sym) bool {
1453+
return l.weakBinding[i]
1454+
}
1455+
1456+
func (l *Loader) SetSymWeakBinding(i Sym, v bool) {
1457+
// reject bad symbols
1458+
if i >= Sym(len(l.objSyms)) || i == 0 {
1459+
panic("bad symbol index in SetSymWeakBinding")
1460+
}
1461+
l.weakBinding[i] = v
1462+
}
1463+
14501464
// SymElfType returns the previously recorded ELF type for a symbol
14511465
// (used only for symbols read from shared libraries by ldshlibsyms).
14521466
// It is not set for symbols defined by the packages being linked or

src/cmd/link/internal/loadmacho/ldmacho.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,9 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
613613
}
614614
if machsym.desc&(N_WEAK_REF|N_WEAK_DEF) != 0 {
615615
l.SetAttrDuplicateOK(s, true)
616+
if machsym.desc&N_WEAK_REF != 0 {
617+
l.SetSymWeakBinding(s, true)
618+
}
616619
}
617620
machsym.sym = s
618621
if machsym.sectnum == 0 { // undefined

0 commit comments

Comments
 (0)