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

     1  // Copyright 2022 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 loong64
     6  
     7  import (
     8  	"cmd/internal/objabi"
     9  	"cmd/internal/sys"
    10  	"cmd/link/internal/ld"
    11  	"cmd/link/internal/loader"
    12  	"cmd/link/internal/sym"
    13  	"debug/elf"
    14  	"fmt"
    15  	"log"
    16  )
    17  
    18  func gentext(ctxt *ld.Link, ldr *loader.Loader) {
    19  	initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
    20  	if initfunc == nil {
    21  		return
    22  	}
    23  
    24  	o := func(op uint32) {
    25  		initfunc.AddUint32(ctxt.Arch, op)
    26  	}
    27  
    28  	// Emit the following function:
    29  	//
    30  	//	local.dso_init:
    31  	//		la.pcrel $a0, local.moduledata
    32  	//		b runtime.addmoduledata
    33  
    34  	//	0000000000000000 <local.dso_init>:
    35  	//	0:	1a000004	pcalau12i	$a0, 0
    36  	//				0: R_LARCH_PCALA_HI20	local.moduledata
    37  	o(0x1a000004)
    38  	rel, _ := initfunc.AddRel(objabi.R_LOONG64_ADDR_HI)
    39  	rel.SetOff(0)
    40  	rel.SetSiz(4)
    41  	rel.SetSym(ctxt.Moduledata)
    42  
    43  	//	4:	02c00084	addi.d	$a0, $a0, 0
    44  	//				4: R_LARCH_PCALA_LO12	local.moduledata
    45  	o(0x02c00084)
    46  	rel2, _ := initfunc.AddRel(objabi.R_LOONG64_ADDR_LO)
    47  	rel2.SetOff(4)
    48  	rel2.SetSiz(4)
    49  	rel2.SetSym(ctxt.Moduledata)
    50  
    51  	//	8:	50000000	b	0
    52  	//				8: R_LARCH_B26	runtime.addmoduledata
    53  	o(0x50000000)
    54  	rel3, _ := initfunc.AddRel(objabi.R_CALLLOONG64)
    55  	rel3.SetOff(8)
    56  	rel3.SetSiz(4)
    57  	rel3.SetSym(addmoduledata)
    58  }
    59  
    60  func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
    61  	log.Fatalf("adddynrel not implemented")
    62  	return false
    63  }
    64  
    65  func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
    66  	// loong64 ELF relocation (endian neutral)
    67  	//		offset     uint64
    68  	//		symreloc   uint64  // The high 32-bit is the symbol, the low 32-bit is the relocation type.
    69  	//		addend     int64
    70  
    71  	elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
    72  	switch r.Type {
    73  	default:
    74  		return false
    75  	case objabi.R_ADDR, objabi.R_DWARFSECREF:
    76  		switch r.Size {
    77  		case 4:
    78  			out.Write64(uint64(sectoff))
    79  			out.Write64(uint64(elf.R_LARCH_32) | uint64(elfsym)<<32)
    80  			out.Write64(uint64(r.Xadd))
    81  		case 8:
    82  			out.Write64(uint64(sectoff))
    83  			out.Write64(uint64(elf.R_LARCH_64) | uint64(elfsym)<<32)
    84  			out.Write64(uint64(r.Xadd))
    85  		default:
    86  			return false
    87  		}
    88  	case objabi.R_LOONG64_TLS_LE_LO:
    89  		out.Write64(uint64(sectoff))
    90  		out.Write64(uint64(elf.R_LARCH_TLS_LE_LO12) | uint64(elfsym)<<32)
    91  		out.Write64(uint64(r.Xadd))
    92  
    93  	case objabi.R_LOONG64_TLS_LE_HI:
    94  		out.Write64(uint64(sectoff))
    95  		out.Write64(uint64(elf.R_LARCH_TLS_LE_HI20) | uint64(elfsym)<<32)
    96  		out.Write64(uint64(r.Xadd))
    97  
    98  	case objabi.R_CALLLOONG64:
    99  		out.Write64(uint64(sectoff))
   100  		out.Write64(uint64(elf.R_LARCH_B26) | uint64(elfsym)<<32)
   101  		out.Write64(uint64(r.Xadd))
   102  
   103  	case objabi.R_LOONG64_TLS_IE_HI:
   104  		out.Write64(uint64(sectoff))
   105  		out.Write64(uint64(elf.R_LARCH_TLS_IE_PC_HI20) | uint64(elfsym)<<32)
   106  		out.Write64(uint64(0x0))
   107  
   108  	case objabi.R_LOONG64_TLS_IE_LO:
   109  		out.Write64(uint64(sectoff))
   110  		out.Write64(uint64(elf.R_LARCH_TLS_IE_PC_LO12) | uint64(elfsym)<<32)
   111  		out.Write64(uint64(0x0))
   112  
   113  	case objabi.R_LOONG64_ADDR_LO:
   114  		out.Write64(uint64(sectoff))
   115  		out.Write64(uint64(elf.R_LARCH_PCALA_LO12) | uint64(elfsym)<<32)
   116  		out.Write64(uint64(r.Xadd))
   117  
   118  	case objabi.R_LOONG64_ADDR_HI:
   119  		out.Write64(uint64(sectoff))
   120  		out.Write64(uint64(elf.R_LARCH_PCALA_HI20) | uint64(elfsym)<<32)
   121  		out.Write64(uint64(r.Xadd))
   122  
   123  	case objabi.R_LOONG64_GOT_HI:
   124  		out.Write64(uint64(sectoff))
   125  		out.Write64(uint64(elf.R_LARCH_GOT_PC_HI20) | uint64(elfsym)<<32)
   126  		out.Write64(uint64(0x0))
   127  
   128  	case objabi.R_LOONG64_GOT_LO:
   129  		out.Write64(uint64(sectoff))
   130  		out.Write64(uint64(elf.R_LARCH_GOT_PC_LO12) | uint64(elfsym)<<32)
   131  		out.Write64(uint64(0x0))
   132  	}
   133  
   134  	return true
   135  }
   136  
   137  func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
   138  	return
   139  }
   140  
   141  func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
   142  	return false
   143  }
   144  
   145  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) {
   146  	rs := r.Sym()
   147  	if target.IsExternal() {
   148  		switch r.Type() {
   149  		default:
   150  			return val, 0, false
   151  		case objabi.R_LOONG64_ADDR_HI,
   152  			objabi.R_LOONG64_ADDR_LO:
   153  			// set up addend for eventual relocation via outer symbol.
   154  			rs, _ := ld.FoldSubSymbolOffset(ldr, rs)
   155  			rst := ldr.SymType(rs)
   156  			if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil {
   157  				ldr.Errorf(s, "missing section for %s", ldr.SymName(rs))
   158  			}
   159  			return val, 1, true
   160  		case objabi.R_LOONG64_TLS_LE_HI,
   161  			objabi.R_LOONG64_TLS_LE_LO,
   162  			objabi.R_CALLLOONG64,
   163  			objabi.R_JMPLOONG64,
   164  			objabi.R_LOONG64_TLS_IE_HI,
   165  			objabi.R_LOONG64_TLS_IE_LO,
   166  			objabi.R_LOONG64_GOT_HI,
   167  			objabi.R_LOONG64_GOT_LO:
   168  			return val, 1, true
   169  		}
   170  	}
   171  
   172  	const isOk = true
   173  	const noExtReloc = 0
   174  
   175  	switch r.Type() {
   176  	case objabi.R_CONST:
   177  		return r.Add(), noExtReloc, isOk
   178  	case objabi.R_GOTOFF:
   179  		return ldr.SymValue(r.Sym()) + r.Add() - ldr.SymValue(syms.GOT), noExtReloc, isOk
   180  	case objabi.R_LOONG64_ADDR_HI,
   181  		objabi.R_LOONG64_ADDR_LO:
   182  		pc := ldr.SymValue(s) + int64(r.Off())
   183  		t := calculatePCAlignedReloc(r.Type(), ldr.SymAddr(rs)+r.Add(), pc)
   184  		if r.Type() == objabi.R_LOONG64_ADDR_LO {
   185  			return int64(val&0xffc003ff | (t << 10)), noExtReloc, isOk
   186  		}
   187  		return int64(val&0xfe00001f | (t << 5)), noExtReloc, isOk
   188  	case objabi.R_LOONG64_TLS_LE_HI,
   189  		objabi.R_LOONG64_TLS_LE_LO:
   190  		t := ldr.SymAddr(rs) + r.Add()
   191  		if r.Type() == objabi.R_LOONG64_TLS_LE_LO {
   192  			return int64(val&0xffc003ff | ((t & 0xfff) << 10)), noExtReloc, isOk
   193  		}
   194  		return int64(val&0xfe00001f | (((t) >> 12 << 5) & 0x1ffffe0)), noExtReloc, isOk
   195  	case objabi.R_CALLLOONG64,
   196  		objabi.R_JMPLOONG64:
   197  		pc := ldr.SymValue(s) + int64(r.Off())
   198  		t := ldr.SymAddr(rs) + r.Add() - pc
   199  		return int64(val&0xfc000000 | (((t >> 2) & 0xffff) << 10) | (((t >> 2) & 0x3ff0000) >> 16)), noExtReloc, isOk
   200  	}
   201  
   202  	return val, 0, false
   203  }
   204  
   205  func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
   206  	return -1
   207  }
   208  
   209  func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
   210  	switch r.Type() {
   211  	case objabi.R_LOONG64_ADDR_HI,
   212  		objabi.R_LOONG64_ADDR_LO,
   213  		objabi.R_LOONG64_GOT_HI,
   214  		objabi.R_LOONG64_GOT_LO:
   215  		return ld.ExtrelocViaOuterSym(ldr, r, s), true
   216  
   217  	case objabi.R_LOONG64_TLS_LE_HI,
   218  		objabi.R_LOONG64_TLS_LE_LO,
   219  		objabi.R_CONST,
   220  		objabi.R_GOTOFF,
   221  		objabi.R_CALLLOONG64,
   222  		objabi.R_JMPLOONG64,
   223  		objabi.R_LOONG64_TLS_IE_HI,
   224  		objabi.R_LOONG64_TLS_IE_LO:
   225  		return ld.ExtrelocSimple(ldr, r), true
   226  	}
   227  	return loader.ExtReloc{}, false
   228  }
   229  
   230  func isRequestingLowPageBits(t objabi.RelocType) bool {
   231  	switch t {
   232  	case objabi.R_LOONG64_ADDR_LO:
   233  		return true
   234  	}
   235  	return false
   236  }
   237  
   238  // Calculates the value to put into the immediate slot, according to the
   239  // desired relocation type, target and PC.
   240  // The value to use varies based on the reloc type. Namely, the absolute low
   241  // bits of the target are to be used for the low part, while the page-aligned
   242  // offset is to be used for the higher part. A "page" here is not related to
   243  // the system's actual page size, but rather a fixed 12-bit range (designed to
   244  // cooperate with ADDI/LD/ST's 12-bit immediates).
   245  func calculatePCAlignedReloc(t objabi.RelocType, tgt int64, pc int64) int64 {
   246  	if isRequestingLowPageBits(t) {
   247  		// corresponding immediate field is 12 bits wide
   248  		return tgt & 0xfff
   249  	}
   250  
   251  	pageDelta := (tgt >> 12) - (pc >> 12)
   252  	if tgt&0xfff >= 0x800 {
   253  		// adjust for sign-extended addition of the low bits
   254  		pageDelta += 1
   255  	}
   256  	// corresponding immediate field is 20 bits wide
   257  	return pageDelta & 0xfffff
   258  }
   259  
   260  // Convert the direct jump relocation r to refer to a trampoline if the target is too far.
   261  func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
   262  	relocs := ldr.Relocs(s)
   263  	r := relocs.At(ri)
   264  	switch r.Type() {
   265  	case objabi.ElfRelocOffset + objabi.RelocType(elf.R_LARCH_B26):
   266  		// Host object relocations that will be turned into a PLT call.
   267  		// The PLT may be too far. Insert a trampoline for them.
   268  		fallthrough
   269  	case objabi.R_CALLLOONG64:
   270  		var t int64
   271  		// ldr.SymValue(rs) == 0 indicates a cross-package jump to a function that is not yet
   272  		// laid out. Conservatively use a trampoline. This should be rare, as we lay out packages
   273  		// in dependency order.
   274  		if ldr.SymValue(rs) != 0 {
   275  			t = ldr.SymValue(rs) + r.Add() - (ldr.SymValue(s) + int64(r.Off()))
   276  		}
   277  		if t >= 1<<27 || t < -1<<27 || ldr.SymValue(rs) == 0 || (*ld.FlagDebugTramp > 1 && (ldr.SymPkg(s) == "" || ldr.SymPkg(s) != ldr.SymPkg(rs))) {
   278  			// direct call too far need to insert trampoline.
   279  			// look up existing trampolines first. if we found one within the range
   280  			// of direct call, we can reuse it. otherwise create a new one.
   281  			var tramp loader.Sym
   282  			for i := 0; ; i++ {
   283  				oName := ldr.SymName(rs)
   284  				name := oName + fmt.Sprintf("%+x-tramp%d", r.Add(), i)
   285  				tramp = ldr.LookupOrCreateSym(name, int(ldr.SymVersion(rs)))
   286  				ldr.SetAttrReachable(tramp, true)
   287  				if ldr.SymType(tramp) == sym.SDYNIMPORT {
   288  					// don't reuse trampoline defined in other module
   289  					continue
   290  				}
   291  				if oName == "runtime.deferreturn" {
   292  					ldr.SetIsDeferReturnTramp(tramp, true)
   293  				}
   294  				if ldr.SymValue(tramp) == 0 {
   295  					// either the trampoline does not exist -- we need to create one,
   296  					// or found one the address which is not assigned -- this will be
   297  					// laid down immediately after the current function. use this one.
   298  					break
   299  				}
   300  
   301  				t = ldr.SymValue(tramp) - (ldr.SymValue(s) + int64(r.Off()))
   302  				if t >= -1<<27 && t < 1<<27 {
   303  					// found an existing trampoline that is not too far
   304  					// we can just use it.
   305  					break
   306  				}
   307  			}
   308  			if ldr.SymType(tramp) == 0 {
   309  				// trampoline does not exist, create one
   310  				trampb := ldr.MakeSymbolUpdater(tramp)
   311  				ctxt.AddTramp(trampb, ldr.SymType(s))
   312  				if ldr.SymType(rs) == sym.SDYNIMPORT {
   313  					if r.Add() != 0 {
   314  						ctxt.Errorf(s, "nonzero addend for DYNIMPORT call: %v+%d", ldr.SymName(rs), r.Add())
   315  					}
   316  					gentrampgot(ctxt, ldr, trampb, rs)
   317  				} else {
   318  					gentramp(ctxt, ldr, trampb, rs, r.Add())
   319  				}
   320  			}
   321  			// modify reloc to point to tramp, which will be resolved later
   322  			sb := ldr.MakeSymbolUpdater(s)
   323  			relocs := sb.Relocs()
   324  			r := relocs.At(ri)
   325  			r.SetSym(tramp)
   326  			r.SetAdd(0) // clear the offset embedded in the instruction
   327  		}
   328  	default:
   329  		ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
   330  	}
   331  }
   332  
   333  // generate a trampoline to target+offset.
   334  func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
   335  	tramp.SetSize(12) // 3 instructions
   336  	P := make([]byte, tramp.Size())
   337  
   338  	o1 := uint32(0x1a00001e) // pcalau12i $r30, 0
   339  	ctxt.Arch.ByteOrder.PutUint32(P, o1)
   340  	r1, _ := tramp.AddRel(objabi.R_LOONG64_ADDR_HI)
   341  	r1.SetOff(0)
   342  	r1.SetSiz(4)
   343  	r1.SetSym(target)
   344  	r1.SetAdd(offset)
   345  
   346  	o2 := uint32(0x02c003de) // addi.d $r30, $r30, 0
   347  	ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
   348  	r2, _ := tramp.AddRel(objabi.R_LOONG64_ADDR_LO)
   349  	r2.SetOff(4)
   350  	r2.SetSiz(4)
   351  	r2.SetSym(target)
   352  	r2.SetAdd(offset)
   353  
   354  	o3 := uint32(0x4c0003c0) // jirl $r0, $r30, 0
   355  	ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
   356  
   357  	tramp.SetData(P)
   358  }
   359  
   360  func gentrampgot(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym) {
   361  	tramp.SetSize(12) // 3 instructions
   362  	P := make([]byte, tramp.Size())
   363  
   364  	o1 := uint32(0x1a00001e) // pcalau12i $r30, 0
   365  	ctxt.Arch.ByteOrder.PutUint32(P, o1)
   366  	r1, _ := tramp.AddRel(objabi.R_LOONG64_GOT_HI)
   367  	r1.SetOff(0)
   368  	r1.SetSiz(4)
   369  	r1.SetSym(target)
   370  
   371  	o2 := uint32(0x28c003de) // ld.d $r30, $r30, 0
   372  	ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
   373  	r2, _ := tramp.AddRel(objabi.R_LOONG64_GOT_LO)
   374  	r2.SetOff(4)
   375  	r2.SetSiz(4)
   376  	r2.SetSym(target)
   377  
   378  	o3 := uint32(0x4c0003c0) // jirl $r0, $r30, 0
   379  	ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
   380  
   381  	tramp.SetData(P)
   382  }
   383  

View as plain text