Home | History | Annotate | Download | only in nm
      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