github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/cmd/addr2line/main.go (about)

     1  // Copyright 2012 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  // Addr2line is a minimal simulation of the GNU addr2line tool,
     6  // just enough to support pprof.
     7  //
     8  // Usage:
     9  //	go tool addr2line binary
    10  //
    11  // Addr2line reads hexadecimal addresses, one per line and with optional 0x prefix,
    12  // from standard input. For each input address, addr2line prints two output lines,
    13  // first the name of the function containing the address and second the file:line
    14  // of the source code corresponding to that address.
    15  //
    16  // This tool is intended for use only by pprof; its interface may change or
    17  // it may be deleted entirely in future releases.
    18  package main
    19  
    20  import (
    21  	"bufio"
    22  	"debug/elf"
    23  	"debug/gosym"
    24  	"debug/macho"
    25  	"debug/pe"
    26  	"debug/plan9obj"
    27  	"flag"
    28  	"fmt"
    29  	"log"
    30  	"os"
    31  	"strconv"
    32  	"strings"
    33  )
    34  
    35  func printUsage(w *os.File) {
    36  	fmt.Fprintf(w, "usage: addr2line binary\n")
    37  	fmt.Fprintf(w, "reads addresses from standard input and writes two lines for each:\n")
    38  	fmt.Fprintf(w, "\tfunction name\n")
    39  	fmt.Fprintf(w, "\tfile:line\n")
    40  }
    41  
    42  func usage() {
    43  	printUsage(os.Stderr)
    44  	os.Exit(2)
    45  }
    46  
    47  func main() {
    48  	log.SetFlags(0)
    49  	log.SetPrefix("addr2line: ")
    50  
    51  	// pprof expects this behavior when checking for addr2line
    52  	if len(os.Args) > 1 && os.Args[1] == "--help" {
    53  		printUsage(os.Stdout)
    54  		os.Exit(0)
    55  	}
    56  
    57  	flag.Usage = usage
    58  	flag.Parse()
    59  	if flag.NArg() != 1 {
    60  		usage()
    61  	}
    62  
    63  	f, err := os.Open(flag.Arg(0))
    64  	if err != nil {
    65  		log.Fatal(err)
    66  	}
    67  
    68  	textStart, symtab, pclntab, err := loadTables(f)
    69  	if err != nil {
    70  		log.Fatalf("reading %s: %v", flag.Arg(0), err)
    71  	}
    72  
    73  	pcln := gosym.NewLineTable(pclntab, textStart)
    74  	tab, err := gosym.NewTable(symtab, pcln)
    75  	if err != nil {
    76  		log.Fatalf("reading %s: %v", flag.Arg(0), err)
    77  	}
    78  
    79  	stdin := bufio.NewScanner(os.Stdin)
    80  	stdout := bufio.NewWriter(os.Stdout)
    81  
    82  	for stdin.Scan() {
    83  		p := stdin.Text()
    84  		if strings.Contains(p, ":") {
    85  			// Reverse translate file:line to pc.
    86  			// This was an extension in the old C version of 'go tool addr2line'
    87  			// and is probably not used by anyone, but recognize the syntax.
    88  			// We don't have an implementation.
    89  			fmt.Fprintf(stdout, "!reverse translation not implemented\n")
    90  			continue
    91  		}
    92  		pc, _ := strconv.ParseUint(strings.TrimPrefix(p, "0x"), 16, 64)
    93  		file, line, fn := tab.PCToLine(pc)
    94  		name := "?"
    95  		if fn != nil {
    96  			name = fn.Name
    97  		} else {
    98  			file = "?"
    99  			line = 0
   100  		}
   101  		fmt.Fprintf(stdout, "%s\n%s:%d\n", name, file, line)
   102  	}
   103  	stdout.Flush()
   104  }
   105  
   106  func loadTables(f *os.File) (textStart uint64, symtab, pclntab []byte, err error) {
   107  	if obj, err := elf.NewFile(f); err == nil {
   108  		if sect := obj.Section(".text"); sect != nil {
   109  			textStart = sect.Addr
   110  		}
   111  		if sect := obj.Section(".gosymtab"); sect != nil {
   112  			if symtab, err = sect.Data(); err != nil {
   113  				return 0, nil, nil, err
   114  			}
   115  		}
   116  		if sect := obj.Section(".gopclntab"); sect != nil {
   117  			if pclntab, err = sect.Data(); err != nil {
   118  				return 0, nil, nil, err
   119  			}
   120  		}
   121  		return textStart, symtab, pclntab, nil
   122  	}
   123  
   124  	if obj, err := macho.NewFile(f); err == nil {
   125  		if sect := obj.Section("__text"); sect != nil {
   126  			textStart = sect.Addr
   127  		}
   128  		if sect := obj.Section("__gosymtab"); sect != nil {
   129  			if symtab, err = sect.Data(); err != nil {
   130  				return 0, nil, nil, err
   131  			}
   132  		}
   133  		if sect := obj.Section("__gopclntab"); sect != nil {
   134  			if pclntab, err = sect.Data(); err != nil {
   135  				return 0, nil, nil, err
   136  			}
   137  		}
   138  		return textStart, symtab, pclntab, nil
   139  	}
   140  
   141  	if obj, err := pe.NewFile(f); err == nil {
   142  		var imageBase uint64
   143  		switch oh := obj.OptionalHeader.(type) {
   144  		case *pe.OptionalHeader32:
   145  			imageBase = uint64(oh.ImageBase)
   146  		case *pe.OptionalHeader64:
   147  			imageBase = oh.ImageBase
   148  		default:
   149  			return 0, nil, nil, fmt.Errorf("pe file format not recognized")
   150  		}
   151  		if sect := obj.Section(".text"); sect != nil {
   152  			textStart = imageBase + uint64(sect.VirtualAddress)
   153  		}
   154  		if pclntab, err = loadPETable(obj, "pclntab", "epclntab"); err != nil {
   155  			return 0, nil, nil, err
   156  		}
   157  		if symtab, err = loadPETable(obj, "symtab", "esymtab"); err != nil {
   158  			return 0, nil, nil, err
   159  		}
   160  		return textStart, symtab, pclntab, nil
   161  	}
   162  
   163  	if obj, err := plan9obj.NewFile(f); err == nil {
   164  		sym, err := findPlan9Symbol(obj, "text")
   165  		if err != nil {
   166  			return 0, nil, nil, err
   167  		}
   168  		textStart = sym.Value
   169  		if pclntab, err = loadPlan9Table(obj, "pclntab", "epclntab"); err != nil {
   170  			return 0, nil, nil, err
   171  		}
   172  		if symtab, err = loadPlan9Table(obj, "symtab", "esymtab"); err != nil {
   173  			return 0, nil, nil, err
   174  		}
   175  		return textStart, symtab, pclntab, nil
   176  	}
   177  
   178  	return 0, nil, nil, fmt.Errorf("unrecognized binary format")
   179  }
   180  
   181  func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
   182  	for _, s := range f.Symbols {
   183  		if s.Name != name {
   184  			continue
   185  		}
   186  		if s.SectionNumber <= 0 {
   187  			return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
   188  		}
   189  		if len(f.Sections) < int(s.SectionNumber) {
   190  			return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
   191  		}
   192  		return s, nil
   193  	}
   194  	return nil, fmt.Errorf("no %s symbol found", name)
   195  }
   196  
   197  func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
   198  	ssym, err := findPESymbol(f, sname)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	esym, err := findPESymbol(f, ename)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	if ssym.SectionNumber != esym.SectionNumber {
   207  		return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
   208  	}
   209  	sect := f.Sections[ssym.SectionNumber-1]
   210  	data, err := sect.Data()
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	return data[ssym.Value:esym.Value], nil
   215  }
   216  
   217  func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) {
   218  	syms, err := f.Symbols()
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	for _, s := range syms {
   223  		if s.Name != name {
   224  			continue
   225  		}
   226  		return &s, nil
   227  	}
   228  	return nil, fmt.Errorf("no %s symbol found", name)
   229  }
   230  
   231  func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) {
   232  	ssym, err := findPlan9Symbol(f, sname)
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  	esym, err := findPlan9Symbol(f, ename)
   237  	if err != nil {
   238  		return nil, err
   239  	}
   240  	text, err := findPlan9Symbol(f, "text")
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	sect := f.Section("text")
   245  	if sect == nil {
   246  		return nil, err
   247  	}
   248  	data, err := sect.Data()
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  	return data[ssym.Value-text.Value : esym.Value-text.Value], nil
   253  }