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