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