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 )
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", "")
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
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 ...any) {
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
View as plain text