summaryrefslogtreecommitdiffstats
path: root/src/cmd/internal/objfile
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 13:14:23 +0000
commit73df946d56c74384511a194dd01dbe099584fd1a (patch)
treefd0bcea490dd81327ddfbb31e215439672c9a068 /src/cmd/internal/objfile
parentInitial commit. (diff)
downloadgolang-1.16-73df946d56c74384511a194dd01dbe099584fd1a.tar.xz
golang-1.16-73df946d56c74384511a194dd01dbe099584fd1a.zip
Adding upstream version 1.16.10.upstream/1.16.10upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/internal/objfile')
-rw-r--r--src/cmd/internal/objfile/disasm.go409
-rw-r--r--src/cmd/internal/objfile/elf.go126
-rw-r--r--src/cmd/internal/objfile/goobj.go346
-rw-r--r--src/cmd/internal/objfile/macho.go136
-rw-r--r--src/cmd/internal/objfile/objfile.go174
-rw-r--r--src/cmd/internal/objfile/pe.go203
-rw-r--r--src/cmd/internal/objfile/plan9obj.go156
-rw-r--r--src/cmd/internal/objfile/xcoff.go163
8 files changed, 1713 insertions, 0 deletions
diff --git a/src/cmd/internal/objfile/disasm.go b/src/cmd/internal/objfile/disasm.go
new file mode 100644
index 0000000..b5f1cd1
--- /dev/null
+++ b/src/cmd/internal/objfile/disasm.go
@@ -0,0 +1,409 @@
+// Copyright 2014 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 objfile
+
+import (
+ "bufio"
+ "bytes"
+ "cmd/internal/src"
+ "container/list"
+ "debug/gosym"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strings"
+ "text/tabwriter"
+
+ "golang.org/x/arch/arm/armasm"
+ "golang.org/x/arch/arm64/arm64asm"
+ "golang.org/x/arch/ppc64/ppc64asm"
+ "golang.org/x/arch/x86/x86asm"
+)
+
+// Disasm is a disassembler for a given File.
+type Disasm struct {
+ syms []Sym //symbols in file, sorted by address
+ pcln Liner // pcln table
+ text []byte // bytes of text segment (actual instructions)
+ textStart uint64 // start PC of text
+ textEnd uint64 // end PC of text
+ goarch string // GOARCH string
+ disasm disasmFunc // disassembler function for goarch
+ byteOrder binary.ByteOrder // byte order for goarch
+}
+
+// Disasm returns a disassembler for the file f.
+func (e *Entry) Disasm() (*Disasm, error) {
+ syms, err := e.Symbols()
+ if err != nil {
+ return nil, err
+ }
+
+ pcln, err := e.PCLineTable()
+ if err != nil {
+ return nil, err
+ }
+
+ textStart, textBytes, err := e.Text()
+ if err != nil {
+ return nil, err
+ }
+
+ goarch := e.GOARCH()
+ disasm := disasms[goarch]
+ byteOrder := byteOrders[goarch]
+ if disasm == nil || byteOrder == nil {
+ return nil, fmt.Errorf("unsupported architecture")
+ }
+
+ // Filter out section symbols, overwriting syms in place.
+ keep := syms[:0]
+ for _, sym := range syms {
+ switch sym.Name {
+ case "runtime.text", "text", "_text", "runtime.etext", "etext", "_etext":
+ // drop
+ default:
+ keep = append(keep, sym)
+ }
+ }
+ syms = keep
+ d := &Disasm{
+ syms: syms,
+ pcln: pcln,
+ text: textBytes,
+ textStart: textStart,
+ textEnd: textStart + uint64(len(textBytes)),
+ goarch: goarch,
+ disasm: disasm,
+ byteOrder: byteOrder,
+ }
+
+ return d, nil
+}
+
+// lookup finds the symbol name containing addr.
+func (d *Disasm) lookup(addr uint64) (name string, base uint64) {
+ i := sort.Search(len(d.syms), func(i int) bool { return addr < d.syms[i].Addr })
+ if i > 0 {
+ s := d.syms[i-1]
+ if s.Addr != 0 && s.Addr <= addr && addr < s.Addr+uint64(s.Size) {
+ return s.Name, s.Addr
+ }
+ }
+ return "", 0
+}
+
+// base returns the final element in the path.
+// It works on both Windows and Unix paths,
+// regardless of host operating system.
+func base(path string) string {
+ path = path[strings.LastIndex(path, "/")+1:]
+ path = path[strings.LastIndex(path, `\`)+1:]
+ return path
+}
+
+// CachedFile contains the content of a file split into lines.
+type CachedFile struct {
+ FileName string
+ Lines [][]byte
+}
+
+// FileCache is a simple LRU cache of file contents.
+type FileCache struct {
+ files *list.List
+ maxLen int
+}
+
+// NewFileCache returns a FileCache which can contain up to maxLen cached file contents.
+func NewFileCache(maxLen int) *FileCache {
+ return &FileCache{
+ files: list.New(),
+ maxLen: maxLen,
+ }
+}
+
+// Line returns the source code line for the given file and line number.
+// If the file is not already cached, reads it, inserts it into the cache,
+// and removes the least recently used file if necessary.
+// If the file is in cache, it is moved to the front of the list.
+func (fc *FileCache) Line(filename string, line int) ([]byte, error) {
+ if filepath.Ext(filename) != ".go" {
+ return nil, nil
+ }
+
+ // Clean filenames returned by src.Pos.SymFilename()
+ // or src.PosBase.SymFilename() removing
+ // the leading src.FileSymPrefix.
+ filename = strings.TrimPrefix(filename, src.FileSymPrefix)
+
+ // Expand literal "$GOROOT" rewritten by obj.AbsFile()
+ filename = filepath.Clean(os.ExpandEnv(filename))
+
+ var cf *CachedFile
+ var e *list.Element
+
+ for e = fc.files.Front(); e != nil; e = e.Next() {
+ cf = e.Value.(*CachedFile)
+ if cf.FileName == filename {
+ break
+ }
+ }
+
+ if e == nil {
+ content, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+
+ cf = &CachedFile{
+ FileName: filename,
+ Lines: bytes.Split(content, []byte{'\n'}),
+ }
+ fc.files.PushFront(cf)
+
+ if fc.files.Len() >= fc.maxLen {
+ fc.files.Remove(fc.files.Back())
+ }
+ } else {
+ fc.files.MoveToFront(e)
+ }
+
+ // because //line directives can be out-of-range. (#36683)
+ if line-1 >= len(cf.Lines) || line-1 < 0 {
+ return nil, nil
+ }
+
+ return cf.Lines[line-1], nil
+}
+
+// Print prints a disassembly of the file to w.
+// If filter is non-nil, the disassembly only includes functions with names matching filter.
+// If printCode is true, the disassembly includs corresponding source lines.
+// The disassembly only includes functions that overlap the range [start, end).
+func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64, printCode bool, gnuAsm bool) {
+ if start < d.textStart {
+ start = d.textStart
+ }
+ if end > d.textEnd {
+ end = d.textEnd
+ }
+ printed := false
+ bw := bufio.NewWriter(w)
+
+ var fc *FileCache
+ if printCode {
+ fc = NewFileCache(8)
+ }
+
+ tw := tabwriter.NewWriter(bw, 18, 8, 1, '\t', tabwriter.StripEscape)
+ for _, sym := range d.syms {
+ symStart := sym.Addr
+ symEnd := sym.Addr + uint64(sym.Size)
+ relocs := sym.Relocs
+ if sym.Code != 'T' && sym.Code != 't' ||
+ symStart < d.textStart ||
+ symEnd <= start || end <= symStart ||
+ filter != nil && !filter.MatchString(sym.Name) {
+ continue
+ }
+ if printed {
+ fmt.Fprintf(bw, "\n")
+ }
+ printed = true
+
+ file, _, _ := d.pcln.PCToLine(sym.Addr)
+ fmt.Fprintf(bw, "TEXT %s(SB) %s\n", sym.Name, file)
+
+ if symEnd > end {
+ symEnd = end
+ }
+ code := d.text[:end-d.textStart]
+
+ var lastFile string
+ var lastLine int
+
+ d.Decode(symStart, symEnd, relocs, gnuAsm, func(pc, size uint64, file string, line int, text string) {
+ i := pc - d.textStart
+
+ if printCode {
+ if file != lastFile || line != lastLine {
+ if srcLine, err := fc.Line(file, line); err == nil {
+ fmt.Fprintf(tw, "%s%s%s\n", []byte{tabwriter.Escape}, srcLine, []byte{tabwriter.Escape})
+ }
+
+ lastFile, lastLine = file, line
+ }
+
+ fmt.Fprintf(tw, " %#x\t", pc)
+ } else {
+ fmt.Fprintf(tw, " %s:%d\t%#x\t", base(file), line, pc)
+ }
+
+ if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" {
+ // Print instruction as bytes.
+ fmt.Fprintf(tw, "%x", code[i:i+size])
+ } else {
+ // Print instruction as 32-bit words.
+ for j := uint64(0); j < size; j += 4 {
+ if j > 0 {
+ fmt.Fprintf(tw, " ")
+ }
+ fmt.Fprintf(tw, "%08x", d.byteOrder.Uint32(code[i+j:]))
+ }
+ }
+ fmt.Fprintf(tw, "\t%s\t\n", text)
+ })
+ tw.Flush()
+ }
+ bw.Flush()
+}
+
+// Decode disassembles the text segment range [start, end), calling f for each instruction.
+func (d *Disasm) Decode(start, end uint64, relocs []Reloc, gnuAsm bool, f func(pc, size uint64, file string, line int, text string)) {
+ if start < d.textStart {
+ start = d.textStart
+ }
+ if end > d.textEnd {
+ end = d.textEnd
+ }
+ code := d.text[:end-d.textStart]
+ lookup := d.lookup
+ for pc := start; pc < end; {
+ i := pc - d.textStart
+ text, size := d.disasm(code[i:], pc, lookup, d.byteOrder, gnuAsm)
+ file, line, _ := d.pcln.PCToLine(pc)
+ sep := "\t"
+ for len(relocs) > 0 && relocs[0].Addr < i+uint64(size) {
+ text += sep + relocs[0].Stringer.String(pc-start)
+ sep = " "
+ relocs = relocs[1:]
+ }
+ f(pc, uint64(size), file, line, text)
+ pc += uint64(size)
+ }
+}
+
+type lookupFunc = func(addr uint64) (sym string, base uint64)
+type disasmFunc func(code []byte, pc uint64, lookup lookupFunc, ord binary.ByteOrder, _ bool) (text string, size int)
+
+func disasm_386(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder, gnuAsm bool) (string, int) {
+ return disasm_x86(code, pc, lookup, 32, gnuAsm)
+}
+
+func disasm_amd64(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder, gnuAsm bool) (string, int) {
+ return disasm_x86(code, pc, lookup, 64, gnuAsm)
+}
+
+func disasm_x86(code []byte, pc uint64, lookup lookupFunc, arch int, gnuAsm bool) (string, int) {
+ inst, err := x86asm.Decode(code, arch)
+ var text string
+ size := inst.Len
+ if err != nil || size == 0 || inst.Op == 0 {
+ size = 1
+ text = "?"
+ } else {
+ if gnuAsm {
+ text = fmt.Sprintf("%-36s // %s", x86asm.GoSyntax(inst, pc, lookup), x86asm.GNUSyntax(inst, pc, nil))
+ } else {
+ text = x86asm.GoSyntax(inst, pc, lookup)
+ }
+ }
+ return text, size
+}
+
+type textReader struct {
+ code []byte
+ pc uint64
+}
+
+func (r textReader) ReadAt(data []byte, off int64) (n int, err error) {
+ if off < 0 || uint64(off) < r.pc {
+ return 0, io.EOF
+ }
+ d := uint64(off) - r.pc
+ if d >= uint64(len(r.code)) {
+ return 0, io.EOF
+ }
+ n = copy(data, r.code[d:])
+ if n < len(data) {
+ err = io.ErrUnexpectedEOF
+ }
+ return
+}
+
+func disasm_arm(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder, gnuAsm bool) (string, int) {
+ inst, err := armasm.Decode(code, armasm.ModeARM)
+ var text string
+ size := inst.Len
+ if err != nil || size == 0 || inst.Op == 0 {
+ size = 4
+ text = "?"
+ } else if gnuAsm {
+ text = fmt.Sprintf("%-36s // %s", armasm.GoSyntax(inst, pc, lookup, textReader{code, pc}), armasm.GNUSyntax(inst))
+ } else {
+ text = armasm.GoSyntax(inst, pc, lookup, textReader{code, pc})
+ }
+ return text, size
+}
+
+func disasm_arm64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder, gnuAsm bool) (string, int) {
+ inst, err := arm64asm.Decode(code)
+ var text string
+ if err != nil || inst.Op == 0 {
+ text = "?"
+ } else if gnuAsm {
+ text = fmt.Sprintf("%-36s // %s", arm64asm.GoSyntax(inst, pc, lookup, textReader{code, pc}), arm64asm.GNUSyntax(inst))
+ } else {
+ text = arm64asm.GoSyntax(inst, pc, lookup, textReader{code, pc})
+ }
+ return text, 4
+}
+
+func disasm_ppc64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder, gnuAsm bool) (string, int) {
+ inst, err := ppc64asm.Decode(code, byteOrder)
+ var text string
+ size := inst.Len
+ if err != nil || size == 0 {
+ size = 4
+ text = "?"
+ } else {
+ if gnuAsm {
+ text = fmt.Sprintf("%-36s // %s", ppc64asm.GoSyntax(inst, pc, lookup), ppc64asm.GNUSyntax(inst, pc))
+ } else {
+ text = ppc64asm.GoSyntax(inst, pc, lookup)
+ }
+ }
+ return text, size
+}
+
+var disasms = map[string]disasmFunc{
+ "386": disasm_386,
+ "amd64": disasm_amd64,
+ "arm": disasm_arm,
+ "arm64": disasm_arm64,
+ "ppc64": disasm_ppc64,
+ "ppc64le": disasm_ppc64,
+}
+
+var byteOrders = map[string]binary.ByteOrder{
+ "386": binary.LittleEndian,
+ "amd64": binary.LittleEndian,
+ "arm": binary.LittleEndian,
+ "arm64": binary.LittleEndian,
+ "ppc64": binary.BigEndian,
+ "ppc64le": binary.LittleEndian,
+ "s390x": binary.BigEndian,
+}
+
+type Liner interface {
+ // Given a pc, returns the corresponding file, line, and function data.
+ // If unknown, returns "",0,nil.
+ PCToLine(uint64) (string, int, *gosym.Func)
+}
diff --git a/src/cmd/internal/objfile/elf.go b/src/cmd/internal/objfile/elf.go
new file mode 100644
index 0000000..a48a9df
--- /dev/null
+++ b/src/cmd/internal/objfile/elf.go
@@ -0,0 +1,126 @@
+// Copyright 2013 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.
+
+// Parsing of ELF executables (Linux, FreeBSD, and so on).
+
+package objfile
+
+import (
+ "debug/dwarf"
+ "debug/elf"
+ "encoding/binary"
+ "fmt"
+ "io"
+)
+
+type elfFile struct {
+ elf *elf.File
+}
+
+func openElf(r io.ReaderAt) (rawFile, error) {
+ f, err := elf.NewFile(r)
+ if err != nil {
+ return nil, err
+ }
+ return &elfFile{f}, nil
+}
+
+func (f *elfFile) symbols() ([]Sym, error) {
+ elfSyms, err := f.elf.Symbols()
+ if err != nil {
+ return nil, err
+ }
+
+ var syms []Sym
+ for _, s := range elfSyms {
+ sym := Sym{Addr: s.Value, Name: s.Name, Size: int64(s.Size), Code: '?'}
+ switch s.Section {
+ case elf.SHN_UNDEF:
+ sym.Code = 'U'
+ case elf.SHN_COMMON:
+ sym.Code = 'B'
+ default:
+ i := int(s.Section)
+ if i < 0 || i >= len(f.elf.Sections) {
+ break
+ }
+ sect := f.elf.Sections[i]
+ switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
+ case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
+ sym.Code = 'T'
+ case elf.SHF_ALLOC:
+ sym.Code = 'R'
+ case elf.SHF_ALLOC | elf.SHF_WRITE:
+ sym.Code = 'D'
+ }
+ }
+ if elf.ST_BIND(s.Info) == elf.STB_LOCAL {
+ sym.Code += 'a' - 'A'
+ }
+ syms = append(syms, sym)
+ }
+
+ return syms, nil
+}
+
+func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ if sect := f.elf.Section(".text"); sect != nil {
+ textStart = sect.Addr
+ }
+ if sect := f.elf.Section(".gosymtab"); sect != nil {
+ if symtab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if sect := f.elf.Section(".gopclntab"); sect != nil {
+ if pclntab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
+}
+
+func (f *elfFile) text() (textStart uint64, text []byte, err error) {
+ sect := f.elf.Section(".text")
+ if sect == nil {
+ return 0, nil, fmt.Errorf("text section not found")
+ }
+ textStart = sect.Addr
+ text, err = sect.Data()
+ return
+}
+
+func (f *elfFile) goarch() string {
+ switch f.elf.Machine {
+ case elf.EM_386:
+ return "386"
+ case elf.EM_X86_64:
+ return "amd64"
+ case elf.EM_ARM:
+ return "arm"
+ case elf.EM_AARCH64:
+ return "arm64"
+ case elf.EM_PPC64:
+ if f.elf.ByteOrder == binary.LittleEndian {
+ return "ppc64le"
+ }
+ return "ppc64"
+ case elf.EM_S390:
+ return "s390x"
+ }
+ return ""
+}
+
+func (f *elfFile) loadAddress() (uint64, error) {
+ for _, p := range f.elf.Progs {
+ if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 {
+ return p.Vaddr, nil
+ }
+ }
+ return 0, fmt.Errorf("unknown load address")
+}
+
+func (f *elfFile) dwarf() (*dwarf.Data, error) {
+ return f.elf.DWARF()
+}
diff --git a/src/cmd/internal/objfile/goobj.go b/src/cmd/internal/objfile/goobj.go
new file mode 100644
index 0000000..f19bec5
--- /dev/null
+++ b/src/cmd/internal/objfile/goobj.go
@@ -0,0 +1,346 @@
+// Copyright 2013 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.
+
+// Parsing of Go intermediate object files and archives.
+
+package objfile
+
+import (
+ "cmd/internal/archive"
+ "cmd/internal/goobj"
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "debug/dwarf"
+ "debug/gosym"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+)
+
+type goobjFile struct {
+ goobj *archive.GoObj
+ r *goobj.Reader
+ f *os.File
+ arch *sys.Arch
+}
+
+func openGoFile(f *os.File) (*File, error) {
+ a, err := archive.Parse(f, false)
+ if err != nil {
+ return nil, err
+ }
+ entries := make([]*Entry, 0, len(a.Entries))
+L:
+ for _, e := range a.Entries {
+ switch e.Type {
+ case archive.EntryPkgDef:
+ continue
+ case archive.EntryGoObj:
+ o := e.Obj
+ b := make([]byte, o.Size)
+ _, err := f.ReadAt(b, o.Offset)
+ if err != nil {
+ return nil, err
+ }
+ r := goobj.NewReaderFromBytes(b, false)
+ var arch *sys.Arch
+ for _, a := range sys.Archs {
+ if a.Name == e.Obj.Arch {
+ arch = a
+ break
+ }
+ }
+ entries = append(entries, &Entry{
+ name: e.Name,
+ raw: &goobjFile{e.Obj, r, f, arch},
+ })
+ continue
+ case archive.EntryNativeObj:
+ nr := io.NewSectionReader(f, e.Offset, e.Size)
+ for _, try := range openers {
+ if raw, err := try(nr); err == nil {
+ entries = append(entries, &Entry{
+ name: e.Name,
+ raw: raw,
+ })
+ continue L
+ }
+ }
+ }
+ return nil, fmt.Errorf("open %s: unrecognized archive member %s", f.Name(), e.Name)
+ }
+ return &File{f, entries}, nil
+}
+
+func goobjName(name string, ver int) string {
+ if ver == 0 {
+ return name
+ }
+ return fmt.Sprintf("%s<%d>", name, ver)
+}
+
+type goobjReloc struct {
+ Off int32
+ Size uint8
+ Type objabi.RelocType
+ Add int64
+ Sym string
+}
+
+func (r goobjReloc) String(insnOffset uint64) string {
+ delta := int64(r.Off) - int64(insnOffset)
+ s := fmt.Sprintf("[%d:%d]%s", delta, delta+int64(r.Size), r.Type)
+ if r.Sym != "" {
+ if r.Add != 0 {
+ return fmt.Sprintf("%s:%s+%d", s, r.Sym, r.Add)
+ }
+ return fmt.Sprintf("%s:%s", s, r.Sym)
+ }
+ if r.Add != 0 {
+ return fmt.Sprintf("%s:%d", s, r.Add)
+ }
+ return s
+}
+
+func (f *goobjFile) symbols() ([]Sym, error) {
+ r := f.r
+ var syms []Sym
+
+ // Name of referenced indexed symbols.
+ nrefName := r.NRefName()
+ refNames := make(map[goobj.SymRef]string, nrefName)
+ for i := 0; i < nrefName; i++ {
+ rn := r.RefName(i)
+ refNames[rn.Sym()] = rn.Name(r)
+ }
+
+ abiToVer := func(abi uint16) int {
+ var ver int
+ if abi == goobj.SymABIstatic {
+ // Static symbol
+ ver = 1
+ }
+ return ver
+ }
+
+ resolveSymRef := func(s goobj.SymRef) string {
+ var i uint32
+ switch p := s.PkgIdx; p {
+ case goobj.PkgIdxInvalid:
+ if s.SymIdx != 0 {
+ panic("bad sym ref")
+ }
+ return ""
+ case goobj.PkgIdxHashed64:
+ i = s.SymIdx + uint32(r.NSym())
+ case goobj.PkgIdxHashed:
+ i = s.SymIdx + uint32(r.NSym()+r.NHashed64def())
+ case goobj.PkgIdxNone:
+ i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef())
+ case goobj.PkgIdxBuiltin:
+ name, abi := goobj.BuiltinName(int(s.SymIdx))
+ return goobjName(name, abi)
+ case goobj.PkgIdxSelf:
+ i = s.SymIdx
+ default:
+ return refNames[s]
+ }
+ sym := r.Sym(i)
+ return goobjName(sym.Name(r), abiToVer(sym.ABI()))
+ }
+
+ // Defined symbols
+ ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef())
+ for i := uint32(0); i < ndef; i++ {
+ osym := r.Sym(i)
+ if osym.Name(r) == "" {
+ continue // not a real symbol
+ }
+ name := osym.Name(r)
+ ver := osym.ABI()
+ name = goobjName(name, abiToVer(ver))
+ typ := objabi.SymKind(osym.Type())
+ var code rune = '?'
+ switch typ {
+ case objabi.STEXT:
+ code = 'T'
+ case objabi.SRODATA:
+ code = 'R'
+ case objabi.SDATA:
+ code = 'D'
+ case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
+ code = 'B'
+ }
+ if ver >= goobj.SymABIstatic {
+ code += 'a' - 'A'
+ }
+
+ sym := Sym{
+ Name: name,
+ Addr: uint64(r.DataOff(i)),
+ Size: int64(osym.Siz()),
+ Code: code,
+ }
+
+ relocs := r.Relocs(i)
+ sym.Relocs = make([]Reloc, len(relocs))
+ for j := range relocs {
+ rel := &relocs[j]
+ sym.Relocs[j] = Reloc{
+ Addr: uint64(r.DataOff(i)) + uint64(rel.Off()),
+ Size: uint64(rel.Siz()),
+ Stringer: goobjReloc{
+ Off: rel.Off(),
+ Size: rel.Siz(),
+ Type: objabi.RelocType(rel.Type()),
+ Add: rel.Add(),
+ Sym: resolveSymRef(rel.Sym()),
+ },
+ }
+ }
+
+ syms = append(syms, sym)
+ }
+
+ // Referenced symbols
+ n := ndef + uint32(r.NNonpkgref())
+ for i := ndef; i < n; i++ {
+ osym := r.Sym(i)
+ sym := Sym{Name: osym.Name(r), Code: 'U'}
+ syms = append(syms, sym)
+ }
+ for i := 0; i < nrefName; i++ {
+ rn := r.RefName(i)
+ sym := Sym{Name: rn.Name(r), Code: 'U'}
+ syms = append(syms, sym)
+ }
+
+ return syms, nil
+}
+
+func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ // Should never be called. We implement Liner below, callers
+ // should use that instead.
+ return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
+}
+
+// Find returns the file name, line, and function data for the given pc.
+// Returns "",0,nil if unknown.
+// This function implements the Liner interface in preference to pcln() above.
+func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
+ r := f.r
+ if f.arch == nil {
+ return "", 0, nil
+ }
+ getSymData := func(s goobj.SymRef) []byte {
+ if s.PkgIdx != goobj.PkgIdxHashed {
+ // We don't need the data for non-hashed symbols, yet.
+ panic("not supported")
+ }
+ i := uint32(s.SymIdx + uint32(r.NSym()+r.NHashed64def()))
+ return r.BytesAt(r.DataOff(i), r.DataSize(i))
+ }
+
+ ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef())
+ for i := uint32(0); i < ndef; i++ {
+ osym := r.Sym(i)
+ addr := uint64(r.DataOff(i))
+ if pc < addr || pc >= addr+uint64(osym.Siz()) {
+ continue
+ }
+ isym := ^uint32(0)
+ auxs := r.Auxs(i)
+ for j := range auxs {
+ a := &auxs[j]
+ if a.Type() != goobj.AuxFuncInfo {
+ continue
+ }
+ if a.Sym().PkgIdx != goobj.PkgIdxSelf {
+ panic("funcinfo symbol not defined in current package")
+ }
+ isym = a.Sym().SymIdx
+ }
+ if isym == ^uint32(0) {
+ continue
+ }
+ b := r.BytesAt(r.DataOff(isym), r.DataSize(isym))
+ var info *goobj.FuncInfo
+ pcline := getSymData(info.ReadPcline(b))
+ line := int(pcValue(pcline, pc-addr, f.arch))
+ pcfile := getSymData(info.ReadPcfile(b))
+ fileID := pcValue(pcfile, pc-addr, f.arch)
+ fileName := r.File(int(fileID))
+ // Note: we provide only the name in the Func structure.
+ // We could provide more if needed.
+ return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: osym.Name(r)}}
+ }
+ return "", 0, nil
+}
+
+// pcValue looks up the given PC in a pc value table. target is the
+// offset of the pc from the entry point.
+func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 {
+ val := int32(-1)
+ var pc uint64
+ for step(&tab, &pc, &val, pc == 0, arch) {
+ if target < pc {
+ return val
+ }
+ }
+ return -1
+}
+
+// step advances to the next pc, value pair in the encoded table.
+func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool {
+ uvdelta := readvarint(p)
+ if uvdelta == 0 && !first {
+ return false
+ }
+ if uvdelta&1 != 0 {
+ uvdelta = ^(uvdelta >> 1)
+ } else {
+ uvdelta >>= 1
+ }
+ vdelta := int32(uvdelta)
+ pcdelta := readvarint(p) * uint32(arch.MinLC)
+ *pc += uint64(pcdelta)
+ *val += vdelta
+ return true
+}
+
+// readvarint reads, removes, and returns a varint from *p.
+func readvarint(p *[]byte) uint32 {
+ var v, shift uint32
+ s := *p
+ for shift = 0; ; shift += 7 {
+ b := s[0]
+ s = s[1:]
+ v |= (uint32(b) & 0x7F) << shift
+ if b&0x80 == 0 {
+ break
+ }
+ }
+ *p = s
+ return v
+}
+
+// We treat the whole object file as the text section.
+func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
+ text = make([]byte, f.goobj.Size)
+ _, err = f.f.ReadAt(text, int64(f.goobj.Offset))
+ return
+}
+
+func (f *goobjFile) goarch() string {
+ return f.goobj.Arch
+}
+
+func (f *goobjFile) loadAddress() (uint64, error) {
+ return 0, fmt.Errorf("unknown load address")
+}
+
+func (f *goobjFile) dwarf() (*dwarf.Data, error) {
+ return nil, errors.New("no DWARF data in go object file")
+}
diff --git a/src/cmd/internal/objfile/macho.go b/src/cmd/internal/objfile/macho.go
new file mode 100644
index 0000000..1d6963f
--- /dev/null
+++ b/src/cmd/internal/objfile/macho.go
@@ -0,0 +1,136 @@
+// Copyright 2013 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.
+
+// Parsing of Mach-O executables (OS X).
+
+package objfile
+
+import (
+ "debug/dwarf"
+ "debug/macho"
+ "fmt"
+ "io"
+ "sort"
+)
+
+const stabTypeMask = 0xe0
+
+type machoFile struct {
+ macho *macho.File
+}
+
+func openMacho(r io.ReaderAt) (rawFile, error) {
+ f, err := macho.NewFile(r)
+ if err != nil {
+ return nil, err
+ }
+ return &machoFile{f}, nil
+}
+
+func (f *machoFile) symbols() ([]Sym, error) {
+ if f.macho.Symtab == nil {
+ return nil, nil
+ }
+
+ // Build sorted list of addresses of all symbols.
+ // We infer the size of a symbol by looking at where the next symbol begins.
+ var addrs []uint64
+ for _, s := range f.macho.Symtab.Syms {
+ // Skip stab debug info.
+ if s.Type&stabTypeMask == 0 {
+ addrs = append(addrs, s.Value)
+ }
+ }
+ sort.Sort(uint64s(addrs))
+
+ var syms []Sym
+ for _, s := range f.macho.Symtab.Syms {
+ if s.Type&stabTypeMask != 0 {
+ // Skip stab debug info.
+ continue
+ }
+ sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
+ i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
+ if i < len(addrs) {
+ sym.Size = int64(addrs[i] - s.Value)
+ }
+ if s.Sect == 0 {
+ sym.Code = 'U'
+ } else if int(s.Sect) <= len(f.macho.Sections) {
+ sect := f.macho.Sections[s.Sect-1]
+ switch sect.Seg {
+ case "__TEXT", "__DATA_CONST":
+ sym.Code = 'R'
+ case "__DATA":
+ sym.Code = 'D'
+ }
+ switch sect.Seg + " " + sect.Name {
+ case "__TEXT __text":
+ sym.Code = 'T'
+ case "__DATA __bss", "__DATA __noptrbss":
+ sym.Code = 'B'
+ }
+ }
+ syms = append(syms, sym)
+ }
+
+ return syms, nil
+}
+
+func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ if sect := f.macho.Section("__text"); sect != nil {
+ textStart = sect.Addr
+ }
+ if sect := f.macho.Section("__gosymtab"); sect != nil {
+ if symtab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if sect := f.macho.Section("__gopclntab"); sect != nil {
+ if pclntab, err = sect.Data(); err != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
+}
+
+func (f *machoFile) text() (textStart uint64, text []byte, err error) {
+ sect := f.macho.Section("__text")
+ if sect == nil {
+ return 0, nil, fmt.Errorf("text section not found")
+ }
+ textStart = sect.Addr
+ text, err = sect.Data()
+ return
+}
+
+func (f *machoFile) goarch() string {
+ switch f.macho.Cpu {
+ case macho.Cpu386:
+ return "386"
+ case macho.CpuAmd64:
+ return "amd64"
+ case macho.CpuArm:
+ return "arm"
+ case macho.CpuArm64:
+ return "arm64"
+ case macho.CpuPpc64:
+ return "ppc64"
+ }
+ return ""
+}
+
+type uint64s []uint64
+
+func (x uint64s) Len() int { return len(x) }
+func (x uint64s) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x uint64s) Less(i, j int) bool { return x[i] < x[j] }
+
+func (f *machoFile) loadAddress() (uint64, error) {
+ return 0, fmt.Errorf("unknown load address")
+}
+
+func (f *machoFile) dwarf() (*dwarf.Data, error) {
+ return f.macho.DWARF()
+}
diff --git a/src/cmd/internal/objfile/objfile.go b/src/cmd/internal/objfile/objfile.go
new file mode 100644
index 0000000..a58e0e1
--- /dev/null
+++ b/src/cmd/internal/objfile/objfile.go
@@ -0,0 +1,174 @@
+// Copyright 2014 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 objfile implements portable access to OS-specific executable files.
+package objfile
+
+import (
+ "debug/dwarf"
+ "debug/gosym"
+ "fmt"
+ "io"
+ "os"
+ "sort"
+)
+
+type rawFile interface {
+ symbols() (syms []Sym, err error)
+ pcln() (textStart uint64, symtab, pclntab []byte, err error)
+ text() (textStart uint64, text []byte, err error)
+ goarch() string
+ loadAddress() (uint64, error)
+ dwarf() (*dwarf.Data, error)
+}
+
+// A File is an opened executable file.
+type File struct {
+ r *os.File
+ entries []*Entry
+}
+
+type Entry struct {
+ name string
+ raw rawFile
+}
+
+// A Sym is a symbol defined in an executable file.
+type Sym struct {
+ Name string // symbol name
+ Addr uint64 // virtual address of symbol
+ Size int64 // size in bytes
+ Code rune // nm code (T for text, D for data, and so on)
+ Type string // XXX?
+ Relocs []Reloc // in increasing Addr order
+}
+
+type Reloc struct {
+ Addr uint64 // Address of first byte that reloc applies to.
+ Size uint64 // Number of bytes
+ Stringer RelocStringer
+}
+
+type RelocStringer interface {
+ // insnOffset is the offset of the instruction containing the relocation
+ // from the start of the symbol containing the relocation.
+ String(insnOffset uint64) string
+}
+
+var openers = []func(io.ReaderAt) (rawFile, error){
+ openElf,
+ openMacho,
+ openPE,
+ openPlan9,
+ openXcoff,
+}
+
+// Open opens the named file.
+// The caller must call f.Close when the file is no longer needed.
+func Open(name string) (*File, error) {
+ r, err := os.Open(name)
+ if err != nil {
+ return nil, err
+ }
+ if f, err := openGoFile(r); err == nil {
+ return f, nil
+ }
+ for _, try := range openers {
+ if raw, err := try(r); err == nil {
+ return &File{r, []*Entry{{raw: raw}}}, nil
+ }
+ }
+ r.Close()
+ return nil, fmt.Errorf("open %s: unrecognized object file", name)
+}
+
+func (f *File) Close() error {
+ return f.r.Close()
+}
+
+func (f *File) Entries() []*Entry {
+ return f.entries
+}
+
+func (f *File) Symbols() ([]Sym, error) {
+ return f.entries[0].Symbols()
+}
+
+func (f *File) PCLineTable() (Liner, error) {
+ return f.entries[0].PCLineTable()
+}
+
+func (f *File) Text() (uint64, []byte, error) {
+ return f.entries[0].Text()
+}
+
+func (f *File) GOARCH() string {
+ return f.entries[0].GOARCH()
+}
+
+func (f *File) LoadAddress() (uint64, error) {
+ return f.entries[0].LoadAddress()
+}
+
+func (f *File) DWARF() (*dwarf.Data, error) {
+ return f.entries[0].DWARF()
+}
+
+func (f *File) Disasm() (*Disasm, error) {
+ return f.entries[0].Disasm()
+}
+
+func (e *Entry) Name() string {
+ return e.name
+}
+
+func (e *Entry) Symbols() ([]Sym, error) {
+ syms, err := e.raw.symbols()
+ if err != nil {
+ return nil, err
+ }
+ sort.Sort(byAddr(syms))
+ return syms, nil
+}
+
+type byAddr []Sym
+
+func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
+func (x byAddr) Len() int { return len(x) }
+func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+func (e *Entry) PCLineTable() (Liner, error) {
+ // If the raw file implements Liner directly, use that.
+ // Currently, only Go intermediate objects and archives (goobj) use this path.
+ if pcln, ok := e.raw.(Liner); ok {
+ return pcln, nil
+ }
+ // Otherwise, read the pcln tables and build a Liner out of that.
+ textStart, symtab, pclntab, err := e.raw.pcln()
+ if err != nil {
+ return nil, err
+ }
+ return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart))
+}
+
+func (e *Entry) Text() (uint64, []byte, error) {
+ return e.raw.text()
+}
+
+func (e *Entry) GOARCH() string {
+ return e.raw.goarch()
+}
+
+// LoadAddress returns the expected load address of the file.
+// This differs from the actual load address for a position-independent
+// executable.
+func (e *Entry) LoadAddress() (uint64, error) {
+ return e.raw.loadAddress()
+}
+
+// DWARF returns DWARF debug data for the file, if any.
+// This is for cmd/pprof to locate cgo functions.
+func (e *Entry) DWARF() (*dwarf.Data, error) {
+ return e.raw.dwarf()
+}
diff --git a/src/cmd/internal/objfile/pe.go b/src/cmd/internal/objfile/pe.go
new file mode 100644
index 0000000..b20cda9
--- /dev/null
+++ b/src/cmd/internal/objfile/pe.go
@@ -0,0 +1,203 @@
+// Copyright 2013 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.
+
+// Parsing of PE executables (Microsoft Windows).
+
+package objfile
+
+import (
+ "debug/dwarf"
+ "debug/pe"
+ "fmt"
+ "io"
+ "sort"
+)
+
+type peFile struct {
+ pe *pe.File
+}
+
+func openPE(r io.ReaderAt) (rawFile, error) {
+ f, err := pe.NewFile(r)
+ if err != nil {
+ return nil, err
+ }
+ return &peFile{f}, nil
+}
+
+func (f *peFile) symbols() ([]Sym, error) {
+ // Build sorted list of addresses of all symbols.
+ // We infer the size of a symbol by looking at where the next symbol begins.
+ var addrs []uint64
+
+ var imageBase uint64
+ switch oh := f.pe.OptionalHeader.(type) {
+ case *pe.OptionalHeader32:
+ imageBase = uint64(oh.ImageBase)
+ case *pe.OptionalHeader64:
+ imageBase = oh.ImageBase
+ }
+
+ var syms []Sym
+ for _, s := range f.pe.Symbols {
+ const (
+ N_UNDEF = 0 // An undefined (extern) symbol
+ N_ABS = -1 // An absolute symbol (e_value is a constant, not an address)
+ N_DEBUG = -2 // A debugging symbol
+ )
+ sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'}
+ switch s.SectionNumber {
+ case N_UNDEF:
+ sym.Code = 'U'
+ case N_ABS:
+ sym.Code = 'C'
+ case N_DEBUG:
+ sym.Code = '?'
+ default:
+ if s.SectionNumber < 0 || len(f.pe.Sections) < int(s.SectionNumber) {
+ return nil, fmt.Errorf("invalid section number in symbol table")
+ }
+ sect := f.pe.Sections[s.SectionNumber-1]
+ const (
+ text = 0x20
+ data = 0x40
+ bss = 0x80
+ permW = 0x80000000
+ )
+ ch := sect.Characteristics
+ switch {
+ case ch&text != 0:
+ sym.Code = 'T'
+ case ch&data != 0:
+ if ch&permW == 0 {
+ sym.Code = 'R'
+ } else {
+ sym.Code = 'D'
+ }
+ case ch&bss != 0:
+ sym.Code = 'B'
+ }
+ sym.Addr += imageBase + uint64(sect.VirtualAddress)
+ }
+ syms = append(syms, sym)
+ addrs = append(addrs, sym.Addr)
+ }
+
+ sort.Sort(uint64s(addrs))
+ for i := range syms {
+ j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr })
+ if j < len(addrs) {
+ syms[i].Size = int64(addrs[j] - syms[i].Addr)
+ }
+ }
+
+ return syms, nil
+}
+
+func (f *peFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ var imageBase uint64
+ switch oh := f.pe.OptionalHeader.(type) {
+ case *pe.OptionalHeader32:
+ imageBase = uint64(oh.ImageBase)
+ case *pe.OptionalHeader64:
+ imageBase = oh.ImageBase
+ default:
+ return 0, nil, nil, fmt.Errorf("pe file format not recognized")
+ }
+ if sect := f.pe.Section(".text"); sect != nil {
+ textStart = imageBase + uint64(sect.VirtualAddress)
+ }
+ if pclntab, err = loadPETable(f.pe, "runtime.pclntab", "runtime.epclntab"); err != nil {
+ // We didn't find the symbols, so look for the names used in 1.3 and earlier.
+ // TODO: Remove code looking for the old symbols when we no longer care about 1.3.
+ var err2 error
+ if pclntab, err2 = loadPETable(f.pe, "pclntab", "epclntab"); err2 != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if symtab, err = loadPETable(f.pe, "runtime.symtab", "runtime.esymtab"); err != nil {
+ // Same as above.
+ var err2 error
+ if symtab, err2 = loadPETable(f.pe, "symtab", "esymtab"); err2 != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
+}
+
+func (f *peFile) text() (textStart uint64, text []byte, err error) {
+ var imageBase uint64
+ switch oh := f.pe.OptionalHeader.(type) {
+ case *pe.OptionalHeader32:
+ imageBase = uint64(oh.ImageBase)
+ case *pe.OptionalHeader64:
+ imageBase = oh.ImageBase
+ default:
+ return 0, nil, fmt.Errorf("pe file format not recognized")
+ }
+ sect := f.pe.Section(".text")
+ if sect == nil {
+ return 0, nil, fmt.Errorf("text section not found")
+ }
+ textStart = imageBase + uint64(sect.VirtualAddress)
+ text, err = sect.Data()
+ return
+}
+
+func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
+ for _, s := range f.Symbols {
+ if s.Name != name {
+ continue
+ }
+ if s.SectionNumber <= 0 {
+ return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
+ }
+ if len(f.Sections) < int(s.SectionNumber) {
+ return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
+ }
+ return s, nil
+ }
+ return nil, fmt.Errorf("no %s symbol found", name)
+}
+
+func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
+ ssym, err := findPESymbol(f, sname)
+ if err != nil {
+ return nil, err
+ }
+ esym, err := findPESymbol(f, ename)
+ if err != nil {
+ return nil, err
+ }
+ if ssym.SectionNumber != esym.SectionNumber {
+ return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
+ }
+ sect := f.Sections[ssym.SectionNumber-1]
+ data, err := sect.Data()
+ if err != nil {
+ return nil, err
+ }
+ return data[ssym.Value:esym.Value], nil
+}
+
+func (f *peFile) goarch() string {
+ switch f.pe.Machine {
+ case pe.IMAGE_FILE_MACHINE_I386:
+ return "386"
+ case pe.IMAGE_FILE_MACHINE_AMD64:
+ return "amd64"
+ case pe.IMAGE_FILE_MACHINE_ARMNT:
+ return "arm"
+ default:
+ return ""
+ }
+}
+
+func (f *peFile) loadAddress() (uint64, error) {
+ return 0, fmt.Errorf("unknown load address")
+}
+
+func (f *peFile) dwarf() (*dwarf.Data, error) {
+ return f.pe.DWARF()
+}
diff --git a/src/cmd/internal/objfile/plan9obj.go b/src/cmd/internal/objfile/plan9obj.go
new file mode 100644
index 0000000..da0b345
--- /dev/null
+++ b/src/cmd/internal/objfile/plan9obj.go
@@ -0,0 +1,156 @@
+// Copyright 2014 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.
+
+// Parsing of Plan 9 a.out executables.
+
+package objfile
+
+import (
+ "debug/dwarf"
+ "debug/plan9obj"
+ "errors"
+ "fmt"
+ "io"
+ "sort"
+)
+
+var validSymType = map[rune]bool{
+ 'T': true,
+ 't': true,
+ 'D': true,
+ 'd': true,
+ 'B': true,
+ 'b': true,
+}
+
+type plan9File struct {
+ plan9 *plan9obj.File
+}
+
+func openPlan9(r io.ReaderAt) (rawFile, error) {
+ f, err := plan9obj.NewFile(r)
+ if err != nil {
+ return nil, err
+ }
+ return &plan9File{f}, nil
+}
+
+func (f *plan9File) symbols() ([]Sym, error) {
+ plan9Syms, err := f.plan9.Symbols()
+ if err != nil {
+ return nil, err
+ }
+
+ // Build sorted list of addresses of all symbols.
+ // We infer the size of a symbol by looking at where the next symbol begins.
+ var addrs []uint64
+ for _, s := range plan9Syms {
+ if !validSymType[s.Type] {
+ continue
+ }
+ addrs = append(addrs, s.Value)
+ }
+ sort.Sort(uint64s(addrs))
+
+ var syms []Sym
+
+ for _, s := range plan9Syms {
+ if !validSymType[s.Type] {
+ continue
+ }
+ sym := Sym{Addr: s.Value, Name: s.Name, Code: s.Type}
+ i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
+ if i < len(addrs) {
+ sym.Size = int64(addrs[i] - s.Value)
+ }
+ syms = append(syms, sym)
+ }
+
+ return syms, nil
+}
+
+func (f *plan9File) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ textStart = f.plan9.LoadAddress + f.plan9.HdrSize
+ if pclntab, err = loadPlan9Table(f.plan9, "runtime.pclntab", "runtime.epclntab"); err != nil {
+ // We didn't find the symbols, so look for the names used in 1.3 and earlier.
+ // TODO: Remove code looking for the old symbols when we no longer care about 1.3.
+ var err2 error
+ if pclntab, err2 = loadPlan9Table(f.plan9, "pclntab", "epclntab"); err2 != nil {
+ return 0, nil, nil, err
+ }
+ }
+ if symtab, err = loadPlan9Table(f.plan9, "runtime.symtab", "runtime.esymtab"); err != nil {
+ // Same as above.
+ var err2 error
+ if symtab, err2 = loadPlan9Table(f.plan9, "symtab", "esymtab"); err2 != nil {
+ return 0, nil, nil, err
+ }
+ }
+ return textStart, symtab, pclntab, nil
+}
+
+func (f *plan9File) text() (textStart uint64, text []byte, err error) {
+ sect := f.plan9.Section("text")
+ if sect == nil {
+ return 0, nil, fmt.Errorf("text section not found")
+ }
+ textStart = f.plan9.LoadAddress + f.plan9.HdrSize
+ text, err = sect.Data()
+ return
+}
+
+func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) {
+ syms, err := f.Symbols()
+ if err != nil {
+ return nil, err
+ }
+ for _, s := range syms {
+ if s.Name != name {
+ continue
+ }
+ return &s, nil
+ }
+ return nil, fmt.Errorf("no %s symbol found", name)
+}
+
+func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) {
+ ssym, err := findPlan9Symbol(f, sname)
+ if err != nil {
+ return nil, err
+ }
+ esym, err := findPlan9Symbol(f, ename)
+ if err != nil {
+ return nil, err
+ }
+ sect := f.Section("text")
+ if sect == nil {
+ return nil, err
+ }
+ data, err := sect.Data()
+ if err != nil {
+ return nil, err
+ }
+ textStart := f.LoadAddress + f.HdrSize
+ return data[ssym.Value-textStart : esym.Value-textStart], nil
+}
+
+func (f *plan9File) goarch() string {
+ switch f.plan9.Magic {
+ case plan9obj.Magic386:
+ return "386"
+ case plan9obj.MagicAMD64:
+ return "amd64"
+ case plan9obj.MagicARM:
+ return "arm"
+ }
+ return ""
+}
+
+func (f *plan9File) loadAddress() (uint64, error) {
+ return 0, fmt.Errorf("unknown load address")
+}
+
+func (f *plan9File) dwarf() (*dwarf.Data, error) {
+ return nil, errors.New("no DWARF data in Plan 9 file")
+}
diff --git a/src/cmd/internal/objfile/xcoff.go b/src/cmd/internal/objfile/xcoff.go
new file mode 100644
index 0000000..d6df4db
--- /dev/null
+++ b/src/cmd/internal/objfile/xcoff.go
@@ -0,0 +1,163 @@
+// 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.
+
+// Parsing of XCOFF executable (AIX)
+
+package objfile
+
+import (
+ "debug/dwarf"
+ "fmt"
+ "internal/xcoff"
+ "io"
+ "unicode"
+)
+
+type xcoffFile struct {
+ xcoff *xcoff.File
+}
+
+func openXcoff(r io.ReaderAt) (rawFile, error) {
+ f, err := xcoff.NewFile(r)
+ if err != nil {
+ return nil, err
+ }
+ return &xcoffFile{f}, nil
+}
+
+func (f *xcoffFile) symbols() ([]Sym, error) {
+ var syms []Sym
+ for _, s := range f.xcoff.Symbols {
+ const (
+ N_UNDEF = 0 // An undefined (extern) symbol
+ N_ABS = -1 // An absolute symbol (e_value is a constant, not an address)
+ N_DEBUG = -2 // A debugging symbol
+ )
+ sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
+
+ switch s.SectionNumber {
+ case N_UNDEF:
+ sym.Code = 'U'
+ case N_ABS:
+ sym.Code = 'C'
+ case N_DEBUG:
+ sym.Code = '?'
+ default:
+ if s.SectionNumber < 0 || len(f.xcoff.Sections) < int(s.SectionNumber) {
+ return nil, fmt.Errorf("invalid section number in symbol table")
+ }
+ sect := f.xcoff.Sections[s.SectionNumber-1]
+
+ // debug/xcoff returns an offset in the section not the actual address
+ sym.Addr += sect.VirtualAddress
+
+ if s.AuxCSect.SymbolType&0x3 == xcoff.XTY_LD {
+ // The size of a function is contained in the
+ // AUX_FCN entry
+ sym.Size = s.AuxFcn.Size
+ } else {
+ sym.Size = s.AuxCSect.Length
+ }
+
+ sym.Size = s.AuxCSect.Length
+
+ switch sect.Type {
+ case xcoff.STYP_TEXT:
+ if s.AuxCSect.StorageMappingClass == xcoff.XMC_RO {
+ sym.Code = 'R'
+ } else {
+ sym.Code = 'T'
+ }
+ case xcoff.STYP_DATA:
+ sym.Code = 'D'
+ case xcoff.STYP_BSS:
+ sym.Code = 'B'
+ }
+
+ if s.StorageClass == xcoff.C_HIDEXT {
+ // Local symbol
+ sym.Code = unicode.ToLower(sym.Code)
+ }
+
+ }
+ syms = append(syms, sym)
+ }
+
+ return syms, nil
+}
+
+func (f *xcoffFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ if sect := f.xcoff.Section(".text"); sect != nil {
+ textStart = sect.VirtualAddress
+ }
+ if pclntab, err = loadXCOFFTable(f.xcoff, "runtime.pclntab", "runtime.epclntab"); err != nil {
+ return 0, nil, nil, err
+ }
+ symtab, _ = loadXCOFFTable(f.xcoff, "runtime.symtab", "runtime.esymtab") // ignore error, this symbol is not useful anyway
+ return textStart, symtab, pclntab, nil
+}
+
+func (f *xcoffFile) text() (textStart uint64, text []byte, err error) {
+ sect := f.xcoff.Section(".text")
+ if sect == nil {
+ return 0, nil, fmt.Errorf("text section not found")
+ }
+ textStart = sect.VirtualAddress
+ text, err = sect.Data()
+ return
+}
+
+func findXCOFFSymbol(f *xcoff.File, name string) (*xcoff.Symbol, error) {
+ for _, s := range f.Symbols {
+ if s.Name != name {
+ continue
+ }
+ if s.SectionNumber <= 0 {
+ return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
+ }
+ if len(f.Sections) < int(s.SectionNumber) {
+ return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
+ }
+ return s, nil
+ }
+ return nil, fmt.Errorf("no %s symbol found", name)
+}
+
+func loadXCOFFTable(f *xcoff.File, sname, ename string) ([]byte, error) {
+ ssym, err := findXCOFFSymbol(f, sname)
+ if err != nil {
+ return nil, err
+ }
+ esym, err := findXCOFFSymbol(f, ename)
+ if err != nil {
+ return nil, err
+ }
+ if ssym.SectionNumber != esym.SectionNumber {
+ return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
+ }
+ sect := f.Sections[ssym.SectionNumber-1]
+ data, err := sect.Data()
+ if err != nil {
+ return nil, err
+ }
+ return data[ssym.Value:esym.Value], nil
+}
+
+func (f *xcoffFile) goarch() string {
+ switch f.xcoff.TargetMachine {
+ case xcoff.U802TOCMAGIC:
+ return "ppc"
+ case xcoff.U64_TOCMAGIC:
+ return "ppc64"
+ }
+ return ""
+}
+
+func (f *xcoffFile) loadAddress() (uint64, error) {
+ return 0, fmt.Errorf("unknown load address")
+}
+
+func (f *xcoffFile) dwarf() (*dwarf.Data, error) {
+ return f.xcoff.DWARF()
+}