]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile,cmd/link: resolve cgo symbols to the correct Go ABI
authorAustin Clements <austin@google.com>
Sun, 11 Apr 2021 19:44:25 +0000 (15:44 -0400)
committerAustin Clements <austin@google.com>
Tue, 13 Apr 2021 20:07:47 +0000 (20:07 +0000)
Currently, Go functions exported to cgo have some confusion around
ABIs that leads to crashes. The cmd/cgo-generated C code references an
exported Go wrapper function (which calls the underlying exported user
function). The linker resolves this reference to the ABI0 entry-point
to that Go wrapper function because all host object references are
currently assumed to be to version 0 of a symbol. This gets passed via
crosscall2 and winds its way to cgocallbackg1, which puts this ABI0
entry-point into a function value and calls it. Unfortunately,
function values always use the ABIInternal calling convention, so
calling this ABI0 entry-point goes poorly.

Fix this by threading definition ABIs through the cgo export mechanism
so the linker can resolve host object references (which have no
concept of multiple ABIs) to the correct Go symbol. This involves a
few pieces:

- The compiler extends the cgo_export_{static,dynamic} directives that
  get passed on to the linker with symbol definition ABIs.

- The linker parses the ABIs in the cgo_export_{static,dynamic}
  directives to look up the right symbol to apply export attributes to
  and put in the dynexp list.

- For internal linking, the linker's Loader structure tracks the right
  symbol (in particular the right ABI) to resolve host object
  references to, and we use this in all of the host object loaders.

- For external linking, we mangle only the non-ABIInternal symbols
  now, so the external linker is able to resolve the correct reference
  from host objects to Go symbols.

Updates #40724.

Change-Id: I70a0b1610596768c3f473745fa1a3e630afbf1a8
Reviewed-on: https://go-review.googlesource.com/c/go/+/309341
Trust: Austin Clements <austin@google.com>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
src/cmd/compile/internal/ssagen/abi.go
src/cmd/link/internal/ld/go.go
src/cmd/link/internal/ld/macho.go
src/cmd/link/internal/ld/symtab.go
src/cmd/link/internal/loadelf/ldelf.go
src/cmd/link/internal/loader/loader.go
src/cmd/link/internal/loadmacho/ldmacho.go
src/cmd/link/internal/loadpe/ldpe.go
src/cmd/link/internal/loadxcoff/ldxcoff.go

