summaryrefslogtreecommitdiff
path: root/src/cmd/nm
diff options
context:
space:
mode:
authorTianon Gravi <admwiggin@gmail.com>2015-01-15 11:54:00 -0700
committerTianon Gravi <admwiggin@gmail.com>2015-01-15 11:54:00 -0700
commitf154da9e12608589e8d5f0508f908a0c3e88a1bb (patch)
treef8255d51e10c6f1e0ed69702200b966c9556a431 /src/cmd/nm
parent8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff)
downloadgolang-upstream/1.4.tar.gz
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/cmd/nm')
-rw-r--r--src/cmd/nm/debug_goobj.go670
-rw-r--r--src/cmd/nm/elf.go57
-rw-r--r--src/cmd/nm/goobj.go67
-rw-r--r--src/cmd/nm/macho.go69
-rw-r--r--src/cmd/nm/nm.go57
-rw-r--r--src/cmd/nm/nm_test.go11
-rw-r--r--src/cmd/nm/pe.go98
-rw-r--r--src/cmd/nm/plan9obj.go48
8 files changed, 18 insertions, 1059 deletions
diff --git a/src/cmd/nm/debug_goobj.go b/src/cmd/nm/debug_goobj.go
deleted file mode 100644
index 9a067e2b9..000000000
--- a/src/cmd/nm/debug_goobj.go
+++ /dev/null
@@ -1,670 +0,0 @@
-// DO NOT EDIT. Generated by code.google.com/p/rsc/cmd/bundle
-// bundle -p main -x goobj_ debug/goobj
-
-/* read.go */
-
-// 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.
-
-// Package goobj implements reading of Go object files and archives.
-//
-// TODO(rsc): Decide where this package should live. (golang.org/issue/6932)
-// TODO(rsc): Decide the appropriate integer types for various fields.
-// TODO(rsc): Write tests. (File format still up in the air a little.)
-
-package main
-
-import (
- "bufio"
- "bytes"
- "errors"
- "fmt"
- "io"
- "strconv"
- "strings"
-)
-
-// A SymKind describes the kind of memory represented by a symbol.
-type goobj_SymKind int
-
-// This list is taken from include/link.h.
-
-// Defined SymKind values.
-// TODO(rsc): Give idiomatic Go names.
-// TODO(rsc): Reduce the number of symbol types in the object files.
-const (
- _ goobj_SymKind = iota
-
- // readonly, executable
- goobj_STEXT
- goobj_SELFRXSECT
-
- // readonly, non-executable
- goobj_STYPE
- goobj_SSTRING
- goobj_SGOSTRING
- goobj_SGOFUNC
- goobj_SRODATA
- goobj_SFUNCTAB
- goobj_STYPELINK
- goobj_SSYMTAB // TODO: move to unmapped section
- goobj_SPCLNTAB
- goobj_SELFROSECT
-
- // writable, non-executable
- goobj_SMACHOPLT
- goobj_SELFSECT
- goobj_SMACHO // Mach-O __nl_symbol_ptr
- goobj_SMACHOGOT
- goobj_SNOPTRDATA
- goobj_SINITARR
- goobj_SDATA
- goobj_SWINDOWS
- goobj_SBSS
- goobj_SNOPTRBSS
- goobj_STLSBSS
-
- // not mapped
- goobj_SXREF
- goobj_SMACHOSYMSTR
- goobj_SMACHOSYMTAB
- goobj_SMACHOINDIRECTPLT
- goobj_SMACHOINDIRECTGOT
- goobj_SFILE
- goobj_SFILEPATH
- goobj_SCONST
- goobj_SDYNIMPORT
- goobj_SHOSTOBJ
-)
-
-var goobj_symKindStrings = []string{
- goobj_SBSS: "SBSS",
- goobj_SCONST: "SCONST",
- goobj_SDATA: "SDATA",
- goobj_SDYNIMPORT: "SDYNIMPORT",
- goobj_SELFROSECT: "SELFROSECT",
- goobj_SELFRXSECT: "SELFRXSECT",
- goobj_SELFSECT: "SELFSECT",
- goobj_SFILE: "SFILE",
- goobj_SFILEPATH: "SFILEPATH",
- goobj_SFUNCTAB: "SFUNCTAB",
- goobj_SGOFUNC: "SGOFUNC",
- goobj_SGOSTRING: "SGOSTRING",
- goobj_SHOSTOBJ: "SHOSTOBJ",
- goobj_SINITARR: "SINITARR",
- goobj_SMACHO: "SMACHO",
- goobj_SMACHOGOT: "SMACHOGOT",
- goobj_SMACHOINDIRECTGOT: "SMACHOINDIRECTGOT",
- goobj_SMACHOINDIRECTPLT: "SMACHOINDIRECTPLT",
- goobj_SMACHOPLT: "SMACHOPLT",
- goobj_SMACHOSYMSTR: "SMACHOSYMSTR",
- goobj_SMACHOSYMTAB: "SMACHOSYMTAB",
- goobj_SNOPTRBSS: "SNOPTRBSS",
- goobj_SNOPTRDATA: "SNOPTRDATA",
- goobj_SPCLNTAB: "SPCLNTAB",
- goobj_SRODATA: "SRODATA",
- goobj_SSTRING: "SSTRING",
- goobj_SSYMTAB: "SSYMTAB",
- goobj_STEXT: "STEXT",
- goobj_STLSBSS: "STLSBSS",
- goobj_STYPE: "STYPE",
- goobj_STYPELINK: "STYPELINK",
- goobj_SWINDOWS: "SWINDOWS",
- goobj_SXREF: "SXREF",
-}
-
-func (k goobj_SymKind) String() string {
- if k < 0 || int(k) >= len(goobj_symKindStrings) {
- return fmt.Sprintf("SymKind(%d)", k)
- }
- return goobj_symKindStrings[k]
-}
-
-// A Sym is a named symbol in an object file.
-type goobj_Sym struct {
- goobj_SymID // symbol identifier (name and version)
- Kind goobj_SymKind // kind of symbol
- DupOK bool // are duplicate definitions okay?
- Size int // size of corresponding data
- Type goobj_SymID // symbol for Go type information
- Data goobj_Data // memory image of symbol
- Reloc []goobj_Reloc // relocations to apply to Data
- Func *goobj_Func // additional data for functions
-}
-
-// A SymID - the combination of Name and Version - uniquely identifies
-// a symbol within a package.
-type goobj_SymID struct {
- // Name is the name of a symbol.
- Name string
-
- // Version is zero for symbols with global visibility.
- // Symbols with only file visibility (such as file-level static
- // declarations in C) have a non-zero version distinguishing
- // a symbol in one file from a symbol of the same name
- // in another file
- Version int
-}
-
-func (s goobj_SymID) String() string {
- if s.Version == 0 {
- return s.Name
- }
- return fmt.Sprintf("%s<%d>", s.Name, s.Version)
-}
-
-// A Data is a reference to data stored in an object file.
-// It records the offset and size of the data, so that a client can
-// read the data only if necessary.
-type goobj_Data struct {
- Offset int64
- Size int64
-}
-
-// A Reloc describes a relocation applied to a memory image to refer
-// to an address within a particular symbol.
-type goobj_Reloc struct {
- // The bytes at [Offset, Offset+Size) within the memory image
- // should be updated to refer to the address Add bytes after the start
- // of the symbol Sym.
- Offset int
- Size int
- Sym goobj_SymID
- Add int
-
- // The Type records the form of address expected in the bytes
- // described by the previous fields: absolute, PC-relative, and so on.
- // TODO(rsc): The interpretation of Type is not exposed by this package.
- Type int
-}
-
-// A Var describes a variable in a function stack frame: a declared
-// local variable, an input argument, or an output result.
-type goobj_Var struct {
- // The combination of Name, Kind, and Offset uniquely
- // identifies a variable in a function stack frame.
- // Using fewer of these - in particular, using only Name - does not.
- Name string // Name of variable.
- Kind int // TODO(rsc): Define meaning.
- Offset int // Frame offset. TODO(rsc): Define meaning.
-
- Type goobj_SymID // Go type for variable.
-}
-
-// Func contains additional per-symbol information specific to functions.
-type goobj_Func struct {
- Args int // size in bytes of argument frame: inputs and outputs
- Frame int // size in bytes of local variable frame
- Leaf bool // function omits save of link register (ARM)
- NoSplit bool // function omits stack split prologue
- Var []goobj_Var // detail about local variables
- PCSP goobj_Data // PC → SP offset map
- PCFile goobj_Data // PC → file number map (index into File)
- PCLine goobj_Data // PC → line number map
- PCData []goobj_Data // PC → runtime support data map
- FuncData []goobj_FuncData // non-PC-specific runtime support data
- File []string // paths indexed by PCFile
-}
-
-// TODO: Add PCData []byte and PCDataIter (similar to liblink).
-
-// A FuncData is a single function-specific data value.
-type goobj_FuncData struct {
- Sym goobj_SymID // symbol holding data
- Offset int64 // offset into symbol for funcdata pointer
-}
-
-// A Package is a parsed Go object file or archive defining a Go package.
-type goobj_Package struct {
- ImportPath string // import path denoting this package
- Imports []string // packages imported by this package
- Syms []*goobj_Sym // symbols defined by this package
- MaxVersion int // maximum Version in any SymID in Syms
-}
-
-var (
- goobj_archiveHeader = []byte("!<arch>\n")
- goobj_archiveMagic = []byte("`\n")
- goobj_goobjHeader = []byte("go objec") // truncated to size of archiveHeader
-
- goobj_errCorruptArchive = errors.New("corrupt archive")
- goobj_errTruncatedArchive = errors.New("truncated archive")
- goobj_errNotArchive = errors.New("unrecognized archive format")
-
- goobj_errCorruptObject = errors.New("corrupt object file")
- goobj_errTruncatedObject = errors.New("truncated object file")
- goobj_errNotObject = errors.New("unrecognized object file format")
-)
-
-// An objReader is an object file reader.
-type goobj_objReader struct {
- p *goobj_Package
- b *bufio.Reader
- f io.ReadSeeker
- err error
- offset int64
- limit int64
- tmp [256]byte
- pkg string
- pkgprefix string
-}
-
-// importPathToPrefix returns the prefix that will be used in the
-// final symbol table for the given import path.
-// We escape '%', '"', all control characters and non-ASCII bytes,
-// and any '.' after the final slash.
-//
-// See ../../../cmd/ld/lib.c:/^pathtoprefix and
-// ../../../cmd/gc/subr.c:/^pathtoprefix.
-func goobj_importPathToPrefix(s string) string {
- // find index of last slash, if any, or else -1.
- // used for determining whether an index is after the last slash.
- slash := strings.LastIndex(s, "/")
-
- // check for chars that need escaping
- n := 0
- for r := 0; r < len(s); r++ {
- if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
- n++
- }
- }
-
- // quick exit
- if n == 0 {
- return s
- }
-
- // escape
- const hex = "0123456789abcdef"
- p := make([]byte, 0, len(s)+2*n)
- for r := 0; r < len(s); r++ {
- if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
- p = append(p, '%', hex[c>>4], hex[c&0xF])
- } else {
- p = append(p, c)
- }
- }
-
- return string(p)
-}
-
-// init initializes r to read package p from f.
-func (r *goobj_objReader) init(f io.ReadSeeker, p *goobj_Package) {
- r.f = f
- r.p = p
- r.offset, _ = f.Seek(0, 1)
- r.limit, _ = f.Seek(0, 2)
- f.Seek(r.offset, 0)
- r.b = bufio.NewReader(f)
- r.pkgprefix = goobj_importPathToPrefix(p.ImportPath) + "."
-}
-
-// error records that an error occurred.
-// It returns only the first error, so that an error
-// caused by an earlier error does not discard information
-// about the earlier error.
-func (r *goobj_objReader) error(err error) error {
- if r.err == nil {
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- }
- r.err = err
- }
- // panic("corrupt") // useful for debugging
- return r.err
-}
-
-// readByte reads and returns a byte from the input file.
-// On I/O error or EOF, it records the error but returns byte 0.
-// A sequence of 0 bytes will eventually terminate any
-// parsing state in the object file. In particular, it ends the
-// reading of a varint.
-func (r *goobj_objReader) readByte() byte {
- if r.err != nil {
- return 0
- }
- if r.offset >= r.limit {
- r.error(io.ErrUnexpectedEOF)
- return 0
- }
- b, err := r.b.ReadByte()
- if err != nil {
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- }
- r.error(err)
- b = 0
- } else {
- r.offset++
- }
- return b
-}
-
-// read reads exactly len(b) bytes from the input file.
-// If an error occurs, read returns the error but also
-// records it, so it is safe for callers to ignore the result
-// as long as delaying the report is not a problem.
-func (r *goobj_objReader) readFull(b []byte) error {
- if r.err != nil {
- return r.err
- }
- if r.offset+int64(len(b)) > r.limit {
- return r.error(io.ErrUnexpectedEOF)
- }
- n, err := io.ReadFull(r.b, b)
- r.offset += int64(n)
- if err != nil {
- return r.error(err)
- }
- return nil
-}
-
-// readInt reads a zigzag varint from the input file.
-func (r *goobj_objReader) readInt() int {
- var u uint64
-
- for shift := uint(0); ; shift += 7 {
- if shift >= 64 {
- r.error(goobj_errCorruptObject)
- return 0
- }
- c := r.readByte()
- u |= uint64(c&0x7F) << shift
- if c&0x80 == 0 {
- break
- }
- }
-
- v := int64(u>>1) ^ (int64(u) << 63 >> 63)
- if int64(int(v)) != v {
- r.error(goobj_errCorruptObject) // TODO
- return 0
- }
- return int(v)
-}
-
-// readString reads a length-delimited string from the input file.
-func (r *goobj_objReader) readString() string {
- n := r.readInt()
- buf := make([]byte, n)
- r.readFull(buf)
- return string(buf)
-}
-
-// readSymID reads a SymID from the input file.
-func (r *goobj_objReader) readSymID() goobj_SymID {
- name, vers := r.readString(), r.readInt()
-
- // In a symbol name in an object file, "". denotes the
- // prefix for the package in which the object file has been found.
- // Expand it.
- name = strings.Replace(name, `"".`, r.pkgprefix, -1)
-
- // An individual object file only records version 0 (extern) or 1 (static).
- // To make static symbols unique across all files being read, we
- // replace version 1 with the version corresponding to the current
- // file number. The number is incremented on each call to parseObject.
- if vers != 0 {
- vers = r.p.MaxVersion
- }
-
- return goobj_SymID{name, vers}
-}
-
-// readData reads a data reference from the input file.
-func (r *goobj_objReader) readData() goobj_Data {
- n := r.readInt()
- d := goobj_Data{Offset: r.offset, Size: int64(n)}
- r.skip(int64(n))
- return d
-}
-
-// skip skips n bytes in the input.
-func (r *goobj_objReader) skip(n int64) {
- if n < 0 {
- r.error(fmt.Errorf("debug/goobj: internal error: misuse of skip"))
- }
- if n < int64(len(r.tmp)) {
- // Since the data is so small, a just reading from the buffered
- // reader is better than flushing the buffer and seeking.
- r.readFull(r.tmp[:n])
- } else if n <= int64(r.b.Buffered()) {
- // Even though the data is not small, it has already been read.
- // Advance the buffer instead of seeking.
- for n > int64(len(r.tmp)) {
- r.readFull(r.tmp[:])
- n -= int64(len(r.tmp))
- }
- r.readFull(r.tmp[:n])
- } else {
- // Seek, giving up buffered data.
- _, err := r.f.Seek(r.offset+n, 0)
- if err != nil {
- r.error(err)
- }
- r.offset += n
- r.b.Reset(r.f)
- }
-}
-
-// Parse parses an object file or archive from r,
-// assuming that its import path is pkgpath.
-func goobj_Parse(r io.ReadSeeker, pkgpath string) (*goobj_Package, error) {
- if pkgpath == "" {
- pkgpath = `""`
- }
- p := new(goobj_Package)
- p.ImportPath = pkgpath
-
- var rd goobj_objReader
- rd.init(r, p)
- err := rd.readFull(rd.tmp[:8])
- if err != nil {
- if err == io.EOF {
- err = io.ErrUnexpectedEOF
- }
- return nil, err
- }
-
- switch {
- default:
- return nil, goobj_errNotObject
-
- case bytes.Equal(rd.tmp[:8], goobj_archiveHeader):
- if err := rd.parseArchive(); err != nil {
- return nil, err
- }
- case bytes.Equal(rd.tmp[:8], goobj_goobjHeader):
- if err := rd.parseObject(goobj_goobjHeader); err != nil {
- return nil, err
- }
- }
-
- return p, nil
-}
-
-// trimSpace removes trailing spaces from b and returns the corresponding string.
-// This effectively parses the form used in archive headers.
-func goobj_trimSpace(b []byte) string {
- return string(bytes.TrimRight(b, " "))
-}
-
-// parseArchive parses a Unix archive of Go object files.
-// TODO(rsc): Need to skip non-Go object files.
-// TODO(rsc): Maybe record table of contents in r.p so that
-// linker can avoid having code to parse archives too.
-func (r *goobj_objReader) parseArchive() error {
- for r.offset < r.limit {
- if err := r.readFull(r.tmp[:60]); err != nil {
- return err
- }
- data := r.tmp[:60]
-
- // Each file is preceded by this text header (slice indices in first column):
- // 0:16 name
- // 16:28 date
- // 28:34 uid
- // 34:40 gid
- // 40:48 mode
- // 48:58 size
- // 58:60 magic - `\n
- // We only care about name, size, and magic.
- // The fields are space-padded on the right.
- // The size is in decimal.
- // The file data - size bytes - follows the header.
- // Headers are 2-byte aligned, so if size is odd, an extra padding
- // byte sits between the file data and the next header.
- // The file data that follows is padded to an even number of bytes:
- // if size is odd, an extra padding byte is inserted betw the next header.
- if len(data) < 60 {
- return goobj_errTruncatedArchive
- }
- if !bytes.Equal(data[58:60], goobj_archiveMagic) {
- return goobj_errCorruptArchive
- }
- name := goobj_trimSpace(data[0:16])
- size, err := strconv.ParseInt(goobj_trimSpace(data[48:58]), 10, 64)
- if err != nil {
- return goobj_errCorruptArchive
- }
- data = data[60:]
- fsize := size + size&1
- if fsize < 0 || fsize < size {
- return goobj_errCorruptArchive
- }
- switch name {
- case "__.SYMDEF", "__.GOSYMDEF", "__.PKGDEF":
- r.skip(size)
- default:
- oldLimit := r.limit
- r.limit = r.offset + size
- if err := r.parseObject(nil); err != nil {
- return fmt.Errorf("parsing archive member %q: %v", name, err)
- }
- r.skip(r.limit - r.offset)
- r.limit = oldLimit
- }
- if size&1 != 0 {
- r.skip(1)
- }
- }
- return nil
-}
-
-// parseObject parses a single Go object file.
-// The prefix is the bytes already read from the file,
-// typically in order to detect that this is an object file.
-// The object file consists of a textual header ending in "\n!\n"
-// and then the part we want to parse begins.
-// The format of that part is defined in a comment at the top
-// of src/liblink/objfile.c.
-func (r *goobj_objReader) parseObject(prefix []byte) error {
- // TODO(rsc): Maybe use prefix and the initial input to
- // record the header line from the file, which would
- // give the architecture and other version information.
-
- r.p.MaxVersion++
- var c1, c2, c3 byte
- for {
- c1, c2, c3 = c2, c3, r.readByte()
- if c3 == 0 { // NUL or EOF, either is bad
- return goobj_errCorruptObject
- }
- if c1 == '\n' && c2 == '!' && c3 == '\n' {
- break
- }
- }
-
- r.readFull(r.tmp[:8])
- if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go13ld")) {
- return r.error(goobj_errCorruptObject)
- }
-
- b := r.readByte()
- if b != 1 {
- return r.error(goobj_errCorruptObject)
- }
-
- // Direct package dependencies.
- for {
- s := r.readString()
- if s == "" {
- break
- }
- r.p.Imports = append(r.p.Imports, s)
- }
-
- // Symbols.
- for {
- if b := r.readByte(); b != 0xfe {
- if b != 0xff {
- return r.error(goobj_errCorruptObject)
- }
- break
- }
-
- typ := r.readInt()
- s := &goobj_Sym{goobj_SymID: r.readSymID()}
- r.p.Syms = append(r.p.Syms, s)
- s.Kind = goobj_SymKind(typ)
- s.DupOK = r.readInt() != 0
- s.Size = r.readInt()
- s.Type = r.readSymID()
- s.Data = r.readData()
- s.Reloc = make([]goobj_Reloc, r.readInt())
- for i := range s.Reloc {
- rel := &s.Reloc[i]
- rel.Offset = r.readInt()
- rel.Size = r.readInt()
- rel.Type = r.readInt()
- rel.Add = r.readInt()
- r.readInt() // Xadd - ignored
- rel.Sym = r.readSymID()
- r.readSymID() // Xsym - ignored
- }
-
- if s.Kind == goobj_STEXT {
- f := new(goobj_Func)
- s.Func = f
- f.Args = r.readInt()
- f.Frame = r.readInt()
- f.Leaf = r.readInt() != 0
- f.NoSplit = r.readInt() != 0
- f.Var = make([]goobj_Var, r.readInt())
- for i := range f.Var {
- v := &f.Var[i]
- v.Name = r.readSymID().Name
- v.Offset = r.readInt()
- v.Kind = r.readInt()
- v.Type = r.readSymID()
- }
-
- f.PCSP = r.readData()
- f.PCFile = r.readData()
- f.PCLine = r.readData()
- f.PCData = make([]goobj_Data, r.readInt())
- for i := range f.PCData {
- f.PCData[i] = r.readData()
- }
- f.FuncData = make([]goobj_FuncData, r.readInt())
- for i := range f.FuncData {
- f.FuncData[i].Sym = r.readSymID()
- }
- for i := range f.FuncData {
- f.FuncData[i].Offset = int64(r.readInt()) // TODO
- }
- f.File = make([]string, r.readInt())
- for i := range f.File {
- f.File[i] = r.readSymID().Name
- }
- }
- }
-
- r.readFull(r.tmp[:7])
- if !bytes.Equal(r.tmp[:7], []byte("\xffgo13ld")) {
- return r.error(goobj_errCorruptObject)
- }
-
- return nil
-}
diff --git a/src/cmd/nm/elf.go b/src/cmd/nm/elf.go
deleted file mode 100644
index 5aaa194dd..000000000
--- 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 5e0817d95..000000000
--- a/src/cmd/nm/goobj.go
+++ /dev/null
@@ -1,67 +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 (
- "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.goobj_SymID] = true
- sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.goobj_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 c60bde55b..000000000
--- 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 a4036184e..3089e481b 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 f4e47a42d..cb555d827 100644
--- a/src/cmd/nm/nm_test.go
+++ b/src/cmd/nm/nm_test.go
@@ -55,8 +55,9 @@ func checkSymbols(t *testing.T, nmoutput []byte) {
}
func TestNM(t *testing.T) {
- if runtime.GOOS == "nacl" {
- t.Skip("skipping on nacl")
+ switch runtime.GOOS {
+ case "android", "nacl":
+ t.Skipf("skipping on %s", runtime.GOOS)
}
tmpDir, err := ioutil.TempDir("", "TestNM")
@@ -76,17 +77,17 @@ 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",
}
for _, f := range testfiles {
- exepath := filepath.Join(runtime.GOROOT(), "src", "pkg", "debug", f)
+ exepath := filepath.Join(runtime.GOROOT(), "src", "debug", f)
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 52d05e51d..000000000
--- 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 006c66ebf..000000000
--- 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
-}