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