index 9229d0212ce4e1c664e8f0272c177540639640da..8103b08ce51a7b1d5ef85b4f323a0296a898557e 100644 (file)
@@ -108,12 +108,20 @@ func (s *SymABIs) ReadSymABIs(file string) {
 // GenABIWrappers applies ABI information to Funcs and generates ABI
 // wrapper functions where necessary.
 func (s *SymABIs) GenABIWrappers() {
-       // The linker expects an ABI0 wrapper for all cgo-exported
-       // functions.
-       for _, prag := range typecheck.Target.CgoPragmas {
+       // For cgo exported symbols, we tell the linker to export the
+       // definition ABI to C. That also means that we don't want to
+       // create ABI wrappers even if there's a linkname.
+       //
+       // TODO(austin): Maybe we want to create the ABI wrappers, but
+       // ensure the linker exports the right ABI definition under
+       // the unmangled name?
+       cgoExports := make(map[string][]*[]string)
+       for i, prag := range typecheck.Target.CgoPragmas {
                switch prag[0] {
                case "cgo_export_static", "cgo_export_dynamic":
-                       s.refs[s.canonicalize(prag[1])] |= obj.ABISetOf(obj.ABI0)
+                       symName := s.canonicalize(prag[1])
+                       pprag := &typecheck.Target.CgoPragmas[i]
+                       cgoExports[symName] = append(cgoExports[symName], pprag)
                }
        }
 
@@ -153,6 +161,27 @@ func (s *SymABIs) GenABIWrappers() {
                        fn.ABI = obj.ABI0
                }
 
+               // If cgo-exported, add the definition ABI to the cgo
+               // pragmas.
+               cgoExport := cgoExports[symName]
+               for _, pprag := range cgoExport {
+                       // The export pragmas have the form:
+                       //
+                       //   cgo_export_* <local> [<remote>]
+                       //
+                       // If <remote> is omitted, it's the same as
+                       // <local>.
+                       //
+                       // Expand to
+                       //
+                       //   cgo_export_* <local> <remote> <ABI>
+                       if len(*pprag) == 2 {
+                               *pprag = append(*pprag, (*pprag)[1])
+                       }
+                       // Add the ABI argument.
+                       *pprag = append(*pprag, fn.ABI.String())
+               }
+
                // Apply references.
                if abis, ok := s.refs[symName]; ok {
                        fn.ABIRefs |= abis
@@ -169,11 +198,21 @@ func (s *SymABIs) GenABIWrappers() {
                // it's defined in this package since other packages
                // may "pull" symbols using linkname and we don't want
                // to create duplicate ABI wrappers.
+               //
+               // However, if it's given a linkname for exporting to
+               // C, then we don't make ABI wrappers because the cgo
+               // tool wants the original definition.
                hasBody := len(fn.Body) != 0
-               if sym.Linkname != "" && (hasBody || hasDefABI) {
+               if sym.Linkname != "" && (hasBody || hasDefABI) && len(cgoExport) == 0 {
                        fn.ABIRefs |= obj.ABISetCallable
                }
 
+               // Double check that cgo-exported symbols don't get
+               // any wrappers.
+               if len(cgoExport) > 0 && fn.ABIRefs&^obj.ABISetOf(fn.ABI) != 0 {
+                       base.Fatalf("cgo exported function %s cannot have ABI wrappers", fn)
+               }
+
                if !objabi.Experiment.RegabiWrappers {
                        // We'll generate ABI aliases instead of
                        // wrappers once we have LSyms in InitLSym.
index 5dbf6c71537ddb062f17681ff2f6da68e833854f..fc63b30c80f1b8a6dd5c31fe1e9ad503efd2a7ff 100644 (file)
@@ -9,6 +9,7 @@ package ld
 import (
        "bytes"
        "cmd/internal/bio"
+       "cmd/internal/obj"
        "cmd/internal/objabi"
        "cmd/internal/sys"
        "cmd/link/internal/loader"
@@ -184,7 +185,7 @@ func setCgoAttr(ctxt *Link, file string, pkg string, directives [][]string, host
                        continue
 
                case "cgo_export_static", "cgo_export_dynamic":
-                       if len(f) < 2 || len(f) > 3 {
+                       if len(f) < 2 || len(f) > 4 {
                                break
                        }
                        local := f[1]
@@ -193,13 +194,20 @@ func setCgoAttr(ctxt *Link, file string, pkg string, directives [][]string, host
                                remote = f[2]
                        }
                        local = expandpkg(local, pkg)
+                       // The compiler adds a fourth argument giving
+                       // the definition ABI of function symbols.
+                       abi := obj.ABI0
+                       if len(f) > 3 {
+                               var ok bool
+                               abi, ok = obj.ParseABI(f[3])
+                               if !ok {
+                                       fmt.Fprintf(os.Stderr, "%s: bad ABI in cgo_export directive %s\n", os.Args[0], f)
+                                       nerrors++
+                                       return
+                               }
+                       }
 
-                       // The compiler arranges for an ABI0 wrapper
-                       // to be available for all cgo-exported
-                       // functions. Link.loadlib will resolve any
-                       // ABI aliases we find here (since we may not
-                       // yet know it's an alias).
-                       s := l.LookupOrCreateSym(local, 0)
+                       s := l.LookupOrCreateSym(local, sym.ABIToVersion(abi))
 
                        if l.SymType(s) == sym.SHOSTOBJ {
                                hostObjSyms[s] = struct{}{}
@@ -239,6 +247,14 @@ func setCgoAttr(ctxt *Link, file string, pkg string, directives [][]string, host
                                        // in the exported symbol table.
                                        ctxt.dynexp = append(ctxt.dynexp, s)
                                }
+                               if ctxt.LinkMode == LinkInternal {
+                                       // For internal linking, we're
+                                       // responsible for resolving
+                                       // relocations from host objects.
+                                       // Record the right Go symbol
+                                       // version to use.
+                                       l.AddCgoExport(s)
+                               }
                                l.SetAttrCgoExportStatic(s, true)
                        } else {
                                if ctxt.LinkMode == LinkInternal && !l.AttrCgoExportDynamic(s) {
@@ -437,16 +453,6 @@ func (ctxt *Link) addexport() {
                        panic("dynexp entry not reachable")
                }
 
-               // Resolve ABI aliases in the list of cgo-exported functions.
-               // This is necessary because we load the ABI0 symbol for all
-               // cgo exports.
-               if ctxt.loader.SymType(s) == sym.SABIALIAS {
-                       t := ctxt.loader.ResolveABIAlias(s)
-                       ctxt.loader.CopyAttributes(s, t)
-                       ctxt.loader.SetSymExtname(t, ctxt.loader.SymExtname(s))
-                       s = t
-               }
-
                Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, s)
        }
 
index 872144e723e30d2f938a207c7d8c0758fc1eecbf..4c55c5761fe36d83f50f0b1601f8af90b631ed73 100644 (file)
@@ -7,6 +7,7 @@ package ld
 import (
        "bytes"
        "cmd/internal/codesign"
+       "cmd/internal/obj"
        "cmd/internal/objabi"
        "cmd/internal/sys"
        "cmd/link/internal/loader"
@@ -535,12 +536,31 @@ func (ctxt *Link) domacho() {
                sb.AddUint8(0)
        }
 
-       // Do not export C symbols dynamically in plugins, as runtime C symbols like crosscall2
-       // are in pclntab and end up pointing at the host binary, breaking unwinding.
-       // See Issue #18190.
+       // Un-export runtime symbols from plugins. Since the runtime
+       // is included in both the main binary and each plugin, these
+       // symbols appear in both images. If we leave them exported in
+       // the plugin, then the dynamic linker will resolve
+       // relocations to these functions in the plugin's functab to
+       // point to the main image, causing the runtime to think the
+       // plugin's functab is corrupted. By unexporting them, these
+       // become static references, which are resolved to the
+       // plugin's text.
+       //
+       // It would be better to omit the runtime from plugins. (Using
+       // relative PCs in the functab instead of relocations would
+       // also address this.)
+       //
+       // See issue #18190.
        if ctxt.BuildMode == BuildModePlugin {
                for _, name := range []string{"_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2"} {
-                       s := ctxt.loader.Lookup(name, 0)
+                       // Most of these are data symbols or C
+                       // symbols, so they have symbol version 0.
+                       ver := 0
+                       // _cgo_panic is a Go function, so it uses ABIInternal.
+                       if name == "_cgo_panic" {
+                               ver = sym.ABIToVersion(obj.ABIInternal)
+                       }
+                       s := ctxt.loader.Lookup(name, ver)
                        if s != 0 {
                                ctxt.loader.SetAttrCgoExportDynamic(s, false)
                        }
index bd8e4cb4bdec104175649846c97fe4227025b931..dcb9d7eb484dedb65f4f34c392dd83f0fe147961 100644 (file)
@@ -837,33 +837,20 @@ func mangleABIName(ldr *loader.Loader, x loader.Sym, name string) string {
        // For functions with ABI wrappers, we have to make sure that we
        // don't wind up with two elf symbol table entries with the same
        // name (since this will generated an error from the external
-       // linker). In the CgoExportStatic case, we want the ABI0 symbol
-       // to have the primary symbol table entry (since it's going to be
-       // called from C), so we rename the ABIInternal symbol. In all
-       // other cases, we rename the ABI0 symbol, since we want
-       // cross-load-module calls to target ABIInternal.
-       //
-       // TODO: this is currently only used on ELF and PE. Other platforms?
+       // linker). If we have wrappers, keep the ABIInternal name
+       // unmangled since we want cross-load-module calls to target
+       // ABIInternal, and rename other symbols.
        //
        // TODO: avoid the ldr.Lookup calls below by instead using an aux
        // sym or marker relocation to associate the wrapper with the
        // wrapped function.
-       //
        if !objabi.Experiment.RegabiWrappers {
                return name
        }
-       if !ldr.IsExternal(x) && ldr.SymType(x) == sym.STEXT {
-               // First case
-               if ldr.SymVersion(x) == sym.SymVerABIInternal {
-                       if s2 := ldr.Lookup(name, sym.SymVerABI0); s2 != 0 && ldr.AttrCgoExportStatic(s2) && ldr.SymType(s2) == sym.STEXT {
-                               name = name + ".abiinternal"
-                       }
-               }
-               // Second case
-               if ldr.SymVersion(x) == sym.SymVerABI0 && !ldr.AttrCgoExportStatic(x) {
-                       if s2 := ldr.Lookup(name, sym.SymVerABIInternal); s2 != 0 && ldr.SymType(s2) == sym.STEXT {
-                               name = name + ".abi0"
-                       }
+
+       if !ldr.IsExternal(x) && ldr.SymType(x) == sym.STEXT && ldr.SymVersion(x) != sym.SymVerABIInternal {
+               if s2 := ldr.Lookup(name, sym.SymVerABIInternal); s2 != 0 && ldr.SymType(s2) == sym.STEXT {
+                       name = fmt.Sprintf("%s.abi%d", name, ldr.SymVersion(x))
                }
        }
        return name
index 28284e9de0d0f2965446e1c57fcf3ac9001e18ab..c6956297f6c79f8ba62b7e68a6609a0579353b97 100644 (file)
@@ -245,9 +245,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
        newSym := func(name string, version int) loader.Sym {
                return l.CreateStaticSym(name)
        }
-       lookup := func(name string, version int) loader.Sym {
-               return l.LookupOrCreateSym(name, version)
-       }
+       lookup := l.LookupOrCreateCgoExport
        errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) {
                return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
        }
index adc8195ace68e9e74cb1ab0017ef173ecf6e5c41..141dd0ac688eb6d725c0602b674cf68c7aa23076 100644 (file)
@@ -257,6 +257,9 @@ type Loader struct {
        // the symbol that triggered the marking of symbol K as live.
        Reachparent []Sym
 
+       // CgoExports records cgo-exported symbols by SymName.
+       CgoExports map[string]Sym
+
        flags uint32
 
        hasUnknownPkgPath bool // if any Go object has unknown package path
@@ -514,6 +517,36 @@ func (l *Loader) LookupOrCreateSym(name string, ver int) Sym {
        return i
 }
 
+// AddCgoExport records a cgo-exported symbol in l.CgoExports.
+// This table is used to identify the correct Go symbol ABI to use
+// to resolve references from host objects (which don't have ABIs).
+func (l *Loader) AddCgoExport(s Sym) {
+       if l.CgoExports == nil {
+               l.CgoExports = make(map[string]Sym)
+       }
+       l.CgoExports[l.SymName(s)] = s
+}
+
+// LookupOrCreateCgoExport is like LookupOrCreateSym, but if ver
+// indicates a global symbol, it uses the CgoExport table to determine
+// the appropriate symbol version (ABI) to use. ver must be either 0
+// or a static symbol version.
+func (l *Loader) LookupOrCreateCgoExport(name string, ver int) Sym {
+       if ver >= sym.SymVerStatic {
+               return l.LookupOrCreateSym(name, ver)
+       }
+       if ver != 0 {
+               panic("ver must be 0 or a static version")
+       }
+       // Look for a cgo-exported symbol from Go.
+       if s, ok := l.CgoExports[name]; ok {
+               return s
+       }
+       // Otherwise, this must just be a symbol in the host object.
+       // Create a version 0 symbol for it.
+       return l.LookupOrCreateSym(name, 0)
+}
+
 func (l *Loader) IsExternal(i Sym) bool {
        r, _ := l.toLocal(i)
        return l.isExtReader(r)
index 6d1d9bb29edd5646994e454a2b9a5da1dcacc338..e7d9eebc33f792236e84bc0a9ae725756963f098 100644 (file)
@@ -607,7 +607,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader,
                if machsym.type_&N_EXT == 0 {
                        v = localSymVersion
                }
-               s := l.LookupOrCreateSym(name, v)
+               s := l.LookupOrCreateCgoExport(name, v)
                if machsym.type_&N_EXT == 0 {
                        l.SetAttrDuplicateOK(s, true)
                }
index f474dfb276a5e41b76e4110774976ed6296a8059..9cc7effe1f4b858385b09dfc56565bf4c516f9b0 100644 (file)
@@ -178,11 +178,7 @@ func makeUpdater(l *loader.Loader, bld *loader.SymbolBuilder, s loader.Sym) *loa
 // If an .rsrc section or set of .rsrc$xx sections is found, its symbols are
 // returned as rsrc.
 func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, rsrc []loader.Sym, err error) {
-       lookup := func(name string, version int) (*loader.SymbolBuilder, loader.Sym) {
-               s := l.LookupOrCreateSym(name, version)
-               sb := l.MakeSymbolUpdater(s)
-               return sb, s
-       }
+       lookup := l.LookupOrCreateCgoExport
        sectsyms := make(map[*pe.Section]loader.Sym)
        sectdata := make(map[*pe.Section][]byte)
 
@@ -214,7 +210,8 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
                }
 
                name := fmt.Sprintf("%s(%s)", pkg, sect.Name)
-               bld, s := lookup(name, localSymVersion)
+               s := lookup(name, localSymVersion)
+               bld := l.MakeSymbolUpdater(s)
 
                switch sect.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) {
                case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata
@@ -272,7 +269,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
                                return nil, nil, fmt.Errorf("relocation number %d symbol index idx=%d cannot be large then number of symbols %d", j, r.SymbolTableIndex, len(f.COFFSymbols))
                        }
                        pesym := &f.COFFSymbols[r.SymbolTableIndex]
-                       _, gosym, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
+                       _, gosym, err := readpesym(l, arch, lookup, f, pesym, sectsyms, localSymVersion)
                        if err != nil {
                                return nil, nil, err
                        }
@@ -414,7 +411,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
                        }
                }
 
-               bld, s, err := readpesym(l, arch, l.LookupOrCreateSym, f, pesym, sectsyms, localSymVersion)
+               bld, s, err := readpesym(l, arch, lookup, f, pesym, sectsyms, localSymVersion)
                if err != nil {
                        return nil, nil, err
                }
index a5744216d66ae108d6ec1f8a652fb9f2b6a1ce4b..920e1c85fd4d7c6ba366d4aa2009ddd6ff8247b8 100644 (file)
@@ -121,7 +121,7 @@ func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Read
                }
                sb := l.MakeSymbolUpdater(sect.sym)
                for _, rx := range sect.Relocs {
-                       rSym := l.LookupOrCreateSym(rx.Symbol.Name, 0)
+                       rSym := l.LookupOrCreateCgoExport(rx.Symbol.Name, 0)
                        if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress {
                                return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress)
                        }