Source file
src/cmd/nm/nm.go
1
2
3
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 "cmd/internal/telemetry/counter"
17 )
18
19 const 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
32 func usage() {
33 fmt.Fprint(os.Stderr, helpText)
34 os.Exit(2)
35 }
36
37 var (
38 sortOrder = flag.String("sort", "name", "")
39 printSize = flag.Bool("size", false, "")
40 printType = flag.Bool("type", false, "")
41
42 filePrefix = false
43 )
44
45 func init() {
46 flag.Var(nflag(0), "n", "")
47 }
48
49 type nflag int
50
51 func (nflag) IsBoolFlag() bool {
52 return true
53 }
54
55 func (nflag) Set(value string) error {
56 if value == "true" {
57 *sortOrder = "address"
58 }
59 return nil
60 }
61
62 func (nflag) String() string {
63 if *sortOrder == "address" {
64 return "true"
65 }
66 return "false"
67 }
68
69 func 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
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
98 var exitCode = 0
99
100 func errorf(format string, args ...any) {
101 log.Printf(format, args...)
102 exitCode = 1
103 }
104
105 func 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
View as plain text