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
5package loong64
6
7import (
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	"log"
15)
16
17func gentext(ctxt *ld.Link, ldr *loader.Loader) {
18	initfunc, addmoduledata := ld.PrepareAddmoduledata(ctxt)
19	if initfunc == nil {
20		return
21	}
22
23	o := func(op uint32) {
24		initfunc.AddUint32(ctxt.Arch, op)
25	}
26
27	// Emit the following function:
28	//
29	//	local.dso_init:
30	//		la.pcrel $a0, local.moduledata
31	//		b runtime.addmoduledata
32
33	//	0000000000000000 <local.dso_init>:
34	//	0:	1a000004	pcalau12i	$a0, 0
35	//				0: R_LARCH_PCALA_HI20	local.moduledata
36	o(0x1a000004)
37	rel, _ := initfunc.AddRel(objabi.R_LOONG64_ADDR_HI)
38	rel.SetOff(0)
39	rel.SetSiz(4)
40	rel.SetSym(ctxt.Moduledata)
41
42	//	4:	02c00084	addi.d	$a0, $a0, 0
43	//				4: R_LARCH_PCALA_LO12	local.moduledata
44	o(0x02c00084)
45	rel2, _ := initfunc.AddRel(objabi.R_LOONG64_ADDR_LO)
46	rel2.SetOff(4)
47	rel2.SetSiz(4)
48	rel2.SetSym(ctxt.Moduledata)
49
50	//	8:	50000000	b	0
51	//				8: R_LARCH_B26	runtime.addmoduledata
52	o(0x50000000)
53	rel3, _ := initfunc.AddRel(objabi.R_CALLLOONG64)
54	rel3.SetOff(8)
55	rel3.SetSiz(4)
56	rel3.SetSym(addmoduledata)
57}
58
59func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
60	log.Fatalf("adddynrel not implemented")
61	return false
62}
63
64func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, r loader.ExtReloc, ri int, sectoff int64) bool {
65	// loong64 ELF relocation (endian neutral)
66	//		offset     uint64
67	//		symreloc   uint64  // The high 32-bit is the symbol, the low 32-bit is the relocation type.
68	//		addend     int64
69
70	elfsym := ld.ElfSymForReloc(ctxt, r.Xsym)
71	switch r.Type {
72	default:
73		return false
74	case objabi.R_ADDR, objabi.R_DWARFSECREF:
75		switch r.Size {
76		case 4:
77			out.Write64(uint64(sectoff))
78			out.Write64(uint64(elf.R_LARCH_32) | uint64(elfsym)<<32)
79			out.Write64(uint64(r.Xadd))
80		case 8:
81			out.Write64(uint64(sectoff))
82			out.Write64(uint64(elf.R_LARCH_64) | uint64(elfsym)<<32)
83			out.Write64(uint64(r.Xadd))
84		default:
85			return false
86		}
87	case objabi.R_LOONG64_TLS_LE_LO:
88		out.Write64(uint64(sectoff))
89		out.Write64(uint64(elf.R_LARCH_TLS_LE_LO12) | uint64(elfsym)<<32)
90		out.Write64(uint64(r.Xadd))
91
92	case objabi.R_LOONG64_TLS_LE_HI:
93		out.Write64(uint64(sectoff))
94		out.Write64(uint64(elf.R_LARCH_TLS_LE_HI20) | uint64(elfsym)<<32)
95		out.Write64(uint64(r.Xadd))
96
97	case objabi.R_CALLLOONG64:
98		out.Write64(uint64(sectoff))
99		out.Write64(uint64(elf.R_LARCH_B26) | uint64(elfsym)<<32)
100		out.Write64(uint64(r.Xadd))
101
102	case objabi.R_LOONG64_TLS_IE_HI:
103		out.Write64(uint64(sectoff))
104		out.Write64(uint64(elf.R_LARCH_TLS_IE_PC_HI20) | uint64(elfsym)<<32)
105		out.Write64(uint64(0x0))
106
107	case objabi.R_LOONG64_TLS_IE_LO:
108		out.Write64(uint64(sectoff))
109		out.Write64(uint64(elf.R_LARCH_TLS_IE_PC_LO12) | uint64(elfsym)<<32)
110		out.Write64(uint64(0x0))
111
112	case objabi.R_LOONG64_ADDR_LO:
113		out.Write64(uint64(sectoff))
114		out.Write64(uint64(elf.R_LARCH_PCALA_LO12) | uint64(elfsym)<<32)
115		out.Write64(uint64(r.Xadd))
116
117	case objabi.R_LOONG64_ADDR_HI:
118		out.Write64(uint64(sectoff))
119		out.Write64(uint64(elf.R_LARCH_PCALA_HI20) | uint64(elfsym)<<32)
120		out.Write64(uint64(r.Xadd))
121
122	case objabi.R_LOONG64_GOT_HI:
123		out.Write64(uint64(sectoff))
124		out.Write64(uint64(elf.R_LARCH_GOT_PC_HI20) | uint64(elfsym)<<32)
125		out.Write64(uint64(0x0))
126
127	case objabi.R_LOONG64_GOT_LO:
128		out.Write64(uint64(sectoff))
129		out.Write64(uint64(elf.R_LARCH_GOT_PC_LO12) | uint64(elfsym)<<32)
130		out.Write64(uint64(0x0))
131	}
132
133	return true
134}
135
136func elfsetupplt(ctxt *ld.Link, ldr *loader.Loader, plt, gotplt *loader.SymbolBuilder, dynamic loader.Sym) {
137	return
138}
139
140func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool {
141	return false
142}
143
144func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loader.Reloc, s loader.Sym, val int64) (o int64, nExtReloc int, ok bool) {
145	rs := r.Sym()
146	if target.IsExternal() {
147		switch r.Type() {
148		default:
149			return val, 0, false
150		case objabi.R_LOONG64_ADDR_HI,
151			objabi.R_LOONG64_ADDR_LO:
152			// set up addend for eventual relocation via outer symbol.
153			rs, _ := ld.FoldSubSymbolOffset(ldr, rs)
154			rst := ldr.SymType(rs)
155			if rst != sym.SHOSTOBJ && rst != sym.SDYNIMPORT && ldr.SymSect(rs) == nil {
156				ldr.Errorf(s, "missing section for %s", ldr.SymName(rs))
157			}
158			return val, 1, true
159		case objabi.R_LOONG64_TLS_LE_HI,
160			objabi.R_LOONG64_TLS_LE_LO,
161			objabi.R_CALLLOONG64,
162			objabi.R_JMPLOONG64,
163			objabi.R_LOONG64_TLS_IE_HI,
164			objabi.R_LOONG64_TLS_IE_LO,
165			objabi.R_LOONG64_GOT_HI,
166			objabi.R_LOONG64_GOT_LO:
167			return val, 1, true
168		}
169	}
170
171	const isOk = true
172	const noExtReloc = 0
173
174	switch r.Type() {
175	case objabi.R_CONST:
176		return r.Add(), noExtReloc, isOk
177	case objabi.R_GOTOFF:
178		return ldr.SymValue(r.Sym()) + r.Add() - ldr.SymValue(syms.GOT), noExtReloc, isOk
179	case objabi.R_LOONG64_ADDR_HI,
180		objabi.R_LOONG64_ADDR_LO:
181		pc := ldr.SymValue(s) + int64(r.Off())
182		t := calculatePCAlignedReloc(r.Type(), ldr.SymAddr(rs)+r.Add(), pc)
183		if r.Type() == objabi.R_LOONG64_ADDR_LO {
184			return int64(val&0xffc003ff | (t << 10)), noExtReloc, isOk
185		}
186		return int64(val&0xfe00001f | (t << 5)), noExtReloc, isOk
187	case objabi.R_LOONG64_TLS_LE_HI,
188		objabi.R_LOONG64_TLS_LE_LO:
189		t := ldr.SymAddr(rs) + r.Add()
190		if r.Type() == objabi.R_LOONG64_TLS_LE_LO {
191			return int64(val&0xffc003ff | ((t & 0xfff) << 10)), noExtReloc, isOk
192		}
193		return int64(val&0xfe00001f | (((t) >> 12 << 5) & 0x1ffffe0)), noExtReloc, isOk
194	case objabi.R_CALLLOONG64,
195		objabi.R_JMPLOONG64:
196		pc := ldr.SymValue(s) + int64(r.Off())
197		t := ldr.SymAddr(rs) + r.Add() - pc
198		return int64(val&0xfc000000 | (((t >> 2) & 0xffff) << 10) | (((t >> 2) & 0x3ff0000) >> 16)), noExtReloc, isOk
199	}
200
201	return val, 0, false
202}
203
204func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64, []byte) int64 {
205	return -1
206}
207
208func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
209	switch r.Type() {
210	case objabi.R_LOONG64_ADDR_HI,
211		objabi.R_LOONG64_ADDR_LO,
212		objabi.R_LOONG64_GOT_HI,
213		objabi.R_LOONG64_GOT_LO:
214		return ld.ExtrelocViaOuterSym(ldr, r, s), true
215
216	case objabi.R_LOONG64_TLS_LE_HI,
217		objabi.R_LOONG64_TLS_LE_LO,
218		objabi.R_CONST,
219		objabi.R_GOTOFF,
220		objabi.R_CALLLOONG64,
221		objabi.R_JMPLOONG64,
222		objabi.R_LOONG64_TLS_IE_HI,
223		objabi.R_LOONG64_TLS_IE_LO:
224		return ld.ExtrelocSimple(ldr, r), true
225	}
226	return loader.ExtReloc{}, false
227}
228
229func isRequestingLowPageBits(t objabi.RelocType) bool {
230	switch t {
231	case objabi.R_LOONG64_ADDR_LO:
232		return true
233	}
234	return false
235}
236
237// Calculates the value to put into the immediate slot, according to the
238// desired relocation type, target and PC.
239// The value to use varies based on the reloc type. Namely, the absolute low
240// bits of the target are to be used for the low part, while the page-aligned
241// offset is to be used for the higher part. A "page" here is not related to
242// the system's actual page size, but rather a fixed 12-bit range (designed to
243// cooperate with ADDI/LD/ST's 12-bit immediates).
244func calculatePCAlignedReloc(t objabi.RelocType, tgt int64, pc int64) int64 {
245	if isRequestingLowPageBits(t) {
246		// corresponding immediate field is 12 bits wide
247		return tgt & 0xfff
248	}
249
250	pageDelta := (tgt >> 12) - (pc >> 12)
251	if tgt&0xfff >= 0x800 {
252		// adjust for sign-extended addition of the low bits
253		pageDelta += 1
254	}
255	// corresponding immediate field is 20 bits wide
256	return pageDelta & 0xfffff
257}
258