]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/addr2line, cmd/nm: factor object reading into cmd/internal/objfile
authorRuss Cox <rsc@golang.org>
Thu, 7 Aug 2014 16:33:06 +0000 (12:33 -0400)
committerRuss Cox <rsc@golang.org>
Thu, 7 Aug 2014 16:33:06 +0000 (12:33 -0400)
To do in another CL: make cmd/objdump use cmd/internal/objfile too.

There is a package placement decision in this CL:
cmd/internal/objfile instead of internal/objfile.
I chose to put internal under cmd to make clear (and enforce)
that no standard library packages should use this
(it's a bit dependency-heavy).

LGTM=r
R=r
CC=golang-codereviews
https://golang.org/cl/123910043

src/cmd/addr2line/main.go
src/cmd/internal/objfile/elf.go [moved from src/cmd/nm/elf.go with 52% similarity]
src/cmd/internal/objfile/goobj.go [moved from src/cmd/nm/goobj.go with 72% similarity]
src/cmd/internal/objfile/macho.go [moved from src/cmd/nm/macho.go with 55% similarity]
src/cmd/internal/objfile/objfile.go [new file with mode: 0644]
src/cmd/internal/objfile/pe.go [new file with mode: 0644]
src/cmd/internal/objfile/plan9obj.go [new file with mode: 0644]
src/cmd/nm/nm.go
src/cmd/nm/nm_test.go
src/cmd/nm/pe.go [deleted file]
src/cmd/nm/plan9obj.go [deleted file]

index 3802f764f9195176c85513c5c8bf0bb55ff3c4af..267f4170a82faad46d68f03b75860d87ecf3b192 100644 (file)
@@ -19,17 +19,14 @@ package main
 
 import (
        "bufio"
-       "debug/elf"
-       "debug/gosym"
-       "debug/macho"
-       "debug/pe"
-       "debug/plan9obj"
        "flag"
        "fmt"
        "log"
        "os"
        "strconv"
        "strings"
+
+       "cmd/internal/objfile"
 )
 
 func printUsage(w *os.File) {
@@ -60,18 +57,12 @@ func main() {
                usage()
        }
 
-       f, err := os.Open(flag.Arg(0))
+       f, err := objfile.Open(flag.Arg(0))
        if err != nil {
                log.Fatal(err)
        }
 
-       textStart, symtab, pclntab, err := loadTables(f)
-       if err != nil {
-               log.Fatalf("reading %s: %v", flag.Arg(0), err)
-       }
-
-       pcln := gosym.NewLineTable(pclntab, textStart)
-       tab, err := gosym.NewTable(symtab, pcln)
+       tab, err := f.PCLineTable()
        if err != nil {
                log.Fatalf("reading %s: %v", flag.Arg(0), err)
        }
@@ -102,145 +93,3 @@ func main() {
        }
        stdout.Flush()
 }
-
-func loadTables(f *os.File) (textStart uint64, symtab, pclntab []byte, err error) {
-       if obj, err := elf.NewFile(f); err == nil {
-               if sect := obj.Section(".text"); sect != nil {
-                       textStart = sect.Addr
-               }
-               if sect := obj.Section(".gosymtab"); sect != nil {
-                       if symtab, err = sect.Data(); err != nil {
-                               return 0, nil, nil, err
-                       }
-               }
-               if sect := obj.Section(".gopclntab"); sect != nil {
-                       if pclntab, err = sect.Data(); err != nil {
-                               return 0, nil, nil, err
-                       }
-               }
-               return textStart, symtab, pclntab, nil
-       }
-
-       if obj, err := macho.NewFile(f); err == nil {
-               if sect := obj.Section("__text"); sect != nil {
-                       textStart = sect.Addr
-               }
-               if sect := obj.Section("__gosymtab"); sect != nil {
-                       if symtab, err = sect.Data(); err != nil {
-                               return 0, nil, nil, err
-                       }
-               }
-               if sect := obj.Section("__gopclntab"); sect != nil {
-                       if pclntab, err = sect.Data(); err != nil {
-                               return 0, nil, nil, err
-                       }
-               }
-               return textStart, symtab, pclntab, nil
-       }
-
-       if obj, err := pe.NewFile(f); err == nil {
-               var imageBase uint64
-               switch oh := obj.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 := obj.Section(".text"); sect != nil {
-                       textStart = imageBase + uint64(sect.VirtualAddress)
-               }
-               if pclntab, err = loadPETable(obj, "pclntab", "epclntab"); err != nil {
-                       return 0, nil, nil, err
-               }
-               if symtab, err = loadPETable(obj, "symtab", "esymtab"); err != nil {
-                       return 0, nil, nil, err
-               }
-               return textStart, symtab, pclntab, nil
-       }
-
-       if obj, err := plan9obj.NewFile(f); err == nil {
-               textStart = obj.LoadAddress + obj.HdrSize
-               if pclntab, err = loadPlan9Table(obj, "pclntab", "epclntab"); err != nil {
-                       return 0, nil, nil, err
-               }
-               if symtab, err = loadPlan9Table(obj, "symtab", "esymtab"); err != nil {
-                       return 0, nil, nil, err
-               }
-               return textStart, symtab, pclntab, nil
-       }
-
-       return 0, nil, nil, fmt.Errorf("unrecognized binary format")
-}
-
-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 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
-}
similarity index 52%
rename from src/cmd/nm/elf.go
rename to src/cmd/internal/objfile/elf.go
index 5aaa194dd1ee789c6730e116257d7c31d981c21a..8495fa753246a3bb9d54c230729648a07979b386 100644 (file)
@@ -4,24 +4,29 @@
 
 // Parsing of ELF executables (Linux, FreeBSD, and so on).
 
