// 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/sym"
"errors"
"fmt"
"internal/xcoff"
)
// ldSection is an XCOFF section with its symbols.
type ldSection struct {
xcoff.Section
sym *sym.Symbol
}
// 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)).Seek(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
}
// Load loads the Xcoff file pn from f.
// Symbols are written into syms, and a slice of the text symbols is returned.
func Load(arch *sys.Arch, syms *sym.Symbols, input *bio.Reader, pkg string, length int64, pn string) (textp []*sym.Symbol, err error) {
errorf := func(str string, args ...interface{}) ([]*sym.Symbol, error) {
return nil, fmt.Errorf("loadxcoff: %v: %v", pn, fmt.Sprintf(str, args...))
}
localSymVersion := syms.IncVersion()
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)
s := syms.Lookup(name, localSymVersion)
switch lds.Type {
default:
return errorf("unrecognized section type 0x%x", lds.Type)
case xcoff.STYP_TEXT:
s.Type = sym.STEXT
case xcoff.STYP_DATA:
s.Type = sym.SNOPTRDATA
case xcoff.STYP_BSS:
s.Type = sym.SNOPTRBSS
}
s.Size = int64(lds.Size)
if s.Type != sym.SNOPTRBSS {
data, err := lds.Section.Data()
if err != nil {
return nil, err
}
s.P = data
}
lds.sym = s
ldSections = append(ldSections, lds)
}
// sx = symbol from file
// s = symbol for syms
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 := syms.Lookup(sx.Name, 0)
// Text symbol
if s.Type == sym.STEXT {
if s.Attr.OnList() {
return errorf("symbol %s listed multiple times", s.Name)
}
s.Attr |= sym.AttrOnList
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
}
rs := make([]sym.Reloc, sect.Nreloc)
for i, rx := range sect.Relocs {
r := &rs[i]
r.Sym = syms.Lookup(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)
}
r.Off = int32(rx.VirtualAddress)
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)
}
r.Siz = 8
r.Type = objabi.R_CONST
r.Add = int64(rx.Symbol.Value)
case xcoff.R_RBR:
r.Siz = 4
r.Type = objabi.R_CALLPOWER
r.Add = 0 //
}
}
s := sect.sym
s.R = rs
s.R = s.R[:sect.Nreloc]
}
return textp, nil
}
// Convert symbol xcoff type to sym.SymKind
// Returns nil if this shouldn't be added into syms (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)
}
}
}