]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/xcoff: add new debug package for cmd
authorClément Chigot <clement.chigot@atos.net>
Fri, 28 Sep 2018 14:15:13 +0000 (16:15 +0200)
committerIan Lance Taylor <iant@golang.org>
Fri, 19 Oct 2018 14:02:07 +0000 (14:02 +0000)
This commit adds a new package in cmd/internal which aims
to debug and load XCOFF files.

Updates: #25893, #28037

Change-Id: I47db495bedfa43e9129a831b9b8bbc35b703567b
Reviewed-on: https://go-review.googlesource.com/c/138727
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
misc/nacl/testzip.proto
src/cmd/internal/xcoff/file.go [new file with mode: 0644]
src/cmd/internal/xcoff/file_test.go [new file with mode: 0644]
src/cmd/internal/xcoff/testdata/gcc-ppc32-aix-dwarf2-exec [new file with mode: 0644]
src/cmd/internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec [new file with mode: 0644]
src/cmd/internal/xcoff/testdata/hello.c [new file with mode: 0644]
src/cmd/internal/xcoff/xcoff.go [new file with mode: 0644]

index 1e9279e4e0c136830c2004ca21641cc750479115..5a6a5f43b23ca8ae38268a915ee4e40434add537 100644 (file)
@@ -37,6 +37,9 @@ go    src=..
                                buildid
                                        testdata
                                                +
+                               xcoff
+                                       testdata
+                                               +
                        gofmt
                                gofmt.go
                                gofmt_test.go
