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 }