blob: a5744216d66ae108d6ec1f8a652fb9f2b6a1ce4b [file] [log] [blame]
// Copyright 2018 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 loadxcoff implements a XCOFF file reader.
package loadxcoff
import (
"cmd/internal/bio"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"errors"
"fmt"
"internal/xcoff"
)
// ldSection is an XCOFF section with its symbols.
type ldSection struct {
xcoff.Section
sym loader.Sym
}
// TODO(brainman): maybe just add ReadAt method to bio.Reader instead of creating xcoffBiobuf
// xcoffBiobuf makes bio.Reader look like io.ReaderAt.
type xcoffBiobuf bio.Reader
func (f *xcoffBiobuf) ReadAt(p []byte, off int64) (int, error) {
ret := ((*bio.Reader)(f)).MustSeek(off, 0)
if ret < 0 {
return 0, errors.New("fail to seek")
}
n, err := f.Read(p)
if err != nil {
return 0, err
}
return n, nil
}
// loads the Xcoff file pn from f.
// Symbols are written into loader, and a slice of the text symbols is returned.
func Load(l *loader.Loader, arch *sys.Arch, localSymVersion int, input *bio.Reader, pkg string, length int64, pn string) (textp []loader.Sym, err error) {
errorf := func(str string, args ...interface{}) ([]loader.Sym, error) {
return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...))
}
var ldSections []*ldSection
f, err := xcoff.NewFile((*xcoffBiobuf)(input))
if err != nil {
return nil, err
}
defer f.Close()
for _, sect := range f.Sections {
//only text, data and bss section
if sect.Type < xcoff.STYP_TEXT || sect.Type > xcoff.STYP_BSS {
continue
}
lds := new(ldSection)
lds.Section = *sect
name := fmt.Sprintf("%s(%s)", pkg, lds.Name)
symbol := l.LookupOrCreateSym(name, localSymVersion)
s := l.MakeSymbolUpdater(symbol)
switch lds.Type {
default:
return errorf("unrecognized section type 0x%x", lds.Type)
case xcoff.STYP_TEXT:
s.SetType(sym.STEXT)
case xcoff.STYP_DATA:
s.SetType(sym.SNOPTRDATA)
case xcoff.STYP_BSS:
s.SetType(sym.SNOPTRBSS)
}
s.SetSize(int64(lds.Size))
if s.Type() != sym.SNOPTRBSS {
data, err := lds.Section.Data()
if err != nil {
return nil, err
}
s.SetData(data)
}
lds.sym = symbol
ldSections = append(ldSections, lds)
}
// sx = symbol from file
// s = symbol for loader
for _, sx := range f.Symbols {
// get symbol type
stype, errmsg := getSymbolType(f, sx)
if errmsg != "" {
return errorf("error reading symbol %s: %s", sx.Name, errmsg)
}
if stype == sym.Sxxx {
continue
}
s := l.LookupOrCreateSym(sx.Name, 0)
// Text symbol
if l.SymType(s) == sym.STEXT {
if l.AttrOnList(s) {
return errorf("symbol %s listed multiple times", l.SymName(s))
}
l.SetAttrOnList(s, true)
textp = append(textp, s)
}
}
// Read relocations
for _, sect := range ldSections {
// TODO(aix): Dwarf section relocation if needed
if sect.Type != xcoff.STYP_TEXT && sect.Type != xcoff.STYP_DATA {
continue
}
sb := l.MakeSymbolUpdater(sect.sym)
for _, rx := range sect.Relocs {
rSym := l.LookupOrCreateSym(rx.Symbol.Name, 0)
if uint64(int32(rx.VirtualAddress)) != rx.VirtualAddress {
return errorf("virtual address of a relocation is too big: 0x%x", rx.VirtualAddress)
}
rOff := int32(rx.VirtualAddress)
var rSize uint8
var rType objabi.RelocType
var rAdd int64
switch rx.Type {
default:
return errorf("section %s: unknown relocation of type 0x%x", sect.Name, rx.Type)
case xcoff.R_POS:
// Reloc the address of r.Sym
// Length should be 64
if rx.Length != 64 {
return errorf("section %s: relocation R_POS has length different from 64: %d", sect.Name, rx.Length)
}
rSize = 8
rType = objabi.R_CONST
rAdd = int64(rx.Symbol.Value)
case xcoff.R_RBR:
rSize = 4
rType = objabi.R_CALLPOWER
rAdd = 0
}
r, _ := sb.AddRel(rType)
r.SetOff(rOff)
r.SetSiz(rSize)
r.SetSym(rSym)
r.SetAdd(rAdd)
}
}
return textp, nil
}
// Convert symbol xcoff type to sym.SymKind
// Returns nil if this shouldn't be added into loader (like .file or .dw symbols )
func getSymbolType(f *xcoff.File, s *xcoff.Symbol) (stype sym.SymKind, err string) {
// .file symbol
if s.SectionNumber == -2 {
if s.StorageClass == xcoff.C_FILE {
return sym.Sxxx, ""
}
return sym.Sxxx, "unrecognised StorageClass for sectionNumber = -2"
}
// extern symbols
// TODO(aix)
if s.SectionNumber == 0 {
return sym.Sxxx, ""
}
sectType := f.Sections[s.SectionNumber-1].SectionHeader.Type
switch sectType {
default:
return sym.Sxxx, fmt.Sprintf("getSymbolType for Section type 0x%x not implemented", sectType)
case xcoff.STYP_DWARF, xcoff.STYP_DEBUG:
return sym.Sxxx, ""
case xcoff.STYP_DATA, xcoff.STYP_BSS, xcoff.STYP_TEXT:
}
switch s.StorageClass {
default:
return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x not implemented", s.StorageClass)
case xcoff.C_HIDEXT, xcoff.C_EXT, xcoff.C_WEAKEXT:
switch s.AuxCSect.StorageMappingClass {
default:
return sym.Sxxx, fmt.Sprintf("getSymbolType for Storage class 0x%x and Storage Map 0x%x not implemented", s.StorageClass, s.AuxCSect.StorageMappingClass)
// Program Code
case xcoff.XMC_PR:
if sectType == xcoff.STYP_TEXT {
return sym.STEXT, ""
}
return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_PR", sectType, s.StorageClass)
// Read/Write Data
case xcoff.XMC_RW:
if sectType == xcoff.STYP_DATA {
return sym.SDATA, ""
}
if sectType == xcoff.STYP_BSS {
return sym.SBSS, ""
}
return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_RW", sectType, s.StorageClass)
// Function descriptor
case xcoff.XMC_DS:
if sectType == xcoff.STYP_DATA {
return sym.SDATA, ""
}
return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass)
// TOC anchor and TOC entry
case xcoff.XMC_TC0, xcoff.XMC_TE:
if sectType == xcoff.STYP_DATA {
return sym.SXCOFFTOC, ""
}
return sym.Sxxx, fmt.Sprintf("unrecognised Section Type 0x%x for Storage Class 0x%x with Storage Map XMC_DS", sectType, s.StorageClass)
}
}
}