summaryrefslogtreecommitdiffstats
path: root/src/cmd/link/internal/loadelf/ldelf.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link/internal/loadelf/ldelf.go')
-rw-r--r--src/cmd/link/internal/loadelf/ldelf.go1151
1 files changed, 1151 insertions, 0 deletions
diff --git a/src/cmd/link/internal/loadelf/ldelf.go b/src/cmd/link/internal/loadelf/ldelf.go
new file mode 100644
index 0000000..7ac7699
--- /dev/null
+++ b/src/cmd/link/internal/loadelf/ldelf.go
@@ -0,0 +1,1151 @@
+// 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 loadelf implements an ELF file reader.
+package loadelf
+
+import (
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "cmd/link/internal/loader"
+ "cmd/link/internal/sym"
+ "debug/elf"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "log"
+ "strings"
+)
+
+/*
+Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c
+https://github.com/9fans/plan9port/tree/master/src/libmach/
+
+ Copyright © 2004 Russ Cox.
+ Portions Copyright © 2008-2010 Google Inc.
+ Portions Copyright © 2010 The Go Authors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+const (
+ SHT_ARM_ATTRIBUTES = 0x70000003
+)
+
+type ElfSect struct {
+ name string
+ nameoff uint32
+ type_ elf.SectionType
+ flags elf.SectionFlag
+ addr uint64
+ off uint64
+ size uint64
+ link uint32
+ info uint32
+ align uint64
+ entsize uint64
+ base []byte
+ readOnlyMem bool // Is this section in readonly memory?
+ sym loader.Sym
+}
+
+type ElfObj struct {
+ f *bio.Reader
+ base int64 // offset in f where ELF begins
+ length int64 // length of ELF
+ is64 int
+ name string
+ e binary.ByteOrder
+ sect []ElfSect
+ nsect uint
+ nsymtab int
+ symtab *ElfSect
+ symstr *ElfSect
+ type_ uint32
+ machine uint32
+ version uint32
+ entry uint64
+ phoff uint64
+ shoff uint64
+ flags uint32
+ ehsize uint32
+ phentsize uint32
+ phnum uint32
+ shentsize uint32
+ shnum uint32
+ shstrndx uint32
+}
+
+type ElfSym struct {
+ name string
+ value uint64
+ size uint64
+ bind elf.SymBind
+ type_ elf.SymType
+ other uint8
+ shndx elf.SectionIndex
+ sym loader.Sym
+}
+
+const (
+ TagFile = 1
+ TagCPUName = 4
+ TagCPURawName = 5
+ TagCompatibility = 32
+ TagNoDefaults = 64
+ TagAlsoCompatibleWith = 65
+ TagABIVFPArgs = 28
+)
+
+type elfAttribute struct {
+ tag uint64
+ sval string
+ ival uint64
+}
+
+type elfAttributeList struct {
+ data []byte
+ err error
+}
+
+func (a *elfAttributeList) string() string {
+ if a.err != nil {
+ return ""
+ }
+ nul := bytes.IndexByte(a.data, 0)
+ if nul < 0 {
+ a.err = io.EOF
+ return ""
+ }
+ s := string(a.data[:nul])
+ a.data = a.data[nul+1:]
+ return s
+}
+
+func (a *elfAttributeList) uleb128() uint64 {
+ if a.err != nil {
+ return 0
+ }
+ v, size := binary.Uvarint(a.data)
+ a.data = a.data[size:]
+ return v
+}
+
+// Read an elfAttribute from the list following the rules used on ARM systems.
+func (a *elfAttributeList) armAttr() elfAttribute {
+ attr := elfAttribute{tag: a.uleb128()}
+ switch {
+ case attr.tag == TagCompatibility:
+ attr.ival = a.uleb128()
+ attr.sval = a.string()
+
+ case attr.tag == TagNoDefaults: // Tag_nodefaults has no argument
+
+ case attr.tag == TagAlsoCompatibleWith:
+ // Not really, but we don't actually care about this tag.
+ attr.sval = a.string()
+
+ // Tag with string argument
+ case attr.tag == TagCPUName || attr.tag == TagCPURawName || (attr.tag >= 32 && attr.tag&1 != 0):
+ attr.sval = a.string()
+
+ default: // Tag with integer argument
+ attr.ival = a.uleb128()
+ }
+ return attr
+}
+
+func (a *elfAttributeList) done() bool {
+ if a.err != nil || len(a.data) == 0 {
+ return true
+ }
+ return false
+}
+
+// Look for the attribute that indicates the object uses the hard-float ABI (a
+// file-level attribute with tag Tag_VFP_arch and value 1). Unfortunately the
+// format used means that we have to parse all of the file-level attributes to
+// find the one we are looking for. This format is slightly documented in "ELF
+// for the ARM Architecture" but mostly this is derived from reading the source
+// to gold and readelf.
+func parseArmAttributes(e binary.ByteOrder, data []byte) (found bool, ehdrFlags uint32, err error) {
+ found = false
+ if data[0] != 'A' {
+ return false, 0, fmt.Errorf(".ARM.attributes has unexpected format %c\n", data[0])
+ }
+ data = data[1:]
+ for len(data) != 0 {
+ sectionlength := e.Uint32(data)
+ sectiondata := data[4:sectionlength]
+ data = data[sectionlength:]
+
+ nulIndex := bytes.IndexByte(sectiondata, 0)
+ if nulIndex < 0 {
+ return false, 0, fmt.Errorf("corrupt .ARM.attributes (section name not NUL-terminated)\n")
+ }
+ name := string(sectiondata[:nulIndex])
+ sectiondata = sectiondata[nulIndex+1:]
+
+ if name != "aeabi" {
+ continue
+ }
+ for len(sectiondata) != 0 {
+ subsectiontag, sz := binary.Uvarint(sectiondata)
+ subsectionsize := e.Uint32(sectiondata[sz:])
+ subsectiondata := sectiondata[sz+4 : subsectionsize]
+ sectiondata = sectiondata[subsectionsize:]
+
+ if subsectiontag != TagFile {
+ continue
+ }
+ attrList := elfAttributeList{data: subsectiondata}
+ for !attrList.done() {
+ attr := attrList.armAttr()
+ if attr.tag == TagABIVFPArgs && attr.ival == 1 {
+ found = true
+ ehdrFlags = 0x5000402 // has entry point, Version5 EABI, hard-float ABI
+ }
+ }
+ if attrList.err != nil {
+ return false, 0, fmt.Errorf("could not parse .ARM.attributes\n")
+ }
+ }
+ }
+ return found, ehdrFlags, nil
+}
+
+// Load loads the ELF file pn from f.
+// Symbols are installed into the loader, and a slice of the text symbols is returned.
+//
+// On ARM systems, Load will attempt to determine what ELF header flags to
+// emit by scanning the attributes in the ELF file being loaded. The
+// parameter initEhdrFlags contains the current header flags for the output
+// object, and the returned ehdrFlags contains what this Load function computes.
+// TODO: find a better place for this logic.
+func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, f *bio.Reader, pkg string, length int64, pn string, initEhdrFlags uint32) (textp []loader.Sym, ehdrFlags uint32, err error) {
+ newSym := func(name string, version int) loader.Sym {
+ return l.CreateStaticSym(name)
+ }
+ lookup := l.LookupOrCreateCgoExport
+ errorf := func(str string, args ...interface{}) ([]loader.Sym, uint32, error) {
+ return nil, 0, fmt.Errorf("loadelf: %s: %v", pn, fmt.Sprintf(str, args...))
+ }
+
+ ehdrFlags = initEhdrFlags
+
+ base := f.Offset()
+
+ var hdrbuf [64]byte
+ if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+
+ var e binary.ByteOrder
+ switch elf.Data(hdrbuf[elf.EI_DATA]) {
+ case elf.ELFDATA2LSB:
+ e = binary.LittleEndian
+
+ case elf.ELFDATA2MSB:
+ e = binary.BigEndian
+
+ default:
+ return errorf("malformed elf file, unknown header")
+ }
+
+ hdr := new(elf.Header32)
+ binary.Read(bytes.NewReader(hdrbuf[:]), e, hdr)
+
+ if string(hdr.Ident[:elf.EI_CLASS]) != elf.ELFMAG {
+ return errorf("malformed elf file, bad header")
+ }
+
+ // read header
+ elfobj := new(ElfObj)
+
+ elfobj.e = e
+ elfobj.f = f
+ elfobj.base = base
+ elfobj.length = length
+ elfobj.name = pn
+
+ is64 := 0
+ class := elf.Class(hdrbuf[elf.EI_CLASS])
+ if class == elf.ELFCLASS64 {
+ is64 = 1
+ hdr := new(elf.Header64)
+ binary.Read(bytes.NewReader(hdrbuf[:]), e, hdr)
+ elfobj.type_ = uint32(hdr.Type)
+ elfobj.machine = uint32(hdr.Machine)
+ elfobj.version = hdr.Version
+ elfobj.entry = hdr.Entry
+ elfobj.phoff = hdr.Phoff
+ elfobj.shoff = hdr.Shoff
+ elfobj.flags = hdr.Flags
+ elfobj.ehsize = uint32(hdr.Ehsize)
+ elfobj.phentsize = uint32(hdr.Phentsize)
+ elfobj.phnum = uint32(hdr.Phnum)
+ elfobj.shentsize = uint32(hdr.Shentsize)
+ elfobj.shnum = uint32(hdr.Shnum)
+ elfobj.shstrndx = uint32(hdr.Shstrndx)
+ } else {
+ elfobj.type_ = uint32(hdr.Type)
+ elfobj.machine = uint32(hdr.Machine)
+ elfobj.version = hdr.Version
+ elfobj.entry = uint64(hdr.Entry)
+ elfobj.phoff = uint64(hdr.Phoff)
+ elfobj.shoff = uint64(hdr.Shoff)
+ elfobj.flags = hdr.Flags
+ elfobj.ehsize = uint32(hdr.Ehsize)
+ elfobj.phentsize = uint32(hdr.Phentsize)
+ elfobj.phnum = uint32(hdr.Phnum)
+ elfobj.shentsize = uint32(hdr.Shentsize)
+ elfobj.shnum = uint32(hdr.Shnum)
+ elfobj.shstrndx = uint32(hdr.Shstrndx)
+ }
+
+ elfobj.is64 = is64
+
+ if v := uint32(hdrbuf[elf.EI_VERSION]); v != elfobj.version {
+ return errorf("malformed elf version: got %d, want %d", v, elfobj.version)
+ }
+
+ if elf.Type(elfobj.type_) != elf.ET_REL {
+ return errorf("elf but not elf relocatable object")
+ }
+
+ mach := elf.Machine(elfobj.machine)
+ switch arch.Family {
+ default:
+ return errorf("elf %s unimplemented", arch.Name)
+
+ case sys.MIPS:
+ if mach != elf.EM_MIPS || class != elf.ELFCLASS32 {
+ return errorf("elf object but not mips")
+ }
+
+ case sys.MIPS64:
+ if mach != elf.EM_MIPS || class != elf.ELFCLASS64 {
+ return errorf("elf object but not mips64")
+ }
+ case sys.Loong64:
+ if mach != elf.EM_LOONGARCH || class != elf.ELFCLASS64 {
+ return errorf("elf object but not loong64")
+ }
+
+ case sys.ARM:
+ if e != binary.LittleEndian || mach != elf.EM_ARM || class != elf.ELFCLASS32 {
+ return errorf("elf object but not arm")
+ }
+
+ case sys.AMD64:
+ if e != binary.LittleEndian || mach != elf.EM_X86_64 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not amd64")
+ }
+
+ case sys.ARM64:
+ if e != binary.LittleEndian || mach != elf.EM_AARCH64 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not arm64")
+ }
+
+ case sys.I386:
+ if e != binary.LittleEndian || mach != elf.EM_386 || class != elf.ELFCLASS32 {
+ return errorf("elf object but not 386")
+ }
+
+ case sys.PPC64:
+ if mach != elf.EM_PPC64 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not ppc64")
+ }
+
+ case sys.RISCV64:
+ if mach != elf.EM_RISCV || class != elf.ELFCLASS64 {
+ return errorf("elf object but not riscv64")
+ }
+
+ case sys.S390X:
+ if mach != elf.EM_S390 || class != elf.ELFCLASS64 {
+ return errorf("elf object but not s390x")
+ }
+ }
+
+ // load section list into memory.
+ elfobj.sect = make([]ElfSect, elfobj.shnum)
+
+ elfobj.nsect = uint(elfobj.shnum)
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ f.MustSeek(int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0)
+ sect := &elfobj.sect[i]
+ if is64 != 0 {
+ var b elf.Section64
+ if err := binary.Read(f, e, &b); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+
+ sect.nameoff = b.Name
+ sect.type_ = elf.SectionType(b.Type)
+ sect.flags = elf.SectionFlag(b.Flags)
+ sect.addr = b.Addr
+ sect.off = b.Off
+ sect.size = b.Size
+ sect.link = b.Link
+ sect.info = b.Info
+ sect.align = b.Addralign
+ sect.entsize = b.Entsize
+ } else {
+ var b elf.Section32
+
+ if err := binary.Read(f, e, &b); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ sect.nameoff = b.Name
+ sect.type_ = elf.SectionType(b.Type)
+ sect.flags = elf.SectionFlag(b.Flags)
+ sect.addr = uint64(b.Addr)
+ sect.off = uint64(b.Off)
+ sect.size = uint64(b.Size)
+ sect.link = b.Link
+ sect.info = b.Info
+ sect.align = uint64(b.Addralign)
+ sect.entsize = uint64(b.Entsize)
+ }
+ }
+
+ // read section string table and translate names
+ if elfobj.shstrndx >= uint32(elfobj.nsect) {
+ return errorf("malformed elf file: shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect)
+ }
+
+ sect := &elfobj.sect[elfobj.shstrndx]
+ if err := elfmap(elfobj, sect); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ if elfobj.sect[i].nameoff != 0 {
+ elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:])
+ }
+ }
+
+ // load string table for symbols into memory.
+ elfobj.symtab = section(elfobj, ".symtab")
+
+ if elfobj.symtab == nil {
+ // our work is done here - no symbols means nothing can refer to this file
+ return
+ }
+
+ if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) {
+ return errorf("elf object has symbol table with invalid string table link")
+ }
+
+ elfobj.symstr = &elfobj.sect[elfobj.symtab.link]
+ if is64 != 0 {
+ elfobj.nsymtab = int(elfobj.symtab.size / elf.Sym64Size)
+ } else {
+ elfobj.nsymtab = int(elfobj.symtab.size / elf.Sym32Size)
+ }
+
+ if err := elfmap(elfobj, elfobj.symtab); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ if err := elfmap(elfobj, elfobj.symstr); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+
+ // load text and data segments into memory.
+ // they are not as small as the section lists, but we'll need
+ // the memory anyway for the symbol images, so we might
+ // as well use one large chunk.
+
+ // create symbols for elfmapped sections
+ sectsymNames := make(map[string]bool)
+ counter := 0
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ sect = &elfobj.sect[i]
+ if sect.type_ == SHT_ARM_ATTRIBUTES && sect.name == ".ARM.attributes" {
+ if err := elfmap(elfobj, sect); err != nil {
+ return errorf("%s: malformed elf file: %v", pn, err)
+ }
+ // We assume the soft-float ABI unless we see a tag indicating otherwise.
+ if initEhdrFlags == 0x5000002 {
+ ehdrFlags = 0x5000202
+ } else {
+ ehdrFlags = initEhdrFlags
+ }
+ found, newEhdrFlags, err := parseArmAttributes(e, sect.base[:sect.size])
+ if err != nil {
+ // TODO(dfc) should this return an error?
+ log.Printf("%s: %v", pn, err)
+ }
+ if found {
+ ehdrFlags = newEhdrFlags
+ }
+ }
+ if (sect.type_ != elf.SHT_PROGBITS && sect.type_ != elf.SHT_NOBITS) || sect.flags&elf.SHF_ALLOC == 0 {
+ continue
+ }
+ if sect.type_ != elf.SHT_NOBITS {
+ if err := elfmap(elfobj, sect); err != nil {
+ return errorf("%s: malformed elf file: %v", pn, err)
+ }
+ }
+
+ name := fmt.Sprintf("%s(%s)", pkg, sect.name)
+ for sectsymNames[name] {
+ counter++
+ name = fmt.Sprintf("%s(%s%d)", pkg, sect.name, counter)
+ }
+ sectsymNames[name] = true
+
+ sb := l.MakeSymbolUpdater(lookup(name, localSymVersion))
+
+ switch sect.flags & (elf.SHF_ALLOC | elf.SHF_WRITE | elf.SHF_EXECINSTR) {
+ default:
+ return errorf("%s: unexpected flags for ELF section %s", pn, sect.name)
+
+ case elf.SHF_ALLOC:
+ sb.SetType(sym.SRODATA)
+
+ case elf.SHF_ALLOC + elf.SHF_WRITE:
+ if sect.type_ == elf.SHT_NOBITS {
+ sb.SetType(sym.SNOPTRBSS)
+ } else {
+ sb.SetType(sym.SNOPTRDATA)
+ }
+
+ case elf.SHF_ALLOC + elf.SHF_EXECINSTR:
+ sb.SetType(sym.STEXT)
+ }
+
+ if sect.name == ".got" || sect.name == ".toc" {
+ sb.SetType(sym.SELFGOT)
+ }
+ if sect.type_ == elf.SHT_PROGBITS {
+ sb.SetData(sect.base[:sect.size])
+ }
+
+ sb.SetSize(int64(sect.size))
+ sb.SetAlign(int32(sect.align))
+ sb.SetReadOnly(sect.readOnlyMem)
+
+ sect.sym = sb.Sym()
+ }
+
+ // enter sub-symbols into symbol table.
+ // symbol 0 is the null symbol.
+ symbols := make([]loader.Sym, elfobj.nsymtab)
+
+ for i := 1; i < elfobj.nsymtab; i++ {
+ var elfsym ElfSym
+ if err := readelfsym(newSym, lookup, l, arch, elfobj, i, &elfsym, 1, localSymVersion); err != nil {
+ return errorf("%s: malformed elf file: %v", pn, err)
+ }
+ symbols[i] = elfsym.sym
+ if elfsym.type_ != elf.STT_FUNC && elfsym.type_ != elf.STT_OBJECT && elfsym.type_ != elf.STT_NOTYPE && elfsym.type_ != elf.STT_COMMON {
+ continue
+ }
+ if elfsym.shndx == elf.SHN_COMMON || elfsym.type_ == elf.STT_COMMON {
+ sb := l.MakeSymbolUpdater(elfsym.sym)
+ if uint64(sb.Size()) < elfsym.size {
+ sb.SetSize(int64(elfsym.size))
+ }
+ if sb.Type() == 0 || sb.Type() == sym.SXREF {
+ sb.SetType(sym.SNOPTRBSS)
+ }
+ continue
+ }
+
+ if uint(elfsym.shndx) >= elfobj.nsect || elfsym.shndx == 0 {
+ continue
+ }
+
+ // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols
+ if elfsym.sym == 0 {
+ continue
+ }
+ sect = &elfobj.sect[elfsym.shndx]
+ if sect.sym == 0 {
+ if strings.HasPrefix(elfsym.name, ".Linfo_string") { // clang does this
+ continue
+ }
+
+ if elfsym.name == "" && elfsym.type_ == 0 && sect.name == ".debug_str" {
+ // This reportedly happens with clang 3.7 on ARM.
+ // See issue 13139.
+ continue
+ }
+
+ if strings.HasPrefix(elfsym.name, "$d") && elfsym.type_ == 0 && sect.name == ".debug_frame" {
+ // "$d" is a marker, not a real symbol.
+ // This happens with gcc on ARM64.
+ // See https://sourceware.org/bugzilla/show_bug.cgi?id=21809
+ continue
+ }
+
+ if strings.HasPrefix(elfsym.name, ".LASF") { // gcc on s390x does this
+ continue
+ }
+ return errorf("%v: sym#%d (%s): ignoring symbol in section %d (type %d)", elfsym.sym, i, elfsym.name, elfsym.shndx, elfsym.type_)
+ }
+
+ s := elfsym.sym
+ if l.OuterSym(s) != 0 {
+ if l.AttrDuplicateOK(s) {
+ continue
+ }
+ return errorf("duplicate symbol reference: %s in both %s and %s",
+ l.SymName(s), l.SymName(l.OuterSym(s)), l.SymName(sect.sym))
+ }
+
+ sectsb := l.MakeSymbolUpdater(sect.sym)
+ sb := l.MakeSymbolUpdater(s)
+
+ sb.SetType(sectsb.Type())
+ sectsb.AddInteriorSym(s)
+ if !l.AttrCgoExportDynamic(s) {
+ sb.SetDynimplib("") // satisfy dynimport
+ }
+ sb.SetValue(int64(elfsym.value))
+ sb.SetSize(int64(elfsym.size))
+ if sectsb.Type() == sym.STEXT {
+ if l.AttrExternal(s) && !l.AttrDuplicateOK(s) {
+ return errorf("%s: duplicate symbol definition", sb.Name())
+ }
+ l.SetAttrExternal(s, true)
+ }
+
+ if elf.Machine(elfobj.machine) == elf.EM_PPC64 {
+ flag := int(elfsym.other) >> 5
+ switch flag {
+ case 0:
+ // No local entry. R2 is preserved.
+ case 1:
+ // These require R2 be saved and restored by the caller. This isn't supported today.
+ return errorf("%s: unable to handle local entry type 1", sb.Name())
+ case 7:
+ return errorf("%s: invalid sym.other 0x%x", sb.Name(), elfsym.other)
+ default:
+ l.SetSymLocalentry(s, 4<<uint(flag-2))
+ }
+ }
+ }
+
+ // Sort outer lists by address, adding to textp.
+ // This keeps textp in increasing address order.
+ for i := uint(0); i < elfobj.nsect; i++ {
+ s := elfobj.sect[i].sym
+ if s == 0 {
+ continue
+ }
+ sb := l.MakeSymbolUpdater(s)
+ if l.SubSym(s) != 0 {
+ sb.SortSub()
+ }
+ if sb.Type() == sym.STEXT {
+ if l.AttrOnList(s) {
+ return errorf("symbol %s listed multiple times",
+ l.SymName(s))
+ }
+ l.SetAttrOnList(s, true)
+ textp = append(textp, s)
+ for ss := l.SubSym(s); ss != 0; ss = l.SubSym(ss) {
+ if l.AttrOnList(ss) {
+ return errorf("symbol %s listed multiple times",
+ l.SymName(ss))
+ }
+ l.SetAttrOnList(ss, true)
+ textp = append(textp, ss)
+ }
+ }
+ }
+
+ // load relocations
+ for i := uint(0); i < elfobj.nsect; i++ {
+ rsect := &elfobj.sect[i]
+ if rsect.type_ != elf.SHT_RELA && rsect.type_ != elf.SHT_REL {
+ continue
+ }
+ if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil {
+ continue
+ }
+ sect = &elfobj.sect[rsect.info]
+ if err := elfmap(elfobj, rsect); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ rela := 0
+ if rsect.type_ == elf.SHT_RELA {
+ rela = 1
+ }
+ n := int(rsect.size / uint64(4+4*is64) / uint64(2+rela))
+ p := rsect.base
+ sb := l.MakeSymbolUpdater(sect.sym)
+ for j := 0; j < n; j++ {
+ var add uint64
+ var symIdx int
+ var relocType uint64
+ var rOff int32
+ var rAdd int64
+ var rSym loader.Sym
+
+ if is64 != 0 {
+ // 64-bit rel/rela
+ rOff = int32(e.Uint64(p))
+
+ p = p[8:]
+ switch arch.Family {
+ case sys.MIPS64:
+ // https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf
+ // The doc shows it's different with general Linux ELF
+ symIdx = int(e.Uint32(p))
+ relocType = uint64(p[7])
+ default:
+ info := e.Uint64(p)
+ relocType = info & 0xffffffff
+ symIdx = int(info >> 32)
+ }
+ p = p[8:]
+ if rela != 0 {
+ add = e.Uint64(p)
+ p = p[8:]
+ }
+ } else {
+ // 32-bit rel/rela
+ rOff = int32(e.Uint32(p))
+
+ p = p[4:]
+ info := e.Uint32(p)
+ relocType = uint64(info & 0xff)
+ symIdx = int(info >> 8)
+ p = p[4:]
+ if rela != 0 {
+ add = uint64(e.Uint32(p))
+ p = p[4:]
+ }
+ }
+
+ if relocType == 0 { // skip R_*_NONE relocation
+ j--
+ n--
+ continue
+ }
+
+ if symIdx == 0 { // absolute relocation, don't bother reading the null symbol
+ rSym = 0
+ } else {
+ var elfsym ElfSym
+ if err := readelfsym(newSym, lookup, l, arch, elfobj, int(symIdx), &elfsym, 0, 0); err != nil {
+ return errorf("malformed elf file: %v", err)
+ }
+ elfsym.sym = symbols[symIdx]
+ if elfsym.sym == 0 {
+ return errorf("malformed elf file: %s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", l.SymName(sect.sym), j, int(symIdx), elfsym.name, elfsym.shndx, elfsym.type_)
+ }
+
+ rSym = elfsym.sym
+ }
+
+ rType := objabi.ElfRelocOffset + objabi.RelocType(relocType)
+ rSize, addendSize, err := relSize(arch, pn, uint32(relocType))
+ if err != nil {
+ return nil, 0, err
+ }
+ if rela != 0 {
+ rAdd = int64(add)
+ } else {
+ // load addend from image
+ if rSize == 4 {
+ rAdd = int64(e.Uint32(sect.base[rOff:]))
+ } else if rSize == 8 {
+ rAdd = int64(e.Uint64(sect.base[rOff:]))
+ } else {
+ return errorf("invalid rela size %d", rSize)
+ }
+ }
+
+ if addendSize == 2 {
+ rAdd = int64(int16(rAdd))
+ }
+ if addendSize == 4 {
+ rAdd = int64(int32(rAdd))
+ }
+
+ r, _ := sb.AddRel(rType)
+ r.SetOff(rOff)
+ r.SetSiz(rSize)
+ r.SetSym(rSym)
+ r.SetAdd(rAdd)
+ }
+
+ sb.SortRelocs() // just in case
+ }
+
+ return textp, ehdrFlags, nil
+}
+
+func section(elfobj *ElfObj, name string) *ElfSect {
+ for i := 0; uint(i) < elfobj.nsect; i++ {
+ if elfobj.sect[i].name != "" && name != "" && elfobj.sect[i].name == name {
+ return &elfobj.sect[i]
+ }
+ }
+ return nil
+}
+
+func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) {
+ if sect.base != nil {
+ return nil
+ }
+
+ if sect.off+sect.size > uint64(elfobj.length) {
+ err = fmt.Errorf("elf section past end of file")
+ return err
+ }
+
+ elfobj.f.MustSeek(int64(uint64(elfobj.base)+sect.off), 0)
+ sect.base, sect.readOnlyMem, err = elfobj.f.Slice(uint64(sect.size))
+ if err != nil {
+ return fmt.Errorf("short read: %v", err)
+ }
+
+ return nil
+}
+
+func readelfsym(newSym, lookup func(string, int) loader.Sym, l *loader.Loader, arch *sys.Arch, elfobj *ElfObj, i int, elfsym *ElfSym, needSym int, localSymVersion int) (err error) {
+ if i >= elfobj.nsymtab || i < 0 {
+ err = fmt.Errorf("invalid elf symbol index")
+ return err
+ }
+
+ if i == 0 {
+ return fmt.Errorf("readym: read null symbol!")
+ }
+
+ if elfobj.is64 != 0 {
+ b := new(elf.Sym64)
+ binary.Read(bytes.NewReader(elfobj.symtab.base[i*elf.Sym64Size:(i+1)*elf.Sym64Size]), elfobj.e, b)
+ elfsym.name = cstring(elfobj.symstr.base[b.Name:])
+ elfsym.value = b.Value
+ elfsym.size = b.Size
+ elfsym.shndx = elf.SectionIndex(b.Shndx)
+ elfsym.bind = elf.ST_BIND(b.Info)
+ elfsym.type_ = elf.ST_TYPE(b.Info)
+ elfsym.other = b.Other
+ } else {
+ b := new(elf.Sym32)
+ binary.Read(bytes.NewReader(elfobj.symtab.base[i*elf.Sym32Size:(i+1)*elf.Sym32Size]), elfobj.e, b)
+ elfsym.name = cstring(elfobj.symstr.base[b.Name:])
+ elfsym.value = uint64(b.Value)
+ elfsym.size = uint64(b.Size)
+ elfsym.shndx = elf.SectionIndex(b.Shndx)
+ elfsym.bind = elf.ST_BIND(b.Info)
+ elfsym.type_ = elf.ST_TYPE(b.Info)
+ elfsym.other = b.Other
+ }
+
+ var s loader.Sym
+
+ if elfsym.name == "_GLOBAL_OFFSET_TABLE_" {
+ elfsym.name = ".got"
+ }
+ if elfsym.name == ".TOC." {
+ // Magic symbol on ppc64. Will be set to this object
+ // file's .got+0x8000.
+ elfsym.bind = elf.STB_LOCAL
+ }
+
+ switch elfsym.type_ {
+ case elf.STT_SECTION:
+ s = elfobj.sect[elfsym.shndx].sym
+
+ case elf.STT_OBJECT, elf.STT_FUNC, elf.STT_NOTYPE, elf.STT_COMMON:
+ switch elfsym.bind {
+ case elf.STB_GLOBAL:
+ if needSym != 0 {
+ s = lookup(elfsym.name, 0)
+
+ // for global scoped hidden symbols we should insert it into
+ // symbol hash table, but mark them as hidden.
+ // __i686.get_pc_thunk.bx is allowed to be duplicated, to
+ // workaround that we set dupok.
+ // TODO(minux): correctly handle __i686.get_pc_thunk.bx without
+ // set dupok generally. See https://golang.org/cl/5823055
+ // comment #5 for details.
+ if s != 0 && elfsym.other == 2 {
+ if !l.IsExternal(s) {
+ l.MakeSymbolUpdater(s)
+ }
+ l.SetAttrDuplicateOK(s, true)
+ l.SetAttrVisibilityHidden(s, true)
+ }
+ }
+
+ case elf.STB_LOCAL:
+ if (arch.Family == sys.ARM || arch.Family == sys.ARM64) && (strings.HasPrefix(elfsym.name, "$a") || strings.HasPrefix(elfsym.name, "$d") || strings.HasPrefix(elfsym.name, "$x")) {
+ // binutils for arm and arm64 generate these mapping
+ // symbols, ignore these
+ break
+ }
+
+ if elfsym.name == ".TOC." {
+ // We need to be able to look this up,
+ // so put it in the hash table.
+ if needSym != 0 {
+ s = lookup(elfsym.name, localSymVersion)
+ l.SetAttrVisibilityHidden(s, true)
+ }
+ break
+ }
+
+ if needSym != 0 {
+ // local names and hidden global names are unique
+ // and should only be referenced by their index, not name, so we
+ // don't bother to add them into the hash table
+ // FIXME: pass empty string here for name? This would
+ // reduce mem use, but also (possibly) make it harder
+ // to debug problems.
+ s = newSym(elfsym.name, localSymVersion)
+ l.SetAttrVisibilityHidden(s, true)
+ }
+
+ case elf.STB_WEAK:
+ if needSym != 0 {
+ s = lookup(elfsym.name, 0)
+ if elfsym.other == 2 {
+ l.SetAttrVisibilityHidden(s, true)
+ }
+
+ // Allow weak symbols to be duplicated when already defined.
+ if l.OuterSym(s) != 0 {
+ l.SetAttrDuplicateOK(s, true)
+ }
+ }
+
+ default:
+ err = fmt.Errorf("%s: invalid symbol binding %d", elfsym.name, elfsym.bind)
+ return err
+ }
+ }
+
+ if s != 0 && l.SymType(s) == 0 && elfsym.type_ != elf.STT_SECTION {
+ sb := l.MakeSymbolUpdater(s)
+ sb.SetType(sym.SXREF)
+ }
+ elfsym.sym = s
+
+ return nil
+}
+
+// Return the size of the relocated field, and the size of the addend as the first
+// and second values. Note, the addend may be larger than the relocation field in
+// some cases when a relocated value is split across multiple relocations.
+func relSize(arch *sys.Arch, pn string, elftype uint32) (uint8, uint8, error) {
+ // TODO(mdempsky): Replace this with a struct-valued switch statement
+ // once golang.org/issue/15164 is fixed or found to not impair cmd/link
+ // performance.
+
+ const (
+ AMD64 = uint32(sys.AMD64)
+ ARM = uint32(sys.ARM)
+ ARM64 = uint32(sys.ARM64)
+ I386 = uint32(sys.I386)
+ LOONG64 = uint32(sys.Loong64)
+ MIPS = uint32(sys.MIPS)
+ MIPS64 = uint32(sys.MIPS64)
+ PPC64 = uint32(sys.PPC64)
+ RISCV64 = uint32(sys.RISCV64)
+ S390X = uint32(sys.S390X)
+ )
+
+ switch uint32(arch.Family) | elftype<<16 {
+ default:
+ return 0, 0, fmt.Errorf("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype)
+
+ case MIPS | uint32(elf.R_MIPS_HI16)<<16,
+ MIPS | uint32(elf.R_MIPS_LO16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_HI16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_LO16)<<16,
+ MIPS | uint32(elf.R_MIPS_GPREL16)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_PAGE)<<16,
+ MIPS | uint32(elf.R_MIPS_JALR)<<16,
+ MIPS | uint32(elf.R_MIPS_GOT_OFST)<<16,
+ MIPS64 | uint32(elf.R_MIPS_HI16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_LO16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_HI16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_LO16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GPREL16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_PAGE)<<16,
+ MIPS64 | uint32(elf.R_MIPS_JALR)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_OFST)<<16,
+ MIPS64 | uint32(elf.R_MIPS_CALL16)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GPREL32)<<16,
+ MIPS64 | uint32(elf.R_MIPS_64)<<16,
+ MIPS64 | uint32(elf.R_MIPS_GOT_DISP)<<16:
+ return 4, 4, nil
+
+ case LOONG64 | uint32(elf.R_LARCH_SOP_PUSH_PCREL)<<16,
+ LOONG64 | uint32(elf.R_LARCH_SOP_PUSH_GPREL)<<16,
+ LOONG64 | uint32(elf.R_LARCH_SOP_PUSH_ABSOLUTE)<<16,
+ LOONG64 | uint32(elf.R_LARCH_MARK_LA)<<16,
+ LOONG64 | uint32(elf.R_LARCH_SOP_POP_32_S_0_10_10_16_S2)<<16,
+ LOONG64 | uint32(elf.R_LARCH_64)<<16,
+ LOONG64 | uint32(elf.R_LARCH_MARK_PCREL)<<16,
+ LOONG64 | uint32(elf.R_LARCH_32_PCREL)<<16:
+ return 4, 4, nil
+
+ case S390X | uint32(elf.R_390_8)<<16:
+ return 1, 1, nil
+
+ case PPC64 | uint32(elf.R_PPC64_TOC16)<<16,
+ S390X | uint32(elf.R_390_16)<<16,
+ S390X | uint32(elf.R_390_GOT16)<<16,
+ S390X | uint32(elf.R_390_PC16)<<16,
+ S390X | uint32(elf.R_390_PC16DBL)<<16,
+ S390X | uint32(elf.R_390_PLT16DBL)<<16:
+ return 2, 2, nil
+
+ case ARM | uint32(elf.R_ARM_ABS32)<<16,
+ ARM | uint32(elf.R_ARM_GOT32)<<16,
+ ARM | uint32(elf.R_ARM_PLT32)<<16,
+ ARM | uint32(elf.R_ARM_GOTOFF)<<16,
+ ARM | uint32(elf.R_ARM_GOTPC)<<16,
+ ARM | uint32(elf.R_ARM_THM_PC22)<<16,
+ ARM | uint32(elf.R_ARM_REL32)<<16,
+ ARM | uint32(elf.R_ARM_CALL)<<16,
+ ARM | uint32(elf.R_ARM_V4BX)<<16,
+ ARM | uint32(elf.R_ARM_GOT_PREL)<<16,
+ ARM | uint32(elf.R_ARM_PC24)<<16,
+ ARM | uint32(elf.R_ARM_JUMP24)<<16,
+ ARM64 | uint32(elf.R_AARCH64_CALL26)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ADR_GOT_PAGE)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LD64_GOT_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ADR_PREL_PG_HI21)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ADD_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST8_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST16_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST32_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST64_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_LDST128_ABS_LO12_NC)<<16,
+ ARM64 | uint32(elf.R_AARCH64_PREL32)<<16,
+ ARM64 | uint32(elf.R_AARCH64_JUMP26)<<16,
+ AMD64 | uint32(elf.R_X86_64_PC32)<<16,
+ AMD64 | uint32(elf.R_X86_64_PLT32)<<16,
+ AMD64 | uint32(elf.R_X86_64_GOTPCREL)<<16,
+ AMD64 | uint32(elf.R_X86_64_GOTPCRELX)<<16,
+ AMD64 | uint32(elf.R_X86_64_REX_GOTPCRELX)<<16,
+ I386 | uint32(elf.R_386_32)<<16,
+ I386 | uint32(elf.R_386_PC32)<<16,
+ I386 | uint32(elf.R_386_GOT32)<<16,
+ I386 | uint32(elf.R_386_PLT32)<<16,
+ I386 | uint32(elf.R_386_GOTOFF)<<16,
+ I386 | uint32(elf.R_386_GOTPC)<<16,
+ I386 | uint32(elf.R_386_GOT32X)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL24)<<16,
+ PPC64 | uint32(elf.R_PPC_REL32)<<16,
+ S390X | uint32(elf.R_390_32)<<16,
+ S390X | uint32(elf.R_390_PC32)<<16,
+ S390X | uint32(elf.R_390_GOT32)<<16,
+ S390X | uint32(elf.R_390_PLT32)<<16,
+ S390X | uint32(elf.R_390_PC32DBL)<<16,
+ S390X | uint32(elf.R_390_PLT32DBL)<<16,
+ S390X | uint32(elf.R_390_GOTPCDBL)<<16,
+ S390X | uint32(elf.R_390_GOTENT)<<16:
+ return 4, 4, nil
+
+ case AMD64 | uint32(elf.R_X86_64_64)<<16,
+ AMD64 | uint32(elf.R_X86_64_PC64)<<16,
+ ARM64 | uint32(elf.R_AARCH64_ABS64)<<16,
+ ARM64 | uint32(elf.R_AARCH64_PREL64)<<16,
+ PPC64 | uint32(elf.R_PPC64_ADDR64)<<16,
+ S390X | uint32(elf.R_390_GLOB_DAT)<<16,
+ S390X | uint32(elf.R_390_RELATIVE)<<16,
+ S390X | uint32(elf.R_390_GOTOFF)<<16,
+ S390X | uint32(elf.R_390_GOTPC)<<16,
+ S390X | uint32(elf.R_390_64)<<16,
+ S390X | uint32(elf.R_390_PC64)<<16,
+ S390X | uint32(elf.R_390_GOT64)<<16,
+ S390X | uint32(elf.R_390_PLT64)<<16:
+ return 8, 8, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_SET6)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SUB6)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SET8)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SUB8)<<16:
+ return 1, 1, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_RVC_BRANCH)<<16,
+ RISCV64 | uint32(elf.R_RISCV_RVC_JUMP)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SET16)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SUB16)<<16:
+ return 2, 2, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_32)<<16,
+ RISCV64 | uint32(elf.R_RISCV_BRANCH)<<16,
+ RISCV64 | uint32(elf.R_RISCV_HI20)<<16,
+ RISCV64 | uint32(elf.R_RISCV_LO12_I)<<16,
+ RISCV64 | uint32(elf.R_RISCV_LO12_S)<<16,
+ RISCV64 | uint32(elf.R_RISCV_GOT_HI20)<<16,
+ RISCV64 | uint32(elf.R_RISCV_PCREL_HI20)<<16,
+ RISCV64 | uint32(elf.R_RISCV_PCREL_LO12_I)<<16,
+ RISCV64 | uint32(elf.R_RISCV_PCREL_LO12_S)<<16,
+ RISCV64 | uint32(elf.R_RISCV_ADD32)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SET32)<<16,
+ RISCV64 | uint32(elf.R_RISCV_SUB32)<<16,
+ RISCV64 | uint32(elf.R_RISCV_32_PCREL)<<16,
+ RISCV64 | uint32(elf.R_RISCV_RELAX)<<16:
+ return 4, 4, nil
+
+ case RISCV64 | uint32(elf.R_RISCV_64)<<16,
+ RISCV64 | uint32(elf.R_RISCV_CALL)<<16,
+ RISCV64 | uint32(elf.R_RISCV_CALL_PLT)<<16:
+ return 8, 8, nil
+
+ case PPC64 | uint32(elf.R_PPC64_TOC16_LO)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_HI)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_HA)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_DS)<<16,
+ PPC64 | uint32(elf.R_PPC64_TOC16_LO_DS)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL16_LO)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL16_HI)<<16,
+ PPC64 | uint32(elf.R_PPC64_REL16_HA)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLT16_HA)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLT16_LO_DS)<<16:
+ return 2, 4, nil
+
+ // PPC64 inline PLT sequence hint relocations (-fno-plt)
+ // These are informational annotations to assist linker optimizations.
+ case PPC64 | uint32(elf.R_PPC64_PLTSEQ)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLTCALL)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLTCALL_NOTOC)<<16,
+ PPC64 | uint32(elf.R_PPC64_PLTSEQ_NOTOC)<<16:
+ return 0, 0, nil
+
+ }
+}
+
+func cstring(x []byte) string {
+ i := bytes.IndexByte(x, '\x00')
+ if i >= 0 {
+ x = x[:i]
+ }
+ return string(x)
+}