github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/cmd/internal/objfile/goobj.go (about)

     1  // Copyright 2013 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  // Parsing of Go intermediate object files and archives.
     6  
     7  package objfile
     8  
     9  import (
    10  	"cmd/internal/goobj"
    11  	"cmd/internal/objabi"
    12  	"cmd/internal/sys"
    13  	"debug/dwarf"
    14  	"debug/gosym"
    15  	"errors"
    16  	"fmt"
    17  	"os"
    18  )
    19  
    20  type goobjFile struct {
    21  	goobj *goobj.Package
    22  	f     *os.File // the underlying .o or .a file
    23  }
    24  
    25  func openGoobj(r *os.File) (rawFile, error) {
    26  	f, err := goobj.Parse(r, `""`)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	return &goobjFile{goobj: f, f: r}, nil
    31  }
    32  
    33  func goobjName(id goobj.SymID) string {
    34  	if id.Version == 0 {
    35  		return id.Name
    36  	}
    37  	return fmt.Sprintf("%s<%d>", id.Name, id.Version)
    38  }
    39  
    40  func (f *goobjFile) symbols() ([]Sym, error) {
    41  	seen := make(map[goobj.SymID]bool)
    42  
    43  	var syms []Sym
    44  	for _, s := range f.goobj.Syms {
    45  		seen[s.SymID] = true
    46  		sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'}
    47  		switch s.Kind {
    48  		case objabi.STEXT:
    49  			sym.Code = 'T'
    50  		case objabi.SRODATA:
    51  			sym.Code = 'R'
    52  		case objabi.SDATA:
    53  			sym.Code = 'D'
    54  		case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
    55  			sym.Code = 'B'
    56  		case objabi.SXREF, objabi.SCONST:
    57  			sym.Code = 'X' // should not see
    58  		}
    59  		if s.Version != 0 {
    60  			sym.Code += 'a' - 'A'
    61  		}
    62  		for i, r := range s.Reloc {
    63  			sym.Relocs = append(sym.Relocs, Reloc{Addr: uint64(s.Data.Offset) + uint64(r.Offset), Size: uint64(r.Size), Stringer: &s.Reloc[i]})
    64  		}
    65  		syms = append(syms, sym)
    66  	}
    67  
    68  	for _, s := range f.goobj.Syms {
    69  		for _, r := range s.Reloc {
    70  			if !seen[r.Sym] {
    71  				seen[r.Sym] = true
    72  				sym := Sym{Name: goobjName(r.Sym), Code: 'U'}
    73  				if s.Version != 0 {
    74  					// should not happen but handle anyway
    75  					sym.Code = 'u'
    76  				}
    77  				syms = append(syms, sym)
    78  			}
    79  		}
    80  	}
    81  
    82  	return syms, nil
    83  }
    84  
    85  func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
    86  	// Should never be called.  We implement Liner below, callers
    87  	// should use that instead.
    88  	return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
    89  }
    90  
    91  // Find returns the file name, line, and function data for the given pc.
    92  // Returns "",0,nil if unknown.
    93  // This function implements the Liner interface in preference to pcln() above.
    94  func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
    95  	// TODO: this is really inefficient.  Binary search?  Memoize last result?
    96  	var arch *sys.Arch
    97  	for _, a := range sys.Archs {
    98  		if a.Name == f.goobj.Arch {
    99  			arch = a
   100  			break
   101  		}
   102  	}
   103  	if arch == nil {
   104  		return "", 0, nil
   105  	}
   106  	for _, s := range f.goobj.Syms {
   107  		if pc < uint64(s.Data.Offset) || pc >= uint64(s.Data.Offset+s.Data.Size) {
   108  			continue
   109  		}
   110  		if s.Func == nil {
   111  			return "", 0, nil
   112  		}
   113  		pcfile := make([]byte, s.Func.PCFile.Size)
   114  		_, err := f.f.ReadAt(pcfile, s.Func.PCFile.Offset)
   115  		if err != nil {
   116  			return "", 0, nil
   117  		}
   118  		fileID := int(pcValue(pcfile, pc-uint64(s.Data.Offset), arch))
   119  		fileName := s.Func.File[fileID]
   120  		pcline := make([]byte, s.Func.PCLine.Size)
   121  		_, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset)
   122  		if err != nil {
   123  			return "", 0, nil
   124  		}
   125  		line := int(pcValue(pcline, pc-uint64(s.Data.Offset), arch))
   126  		// Note: we provide only the name in the Func structure.
   127  		// We could provide more if needed.
   128  		return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: s.Name}}
   129  	}
   130  	return "", 0, nil
   131  }
   132  
   133  // pcValue looks up the given PC in a pc value table. target is the
   134  // offset of the pc from the entry point.
   135  func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 {
   136  	val := int32(-1)
   137  	var pc uint64
   138  	for step(&tab, &pc, &val, pc == 0, arch) {
   139  		if target < pc {
   140  			return val
   141  		}
   142  	}
   143  	return -1
   144  }
   145  
   146  // step advances to the next pc, value pair in the encoded table.
   147  func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool {
   148  	uvdelta := readvarint(p)
   149  	if uvdelta == 0 && !first {
   150  		return false
   151  	}
   152  	if uvdelta&1 != 0 {
   153  		uvdelta = ^(uvdelta >> 1)
   154  	} else {
   155  		uvdelta >>= 1
   156  	}
   157  	vdelta := int32(uvdelta)
   158  	pcdelta := readvarint(p) * uint32(arch.MinLC)
   159  	*pc += uint64(pcdelta)
   160  	*val += vdelta
   161  	return true
   162  }
   163  
   164  // readvarint reads, removes, and returns a varint from *p.
   165  func readvarint(p *[]byte) uint32 {
   166  	var v, shift uint32
   167  	s := *p
   168  	for shift = 0; ; shift += 7 {
   169  		b := s[0]
   170  		s = s[1:]
   171  		v |= (uint32(b) & 0x7F) << shift
   172  		if b&0x80 == 0 {
   173  			break
   174  		}
   175  	}
   176  	*p = s
   177  	return v
   178  }
   179  
   180  // We treat the whole object file as the text section.
   181  func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
   182  	var info os.FileInfo
   183  	info, err = f.f.Stat()
   184  	if err != nil {
   185  		return
   186  	}
   187  	text = make([]byte, info.Size())
   188  	_, err = f.f.ReadAt(text, 0)
   189  	return
   190  }
   191  
   192  func (f *goobjFile) goarch() string {
   193  	return f.goobj.Arch
   194  }
   195  
   196  func (f *goobjFile) loadAddress() (uint64, error) {
   197  	return 0, fmt.Errorf("unknown load address")
   198  }
   199  
   200  func (f *goobjFile) dwarf() (*dwarf.Data, error) {
   201  	return nil, errors.New("no DWARF data in go object file")
   202  }