1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bufio" 9 "flag" 10 "fmt" 11 "log" 12 "os" 13 "sort" 14 15 "cmd/internal/objfile" 16 ) 17 18 const helpText = `usage: go tool nm [options] file... 19 -n 20 an alias for -sort address (numeric), 21 for compatibility with other nm commands 22 -size 23 print symbol size in decimal between address and type 24 -sort {address,name,none,size} 25 sort output in the given order (default name) 26 size orders from largest to smallest 27 -type 28 print symbol type after name 29 ` 30 31 func usage() { 32 fmt.Fprintf(os.Stderr, helpText) 33 os.Exit(2) 34 } 35 36 var ( 37 sortOrder = flag.String("sort", "name", "") 38 printSize = flag.Bool("size", false, "") 39 printType = flag.Bool("type", false, "") 40 41 filePrefix = false 42 ) 43 44 func init() { 45 flag.Var(nflag(0), "n", "") // alias for -sort address 46 } 47 48 type nflag int 49 50 func (nflag) IsBoolFlag() bool { 51 return true 52 } 53 54 func (nflag) Set(value string) error { 55 if value == "true" { 56 *sortOrder = "address" 57 } 58 return nil 59 } 60 61 func (nflag) String() string { 62 if *sortOrder == "address" { 63 return "true" 64 } 65 return "false" 66 } 67 68 func main() { 69 log.SetFlags(0) 70 flag.Usage = usage 71 flag.Parse() 72 73 switch *sortOrder { 74 case "address", "name", "none", "size": 75 // ok 76 default: 77 fmt.Fprintf(os.Stderr, "nm: unknown sort order %q\n", *sortOrder) 78 os.Exit(2) 79 } 80 81 args := flag.Args() 82 filePrefix = len(args) > 1 83 if len(args) == 0 { 84 flag.Usage() 85 } 86 87 for _, file := range args { 88 nm(file) 89 } 90 91 os.Exit(exitCode) 92 } 93 94 var exitCode = 0 95 96 func errorf(format string, args ...interface{}) { 97 log.Printf(format, args...) 98 exitCode = 1 99 } 100 101 func nm(file string) { 102 f, err := objfile.Open(file) 103 if err != nil { 104 errorf("%v", err) 105 return 106 } 107 defer f.Close() 108 109 w := bufio.NewWriter(os.Stdout) 110 111 entries := f.Entries() 112 113 var found bool 114 115 for _, e := range entries { 116 syms, err := e.Symbols() 117 if err != nil { 118 errorf("reading %s: %v", file, err) 119 } 120 if len(syms) == 0 { 121 continue 122 } 123 124 found = true 125 126 switch *sortOrder { 127 case "address": 128 sort.Slice(syms, func(i, j int) bool { return syms[i].Addr < syms[j].Addr }) 129 case "name": 130 sort.Slice(syms, func(i, j int) bool { return syms[i].Name < syms[j].Name }) 131 case "size": 132 sort.Slice(syms, func(i, j int) bool { return syms[i].Size > syms[j].Size }) 133 } 134 135 for _, sym := range syms { 136 if len(entries) > 1 { 137 name := e.Name() 138 if name == "" { 139 fmt.Fprintf(w, "%s(%s):\t", file, "_go_.o") 140 } else { 141 fmt.Fprintf(w, "%s(%s):\t", file, name) 142 } 143 } else if filePrefix { 144 fmt.Fprintf(w, "%s:\t", file) 145 } 146 if sym.Code == 'U' { 147 fmt.Fprintf(w, "%8s", "") 148 } else { 149 fmt.Fprintf(w, "%8x", sym.Addr) 150 } 151 if *printSize { 152 fmt.Fprintf(w, " %10d", sym.Size) 153 } 154 fmt.Fprintf(w, " %c %s", sym.Code, sym.Name) 155 if *printType && sym.Type != "" { 156 fmt.Fprintf(w, " %s", sym.Type) 157 } 158 fmt.Fprintf(w, "\n") 159 } 160 } 161 162 if !found { 163 errorf("reading %s: no symbols", file) 164 } 165 166 w.Flush() 167 } 168