diff --git a/src/cmd/internal/xcoff/file.go b/src/cmd/internal/xcoff/file.go
new file mode 100644 (file)
index 0000000..86a85c3
--- /dev/null
@@ -0,0 +1,687 @@
+// 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 xcoff implements access to XCOFF (Extended Common Object File Format) files.
+package xcoff
+
+import (
+       "debug/dwarf"
+       "encoding/binary"
+       "fmt"
+       "io"
+       "os"
+       "strings"
+)
+
+// SectionHeader holds information about an XCOFF section header.
+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
+}
+
+// AuxiliaryCSect holds information about an XCOFF symbol in an AUX_CSECT entry.
+type AuxiliaryCSect struct {
+       Length              int64
+       StorageMappingClass int
+       SymbolType          int
+}
+
+// AuxiliaryFcn holds information about an XCOFF symbol in an AUX_FCN entry.
+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
+}
+
+// ImportedSymbol holds information about an imported XCOFF symbol.
+type ImportedSymbol struct {
+       Name    string
+       Library string
+}
+
+// FileHeader holds information about an XCOFF file header.
+type FileHeader struct {
+       TargetMachine uint16
+}
+
+// A File represents an open XCOFF file.
+type File struct {
+       FileHeader
+       Sections     []*Section
+       Symbols      []*Symbol
+       StringTable  []byte
+       LibraryPaths []string
+
+       closer io.Closer
+}
+
+// Open opens the named file using os.Open and prepares it for use as an XCOFF binary.
+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
+}
+
+// Close closes the File.
+// If the File was created using NewFile directly instead of Open,
+// Close has no effect.
+func (f *File) Close() error {
+       var err error
+       if f.closer != nil {
+               err = f.closer.Close()
+               f.closer = nil
+       }
+       return err
+}
+
+// Section returns the first section with the given name, or nil if no such
+// section exists.
+// Xcoff have section's name limited to 8 bytes. Some sections like .gosymtab
+// can be trunked but this method will still find them.
+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
+}
+
+// SectionByType returns the first section in f with the
+// given type, or nil if there is no such section.
+func (f *File) SectionByType(typ uint32) *Section {
+       for _, s := range f.Sections {
+               if s.Type == typ {
+                       return s
+               }
+       }
+       return nil
+}
+
+// cstring converts ASCII byte sequence b to string.
+// It stops once it finds 0 or reaches end of b.
+func cstring(b []byte) string {
+       var i int
+       for i = 0; i < len(b) && b[i] != 0; i++ {
+       }
+       return string(b[:i])
+}
+
+// getString extracts a string from an XCOFF string table.
+func getString(st []byte, offset uint32) (string, bool) {
+       if offset < 4 || int(offset) >= len(st) {
+               return "", false
+       }
+       return cstring(st[offset:]), true
+}
+
+// NewFile creates a new File for accessing an XCOFF binary in an underlying reader.
+func NewFile(r io.ReaderAt) (*File, error) {
+       sr := io.NewSectionReader(r, 0, 1<<63-1)
+       // Read XCOFF target machine
+       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
+
+       // Read XCOFF file header
+       if _, err := sr.Seek(0, os.SEEK_SET); err != nil {
+               return nil, err
+       }
+       var nscns uint16
+       var symptr uint64
+       var nsyms int32
+       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")
+       }
+
+       // Read string table (located right after symbol table).
+       offset := symptr + uint64(nsyms)*SYMESZ
+       if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil {
+               return nil, err
+       }
+       // The first 4 bytes contain the length (in bytes).
+       var l uint32
+       if err := binary.Read(sr, binary.BigEndian, &l); err != nil {
+               return nil, err
+       }
+       if l > 4 {
+               if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil {
+                       return nil, err
+               }
+               f.StringTable = make([]byte, l)
+               if _, err := io.ReadFull(sr, f.StringTable); err != nil {
+                       return nil, err
+               }
+       }
+
+       // Read section headers
+       if _, err := sr.Seek(int64(hdrsz)+int64(opthdr), os.SEEK_SET); err != nil {
+               return nil, err
+       }
+       f.Sections = make([]*Section, nscns)
+       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 { // .bss must have all 0s
+                       r2 = zeroReaderAt{}
+               }
+               s.sr = io.NewSectionReader(r2, int64(scnptr), int64(s.Size))
+               s.ReaderAt = s.sr
+               f.Sections[i] = s
+       }
+
+       // Symbol map needed by relocation
+       var idxToSym = make(map[int]*Symbol)
+
+       // Read symbol table
+       if _, err := sr.Seek(int64(symptr), os.SEEK_SET); 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
+               }
+               // Must have at least one csect auxiliary entry.
+               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 this symbol is a function, it must retrieve its size from
+               // its AUX_FCN entry.
+               // It can happend that a function symbol doesn't have any AUX_FCN.
+               // In this case, needAuxFcn is false and their size will be set to 0
+               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)
+                       }
+               }
+
+               // Read csect auxiliary entry (by convention, it is the last).
+               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 // Skip auxiliary entries
+               if _, err := sr.Seek(int64(numaux)*SYMESZ, io.SeekCurrent); err != nil {
+                       return nil, err
+               }
+       }
+
+       // Read relocations
+       // Only for .data or .text section
+       for _, sect := range f.Sections {
+               if sect.Type != STYP_TEXT && sect.Type != STYP_DATA {
+                       continue
+               }
+               sect.Relocs = make([]Reloc, sect.Nreloc)
+               if sect.Relptr == 0 {
+                       continue
+               }
+               if _, err := sr.Seek(int64(sect.Relptr), os.SEEK_SET); err != nil {
+                       return nil, err
+               }
+               for i := uint32(0); i < sect.Nreloc; i++ {
+                       switch f.TargetMachine {
+                       case U802TOCMAGIC:
+                               rel := new(Reloc32)
+                               if err := binary.Read(sr, binary.BigEndian, rel); err != nil {
+                                       return nil, err
+                               }
+                               sect.Relocs[i].VirtualAddress = uint64(rel.Rvaddr)
+                               sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)]
+                               sect.Relocs[i].Type = rel.Rtype
+                               sect.Relocs[i].Length = rel.Rsize&0x3F + 1
+
+                               if rel.Rsize&0x80 == 1 {
+                                       sect.Relocs[i].Signed = true
+                               }
+                               if rel.Rsize&0x40 == 1 {
+                                       sect.Relocs[i].InstructionFixed = true
+                               }
+
+                       case U64_TOCMAGIC:
+                               rel := new(Reloc64)
+                               if err := binary.Read(sr, binary.BigEndian, rel); err != nil {
+                                       return nil, err
+                               }
+                               sect.Relocs[i].VirtualAddress = rel.Rvaddr
+                               sect.Relocs[i].Symbol = idxToSym[int(rel.Rsymndx)]
+                               sect.Relocs[i].Type = rel.Rtype
+                               sect.Relocs[i].Length = rel.Rsize&0x3F + 1
+                               if rel.Rsize&0x80 == 1 {
+                                       sect.Relocs[i].Signed = true
+                               }
+                               if rel.Rsize&0x40 == 1 {
+                                       sect.Relocs[i].InstructionFixed = true
+                               }
+                       }
+               }
+       }
+
+       return f, nil
+}
+
+// zeroReaderAt is ReaderAt that reads 0s.
+type zeroReaderAt struct{}
+
+// ReadAt writes len(p) 0s into p.
+func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
+       for i := range p {
+               p[i] = 0
+       }
+       return len(p), nil
+}
+
+// Data reads and returns the contents of the XCOFF section s.
+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
+}
+
+// CSect reads and returns the contents of a csect.
+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) {
+       // There are many other DWARF sections, but these
+       // are the ones the debug/dwarf package uses.
+       // Don't bother loading others.
+       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)
+}
+
+// readImportID returns the import file IDs stored inside the .loader section.
+// Library name pattern is either path/base/member or base/member
+func (f *File) readImportIDs(s *Section) ([]string, error) {
+       // Read loader header
+       if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil {
+               return nil, err
+       }
+       var istlen uint32
+       var nimpid int32
+       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
+       }
+
+       // Read loader import file ID table
+       if _, err := s.sr.Seek(int64(impoff), os.SEEK_SET); err != nil {
+               return nil, err
+       }
+       table := make([]byte, istlen)
+       if _, err := io.ReadFull(s.sr, table); err != nil {
+               return nil, err
+       }
+
+       offset := 0
+       // First import file ID is the default LIBPATH value
+       libpath := cstring(table[offset:])
+       f.LibraryPaths = strings.Split(libpath, ":")
+       offset += len(libpath) + 3 // 3 null bytes
+       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
+}
+
+// ImportedSymbols returns the names of all symbols
+// referred to by the binary f that are expected to be
+// satisfied by other libraries at dynamic load time.
+// It does not return weak symbols.
+func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
+       s := f.SectionByType(STYP_LOADER)
+       if s == nil {
+               return nil, nil
+       }
+       // Read loader header
+       if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil {
+               return nil, err
+       }
+       var stlen uint32
+       var stoff uint64
+       var nsyms int32
+       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
+       }
+
+       // Read loader section string table
+       if _, err := s.sr.Seek(int64(stoff), os.SEEK_SET); err != nil {
+               return nil, err
+       }
+       st := make([]byte, stlen)
+       if _, err := io.ReadFull(s.sr, st); err != nil {
+               return nil, err
+       }
+
+       // Read imported libraries
+       libs, err := f.readImportIDs(s)
+       if err != nil {
+               return nil, err
+       }
+
+       // Read loader symbol table
+       if _, err := s.sr.Seek(int64(symoff), os.SEEK_SET); err != nil {
+               return nil, err
+       }
+       all := make([]ImportedSymbol, 0)
+       for i := 0; i < int(nsyms); i++ {
+               var name string
+               var ifile int32
+               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 // Imported symbols only
+                       }
+                       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 // Imported symbols only
+                       }
+                       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
+}
+
+// ImportedLibraries returns the names of all libraries
+// referred to by the binary f that are expected to be
+// linked with the binary at dynamic link time.
+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
+}
diff --git a/src/cmd/internal/xcoff/file_test.go b/src/cmd/internal/xcoff/file_test.go
new file mode 100644 (file)
index 0000000..a6722e9
--- /dev/null
@@ -0,0 +1,102 @@
+// 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 xcoff
+
+import (
+       "reflect"
+       "testing"
+)
+
+type fileTest struct {
+       file     string
+       hdr      FileHeader
+       sections []*SectionHeader
+       needed   []string
+}
+
+var fileTests = []fileTest{
+       {
+               "testdata/gcc-ppc32-aix-dwarf2-exec",
+               FileHeader{U802TOCMAGIC},
+               []*SectionHeader{
+                       {".text", 0x10000290, 0x00000bbd, STYP_TEXT, 0x7ae6, 0x36},
+                       {".data", 0x20000e4d, 0x00000437, STYP_DATA, 0x7d02, 0x2b},
+                       {".bss", 0x20001284, 0x0000021c, STYP_BSS, 0, 0},
+                       {".loader", 0x00000000, 0x000004b3, STYP_LOADER, 0, 0},
+                       {".dwline", 0x00000000, 0x000000df, STYP_DWARF | SSUBTYP_DWLINE, 0x7eb0, 0x7},
+                       {".dwinfo", 0x00000000, 0x00000314, STYP_DWARF | SSUBTYP_DWINFO, 0x7ef6, 0xa},
+                       {".dwabrev", 0x00000000, 0x000000d6, STYP_DWARF | SSUBTYP_DWABREV, 0, 0},
+                       {".dwarnge", 0x00000000, 0x00000020, STYP_DWARF | SSUBTYP_DWARNGE, 0x7f5a, 0x2},
+                       {".dwloc", 0x00000000, 0x00000074, STYP_DWARF | SSUBTYP_DWLOC, 0, 0},
+                       {".debug", 0x00000000, 0x00005e4f, STYP_DEBUG, 0, 0},
+               },
+               []string{"libc.a/shr.o"},
+       },
+       {
+               "testdata/gcc-ppc64-aix-dwarf2-exec",
+               FileHeader{U64_TOCMAGIC},
+               []*SectionHeader{
+                       {".text", 0x10000480, 0x00000afd, STYP_TEXT, 0x8322, 0x34},
+                       {".data", 0x20000f7d, 0x000002f3, STYP_DATA, 0x85fa, 0x25},
+                       {".bss", 0x20001270, 0x00000428, STYP_BSS, 0, 0},
+                       {".loader", 0x00000000, 0x00000535, STYP_LOADER, 0, 0},
+                       {".dwline", 0x00000000, 0x000000b4, STYP_DWARF | SSUBTYP_DWLINE, 0x8800, 0x4},
+                       {".dwinfo", 0x00000000, 0x0000036a, STYP_DWARF | SSUBTYP_DWINFO, 0x8838, 0x7},
+                       {".dwabrev", 0x00000000, 0x000000b5, STYP_DWARF | SSUBTYP_DWABREV, 0, 0},
+                       {".dwarnge", 0x00000000, 0x00000040, STYP_DWARF | SSUBTYP_DWARNGE, 0x889a, 0x2},
+                       {".dwloc", 0x00000000, 0x00000062, STYP_DWARF | SSUBTYP_DWLOC, 0, 0},
+                       {".debug", 0x00000000, 0x00006605, STYP_DEBUG, 0, 0},
+               },
+               []string{"libc.a/shr_64.o"},
+       },
+}
+
+func TestOpen(t *testing.T) {
+       for i := range fileTests {
+               tt := &fileTests[i]
+
+               f, err := Open(tt.file)
+               if err != nil {
+                       t.Error(err)
+                       continue
+               }
+               if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
+                       t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
+                       continue
+               }
+
+               for i, sh := range f.Sections {
+                       if i >= len(tt.sections) {
+                               break
+                       }
+                       have := &sh.SectionHeader
+                       want := tt.sections[i]
+                       if !reflect.DeepEqual(have, want) {
+                               t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
+                       }
+               }
+               tn := len(tt.sections)
+               fn := len(f.Sections)
+               if tn != fn {
+                       t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn)
+               }
+               tl := tt.needed
+               fl, err := f.ImportedLibraries()
+               if err != nil {
+                       t.Error(err)
+               }
+               if !reflect.DeepEqual(tl, fl) {
+                       t.Errorf("open %s: loader import = %v, want %v", tt.file, tl, fl)
+               }
+       }
+}
+
+func TestOpenFailure(t *testing.T) {
+       filename := "file.go"    // not an XCOFF object file
+       _, err := Open(filename) // don't crash
+       if err == nil {
+               t.Errorf("open %s: succeeded unexpectedly", filename)
+       }
+}
diff --git a/src/cmd/internal/xcoff/testdata/gcc-ppc32-aix-dwarf2-exec b/src/cmd/internal/xcoff/testdata/gcc-ppc32-aix-dwarf2-exec
new file mode 100644 (file)
index 0000000..810e21a
Binary files /dev/null and b/src/cmd/internal/xcoff/testdata/gcc-ppc32-aix-dwarf2-exec differ
diff --git a/src/cmd/internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec b/src/cmd/internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec
new file mode 100644 (file)
index 0000000..707d01e
Binary files /dev/null and b/src/cmd/internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec differ
diff --git a/src/cmd/internal/xcoff/testdata/hello.c b/src/cmd/internal/xcoff/testdata/hello.c
new file mode 100644 (file)
index 0000000..34d9ee7
--- /dev/null
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+void
+main(int argc, char *argv[])
+{
+       printf("hello, world\n");
+}
diff --git a/src/cmd/internal/xcoff/xcoff.go b/src/cmd/internal/xcoff/xcoff.go
new file mode 100644 (file)
index 0000000..f8465d7
--- /dev/null
@@ -0,0 +1,367 @@
+// 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 xcoff
+
+// File Header.
+type FileHeader32 struct {
+       Fmagic   uint16 // Target machine
+       Fnscns   uint16 // Number of sections
+       Ftimedat int32  // Time and date of file creation
+       Fsymptr  uint32 // Byte offset to symbol table start
+       Fnsyms   int32  // Number of entries in symbol table
+       Fopthdr  uint16 // Number of bytes in optional header
+       Fflags   uint16 // Flags
+}
+
+type FileHeader64 struct {
+       Fmagic   uint16 // Target machine
+       Fnscns   uint16 // Number of sections
+       Ftimedat int32  // Time and date of file creation
+       Fsymptr  uint64 // Byte offset to symbol table start
+       Fopthdr  uint16 // Number of bytes in optional header
+       Fflags   uint16 // Flags
+       Fnsyms   int32  // Number of entries in symbol table
+}
+
+const (
+       FILHSZ_32 = 20
+       FILHSZ_64 = 24
+)
+const (
+       U802TOCMAGIC = 0737 // AIX 32-bit XCOFF
+       U64_TOCMAGIC = 0767 // AIX 64-bit XCOFF
+)
+
+// Flags that describe the type of the object file.
+const (
+       F_RELFLG    = 0x0001
+       F_EXEC      = 0x0002
+       F_LNNO      = 0x0004
+       F_FDPR_PROF = 0x0010
+       F_FDPR_OPTI = 0x0020
+       F_DSA       = 0x0040
+       F_VARPG     = 0x0100
+       F_DYNLOAD   = 0x1000
+       F_SHROBJ    = 0x2000
+       F_LOADONLY  = 0x4000
+)
+
+// Section Header.
+type SectionHeader32 struct {
+       Sname    [8]byte // Section name
+       Spaddr   uint32  // Physical address
+       Svaddr   uint32  // Virtual address
+       Ssize    uint32  // Section size
+       Sscnptr  uint32  // Offset in file to raw data for section
+       Srelptr  uint32  // Offset in file to relocation entries for section
+       Slnnoptr uint32  // Offset in file to line number entries for section
+       Snreloc  uint16  // Number of relocation entries
+       Snlnno   uint16  // Number of line number entries
+       Sflags   uint32  // Flags to define the section type
+}
+
+type SectionHeader64 struct {
+       Sname    [8]byte // Section name
+       Spaddr   uint64  // Physical address
+       Svaddr   uint64  // Virtual address
+       Ssize    uint64  // Section size
+       Sscnptr  uint64  // Offset in file to raw data for section
+       Srelptr  uint64  // Offset in file to relocation entries for section
+       Slnnoptr uint64  // Offset in file to line number entries for section
+       Snreloc  uint32  // Number of relocation entries
+       Snlnno   uint32  // Number of line number entries
+       Sflags   uint32  // Flags to define the section type
+       Spad     uint32  // Needs to be 72 bytes long
+}
+
+// Flags defining the section type.
+const (
+       STYP_DWARF  = 0x0010
+       STYP_TEXT   = 0x0020
+       STYP_DATA   = 0x0040
+       STYP_BSS    = 0x0080
+       STYP_EXCEPT = 0x0100
+       STYP_INFO   = 0x0200
+       STYP_TDATA  = 0x0400
+       STYP_TBSS   = 0x0800
+       STYP_LOADER = 0x1000
+       STYP_DEBUG  = 0x2000
+       STYP_TYPCHK = 0x4000
+       STYP_OVRFLO = 0x8000
+)
+const (
+       SSUBTYP_DWINFO  = 0x10000 // DWARF info section
+       SSUBTYP_DWLINE  = 0x20000 // DWARF line-number section
+       SSUBTYP_DWPBNMS = 0x30000 // DWARF public names section
+       SSUBTYP_DWPBTYP = 0x40000 // DWARF public types section
+       SSUBTYP_DWARNGE = 0x50000 // DWARF aranges section
+       SSUBTYP_DWABREV = 0x60000 // DWARF abbreviation section
+       SSUBTYP_DWSTR   = 0x70000 // DWARF strings section
+       SSUBTYP_DWRNGES = 0x80000 // DWARF ranges section
+       SSUBTYP_DWLOC   = 0x90000 // DWARF location lists section
+       SSUBTYP_DWFRAME = 0xA0000 // DWARF frames section
+       SSUBTYP_DWMAC   = 0xB0000 // DWARF macros section
+)
+
+// Symbol Table Entry.
+type SymEnt32 struct {
+       Nname   [8]byte // Symbol name
+       Nvalue  uint32  // Symbol value
+       Nscnum  int16   // Section number of symbol
+       Ntype   uint16  // Basic and derived type specification
+       Nsclass int8    // Storage class of symbol
+       Nnumaux int8    // Number of auxiliary entries
+}
+
+type SymEnt64 struct {
+       Nvalue  uint64 // Symbol value
+       Noffset uint32 // Offset of the name in string table or .debug section
+       Nscnum  int16  // Section number of symbol
+       Ntype   uint16 // Basic and derived type specification
+       Nsclass int8   // Storage class of symbol
+       Nnumaux int8   // Number of auxiliary entries
+}
+
+const SYMESZ = 18
+
+const (
+       // Nscnum
+       N_DEBUG = -2
+       N_ABS   = -1
+       N_UNDEF = 0
+
+       //Ntype
+       SYM_V_INTERNAL  = 0x1000
+       SYM_V_HIDDEN    = 0x2000
+       SYM_V_PROTECTED = 0x3000
+       SYM_V_EXPORTED  = 0x4000
+       SYM_TYPE_FUNC   = 0x0020 // is function
+)
+
+// Storage Class.
+const (
+       C_NULL    = 0   // Symbol table entry marked for deletion
+       C_EXT     = 2   // External symbol
+       C_STAT    = 3   // Static symbol
+       C_BLOCK   = 100 // Beginning or end of inner block
+       C_FCN     = 101 // Beginning or end of function
+       C_FILE    = 103 // Source file name and compiler information
+       C_HIDEXT  = 107 // Unnamed external symbol
+       C_BINCL   = 108 // Beginning of include file
+       C_EINCL   = 109 // End of include file
+       C_WEAKEXT = 111 // Weak external symbol
+       C_DWARF   = 112 // DWARF symbol
+       C_GSYM    = 128 // Global variable
+       C_LSYM    = 129 // Automatic variable allocated on stack
+       C_PSYM    = 130 // Argument to subroutine allocated on stack
+       C_RSYM    = 131 // Register variable
+       C_RPSYM   = 132 // Argument to function or procedure stored in register
+       C_STSYM   = 133 // Statically allocated symbol
+       C_BCOMM   = 135 // Beginning of common block
+       C_ECOML   = 136 // Local member of common block
+       C_ECOMM   = 137 // End of common block
+       C_DECL    = 140 // Declaration of object
+       C_ENTRY   = 141 // Alternate entry
+       C_FUN     = 142 // Function or procedure
+       C_BSTAT   = 143 // Beginning of static block
+       C_ESTAT   = 144 // End of static block
+       C_GTLS    = 145 // Global thread-local variable
+       C_STTLS   = 146 // Static thread-local variable
+)
+
+// File Auxiliary Entry
+type AuxFile64 struct {
+       Xfname   [8]byte // Name or offset inside string table
+       Xftype   uint8   // Source file string type
+       Xauxtype uint8   // Type of auxiliary entry
+}
+
+// Function Auxiliary Entry
+type AuxFcn32 struct {
+       Xexptr   uint32 // File offset to exception table entry
+       Xfsize   uint32 // Size of function in bytes
+       Xlnnoptr uint32 // File pointer to line number
+       Xendndx  uint32 // Symbol table index of next entry
+       Xpad     uint16 // Unused
+}
+type AuxFcn64 struct {
+       Xlnnoptr uint64 // File pointer to line number
+       Xfsize   uint32 // Size of function in bytes
+       Xendndx  uint32 // Symbol table index of next entry
+       Xpad     uint8  // Unused
+       Xauxtype uint8  // Type of auxiliary entry
+}
+
+type AuxSect64 struct {
+       Xscnlen  uint64 // section length
+       Xnreloc  uint64 // Num RLDs
+       pad      uint8
+       Xauxtype uint8 // Type of auxiliary entry
+}
+
+// csect Auxiliary Entry.
+type AuxCSect32 struct {
+       Xscnlen   int32  // Length or symbol table index
+       Xparmhash uint32 // Offset of parameter type-check string
+       Xsnhash   uint16 // .typchk section number
+       Xsmtyp    uint8  // Symbol alignment and type
+       Xsmclas   uint8  // Storage-mapping class
+       Xstab     uint32 // Reserved
+       Xsnstab   uint16 // Reserved
+}
+
+type AuxCSect64 struct {
+       Xscnlenlo uint32 // Lower 4 bytes of length or symbol table index
+       Xparmhash uint32 // Offset of parameter type-check string
+       Xsnhash   uint16 // .typchk section number
+       Xsmtyp    uint8  // Symbol alignment and type
+       Xsmclas   uint8  // Storage-mapping class
+       Xscnlenhi int32  // Upper 4 bytes of length or symbol table index
+       Xpad      uint8  // Unused
+       Xauxtype  uint8  // Type of auxiliary entry
+}
+
+// Auxiliary type
+const (
+       _AUX_EXCEPT = 255
+       _AUX_FCN    = 254
+       _AUX_SYM    = 253
+       _AUX_FILE   = 252
+       _AUX_CSECT  = 251
+       _AUX_SECT   = 250
+)
+
+// Symbol type field.
+const (
+       XTY_ER = 0 // External reference
+       XTY_SD = 1 // Section definition
+       XTY_LD = 2 // Label definition
+       XTY_CM = 3 // Common csect definition
+)
+
+// Defines for File auxiliary definitions: x_ftype field of x_file
+const (
+       XFT_FN = 0   // Source File Name
+       XFT_CT = 1   // Compile Time Stamp
+       XFT_CV = 2   // Compiler Version Number
+       XFT_CD = 128 // Compiler Defined Information
+)
+
+// Storage-mapping class.
+const (
+       XMC_PR     = 0  // Program code
+       XMC_RO     = 1  // Read-only constant
+       XMC_DB     = 2  // Debug dictionary table
+       XMC_TC     = 3  // TOC entry
+       XMC_UA     = 4  // Unclassified
+       XMC_RW     = 5  // Read/Write data
+       XMC_GL     = 6  // Global linkage
+       XMC_XO     = 7  // Extended operation
+       XMC_SV     = 8  // 32-bit supervisor call descriptor
+       XMC_BS     = 9  // BSS class
+       XMC_DS     = 10 // Function descriptor
+       XMC_UC     = 11 // Unnamed FORTRAN common
+       XMC_TC0    = 15 // TOC anchor
+       XMC_TD     = 16 // Scalar data entry in the TOC
+       XMC_SV64   = 17 // 64-bit supervisor call descriptor
+       XMC_SV3264 = 18 // Supervisor call descriptor for both 32-bit and 64-bit
+       XMC_TL     = 20 // Read/Write thread-local data
+       XMC_UL     = 21 // Read/Write thread-local data (.tbss)
+       XMC_TE     = 22 // TOC entry
+)
+
+// Loader Header.
+type LoaderHeader32 struct {
+       Lversion int32  // Loader section version number
+       Lnsyms   int32  // Number of symbol table entries
+       Lnreloc  int32  // Number of relocation table entries
+       Listlen  uint32 // Length of import file ID string table
+       Lnimpid  int32  // Number of import file IDs
+       Limpoff  uint32 // Offset to start of import file IDs
+       Lstlen   uint32 // Length of string table
+       Lstoff   uint32 // Offset to start of string table
+}
+
+type LoaderHeader64 struct {
+       Lversion int32  // Loader section version number
+       Lnsyms   int32  // Number of symbol table entries
+       Lnreloc  int32  // Number of relocation table entries
+       Listlen  uint32 // Length of import file ID string table
+       Lnimpid  int32  // Number of import file IDs
+       Lstlen   uint32 // Length of string table
+       Limpoff  uint64 // Offset to start of import file IDs
+       Lstoff   uint64 // Offset to start of string table
+       Lsymoff  uint64 // Offset to start of symbol table
+       Lrldoff  uint64 // Offset to start of relocation entries
+}
+
+const (
+       LDHDRSZ_32 = 32
+       LDHDRSZ_64 = 56
+)
+
+// Loader Symbol.
+type LoaderSymbol32 struct {
+       Lname   [8]byte // Symbol name or byte offset into string table
+       Lvalue  uint32  // Address field
+       Lscnum  int16   // Section number containing symbol
+       Lsmtype int8    // Symbol type, export, import flags
+       Lsmclas int8    // Symbol storage class
+       Lifile  int32   // Import file ID; ordinal of import file IDs
+       Lparm   uint32  // Parameter type-check field
+}
+
+type LoaderSymbol64 struct {
+       Lvalue  uint64 // Address field
+       Loffset uint32 // Byte offset into string table of symbol name
+       Lscnum  int16  // Section number containing symbol
+       Lsmtype int8   // Symbol type, export, import flags
+       Lsmclas int8   // Symbol storage class
+       Lifile  int32  // Import file ID; ordinal of import file IDs
+       Lparm   uint32 // Parameter type-check field
+}
+
+type Reloc32 struct {
+       Rvaddr  uint32 // (virtual) address of reference
+       Rsymndx uint32 // Index into symbol table
+       Rsize   uint8  // Sign and reloc bit len
+       Rtype   uint8  // Toc relocation type
+}
+
+type Reloc64 struct {
+       Rvaddr  uint64 // (virtual) address of reference
+       Rsymndx uint32 // Index into symbol table
+       Rsize   uint8  // Sign and reloc bit len
+       Rtype   uint8  // Toc relocation type
+}
+
+const (
+       R_POS = 0x00 // A(sym) Positive Relocation
+       R_NEG = 0x01 // -A(sym) Negative Relocation
+       R_REL = 0x02 // A(sym-*) Relative to self
+       R_TOC = 0x03 // A(sym-TOC) Relative to TOC
+       R_TRL = 0x12 // A(sym-TOC) TOC Relative indirect load.
+
+       R_TRLA = 0x13 // A(sym-TOC) TOC Rel load address. modifiable inst
+       R_GL   = 0x05 // A(external TOC of sym) Global Linkage
+       R_TCL  = 0x06 // A(local TOC of sym) Local object TOC address
+       R_RL   = 0x0C // A(sym) Pos indirect load. modifiable instruction
+       R_RLA  = 0x0D // A(sym) Pos Load Address. modifiable instruction
+       R_REF  = 0x0F // AL0(sym) Non relocating ref. No garbage collect
+       R_BA   = 0x08 // A(sym) Branch absolute. Cannot modify instruction
+       R_RBA  = 0x18 // A(sym) Branch absolute. modifiable instruction
+       R_BR   = 0x0A // A(sym-*) Branch rel to self. non modifiable
+       R_RBR  = 0x1A // A(sym-*) Branch rel to self. modifiable instr
+
+       R_TLS    = 0x20 // General-dynamic reference to TLS symbol
+       R_TLS_IE = 0x21 // Initial-exec reference to TLS symbol
+       R_TLS_LD = 0x22 // Local-dynamic reference to TLS symbol
+       R_TLS_LE = 0x23 // Local-exec reference to TLS symbol
+       R_TLSM   = 0x24 // Module reference to TLS symbol
+       R_TLSML  = 0x25 // Module reference to local (own) module
+
+       R_TOCU = 0x30 // Relative to TOC - high order bits
+       R_TOCL = 0x31 // Relative to TOC - low order bits
+)