if oldsym.Dupok() {
return oldi
}
- // If one is a DATA symbol (i.e. has content, DataSize != 0)
- // and the other is BSS, the one with content wins.
+ // If one is a DATA symbol (i.e. has content, DataSize != 0,
+ // including RODATA) 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
//
- // new sym old sym overwrite
+ // 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 result
// ---------------------------------------------
- // DATA DATA true => ERROR
- // DATA lg/eq BSS sm/eq true => new wins
- // DATA small BSS large true => ERROR
- // 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
- if overwrite {
+ // TEXT BSS new wins
+ // DATA DATA ERROR
+ // DATA lg/eq BSS sm/eq new wins
+ // DATA small BSS large ERROR
+ // BSS large DATA small ERROR
+ // BSS large BSS small new wins
+ // BSS sm/eq D/B lg/eq old wins
+ // BSS TEXT old wins
+ oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
+ newtyp := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
+ oldIsText := oldtyp == sym.STEXT
+ newIsText := newtyp == sym.STEXT
+ oldHasContent := oldr.DataSize(oldli) != 0
+ newHasContent := r.DataSize(li) != 0
+ oldIsBSS := oldtyp.IsData() && !oldHasContent
+ newIsBSS := newtyp.IsData() && !newHasContent
+ switch {
+ case newIsText && oldIsBSS,
+ newHasContent && oldIsBSS && sz >= oldsz,
+ newIsBSS && oldIsBSS && sz > oldsz:
// 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)
- }
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)
- }
+ case newIsBSS && (oldsz >= sz || oldIsText):
+ // old win, just ignore the new symbol.
+ default:
+ 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
}
{"ok.go", true},
// push linkname is ok
{"push.go", true},
+ // using a linknamed variable to reference an assembly
+ // function in the same package is ok
+ {"textvar", true},
// pull linkname of blocked symbol is not ok
{"coro.go", false},
{"coro_var.go", false},
test := test
t.Run(test.src, func(t *testing.T) {
t.Parallel()
- src := filepath.Join("testdata", "linkname", test.src)
+ src := "./testdata/linkname/" + test.src
exe := filepath.Join(tmpdir, test.src+".exe")
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
out, err := cmd.CombinedOutput()