]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/link: add support for external linking on linux/riscv64
authorJoel Sing <joel@sing.id.au>
Thu, 13 Aug 2020 14:13:28 +0000 (00:13 +1000)
committerJoel Sing <joel@sing.id.au>
Sun, 18 Oct 2020 08:30:18 +0000 (08:30 +0000)
Fixes #36739

Change-Id: Id7573b343786360c72524f9f27d2a8f08d379cf3
Reviewed-on: https://go-review.googlesource.com/c/go/+/243517
Trust: Joel Sing <joel@sing.id.au>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Go Bot <gobot@golang.org>

src/cmd/dist/test.go
src/cmd/link/internal/ld/config.go
src/cmd/link/internal/ld/elf.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/main.go
src/cmd/link/internal/loader/loader.go
src/cmd/link/internal/riscv64/asm.go
src/cmd/link/internal/riscv64/obj.go

index 94b81613a930647b653de221f07c865e8a2078d2..bcb12f29fbdd4b1a15df2715079fe8eabd160b5f 100644 (file)
@@ -921,7 +921,7 @@ func (t *tester) extLink() bool {
                "darwin-amd64", "darwin-arm64",
                "dragonfly-amd64",
                "freebsd-386", "freebsd-amd64", "freebsd-arm",
-               "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-s390x",
+               "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-mips64", "linux-mips64le", "linux-mips", "linux-mipsle", "linux-riscv64", "linux-s390x",
                "netbsd-386", "netbsd-amd64",
                "openbsd-386", "openbsd-amd64",
                "windows-386", "windows-amd64":
index f55e4fc027d39a38a6a2684140705e04fd19c162..834c87d06be8d9e830ebce1c03f2099a421a1ad0 100644 (file)
@@ -202,7 +202,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
        // Internally linking cgo is incomplete on some architectures.
        // https://golang.org/issue/14449
        // https://golang.org/issue/21961
-       if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64) {
+       if iscgo && ctxt.Arch.InFamily(sys.MIPS64, sys.MIPS, sys.PPC64, sys.RISCV64) {
                return true, objabi.GOARCH + " does not support internal cgo"
        }
        if iscgo && objabi.GOOS == "android" {
@@ -285,8 +285,6 @@ func determineLinkMode(ctxt *Link) {
                }
        case LinkExternal:
                switch {
-               case objabi.GOARCH == "riscv64":
-                       Exitf("external linking not supported for %s/riscv64", objabi.GOOS)
                case objabi.GOARCH == "ppc64" && objabi.GOOS != "aix":
                        Exitf("external linking not supported for %s/ppc64", objabi.GOOS)
                }
index f5a2f899fc4533c0d58fc1fb6af0248cc140fb83..f44e16583db0ec309b8d8a0551a01ec1c7189b7b 100644 (file)
@@ -506,6 +506,9 @@ func Elfinit(ctxt *Link) {
                if ctxt.Arch.Family == sys.MIPS64 {
                        ehdr.flags = 0x20000004 /* MIPS 3 CPIC */
                }
+               if ctxt.Arch.Family == sys.RISCV64 {
+                       ehdr.flags = 0x4 /* RISCV Float ABI Double */
+               }
                elf64 = true
 
                ehdr.phoff = ELF64HDRSIZE      /* Must be ELF64HDRSIZE: first PHdr must follow ELF header */
index 8d04973fcf8291aa7ce50605eb9cce3a910a5f59..a68725bef950e732691feb897c9333988a5eefcc 100644 (file)
@@ -247,12 +247,16 @@ type Arch struct {
        Elfreloc1      func(*Link, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int, int64) bool
        ElfrelocSize   uint32 // size of an ELF relocation record, must match Elfreloc1.
        Elfsetupplt    func(ctxt *Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym)
-       Gentext        func(*Link, *loader.Loader)
+       Gentext        func(*Link, *loader.Loader) // Generate text before addressing has been performed.
        Machoreloc1    func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
        MachorelocSize uint32 // size of an Mach-O relocation record, must match Machoreloc1.
        PEreloc1       func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
        Xcoffreloc1    func(*sys.Arch, *OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool
 
+       // Generate additional symbols for the native symbol table just prior to
+       // code generation.
+       GenSymsLate func(*Link, *loader.Loader)
+
        // TLSIEtoLE converts a TLS Initial Executable relocation to
        // a TLS Local Executable relocation.
        //
index 3f7370b636e33004db3e7ea28222f55fb0057f56..5ae57d19922183cd8cacbd25954521b7b13e33b3 100644 (file)
@@ -341,6 +341,14 @@ func Main(arch *sys.Arch, theArch Arch) {
                }(f, s)
        }
        wg.Wait()
+
+       // Generate additional symbols for the native symbol table just prior
+       // to code generation.
+       bench.Start("GenSymsLate")
+       if thearch.GenSymsLate != nil {
+               thearch.GenSymsLate(ctxt, ctxt.loader)
+       }
+
        bench.Start("Asmb2")
        asmb2(ctxt)
 
index 19d8d98b1e9eab1fa7dbb48c6ba0f4372fe64424..47cac0441bc2f69a7450f621acb81568e017699c 100644 (file)
@@ -1791,6 +1791,11 @@ func (l *Loader) SortSub(s Sym) Sym {
        return sl[0].s
 }
 
+// SortSyms sorts a list of symbols by their value.
+func (l *Loader) SortSyms(ss []Sym) {
+       sort.SliceStable(ss, func(i, j int) bool { return l.SymValue(ss[i]) < l.SymValue(ss[j]) })
+}
+
 // Insure that reachable bitmap and its siblings have enough size.
 func (l *Loader) growAttrBitmaps(reqLen int) {
        if reqLen > l.attrReachable.Len() {
index 1236145fb12929f1d17fc9579f376c67f639ca69..66c47c69f8f55d1b862488ae76cd9816cc4c43aa 100644 (file)
@@ -11,20 +11,138 @@ import (
        "cmd/link/internal/ld"
        "cmd/link/internal/loader"
        "cmd/link/internal/sym"
+       "debug/elf"
        "fmt"
        "log"
+       "sort"
 )
 
+// fakeLabelName matches the RISCV_FAKE_LABEL_NAME from binutils.
+const fakeLabelName = ".L0 "
+
 func gentext(ctxt *ld.Link, ldr *loader.Loader) {
 }
 
+func genSymsLate(ctxt *ld.Link, ldr *loader.Loader) {
+       if ctxt.LinkMode != ld.LinkExternal {
+               return
+       }
+
+       // Generate a local text symbol for each relocation target, as the
+       // R_RISCV_PCREL_LO12_* relocations generated by elfreloc1 need it.
+       if ctxt.Textp == nil {
+               log.Fatal("genSymsLate called before Textp has been assigned")
+       }
+       var hi20Syms []loader.Sym
+       for _, s := range ctxt.Textp {
+               relocs := ldr.Relocs(s)
+               for ri := 0; ri < relocs.Count(); ri++ {
+                       r := relocs.At(ri)
+                       if r.Type() != objabi.R_RISCV_PCREL_ITYPE && r.Type() != objabi.R_RISCV_PCREL_STYPE {
+                               continue
+                       }
+                       if r.Off() == 0 && ldr.SymType(s) == sym.STEXT {
+                               // Use the symbol for the function instead of creating
+                               // an overlapping symbol.
+                               continue
+                       }
+
+                       // TODO(jsing): Consider generating ELF symbols without needing
+                       // loader symbols, in order to reduce memory consumption. This
+                       // would require changes to genelfsym so that it called
+                       // putelfsym and putelfsyment as appropriate.
+                       sb := ldr.MakeSymbolBuilder(fakeLabelName)
+                       sb.SetType(sym.STEXT)
+                       sb.SetValue(ldr.SymValue(s) + int64(r.Off()))
+                       sb.SetLocal(true)
+                       sb.SetReachable(true)
+                       sb.SetVisibilityHidden(true)
+                       sb.SetSect(ldr.SymSect(s))
+                       if outer := ldr.OuterSym(s); outer != 0 {
+                               ldr.AddInteriorSym(outer, sb.Sym())
+                       }
+                       hi20Syms = append(hi20Syms, sb.Sym())
+               }
+       }
+       ctxt.Textp = append(ctxt.Textp, hi20Syms...)
+       ldr.SortSyms(ctxt.Textp)
+}
+
+func findHI20Symbol(ctxt *ld.Link, ldr *loader.Loader, val int64) loader.Sym {
+       idx := sort.Search(len(ctxt.Textp), func(i int) bool { return ldr.SymValue(ctxt.Textp[i]) >= val })
+       if idx >= len(ctxt.Textp) {
+               return 0
+       }
+       if s := ctxt.Textp[idx]; ldr.SymValue(s) == val && ldr.SymType(s) == sym.STEXT {
+               return s
+       }
+       return 0
+}
+
 func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
-       log.Fatalf("elfreloc1")
-       return false
+       elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
+       switch r.Type {
+       case objabi.R_ADDR, objabi.R_DWARFSECREF:
+               out.Write64(uint64(sectoff))
+               switch r.Size {
+               case 4:
+                       out.Write64(uint64(elf.R_RISCV_32) | uint64(elfsym)<<32)
+               case 8:
+                       out.Write64(uint64(elf.R_RISCV_64) | uint64(elfsym)<<32)
+               default:
+                       ld.Errorf(nil, "unknown size %d for %v relocation", r.Size, r.Type)
+                       return false
+               }
+               out.Write64(uint64(r.Xadd))
+
+       case objabi.R_CALLRISCV:
+               // Call relocations are currently handled via R_RISCV_PCREL_ITYPE.
+               // TODO(jsing): Consider generating elf.R_RISCV_CALL instead of a
+               // HI20/LO12_I pair.
+
+       case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
+               // Find the text symbol for the AUIPC instruction targeted
+               // by this relocation.
+               relocs := ldr.Relocs(s)
+               offset := int64(relocs.At(ri).Off())
+               hi20Sym := findHI20Symbol(ctxt, ldr, ldr.SymValue(s)+offset)
+               if hi20Sym == 0 {
+                       ld.Errorf(nil, "failed to find text symbol for HI20 relocation at %d (%x)", sectoff, ldr.SymValue(s)+offset)
+                       return false
+               }
+               hi20ElfSym := ld.ElfSymForReloc(ctxt, hi20Sym)
+
+               // Emit two relocations - a R_RISCV_PCREL_HI20 relocation and a
+               // corresponding R_RISCV_PCREL_LO12_I or R_RISCV_PCREL_LO12_S relocation.
+               // Note that the LO12 relocation must point to a target that has a valid
+               // HI20 PC-relative relocation text symbol, which in turn points to the
+               // given symbol. For further details see the ELF specification for RISC-V:
+               //
+               //   https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#pc-relative-symbol-addresses
+               //
+               var hiRel, loRel elf.R_RISCV
+               switch r.Type {
+               case objabi.R_RISCV_PCREL_ITYPE:
+                       hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_I
+               case objabi.R_RISCV_PCREL_STYPE:
+                       hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_S
+               }
+               out.Write64(uint64(sectoff))
+               out.Write64(uint64(hiRel) | uint64(elfsym)<<32)
+               out.Write64(uint64(r.Xadd))
+               out.Write64(uint64(sectoff + 4))
+               out.Write64(uint64(loRel) | uint64(hi20ElfSym)<<32)
+               out.Write64(uint64(0))
+
+       default:
+               return false
+       }
+
+       return true
 }
 
 func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
-       log.Fatalf("elfsetuplt")
+       log.Fatalf("elfsetupplt")
 }
 
 func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
@@ -33,8 +151,20 @@ func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtRe
 }
 
 func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
-       rs := r.Sym()
-       rs = ldr.ResolveABIAlias(rs)
+       if target.IsExternal() {
+               switch r.Type() {
+               case objabi.R_CALLRISCV:
+                       return val, 0, true
+
+               case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
+                       return val, 2, true
+               }
+
+               return val, 0, false
+       }
+
+       rs := ldr.ResolveABIAlias(r.Sym())
+
        switch r.Type() {
        case objabi.R_CALLRISCV:
                // Nothing to do.
@@ -89,3 +219,11 @@ func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant
        log.Fatalf("archrelocvariant")
        return -1
 }
+
+func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
+       switch r.Type() {
+       case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
+               return ld.ExtrelocViaOuterSym(ldr, r, s), true
+       }
+       return loader.ExtReloc{}, false
+}
index e66d3cd8565d2c04c135589867ea3017e2c12413..917324d92238915584c73452b2eadcc639e108a6 100644 (file)
@@ -23,9 +23,12 @@ func Init() (*sys.Arch, ld.Arch) {
                Archinit:         archinit,
                Archreloc:        archreloc,
                Archrelocvariant: archrelocvariant,
+               Extreloc:         extreloc,
                Elfreloc1:        elfreloc1,
+               ElfrelocSize:     24,
                Elfsetupplt:      elfsetupplt,
                Gentext:          gentext,
+               GenSymsLate:      genSymsLate,
                Machoreloc1:      machoreloc1,
 
                Linuxdynld: "/lib/ld.so.1",