package xcoff
import (
"debug/dwarf"
"encoding/binary"
"errors"
"fmt"
"internal/saferio"
"io"
"os"
"strings"
)
type SectionHeader struct {
Name string
VirtualAddress uint64
Size uint64
Type uint32
Relptr uint64
Nreloc uint32
}
type Section struct {
SectionHeader
Relocs []Reloc
io .ReaderAt
sr *io .SectionReader
}
type AuxiliaryCSect struct {
Length int64
StorageMappingClass int
SymbolType int
}
type AuxiliaryFcn struct {
Size int64
}
type Symbol struct {
Name string
Value uint64
SectionNumber int
StorageClass int
AuxFcn AuxiliaryFcn
AuxCSect AuxiliaryCSect
}
type Reloc struct {
VirtualAddress uint64
Symbol *Symbol
Signed bool
InstructionFixed bool
Length uint8
Type uint8
}
type ImportedSymbol struct {
Name string
Library string
}
type FileHeader struct {
TargetMachine uint16
}
type File struct {
FileHeader
Sections []*Section
Symbols []*Symbol
StringTable []byte
LibraryPaths []string
closer io .Closer
}
func Open (name string ) (*File , error ) {
f , err := os .Open (name )
if err != nil {
return nil , err
}
ff , err := NewFile (f )
if err != nil {
f .Close ()
return nil , err
}
ff .closer = f
return ff , nil
}
func (f *File ) Close () error {
var err error
if f .closer != nil {
err = f .closer .Close ()
f .closer = nil
}
return err
}
func (f *File ) Section (name string ) *Section {
for _ , s := range f .Sections {
if s .Name == name || (len (name ) > 8 && s .Name == name [:8 ]) {
return s
}
}
return nil
}
func (f *File ) SectionByType (typ uint32 ) *Section {
for _ , s := range f .Sections {
if s .Type == typ {
return s
}
}
return nil
}
func cstring(b []byte ) string {
var i int
for i = 0 ; i < len (b ) && b [i ] != 0 ; i ++ {
}
return string (b [:i ])
}
func getString(st []byte , offset uint32 ) (string , bool ) {
if offset < 4 || int (offset ) >= len (st ) {
return "" , false
}
return cstring (st [offset :]), true
}
func NewFile (r io .ReaderAt ) (*File , error ) {
sr := io .NewSectionReader (r , 0 , 1 <<63 -1 )
var magic uint16
if err := binary .Read (sr , binary .BigEndian , &magic ); err != nil {
return nil , err
}
if magic != U802TOCMAGIC && magic != U64_TOCMAGIC {
return nil , fmt .Errorf ("unrecognised XCOFF magic: 0x%x" , magic )
}
f := new (File )
f .TargetMachine = magic
if _ , err := sr .Seek (0 , io .SeekStart ); err != nil {
return nil , err
}
var nscns uint16
var symptr uint64
var nsyms uint32
var opthdr uint16
var hdrsz int
switch f .TargetMachine {
case U802TOCMAGIC :
fhdr := new (FileHeader32 )
if err := binary .Read (sr , binary .BigEndian , fhdr ); err != nil {
return nil , err
}
nscns = fhdr .Fnscns
symptr = uint64 (fhdr .Fsymptr )
nsyms = fhdr .Fnsyms
opthdr = fhdr .Fopthdr
hdrsz = FILHSZ_32
case U64_TOCMAGIC :
fhdr := new (FileHeader64 )
if err := binary .Read (sr , binary .BigEndian , fhdr ); err != nil {
return nil , err
}
nscns = fhdr .Fnscns
symptr = fhdr .Fsymptr
nsyms = fhdr .Fnsyms
opthdr = fhdr .Fopthdr
hdrsz = FILHSZ_64
}
if symptr == 0 || nsyms <= 0 {
return nil , fmt .Errorf ("no symbol table" )
}
offset := symptr + uint64 (nsyms )*SYMESZ
if _ , err := sr .Seek (int64 (offset ), io .SeekStart ); err != nil {
return nil , err
}
var l uint32
if err := binary .Read (sr , binary .BigEndian , &l ); err != nil {
return nil , err
}
if l > 4 {
st , err := saferio .ReadDataAt (sr , uint64 (l ), int64 (offset ))
if err != nil {
return nil , err
}
f .StringTable = st
}
if _ , err := sr .Seek (int64 (hdrsz )+int64 (opthdr ), io .SeekStart ); err != nil {
return nil , err
}
c := saferio .SliceCap [*Section ](uint64 (nscns ))
if c < 0 {
return nil , fmt .Errorf ("too many XCOFF sections (%d)" , nscns )
}
f .Sections = make ([]*Section , 0 , c )
for i := 0 ; i < int (nscns ); i ++ {
var scnptr uint64
s := new (Section )
switch f .TargetMachine {
case U802TOCMAGIC :
shdr := new (SectionHeader32 )
if err := binary .Read (sr , binary .BigEndian , shdr ); err != nil {
return nil , err
}
s .Name = cstring (shdr .Sname [:])
s .VirtualAddress = uint64 (shdr .Svaddr )
s .Size = uint64 (shdr .Ssize )
scnptr = uint64 (shdr .Sscnptr )
s .Type = shdr .Sflags
s .Relptr = uint64 (shdr .Srelptr )
s .Nreloc = uint32 (shdr .Snreloc )
case U64_TOCMAGIC :
shdr := new (SectionHeader64 )
if err := binary .Read (sr , binary .BigEndian , shdr ); err != nil {
return nil , err
}
s .Name = cstring (shdr .Sname [:])
s .VirtualAddress = shdr .Svaddr
s .Size = shdr .Ssize
scnptr = shdr .Sscnptr
s .Type = shdr .Sflags
s .Relptr = shdr .Srelptr
s .Nreloc = shdr .Snreloc
}
r2 := r
if scnptr == 0 {
r2 = &nobitsSectionReader {}
}
s .sr = io .NewSectionReader (r2 , int64 (scnptr ), int64 (s .Size ))
s .ReaderAt = s .sr
f .Sections = append (f .Sections , s )
}
var idxToSym = make (map [int ]*Symbol )
if _ , err := sr .Seek (int64 (symptr ), io .SeekStart ); err != nil {
return nil , err
}
f .Symbols = make ([]*Symbol , 0 )
for i := 0 ; i < int (nsyms ); i ++ {
var numaux int
var ok , needAuxFcn bool
sym := new (Symbol )
switch f .TargetMachine {
case U802TOCMAGIC :
se := new (SymEnt32 )
if err := binary .Read (sr , binary .BigEndian , se ); err != nil {
return nil , err
}
numaux = int (se .Nnumaux )
sym .SectionNumber = int (se .Nscnum )
sym .StorageClass = int (se .Nsclass )
sym .Value = uint64 (se .Nvalue )
needAuxFcn = se .Ntype &SYM_TYPE_FUNC != 0 && numaux > 1
zeroes := binary .BigEndian .Uint32 (se .Nname [:4 ])
if zeroes != 0 {
sym .Name = cstring (se .Nname [:])
} else {
offset := binary .BigEndian .Uint32 (se .Nname [4 :])
sym .Name , ok = getString (f .StringTable , offset )
if !ok {
goto skip
}
}
case U64_TOCMAGIC :
se := new (SymEnt64 )
if err := binary .Read (sr , binary .BigEndian , se ); err != nil {
return nil , err
}
numaux = int (se .Nnumaux )
sym .SectionNumber = int (se .Nscnum )
sym .StorageClass = int (se .Nsclass )
sym .Value = se .Nvalue
needAuxFcn = se .Ntype &SYM_TYPE_FUNC != 0 && numaux > 1
sym .Name , ok = getString (f .StringTable , se .Noffset )
if !ok {
goto skip
}
}
if sym .StorageClass != C_EXT && sym .StorageClass != C_WEAKEXT && sym .StorageClass != C_HIDEXT {
goto skip
}
if numaux < 1 || i +numaux >= int (nsyms ) {
goto skip
}
if sym .SectionNumber > int (nscns ) {
goto skip
}
if sym .SectionNumber == 0 {
sym .Value = 0
} else {
sym .Value -= f .Sections [sym .SectionNumber -1 ].VirtualAddress
}
idxToSym [i ] = sym
if needAuxFcn {
switch f .TargetMachine {
case U802TOCMAGIC :
aux := new (AuxFcn32 )
if err := binary .Read (sr , binary .BigEndian , aux ); err != nil {
return nil , err
}
sym .AuxFcn .Size = int64 (aux .Xfsize )
case U64_TOCMAGIC :
aux := new (AuxFcn64 )
if err := binary .Read (sr , binary .BigEndian , aux ); err != nil {
return nil , err
}
sym .AuxFcn .Size = int64 (aux .Xfsize )
}
}
if !needAuxFcn {
if _ , err := sr .Seek (int64 (numaux -1 )*SYMESZ , io .SeekCurrent ); err != nil {
return nil , err
}
}
i += numaux
numaux = 0
switch f .TargetMachine {
case U802TOCMAGIC :
aux := new (AuxCSect32 )
if err := binary .Read (sr , binary .BigEndian , aux ); err != nil {
return nil , err
}
sym .AuxCSect .SymbolType = int (aux .Xsmtyp & 0x7 )
sym .AuxCSect .StorageMappingClass = int (aux .Xsmclas )
sym .AuxCSect .Length = int64 (aux .Xscnlen )
case U64_TOCMAGIC :
aux := new (AuxCSect64 )
if err := binary .Read (sr , binary .BigEndian , aux ); err != nil {
return nil , err
}
sym .AuxCSect .SymbolType = int (aux .Xsmtyp & 0x7 )
sym .AuxCSect .StorageMappingClass = int (aux .Xsmclas )
sym .AuxCSect .Length = int64 (aux .Xscnlenhi )<<32 | int64 (aux .Xscnlenlo )
}
f .Symbols = append (f .Symbols , sym )
skip :
i += numaux
if _ , err := sr .Seek (int64 (numaux )*SYMESZ , io .SeekCurrent ); err != nil {
return nil , err
}
}
for sectNum , sect := range f .Sections {
if sect .Type != STYP_TEXT && sect .Type != STYP_DATA {
continue
}
if sect .Relptr == 0 {
continue
}
c := saferio .SliceCap [Reloc ](uint64 (sect .Nreloc ))
if c < 0 {
return nil , fmt .Errorf ("too many relocs (%d) for section %d" , sect .Nreloc , sectNum )
}
sect .Relocs = make ([]Reloc , 0 , c )
if _ , err := sr .Seek (int64 (sect .Relptr ), io .SeekStart ); err != nil {
return nil , err
}
for i := uint32 (0 ); i < sect .Nreloc ; i ++ {
var reloc Reloc
switch f .TargetMachine {
case U802TOCMAGIC :
rel := new (Reloc32 )
if err := binary .Read (sr , binary .BigEndian , rel ); err != nil {
return nil , err
}
reloc .VirtualAddress = uint64 (rel .Rvaddr )
reloc .Symbol = idxToSym [int (rel .Rsymndx )]
reloc .Type = rel .Rtype
reloc .Length = rel .Rsize &0x3F + 1
if rel .Rsize &0x80 != 0 {
reloc .Signed = true
}
if rel .Rsize &0x40 != 0 {
reloc .InstructionFixed = true
}
case U64_TOCMAGIC :
rel := new (Reloc64 )
if err := binary .Read (sr , binary .BigEndian , rel ); err != nil {
return nil , err
}
reloc .VirtualAddress = rel .Rvaddr
reloc .Symbol = idxToSym [int (rel .Rsymndx )]
reloc .Type = rel .Rtype
reloc .Length = rel .Rsize &0x3F + 1
if rel .Rsize &0x80 != 0 {
reloc .Signed = true
}
if rel .Rsize &0x40 != 0 {
reloc .InstructionFixed = true
}
}
sect .Relocs = append (sect .Relocs , reloc )
}
}
return f , nil
}
type nobitsSectionReader struct {}
func (*nobitsSectionReader ) ReadAt (p []byte , off int64 ) (n int , err error ) {
return 0 , errors .New ("unexpected read from section with uninitialized data" )
}
func (s *Section ) Data () ([]byte , error ) {
dat := make ([]byte , s .sr .Size ())
n , err := s .sr .ReadAt (dat , 0 )
if n == len (dat ) {
err = nil
}
return dat [:n ], err
}
func (f *File ) CSect (name string ) []byte {
for _ , sym := range f .Symbols {
if sym .Name == name && sym .AuxCSect .SymbolType == XTY_SD {
if i := sym .SectionNumber - 1 ; 0 <= i && i < len (f .Sections ) {
s := f .Sections [i ]
if sym .Value +uint64 (sym .AuxCSect .Length ) <= s .Size {
dat := make ([]byte , sym .AuxCSect .Length )
_ , err := s .sr .ReadAt (dat , int64 (sym .Value ))
if err != nil {
return nil
}
return dat
}
}
break
}
}
return nil
}
func (f *File ) DWARF () (*dwarf .Data , error ) {
var subtypes = [...]uint32 {SSUBTYP_DWABREV , SSUBTYP_DWINFO , SSUBTYP_DWLINE , SSUBTYP_DWRNGES , SSUBTYP_DWSTR }
var dat [len (subtypes )][]byte
for i , subtype := range subtypes {
s := f .SectionByType (STYP_DWARF | subtype )
if s != nil {
b , err := s .Data ()
if err != nil && uint64 (len (b )) < s .Size {
return nil , err
}
dat [i ] = b
}
}
abbrev , info , line , ranges , str := dat [0 ], dat [1 ], dat [2 ], dat [3 ], dat [4 ]
return dwarf .New (abbrev , nil , nil , info , line , nil , ranges , str )
}
func (f *File ) readImportIDs (s *Section ) ([]string , error ) {
if _ , err := s .sr .Seek (0 , io .SeekStart ); err != nil {
return nil , err
}
var istlen uint32
var nimpid uint32
var impoff uint64
switch f .TargetMachine {
case U802TOCMAGIC :
lhdr := new (LoaderHeader32 )
if err := binary .Read (s .sr , binary .BigEndian , lhdr ); err != nil {
return nil , err
}
istlen = lhdr .Listlen
nimpid = lhdr .Lnimpid
impoff = uint64 (lhdr .Limpoff )
case U64_TOCMAGIC :
lhdr := new (LoaderHeader64 )
if err := binary .Read (s .sr , binary .BigEndian , lhdr ); err != nil {
return nil , err
}
istlen = lhdr .Listlen
nimpid = lhdr .Lnimpid
impoff = lhdr .Limpoff
}
if _ , err := s .sr .Seek (int64 (impoff ), io .SeekStart ); err != nil {
return nil , err
}
table := make ([]byte , istlen )
if _ , err := io .ReadFull (s .sr , table ); err != nil {
return nil , err
}
offset := 0
libpath := cstring (table [offset :])
f .LibraryPaths = strings .Split (libpath , ":" )
offset += len (libpath ) + 3
all := make ([]string , 0 )
for i := 1 ; i < int (nimpid ); i ++ {
impidpath := cstring (table [offset :])
offset += len (impidpath ) + 1
impidbase := cstring (table [offset :])
offset += len (impidbase ) + 1
impidmem := cstring (table [offset :])
offset += len (impidmem ) + 1
var path string
if len (impidpath ) > 0 {
path = impidpath + "/" + impidbase + "/" + impidmem
} else {
path = impidbase + "/" + impidmem
}
all = append (all , path )
}
return all , nil
}
func (f *File ) ImportedSymbols () ([]ImportedSymbol , error ) {
s := f .SectionByType (STYP_LOADER )
if s == nil {
return nil , nil
}
if _ , err := s .sr .Seek (0 , io .SeekStart ); err != nil {
return nil , err
}
var stlen uint32
var stoff uint64
var nsyms uint32
var symoff uint64
switch f .TargetMachine {
case U802TOCMAGIC :
lhdr := new (LoaderHeader32 )
if err := binary .Read (s .sr , binary .BigEndian , lhdr ); err != nil {
return nil , err
}
stlen = lhdr .Lstlen
stoff = uint64 (lhdr .Lstoff )
nsyms = lhdr .Lnsyms
symoff = LDHDRSZ_32
case U64_TOCMAGIC :
lhdr := new (LoaderHeader64 )
if err := binary .Read (s .sr , binary .BigEndian , lhdr ); err != nil {
return nil , err
}
stlen = lhdr .Lstlen
stoff = lhdr .Lstoff
nsyms = lhdr .Lnsyms
symoff = lhdr .Lsymoff
}
if _ , err := s .sr .Seek (int64 (stoff ), io .SeekStart ); err != nil {
return nil , err
}
st := make ([]byte , stlen )
if _ , err := io .ReadFull (s .sr , st ); err != nil {
return nil , err
}
libs , err := f .readImportIDs (s )
if err != nil {
return nil , err
}
if _ , err := s .sr .Seek (int64 (symoff ), io .SeekStart ); err != nil {
return nil , err
}
all := make ([]ImportedSymbol , 0 )
for i := 0 ; i < int (nsyms ); i ++ {
var name string
var ifile uint32
var ok bool
switch f .TargetMachine {
case U802TOCMAGIC :
ldsym := new (LoaderSymbol32 )
if err := binary .Read (s .sr , binary .BigEndian , ldsym ); err != nil {
return nil , err
}
if ldsym .Lsmtype &0x40 == 0 {
continue
}
zeroes := binary .BigEndian .Uint32 (ldsym .Lname [:4 ])
if zeroes != 0 {
name = cstring (ldsym .Lname [:])
} else {
offset := binary .BigEndian .Uint32 (ldsym .Lname [4 :])
name , ok = getString (st , offset )
if !ok {
continue
}
}
ifile = ldsym .Lifile
case U64_TOCMAGIC :
ldsym := new (LoaderSymbol64 )
if err := binary .Read (s .sr , binary .BigEndian , ldsym ); err != nil {
return nil , err
}
if ldsym .Lsmtype &0x40 == 0 {
continue
}
name , ok = getString (st , ldsym .Loffset )
if !ok {
continue
}
ifile = ldsym .Lifile
}
var sym ImportedSymbol
sym .Name = name
if ifile >= 1 && int (ifile ) <= len (libs ) {
sym .Library = libs [ifile -1 ]
}
all = append (all , sym )
}
return all , nil
}
func (f *File ) ImportedLibraries () ([]string , error ) {
s := f .SectionByType (STYP_LOADER )
if s == nil {
return nil , nil
}
all , err := f .readImportIDs (s )
return all , err
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .