github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/cmd/internal/obj/obj.go (about) 1 // Copyright 2009 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 obj 6 7 import ( 8 "fmt" 9 "path/filepath" 10 "sort" 11 "strings" 12 ) 13 14 // A LineHist records the history of the file input stack, which maps the virtual line number, 15 // an incrementing count of lines processed in any input file and typically named lineno, 16 // to a stack of file:line pairs showing the path of inclusions that led to that position. 17 // The first line directive (//line in Go, #line in assembly) is treated as pushing 18 // a new entry on the stack, so that errors can report both the actual and translated 19 // line number. 20 // 21 // In typical use, the virtual lineno begins at 1, and file line numbers also begin at 1, 22 // but the only requirements placed upon the numbers by this code are: 23 // - calls to Push, Update, and Pop must be monotonically increasing in lineno 24 // - except as specified by those methods, virtual and file line number increase 25 // together, so that given (only) calls Push(10, "x.go", 1) and Pop(15), 26 // virtual line 12 corresponds to x.go line 3. 27 type LineHist struct { 28 Top *LineStack // current top of stack 29 Ranges []LineRange // ranges for lookup 30 Dir string // directory to qualify relative paths 31 TrimPathPrefix string // remove leading TrimPath from recorded file names 32 PrintFilenameOnly bool // ignore path when pretty-printing a line; internal use only 33 GOROOT string // current GOROOT 34 GOROOT_FINAL string // target GOROOT 35 } 36 37 // A LineStack is an entry in the recorded line history. 38 // Although the history at any given line number is a stack, 39 // the record for all line processed forms a tree, with common 40 // stack prefixes acting as parents. 41 type LineStack struct { 42 Parent *LineStack // parent in inclusion stack 43 Lineno int // virtual line number where this entry takes effect 44 File string // file name used to open source file, for error messages 45 AbsFile string // absolute file name, for pcln tables 46 FileLine int // line number in file at Lineno 47 Directive bool 48 Sym *LSym // for linkgetline - TODO(rsc): remove 49 } 50 51 func (stk *LineStack) fileLineAt(lineno int) int { 52 return stk.FileLine + lineno - stk.Lineno 53 } 54 55 // The span of valid linenos in the recorded line history can be broken 56 // into a set of ranges, each with a particular stack. 57 // A LineRange records one such range. 58 type LineRange struct { 59 Start int // starting lineno 60 Stack *LineStack // top of stack for this range 61 } 62 63 // startRange starts a new range with the given top of stack. 64 func (h *LineHist) startRange(lineno int, top *LineStack) { 65 h.Top = top 66 h.Ranges = append(h.Ranges, LineRange{top.Lineno, top}) 67 } 68 69 // setFile sets stk.File = file and also derives stk.AbsFile. 70 func (h *LineHist) setFile(stk *LineStack, file string) { 71 // Note: The exclusion of stk.Directive may be wrong but matches what we've done before. 72 // The check for < avoids putting a path prefix on "<autogenerated>". 73 abs := file 74 if h.Dir != "" && !filepath.IsAbs(file) && !strings.HasPrefix(file, "<") && !stk.Directive { 75 abs = filepath.Join(h.Dir, file) 76 } 77 78 // Remove leading TrimPathPrefix, or else rewrite $GOROOT to literal $GOROOT. 79 if h.TrimPathPrefix != "" && hasPathPrefix(abs, h.TrimPathPrefix) { 80 if abs == h.TrimPathPrefix { 81 abs = "" 82 } else { 83 abs = abs[len(h.TrimPathPrefix)+1:] 84 } 85 } else if hasPathPrefix(abs, h.GOROOT) { 86 abs = "$GOROOT" + abs[len(h.GOROOT):] 87 } 88 if abs == "" { 89 abs = "??" 90 } 91 abs = filepath.Clean(abs) 92 stk.AbsFile = abs 93 94 if file == "" { 95 file = "??" 96 } 97 stk.File = file 98 } 99 100 // Does s have t as a path prefix? 101 // That is, does s == t or does s begin with t followed by a slash? 102 // For portability, we allow ASCII case folding, so that hasPathPrefix("a/b/c", "A/B") is true. 103 // Similarly, we allow slash folding, so that hasPathPrefix("a/b/c", "a\\b") is true. 104 // We do not allow full Unicode case folding, for fear of causing more confusion 105 // or harm than good. (For an example of the kinds of things that can go wrong, 106 // see http://article.gmane.org/gmane.linux.kernel/1853266.) 107 func hasPathPrefix(s string, t string) bool { 108 if len(t) > len(s) { 109 return false 110 } 111 var i int 112 for i = 0; i < len(t); i++ { 113 cs := int(s[i]) 114 ct := int(t[i]) 115 if 'A' <= cs && cs <= 'Z' { 116 cs += 'a' - 'A' 117 } 118 if 'A' <= ct && ct <= 'Z' { 119 ct += 'a' - 'A' 120 } 121 if cs == '\\' { 122 cs = '/' 123 } 124 if ct == '\\' { 125 ct = '/' 126 } 127 if cs != ct { 128 return false 129 } 130 } 131 return i >= len(s) || s[i] == '/' || s[i] == '\\' 132 } 133 134 // Push records that at that lineno a new file with the given name was pushed onto the input stack. 135 func (h *LineHist) Push(lineno int, file string) { 136 stk := &LineStack{ 137 Parent: h.Top, 138 Lineno: lineno, 139 FileLine: 1, 140 } 141 h.setFile(stk, file) 142 h.startRange(lineno, stk) 143 } 144 145 // Pop records that at lineno the current file was popped from the input stack. 146 func (h *LineHist) Pop(lineno int) { 147 top := h.Top 148 if top == nil { 149 return 150 } 151 if top.Directive && top.Parent != nil { // pop #line level too 152 top = top.Parent 153 } 154 next := top.Parent 155 if next == nil { 156 h.Top = nil 157 h.Ranges = append(h.Ranges, LineRange{lineno, nil}) 158 return 159 } 160 161 // Popping included file. Update parent offset to account for 162 // the virtual line number range taken by the included file. 163 // Cannot modify the LineStack directly, or else lookups 164 // for the earlier line numbers will get the wrong answers, 165 // so make a new one. 166 stk := new(LineStack) 167 *stk = *next 168 stk.Lineno = lineno 169 stk.FileLine = next.fileLineAt(top.Lineno) 170 h.startRange(lineno, stk) 171 } 172 173 // Update records that at lineno the file name and line number were changed using 174 // a line directive (//line in Go, #line in assembly). 175 func (h *LineHist) Update(lineno int, file string, line int) { 176 top := h.Top 177 if top == nil { 178 return // shouldn't happen 179 } 180 var stk *LineStack 181 if top.Directive { 182 // Update existing entry, except make copy to avoid changing earlier history. 183 stk = new(LineStack) 184 *stk = *top 185 } else { 186 // Push new entry. 187 stk = &LineStack{ 188 Parent: top, 189 Directive: true, 190 } 191 } 192 stk.Lineno = lineno 193 if stk.File != file { 194 h.setFile(stk, file) // only retain string if needed 195 } 196 stk.FileLine = line 197 h.startRange(lineno, stk) 198 } 199 200 // AddImport adds a package to the list of imported packages. 201 func (ctxt *Link) AddImport(pkg string) { 202 ctxt.Imports = append(ctxt.Imports, pkg) 203 } 204 205 // At returns the input stack in effect at lineno. 206 func (h *LineHist) At(lineno int) *LineStack { 207 i := sort.Search(len(h.Ranges), func(i int) bool { 208 return h.Ranges[i].Start > lineno 209 }) 210 // Found first entry beyond lineno. 211 if i == 0 { 212 return nil 213 } 214 return h.Ranges[i-1].Stack 215 } 216 217 // LineString returns a string giving the file and line number 218 // corresponding to lineno, for use in error messages. 219 func (h *LineHist) LineString(lineno int) string { 220 stk := h.At(lineno) 221 if stk == nil { 222 return "<unknown line number>" 223 } 224 225 filename := stk.File 226 if h.PrintFilenameOnly { 227 filename = filepath.Base(filename) 228 } 229 text := fmt.Sprintf("%s:%d", filename, stk.fileLineAt(lineno)) 230 if stk.Directive && stk.Parent != nil { 231 stk = stk.Parent 232 filename = stk.File 233 if h.PrintFilenameOnly { 234 filename = filepath.Base(filename) 235 } 236 text += fmt.Sprintf("[%s:%d]", filename, stk.fileLineAt(lineno)) 237 } 238 const showFullStack = false // was used by old C compilers 239 if showFullStack { 240 for stk.Parent != nil { 241 lineno = stk.Lineno - 1 242 stk = stk.Parent 243 text += fmt.Sprintf(" %s:%d", filename, stk.fileLineAt(lineno)) 244 if stk.Directive && stk.Parent != nil { 245 stk = stk.Parent 246 text += fmt.Sprintf("[%s:%d]", filename, stk.fileLineAt(lineno)) 247 } 248 } 249 } 250 return text 251 } 252 253 // FileLine returns the file name and line number 254 // at the top of the stack for the given lineno. 255 func (h *LineHist) FileLine(lineno int) (file string, line int) { 256 stk := h.At(lineno) 257 if stk == nil { 258 return "??", 0 259 } 260 return stk.File, stk.fileLineAt(lineno) 261 } 262 263 // AbsFileLine returns the absolute file name and line number 264 // at the top of the stack for the given lineno. 265 func (h *LineHist) AbsFileLine(lineno int) (file string, line int) { 266 stk := h.At(lineno) 267 if stk == nil { 268 return "??", 0 269 } 270 return stk.AbsFile, stk.fileLineAt(lineno) 271 } 272 273 // This is a simplified copy of linklinefmt above. 274 // It doesn't allow printing the full stack, and it returns the file name and line number separately. 275 // TODO: Unify with linklinefmt somehow. 276 func linkgetline(ctxt *Link, lineno int32) (f *LSym, l int32) { 277 stk := ctxt.LineHist.At(int(lineno)) 278 if stk == nil || stk.AbsFile == "" { 279 return Linklookup(ctxt, "??", HistVersion), 0 280 } 281 if stk.Sym == nil { 282 stk.Sym = Linklookup(ctxt, stk.AbsFile, HistVersion) 283 } 284 return stk.Sym, int32(stk.fileLineAt(int(lineno))) 285 } 286 287 func Linkprfile(ctxt *Link, line int) { 288 fmt.Printf("%s ", ctxt.LineHist.LineString(line)) 289 } 290 291 func fieldtrack(ctxt *Link, cursym *LSym) { 292 p := cursym.Text 293 if p == nil || p.Link == nil { // handle external functions and ELF section symbols 294 return 295 } 296 ctxt.Cursym = cursym 297 298 for ; p != nil; p = p.Link { 299 if p.As == AUSEFIELD { 300 r := Addrel(ctxt.Cursym) 301 r.Off = 0 302 r.Siz = 0 303 r.Sym = p.From.Sym 304 r.Type = R_USEFIELD 305 } 306 } 307 }