github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/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  }