-package main
+package objfile
 
 import (
        "debug/elf"
        "os"
 )
 
-func elfSymbols(f *os.File) []Sym {
-       p, err := elf.NewFile(f)
+type elfFile struct {
+       elf *elf.File
+}
+
+func openElf(r *os.File) (rawFile, error) {
+       f, err := elf.NewFile(r)
        if err != nil {
-               errorf("parsing %s: %v", f.Name(), err)
-               return nil
+               return nil, err
        }
+       return &elfFile{f}, nil
+}
 
-       elfSyms, err := p.Symbols()
+func (f *elfFile) symbols() ([]Sym, error) {
+       elfSyms, err := f.elf.Symbols()
        if err != nil {
-               errorf("parsing %s: %v", f.Name(), err)
-               return nil
+               return nil, err
        }
 
        var syms []Sym
@@ -34,10 +39,10 @@ func elfSymbols(f *os.File) []Sym {
                        sym.Code = 'B'
                default:
                        i := int(s.Section)
-                       if i < 0 || i >= len(p.Sections) {
+                       if i < 0 || i >= len(f.elf.Sections) {
                                break
                        }
-                       sect := p.Sections[i]
+                       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'
@@ -53,5 +58,22 @@ func elfSymbols(f *os.File) []Sym {
                syms = append(syms, sym)
        }
 
-       return syms
+       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
 }
similarity index 72%
rename from src/cmd/nm/goobj.go
rename to src/cmd/internal/objfile/goobj.go
index b0de51db9c033b4c8d7b66e40189ffd9deb9c046..a4f49ebe44db2e3f9be636be87dff4295e28b8da 100644 (file)
@@ -4,7 +4,7 @@
 
 // Parsing of Go intermediate object files and archives.
 
-package main
+package objfile
 
 import (
        "debug/goobj"
@@ -12,6 +12,18 @@ import (
        "os"
 )
 
+type goobjFile struct {
+       goobj *goobj.Package
+}
+
+func openGoobj(r *os.File) (rawFile, error) {
+       f, err := goobj.Parse(r, `""`)
+       if err != nil {
+               return nil, err
+       }
+       return &goobjFile{f}, nil
+}
+
 func goobjName(id goobj.SymID) string {
        if id.Version == 0 {
                return id.Name
@@ -19,17 +31,11 @@ func goobjName(id goobj.SymID) string {
        return fmt.Sprintf("%s<%d>", id.Name, id.Version)
 }
 
-func goobjSymbols(f *os.File) []Sym {
-       pkg, err := goobj.Parse(f, `""`)
-       if err != nil {
-               errorf("parsing %s: %v", f.Name(), err)
-               return nil
-       }
-
+func (f *goobjFile) symbols() ([]Sym, error) {
        seen := make(map[goobj.SymID]bool)
 
        var syms []Sym
-       for _, s := range pkg.Syms {
+       for _, s := range f.goobj.Syms {
                seen[s.SymID] = true
                sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'}
                switch s.Kind {
@@ -50,7 +56,7 @@ func goobjSymbols(f *os.File) []Sym {
                syms = append(syms, sym)
        }
 
-       for _, s := range pkg.Syms {
+       for _, s := range f.goobj.Syms {
                for _, r := range s.Reloc {
                        if !seen[r.Sym] {
                                seen[r.Sym] = true
@@ -64,5 +70,12 @@ func goobjSymbols(f *os.File) []Sym {
                }
        }
 
-       return syms
+       return syms, nil
+}
+
+// pcln does not make sense for Go object files, because each
+// symbol has its own individual pcln table, so there is no global
+// space of addresses to map.
+func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+       return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
 }
similarity index 55%
rename from src/cmd/nm/macho.go
rename to src/cmd/internal/objfile/macho.go
index c60bde55b4ebf0f36fc9fe137efb7507c08b539c..f845792ffa71c782681172d2ae9a2dbcbfccffdb 100644 (file)
@@ -4,36 +4,42 @@
 
 // Parsing of Mach-O executables (OS X).
 
-package main
+package objfile
 
 import (
        "debug/macho"
+       "fmt"
        "os"
        "sort"
 )
 
-func machoSymbols(f *os.File) []Sym {
-       p, err := macho.NewFile(f)
+type machoFile struct {
+       macho *macho.File
+}
+
+func openMacho(r *os.File) (rawFile, error) {
+       f, err := macho.NewFile(r)
        if err != nil {
-               errorf("parsing %s: %v", f.Name(), err)
-               return nil
+               return nil, err
        }
+       return &machoFile{f}, nil
+}
 
-       if p.Symtab == nil {
-               errorf("%s: no symbol table", f.Name())
-               return nil
+func (f *machoFile) symbols() ([]Sym, error) {
+       if f.macho.Symtab == nil {
+               return nil, fmt.Errorf("missing symbol table")
        }
 
        // 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 p.Symtab.Syms {
+       for _, s := range f.macho.Symtab.Syms {
                addrs = append(addrs, s.Value)
        }
        sort.Sort(uint64s(addrs))
 
        var syms []Sym
-       for _, s := range p.Symtab.Syms {
+       for _, s := range f.macho.Symtab.Syms {
                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) {
@@ -41,8 +47,8 @@ func machoSymbols(f *os.File) []Sym {
                }
                if s.Sect == 0 {
                        sym.Code = 'U'
-               } else if int(s.Sect) <= len(p.Sections) {
-                       sect := p.Sections[s.Sect-1]
+               } else if int(s.Sect) <= len(f.macho.Sections) {
+                       sect := f.macho.Sections[s.Sect-1]
                        switch sect.Seg {
                        case "__TEXT":
                                sym.Code = 'R'
@@ -59,7 +65,24 @@ func machoSymbols(f *os.File) []Sym {
                syms = append(syms, sym)
        }
 
-       return syms
+       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
 }
 
 type uint64s []uint64
diff --git a/src/cmd/internal/objfile/objfile.go b/src/cmd/internal/objfile/objfile.go
new file mode 100644 (file)
index 0000000..09fa63e
--- /dev/null
@@ -0,0 +1,72 @@
+// 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/gosym"
+       "fmt"
+       "os"
+)
+
+type rawFile interface {
+       symbols() (syms []Sym, err error)
+       pcln() (textStart uint64, symtab, pclntab []byte, err error)
+}
+
+// A File is an opened executable file.
+type File struct {
+       r   *os.File
+       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?
+}
+
+var openers = []func(*os.File) (rawFile, error){
+       openElf,
+       openGoobj,
+       openMacho,
+       openPE,
+       openPlan9,
+}
+
+// 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
+       }
+       for _, try := range openers {
+               if raw, err := try(r); err == nil {
+                       return &File{r, 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) Symbols() ([]Sym, error) {
+       return f.raw.symbols()
+}
+
+func (f *File) PCLineTable() (*gosym.Table, error) {
+       textStart, symtab, pclntab, err := f.raw.pcln()
+       if err != nil {
+               return nil, err
+       }
+       return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart))
+}
diff --git a/src/cmd/internal/objfile/pe.go b/src/cmd/internal/objfile/pe.go
new file mode 100644 (file)
index 0000000..492766d
--- /dev/null
@@ -0,0 +1,161 @@
+// 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/pe"
+       "fmt"
+       "os"
+       "sort"
+)
+
+type peFile struct {
+       pe *pe.File
+}
+
+func openPE(r *os.File) (rawFile, error) {
+       f, err := pe.NewFile(r)
+       if err != nil {
+               return nil, err
+       }
+       switch f.OptionalHeader.(type) {
+       case *pe.OptionalHeader32, *pe.OptionalHeader64:
+               // ok
+       default:
+               return nil, fmt.Errorf("unrecognized PE format")
+       }
+       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
+                               permX = 0x20000000
+                               permR = 0x40000000
+                               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, "pclntab", "epclntab"); err != nil {
+               return 0, nil, nil, err
+       }
+       if symtab, err = loadPETable(f.pe, "symtab", "esymtab"); err != nil {
+               return 0, nil, nil, err
+       }
+       return textStart, symtab, pclntab, nil
+}
+
+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
+}
diff --git a/src/cmd/internal/objfile/plan9obj.go b/src/cmd/internal/objfile/plan9obj.go
new file mode 100644 (file)
index 0000000..3fe05ec
--- /dev/null
@@ -0,0 +1,100 @@
+// 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/plan9obj"
+       "fmt"
+       "os"
+       "sort"
+)
+
+type plan9File struct {
+       plan9 *plan9obj.File
+}
+
+func openPlan9(r *os.File) (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 {
+               addrs = append(addrs, s.Value)
+       }
+       sort.Sort(uint64s(addrs))
+
+       var syms []Sym
+
+       for _, s := range plan9Syms {
+               sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(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, "pclntab", "epclntab"); err != nil {
+               return 0, nil, nil, err
+       }
+       if symtab, err = loadPlan9Table(f.plan9, "symtab", "esymtab"); err != nil {
+               return 0, nil, nil, err
+       }
+       return textStart, symtab, pclntab, nil
+}
+
+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
+}
index a4036184e48b7c41a5b08af758b8eb77c1b18dee..3089e481be81dddbe444c62456cde41b6eb5faf5 100644 (file)
@@ -6,13 +6,13 @@ package main
 
 import (
        "bufio"
-       "bytes"
        "flag"
        "fmt"
-       "io"
        "log"
        "os"
        "sort"
+
+       "cmd/internal/objfile"
 )
 
 func usage() {
@@ -85,55 +85,22 @@ func errorf(format string, args ...interface{}) {
        exitCode = 1
 }
 
-type Sym struct {
-       Addr uint64
-       Size int64
-       Code rune
-       Name string
-       Type string
-}
-
-var parsers = []struct {
-       prefix []byte
-       parse  func(*os.File) []Sym
-}{
-       {[]byte("!<arch>\n"), goobjSymbols},
-       {[]byte("go object "), goobjSymbols},
-       {[]byte("\x7FELF"), elfSymbols},
-       {[]byte("\xFE\xED\xFA\xCE"), machoSymbols},
-       {[]byte("\xFE\xED\xFA\xCF"), machoSymbols},
-       {[]byte("\xCE\xFA\xED\xFE"), machoSymbols},
-       {[]byte("\xCF\xFA\xED\xFE"), machoSymbols},
-       {[]byte("MZ"), peSymbols},
-       {[]byte("\x00\x00\x01\xEB"), plan9Symbols}, // 386
-       {[]byte("\x00\x00\x04\x07"), plan9Symbols}, // mips
-       {[]byte("\x00\x00\x06\x47"), plan9Symbols}, // arm
-       {[]byte("\x00\x00\x8A\x97"), plan9Symbols}, // amd64
-}
-
 func nm(file string) {
-       f, err := os.Open(file)
+       f, err := objfile.Open(file)
        if err != nil {
                errorf("%v", err)
                return
        }
        defer f.Close()
 
-       buf := make([]byte, 16)
-       io.ReadFull(f, buf)
-       f.Seek(0, 0)
-
-       var syms []Sym
-       for _, p := range parsers {
-               if bytes.HasPrefix(buf, p.prefix) {
-                       syms = p.parse(f)
-                       goto HaveSyms
-               }
+       syms, err := f.Symbols()
+       if err != nil {
+               errorf("reading %s: %v", file, err)
+       }
+       if len(syms) == 0 {
+               errorf("reading %s: no symbols", file)
        }
-       errorf("%v: unknown file format", file)
-       return
 
-HaveSyms:
        switch *sortOrder {
        case "address":
                sort.Sort(byAddr(syms))
@@ -165,19 +132,19 @@ HaveSyms:
        w.Flush()
 }
 
-type byAddr []Sym
+type byAddr []objfile.Sym
 
 func (x byAddr) Len() int           { return len(x) }
 func (x byAddr) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
 func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
 
-type byName []Sym
+type byName []objfile.Sym
 
 func (x byName) Len() int           { return len(x) }
 func (x byName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
 func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name }
 
-type bySize []Sym
+type bySize []objfile.Sym
 
 func (x bySize) Len() int           { return len(x) }
 func (x bySize) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
index 74773877f3226e297953f1ca718f6622cd61e6b6..f447e8e491cbf273c1ff2dbf1a0feaf2bed0d7ec 100644 (file)
@@ -77,7 +77,7 @@ func TestNM(t *testing.T) {
                "elf/testdata/gcc-amd64-linux-exec",
                "macho/testdata/gcc-386-darwin-exec",
                "macho/testdata/gcc-amd64-darwin-exec",
-               "pe/testdata/gcc-amd64-mingw-exec",
+               // "pe/testdata/gcc-amd64-mingw-exec", // no symbols!
                "pe/testdata/gcc-386-mingw-exec",
                "plan9obj/testdata/amd64-plan9-exec",
                "plan9obj/testdata/386-plan9-exec",
@@ -87,7 +87,7 @@ func TestNM(t *testing.T) {
                cmd := exec.Command(testnmpath, exepath)
                out, err := cmd.CombinedOutput()
                if err != nil {
-                       t.Fatalf("go tool nm %v: %v\n%s", exepath, err, string(out))
+                       t.Errorf("go tool nm %v: %v\n%s", exepath, err, string(out))
                }
        }
 
diff --git a/src/cmd/nm/pe.go b/src/cmd/nm/pe.go
deleted file mode 100644 (file)
index 52d05e5..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-// 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 main
-
-import (
-       "debug/pe"
-       "os"
-       "sort"
-)
-
-func peSymbols(f *os.File) []Sym {
-       p, err := pe.NewFile(f)
-       if err != nil {
-               errorf("parsing %s: %v", f.Name(), err)
-               return 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
-
-       var imageBase uint64
-       switch oh := p.OptionalHeader.(type) {
-       case *pe.OptionalHeader32:
-               imageBase = uint64(oh.ImageBase)
-       case *pe.OptionalHeader64:
-               imageBase = oh.ImageBase
-       default:
-               errorf("parsing %s: file format not recognized", f.Name())
-               return nil
-       }
-
-       var syms []Sym
-       for _, s := range p.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 {
-                               errorf("parsing %s: invalid section number %d", f.Name(), s.SectionNumber)
-                               return nil
-                       }
-                       if len(p.Sections) < int(s.SectionNumber) {
-                               errorf("parsing %s: section number %d is large then max %d", f.Name(), s.SectionNumber, len(p.Sections))
-                               return nil
-                       }
-                       sect := p.Sections[s.SectionNumber-1]
-                       const (
-                               text  = 0x20
-                               data  = 0x40
-                               bss   = 0x80
-                               permX = 0x20000000
-                               permR = 0x40000000
-                               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
-}
diff --git a/src/cmd/nm/plan9obj.go b/src/cmd/nm/plan9obj.go
deleted file mode 100644 (file)
index 006c66e..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// 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 main
-
-import (
-       "debug/plan9obj"
-       "os"
-       "sort"
-)
-
-func plan9Symbols(f *os.File) []Sym {
-       p, err := plan9obj.NewFile(f)
-       if err != nil {
-               errorf("parsing %s: %v", f.Name(), err)
-               return nil
-       }
-
-       plan9Syms, err := p.Symbols()
-       if err != nil {
-               errorf("parsing %s: %v", f.Name(), err)
-               return 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 plan9Syms {
-               addrs = append(addrs, s.Value)
-       }
-       sort.Sort(uint64s(addrs))
-
-       var syms []Sym
-
-       for _, s := range plan9Syms {
-               sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(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
-}