Source file src/cmd/nm/nm.go

     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  	"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", "") // alias for -sort address
    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  		// 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  
    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