aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/nm
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-08-07 12:33:06 -0400
committerRuss Cox <rsc@golang.org>2014-08-07 12:33:06 -0400
commit08033f9816e1e33092c93c050dc34514d8e3e926 (patch)
treee7442db7fd5cbf52364c7617d437f30374e1117e /src/cmd/nm
parentfad69a7b77a7996b0308bdbcd559b78b32046bb3 (diff)
downloadgo-08033f9816e1e33092c93c050dc34514d8e3e926.tar.gz
go-08033f9816e1e33092c93c050dc34514d8e3e926.zip
cmd/addr2line, cmd/nm: factor object reading into cmd/internal/objfile
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
Diffstat (limited to 'src/cmd/nm')
-rw-r--r--src/cmd/nm/elf.go57
-rw-r--r--src/cmd/nm/goobj.go68
-rw-r--r--src/cmd/nm/macho.go69
-rw-r--r--src/cmd/nm/nm.go57
-rw-r--r--src/cmd/nm/nm_test.go4
-rw-r--r--src/cmd/nm/pe.go98
-rw-r--r--src/cmd/nm/plan9obj.go48
7 files changed, 14 insertions, 387 deletions
diff --git a/src/cmd/nm/elf.go b/src/cmd/nm/elf.go
deleted file mode 100644
index 5aaa194dd1..0000000000
--- a/src/cmd/nm/elf.go
+++ /dev/null
@@ -1,57 +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 ELF executables (Linux, FreeBSD, and so on).
-
-package main
-
-import (
- "debug/elf"
- "os"
-)
-
-func elfSymbols(f *os.File) []Sym {
- p, err := elf.NewFile(f)
- if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
- }
-
- elfSyms, err := p.Symbols()
- if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
- }
-
- 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(p.Sections) {
- break
- }
- sect := p.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
-}
diff --git a/src/cmd/nm/goobj.go b/src/cmd/nm/goobj.go
deleted file mode 100644
index b0de51db9c..0000000000
--- a/src/cmd/nm/goobj.go
+++ /dev/null
@@ -1,68 +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 Go intermediate object files and archives.
-
-package main
-
-import (
- "debug/goobj"
- "fmt"
- "os"
-)
-
-func goobjName(id goobj.SymID) string {
- if id.Version == 0 {
- return id.Name
- }
- 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
- }
-
- seen := make(map[goobj.SymID]bool)
-
- var syms []Sym
- for _, s := range pkg.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 {
- case goobj.STEXT, goobj.SELFRXSECT:
- sym.Code = 'T'
- case goobj.STYPE, goobj.SSTRING, goobj.SGOSTRING, goobj.SGOFUNC, goobj.SRODATA, goobj.SFUNCTAB, goobj.STYPELINK, goobj.SSYMTAB, goobj.SPCLNTAB, goobj.SELFROSECT:
- sym.Code = 'R'
- case goobj.SMACHOPLT, goobj.SELFSECT, goobj.SMACHO, goobj.SMACHOGOT, goobj.SNOPTRDATA, goobj.SINITARR, goobj.SDATA, goobj.SWINDOWS:
- sym.Code = 'D'
- case goobj.SBSS, goobj.SNOPTRBSS, goobj.STLSBSS:
- sym.Code = 'B'
- case goobj.SXREF, goobj.SMACHOSYMSTR, goobj.SMACHOSYMTAB, goobj.SMACHOINDIRECTPLT, goobj.SMACHOINDIRECTGOT, goobj.SFILE, goobj.SFILEPATH, goobj.SCONST, goobj.SDYNIMPORT, goobj.SHOSTOBJ:
- sym.Code = 'X' // should not see
- }
- if s.Version != 0 {
- sym.Code += 'a' - 'A'
- }
- syms = append(syms, sym)
- }
-
- for _, s := range pkg.Syms {
- for _, r := range s.Reloc {
- if !seen[r.Sym] {
- seen[r.Sym] = true
- sym := Sym{Name: goobjName(r.Sym), Code: 'U'}
- if s.Version != 0 {
- // should not happen but handle anyway
- sym.Code = 'u'
- }
- syms = append(syms, sym)
- }
- }
- }
-
- return syms
-}
diff --git a/src/cmd/nm/macho.go b/src/cmd/nm/macho.go
deleted file mode 100644
index c60bde55b4..0000000000
--- a/src/cmd/nm/macho.go
+++ /dev/null
@@ -1,69 +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 Mach-O executables (OS X).
-
-package main
-
-import (
- "debug/macho"
- "os"
- "sort"
-)
-
-func machoSymbols(f *os.File) []Sym {
- p, err := macho.NewFile(f)
- if err != nil {
- errorf("parsing %s: %v", f.Name(), err)
- return nil
- }
-
- if p.Symtab == nil {
- errorf("%s: no symbol table", f.Name())
- 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 p.Symtab.Syms {
- addrs = append(addrs, s.Value)
- }
- sort.Sort(uint64s(addrs))
-
- var syms []Sym
- for _, s := range p.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) {
- sym.Size = int64(addrs[i] - s.Value)
- }
- if s.Sect == 0 {
- sym.Code = 'U'
- } else if int(s.Sect) <= len(p.Sections) {
- sect := p.Sections[s.Sect-1]
- switch sect.Seg {
- case "__TEXT":
- 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
-}
-
-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] }
diff --git a/src/cmd/nm/nm.go b/src/cmd/nm/nm.go
index a4036184e4..3089e481be 100644
--- a/src/cmd/nm/nm.go
+++ b/src/cmd/nm/nm.go
@@ -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] }
diff --git a/src/cmd/nm/nm_test.go b/src/cmd/nm/nm_test.go
index 74773877f3..f447e8e491 100644
--- a/src/cmd/nm/nm_test.go
+++ b/src/cmd/nm/nm_test.go
@@ -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
index 52d05e51d0..0000000000
--- a/src/cmd/nm/pe.go
+++ /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
index 006c66ebfd..0000000000
--- a/src/cmd/nm/plan9obj.go
+++ /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
-}