Source file src/cmd/link/internal/riscv64/asm.go

     1  // Copyright 2019 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  package riscv64
     6  
     7  import (
     8  	"cmd/internal/obj/riscv"
     9  	"cmd/internal/objabi"
    10  	"cmd/internal/sys"
    11  	"cmd/link/internal/ld"
    12  	"cmd/link/internal/loader"
    13  	"cmd/link/internal/sym"
    14  	"debug/elf"
    15  	"fmt"
    16  	"log"
    17  	"sort"
    18  )
    19  
    20  // fakeLabelName matches the RISCV_FAKE_LABEL_NAME from binutils.
    21  const fakeLabelName = ".L0 "
    22  
    23  func gentext(ctxt *ld.Link, ldr *loader.Loader) {
    24  }
    25  
    26  func genSymsLate(ctxt *ld.Link, ldr *loader.Loader) {
    27  	if ctxt.LinkMode != ld.LinkExternal {
    28  		return
    29  	}
    30  
    31  	// Generate a local text symbol for each relocation target, as the
    32  	// R_RISCV_PCREL_LO12_* relocations generated by elfreloc1 need it.
    33  	if ctxt.Textp == nil {
    34  		log.Fatal("genSymsLate called before Textp has been assigned")
    35  	}
    36  	var hi20Syms []loader.Sym
    37  	for _, s := range ctxt.Textp {
    38  		relocs := ldr.Relocs(s)
    39  		for ri := 0; ri < relocs.Count(); ri++ {
    40  			r := relocs.At(ri)
    41  			if r.Type() != objabi.R_RISCV_PCREL_ITYPE && r.Type() != objabi.R_RISCV_PCREL_STYPE &&
    42  				r.Type() != objabi.R_RISCV_TLS_IE_ITYPE && r.Type() != objabi.R_RISCV_TLS_IE_STYPE {
    43  				continue
    44  			}
    45  			if r.Off() == 0 && ldr.SymType(s) == sym.STEXT {
    46  				// Use the symbol for the function instead of creating
    47  				// an overlapping symbol.
    48  				continue
    49  			}
    50  
    51  			// TODO(jsing): Consider generating ELF symbols without needing
    52  			// loader symbols, in order to reduce memory consumption. This
    53  			// would require changes to genelfsym so that it called
    54  			// putelfsym and putelfsyment as appropriate.
    55  			sb := ldr.MakeSymbolBuilder(fakeLabelName)
    56  			sb.SetType(sym.STEXT)
    57  			sb.SetValue(ldr.SymValue(s) + int64(r.Off()))
    58  			sb.SetLocal(true)
    59  			sb.SetReachable(true)
    60  			sb.SetVisibilityHidden(true)
    61  			sb.SetSect(ldr.SymSect(s))
    62  			if outer := ldr.OuterSym(s); outer != 0 {
    63  				ldr.AddInteriorSym(outer, sb.Sym())
    64  			}
    65  			hi20Syms = append(hi20Syms, sb.Sym())
    66  		}
    67  	}
    68  	ctxt.Textp = append(ctxt.Textp, hi20Syms...)
    69  	ldr.SortSyms(ctxt.Textp)
    70  }
    71  
    72  func findHI20Symbol(ctxt *ld.Link, ldr *loader.Loader, val int64) loader.Sym {
    73  	idx := sort.Search(len(ctxt.Textp), func(i int) bool { return ldr.SymValue(ctxt.Textp[i]) >= val })
    74  	if idx >= len(ctxt.Textp) {
    75  		return 0
    76  	}
    77  	if s := ctxt.Textp[idx]; ldr.SymValue(s) == val && ldr.SymType(s) == sym.STEXT {
    78  		return s
    79  	}
    80  	return 0
    81  }
    82  
    83  func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
    84  	elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
    85  	switch r.Type {
    86  	case objabi.R_ADDR, objabi.R_DWARFSECREF:
    87  		out.Write64(uint64(sectoff))
    88  		switch r.Size {
    89  		case 4:
    90  			out.Write64(uint64(elf.R_RISCV_32) | uint64(elfsym)<<32)
    91  		case 8:
    92  			out.Write64(uint64(elf.R_RISCV_64) | uint64(elfsym)<<32)
    93  		default:
    94  			ld.Errorf(nil, "unknown size %d for %v relocation", r.Size, r.Type)
    95  			return false
    96  		}
    97  		out.Write64(uint64(r.Xadd))
    98  
    99  	case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
   100  		out.Write64(uint64(sectoff))
   101  		out.Write64(uint64(elf.R_RISCV_JAL) | uint64(elfsym)<<32)
   102  		out.Write64(uint64(r.Xadd))
   103  
   104  	case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
   105  		// Find the text symbol for the AUIPC instruction targeted
   106  		// by this relocation.
   107  		relocs := ldr.Relocs(s)
   108  		offset := int64(relocs.At(ri).Off())
   109  		hi20Sym := findHI20Symbol(ctxt, ldr, ldr.SymValue(s)+offset)
   110  		if hi20Sym == 0 {
   111  			ld.Errorf(nil, "failed to find text symbol for HI20 relocation at %d (%x)", sectoff, ldr.SymValue(s)+offset)
   112  			return false
   113  		}
   114  		hi20ElfSym := ld.ElfSymForReloc(ctxt, hi20Sym)
   115  
   116  		// Emit two relocations - a R_RISCV_PCREL_HI20 relocation and a
   117  		// corresponding R_RISCV_PCREL_LO12_I or R_RISCV_PCREL_LO12_S relocation.
   118  		// Note that the LO12 relocation must point to a target that has a valid
   119  		// HI20 PC-relative relocation text symbol, which in turn points to the
   120  		// given symbol. For further details see the ELF specification for RISC-V:
   121  		//
   122  		//   https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#pc-relative-symbol-addresses
   123  		//
   124  		var hiRel, loRel elf.R_RISCV
   125  		switch r.Type {
   126  		case objabi.R_RISCV_PCREL_ITYPE:
   127  			hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_I
   128  		case objabi.R_RISCV_PCREL_STYPE:
   129  			hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_S
   130  		case objabi.R_RISCV_TLS_IE_ITYPE:
   131  			hiRel, loRel = elf.R_RISCV_TLS_GOT_HI20, elf.R_RISCV_PCREL_LO12_I
   132  		case objabi.R_RISCV_TLS_IE_STYPE:
   133  			hiRel, loRel = elf.R_RISCV_TLS_GOT_HI20, elf.R_RISCV_PCREL_LO12_S
   134  		}
   135  		out.Write64(uint64(sectoff))
   136  		out.Write64(uint64(hiRel) | uint64(elfsym)<<32)
   137  		out.Write64(uint64(r.Xadd))
   138  		out.Write64(uint64(sectoff + 4))
   139  		out.Write64(uint64(loRel) | uint64(hi20ElfSym)<<32)
   140  		out.Write64(uint64(0))
   141  
   142  	default:
   143  		return false
   144  	}
   145  
   146  	return true
   147  }
   148  
   149  func elfsetupplt(ctxt *ld.Link, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
   150  	log.Fatalf("elfsetupplt")
   151  }
   152  
   153  func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
   154  	log.Fatalf("machoreloc1 not implemented")
   155  	return false
   156  }
   157  
   158  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) {
   159  	rs := r.Sym()
   160  	pc := ldr.SymValue(s) + int64(r.Off())
   161  
   162  	// If the call points to a trampoline, see if we can reach the symbol
   163  	// directly. This situation can occur when the relocation symbol is
   164  	// not assigned an address until after the trampolines are generated.
   165  	if r.Type() == objabi.R_RISCV_CALL_TRAMP {
   166  		relocs := ldr.Relocs(rs)
   167  		if relocs.Count() != 1 {
   168  			ldr.Errorf(s, "trampoline %v has %d relocations", ldr.SymName(rs), relocs.Count())
   169  		}
   170  		tr := relocs.At(0)
   171  		if tr.Type() != objabi.R_RISCV_PCREL_ITYPE {
   172  			ldr.Errorf(s, "trampoline %v has unexpected relocation %v", ldr.SymName(rs), tr.Type())
   173  		}
   174  		trs := tr.Sym()
   175  		if ldr.SymValue(trs) != 0 && ldr.SymType(trs) != sym.SDYNIMPORT && ldr.SymType(trs) != sym.SUNDEFEXT {
   176  			trsOff := ldr.SymValue(trs) + tr.Add() - pc
   177  			if trsOff >= -(1<<20) && trsOff < (1<<20) {
   178  				r.SetType(objabi.R_RISCV_CALL)
   179  				r.SetSym(trs)
   180  				r.SetAdd(tr.Add())
   181  				rs = trs
   182  			}
   183  		}
   184  
   185  	}
   186  
   187  	if target.IsExternal() {
   188  		switch r.Type() {
   189  		case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
   190  			return val, 1, true
   191  
   192  		case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
   193  			return val, 2, true
   194  		}
   195  
   196  		return val, 0, false
   197  	}
   198  
   199  	off := ldr.SymValue(rs) + r.Add() - pc
   200  
   201  	switch r.Type() {
   202  	case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
   203  		// Generate instruction immediates.
   204  		imm, err := riscv.EncodeJImmediate(off)
   205  		if err != nil {
   206  			ldr.Errorf(s, "cannot encode R_RISCV_CALL relocation offset for %s: %v", ldr.SymName(rs), err)
   207  		}
   208  		immMask := int64(riscv.JTypeImmMask)
   209  
   210  		val = (val &^ immMask) | int64(imm)
   211  
   212  		return val, 0, true
   213  
   214  	case objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
   215  		// TLS relocations are not currently handled for internal linking.
   216  		// For now, TLS is only used when cgo is in use and cgo currently
   217  		// requires external linking. However, we need to accept these
   218  		// relocations so that code containing TLS variables will link,
   219  		// even when they're not being used. For now, replace these
   220  		// instructions with EBREAK to detect accidental use.
   221  		const ebreakIns = 0x00100073
   222  		return ebreakIns<<32 | ebreakIns, 0, true
   223  
   224  	case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
   225  		// Generate AUIPC and second instruction immediates.
   226  		low, high, err := riscv.Split32BitImmediate(off)
   227  		if err != nil {
   228  			ldr.Errorf(s, "R_RISCV_PCREL_ relocation does not fit in 32 bits: %d", off)
   229  		}
   230  
   231  		auipcImm, err := riscv.EncodeUImmediate(high)
   232  		if err != nil {
   233  			ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ AUIPC relocation offset for %s: %v", ldr.SymName(rs), err)
   234  		}
   235  
   236  		var secondImm, secondImmMask int64
   237  		switch r.Type() {
   238  		case objabi.R_RISCV_PCREL_ITYPE:
   239  			secondImmMask = riscv.ITypeImmMask
   240  			secondImm, err = riscv.EncodeIImmediate(low)
   241  			if err != nil {
   242  				ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ITYPE I-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
   243  			}
   244  		case objabi.R_RISCV_PCREL_STYPE:
   245  			secondImmMask = riscv.STypeImmMask
   246  			secondImm, err = riscv.EncodeSImmediate(low)
   247  			if err != nil {
   248  				ldr.Errorf(s, "cannot encode R_RISCV_PCREL_STYPE S-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
   249  			}
   250  		default:
   251  			panic(fmt.Sprintf("Unknown relocation type: %v", r.Type()))
   252  		}
   253  
   254  		auipc := int64(uint32(val))
   255  		second := int64(uint32(val >> 32))
   256  
   257  		auipc = (auipc &^ riscv.UTypeImmMask) | int64(uint32(auipcImm))
   258  		second = (second &^ secondImmMask) | int64(uint32(secondImm))
   259  
   260  		return second<<32 | auipc, 0, true
   261  	}
   262  
   263  	return val, 0, false
   264  }
   265  
   266  func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
   267  	log.Fatalf("archrelocvariant")
   268  	return -1
   269  }
   270  
   271  func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
   272  	switch r.Type() {
   273  	case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
   274  		return ld.ExtrelocSimple(ldr, r), true
   275  
   276  	case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE:
   277  		return ld.ExtrelocViaOuterSym(ldr, r, s), true
   278  	}
   279  	return loader.ExtReloc{}, false
   280  }
   281  
   282  func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
   283  	relocs := ldr.Relocs(s)
   284  	r := relocs.At(ri)
   285  
   286  	switch r.Type() {
   287  	case objabi.R_RISCV_CALL:
   288  		pc := ldr.SymValue(s) + int64(r.Off())
   289  		off := ldr.SymValue(rs) + r.Add() - pc
   290  
   291  		// Relocation symbol has an address and is directly reachable,
   292  		// therefore there is no need for a trampoline.
   293  		if ldr.SymValue(rs) != 0 && off >= -(1<<20) && off < (1<<20) && (*ld.FlagDebugTramp <= 1 || ldr.SymPkg(s) == ldr.SymPkg(rs)) {
   294  			break
   295  		}
   296  
   297  		// Relocation symbol is too far for a direct call or has not
   298  		// yet been given an address. See if an existing trampoline is
   299  		// reachable and if so, reuse it. Otherwise we need to create
   300  		// a new trampoline.
   301  		var tramp loader.Sym
   302  		for i := 0; ; i++ {
   303  			oName := ldr.SymName(rs)
   304  			name := fmt.Sprintf("%s-tramp%d", oName, i)
   305  			if r.Add() != 0 {
   306  				name = fmt.Sprintf("%s%+x-tramp%d", oName, r.Add(), i)
   307  			}
   308  			tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
   309  			ldr.SetAttrReachable(tramp, true)
   310  			if ldr.SymType(tramp) == sym.SDYNIMPORT {
   311  				// Do not reuse trampoline defined in other module.
   312  				continue
   313  			}
   314  			if oName == "runtime.deferreturn" {
   315  				ldr.SetIsDeferReturnTramp(tramp, true)
   316  			}
   317  			if ldr.SymValue(tramp) == 0 {
   318  				// Either trampoline does not exist or we found one
   319  				// that does not have an address assigned and will be
   320  				// laid down immediately after the current function.
   321  				break
   322  			}
   323  
   324  			trampOff := ldr.SymValue(tramp) - (ldr.SymValue(s) + int64(r.Off()))
   325  			if trampOff >= -(1<<20) && trampOff < (1<<20) {
   326  				// An existing trampoline that is reachable.
   327  				break
   328  			}
   329  		}
   330  		if ldr.SymType(tramp) == 0 {
   331  			trampb := ldr.MakeSymbolUpdater(tramp)
   332  			ctxt.AddTramp(trampb)
   333  			genCallTramp(ctxt.Arch, ctxt.LinkMode, ldr, trampb, rs, int64(r.Add()))
   334  		}
   335  		sb := ldr.MakeSymbolUpdater(s)
   336  		if ldr.SymValue(rs) == 0 {
   337  			// In this case the target symbol has not yet been assigned an
   338  			// address, so we have to assume a trampoline is required. Mark
   339  			// this as a call via a trampoline so that we can potentially
   340  			// switch to a direct call during relocation.
   341  			sb.SetRelocType(ri, objabi.R_RISCV_CALL_TRAMP)
   342  		}
   343  		relocs := sb.Relocs()
   344  		r := relocs.At(ri)
   345  		r.SetSym(tramp)
   346  		r.SetAdd(0)
   347  
   348  	default:
   349  		ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
   350  	}
   351  }
   352  
   353  func genCallTramp(arch *sys.Arch, linkmode ld.LinkMode, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
   354  	tramp.AddUint32(arch, 0x00000f97) // AUIPC	$0, X31
   355  	tramp.AddUint32(arch, 0x000f8067) // JALR		X0, (X31)
   356  
   357  	r, _ := tramp.AddRel(objabi.R_RISCV_PCREL_ITYPE)
   358  	r.SetSiz(8)
   359  	r.SetSym(target)
   360  	r.SetAdd(offset)
   361  }
   362  

View as plain text