| // Copyright 2019 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package riscv64 | |
| import ( | |
| "cmd/internal/obj/riscv" | |
| "cmd/internal/objabi" | |
| "cmd/internal/sys" | |
| "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 && | |
| r.Type() != objabi.R_RISCV_TLS_IE_ITYPE && r.Type() != objabi.R_RISCV_TLS_IE_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 { | |
| 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, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_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 | |
| case objabi.R_RISCV_TLS_IE_ITYPE: | |
| hiRel, loRel = elf.R_RISCV_TLS_GOT_HI20, elf.R_RISCV_PCREL_LO12_I | |
| case objabi.R_RISCV_TLS_IE_STYPE: | |
| hiRel, loRel = elf.R_RISCV_TLS_GOT_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("elfsetupplt") | |
| } | |
| func machoreloc1(*sys.Arch, *ld.OutBuf, *loader.Loader, loader.Sym, loader.ExtReloc, int64) bool { | |
| log.Fatalf("machoreloc1 not implemented") | |
| return false | |
| } | |
| 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) { | |
| 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, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE: | |
| return val, 2, true | |
| } | |
| return val, 0, false | |
| } | |
| rs := ldr.ResolveABIAlias(r.Sym()) | |
| switch r.Type() { | |
| case objabi.R_CALLRISCV: | |
| // Nothing to do. | |
| return val, 0, true | |
| case objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE: | |
| // TLS relocations are not currently handled for internal linking. | |
| // For now, TLS is only used when cgo is in use and cgo currently | |
| // requires external linking. However, we need to accept these | |
| // relocations so that code containing TLS variables will link, | |
| // even when they're not being used. For now, replace these | |
| // instructions with EBREAK to detect accidental use. | |
| const ebreakIns = 0x00100073 | |
| return ebreakIns<<32 | ebreakIns, 0, true | |
| case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE: | |
| pc := ldr.SymValue(s) + int64(r.Off()) | |
| off := ldr.SymValue(rs) + r.Add() - pc | |
| // Generate AUIPC and second instruction immediates. | |
| low, high, err := riscv.Split32BitImmediate(off) | |
| if err != nil { | |
| ldr.Errorf(s, "R_RISCV_PCREL_ relocation does not fit in 32-bits: %d", off) | |
| } | |
| auipcImm, err := riscv.EncodeUImmediate(high) | |
| if err != nil { | |
| ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ AUIPC relocation offset for %s: %v", ldr.SymName(rs), err) | |
| } | |
| var secondImm, secondImmMask int64 | |
| switch r.Type() { | |
| case objabi.R_RISCV_PCREL_ITYPE: | |
| secondImmMask = riscv.ITypeImmMask | |
| secondImm, err = riscv.EncodeIImmediate(low) | |
| if err != nil { | |
| ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ITYPE I-type instruction relocation offset for %s: %v", ldr.SymName(rs), err) | |
| } | |
| case objabi.R_RISCV_PCREL_STYPE: | |
| secondImmMask = riscv.STypeImmMask | |
| secondImm, err = riscv.EncodeSImmediate(low) | |
| if err != nil { | |
| ldr.Errorf(s, "cannot encode R_RISCV_PCREL_STYPE S-type instruction relocation offset for %s: %v", ldr.SymName(rs), err) | |
| } | |
| default: | |
| panic(fmt.Sprintf("Unknown relocation type: %v", r.Type())) | |
| } | |
| auipc := int64(uint32(val)) | |
| second := int64(uint32(val >> 32)) | |
| auipc = (auipc &^ riscv.UTypeImmMask) | int64(uint32(auipcImm)) | |
| second = (second &^ secondImmMask) | int64(uint32(secondImm)) | |
| return second<<32 | auipc, 0, true | |
| } | |
| return val, 0, false | |
| } | |
| func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant, loader.Sym, int64) int64 { | |
| 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, objabi.R_RISCV_TLS_IE_ITYPE, objabi.R_RISCV_TLS_IE_STYPE: | |
| return ld.ExtrelocViaOuterSym(ldr, r, s), true | |
| } | |
| return loader.ExtReloc{}, false | |
| } |