github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/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  		}
    57  		if s.Version != 0 {
    58  			sym.Code += 'a' - 'A'
    59  		}
    60  		for i, r := range s.Reloc {
    61  			sym.Relocs = append(sym.Relocs, Reloc{Addr: uint64(s.Data.Offset) + uint64(r.Offset), Size: uint64(r.Size), Stringer: &s.Reloc[i]})
    62  		}
    63  		syms = append(syms, sym)
    64  	}
    65  
    66  	for _, s := range f.goobj.Syms {
    67  		for _, r := range s.Reloc {
    68  			if !seen[r.Sym] {
    69  				seen[r.Sym] = true
    70  				sym := Sym{Name: goobjName(r.Sym), Code: 'U'}
    71  				if s.Version != 0 {
    72  					// should not happen but handle anyway
    73  					sym.Code = 'u'
    74  				}
    75  				syms = append(syms, sym)
    76  			}
    77  		}
    78  	}
    79  
    80  	return syms, nil
    81  }
    82  
    83  func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
    84  	// Should never be called.  We implement Liner below, callers
    85  	// should use that instead.
    86  	return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
    87  }
    88  
    89  // Find returns the file name, line, and function data for the given pc.
    90  // Returns "",0,nil if unknown.
    91  // This function implements the Liner interface in preference to pcln() above.
    92  func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
    93  	// TODO: this is really inefficient.  Binary search?  Memoize last result?
    94  	var arch *sys.Arch
    95  	for _, a := range sys.Archs {
    96  		if a.Name == f.goobj.Arch {
    97  			arch = a
    98  			break
    99  		}
   100  	}
   101  	if arch == nil {
   102  		return "", 0, nil
   103  	}
   104  	for _, s := range f.goobj.Syms {
   105  		if pc < uint64(s.Data.Offset) || pc >= uint64(s.Data.Offset+s.Data.Size) {
   106  			continue
   107  		}
   108  		if s.Func == nil {
   109  			return "", 0, nil
   110  		}
   111  		pcfile := make([]byte, s.Func.PCFile.Size)
   112  		_, err := f.f.ReadAt(pcfile, s.Func.PCFile.Offset)
   113  		if err != nil {
   114  			return "", 0, nil
   115  		}
   116  		fileID := int(pcValue(pcfile, pc-uint64(s.Data.Offset), arch))
   117  		fileName := s.Func.File[fileID]
   118  		pcline := make([]byte, s.Func.PCLine.Size)
   119  		_, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset)
   120  		if err != nil {
   121  			return "", 0, nil
   122  		}
   123  		line := int(pcValue(pcline, pc-uint64(s.Data.Offset), arch))
   124  		// Note: we provide only the name in the Func structure.
   125  		// We could provide more if needed.
   126  		return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: s.Name}}
   127  	}
   128  	return "", 0, nil
   129  }
   130  
   131  // pcValue looks up the given PC in a pc value table. target is the
   132  // offset of the pc from the entry point.
   133  func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 {
   134  	val := int32(-1)
   135  	var pc uint64
   136  	for step(&tab, &pc, &val, pc == 0, arch) {
   137  		if target < pc {
   138  			return val
   139  		}
   140  	}
   141  	return -1
   142  }
   143  
   144  // step advances to the next pc, value pair in the encoded table.
   145  func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool {
   146  	uvdelta := readvarint(p)
   147  	if uvdelta == 0 && !first {
   148  		return false
   149  	}
   150  	if uvdelta&1 != 0 {
   151  		uvdelta = ^(uvdelta >> 1)
   152  	} else {
   153  		uvdelta >>= 1
   154  	}
   155  	vdelta := int32(uvdelta)
   156  	pcdelta := readvarint(p) * uint32(arch.MinLC)
   157  	*pc += uint64(pcdelta)
   158  	*val += vdelta
   159  	return true
   160  }
   161  
   162  // readvarint reads, removes, and returns a varint from *p.
   163  func readvarint(p *[]byte) uint32 {
   164  	var v, shift uint32
   165  	s := *p
   166  	for shift = 0; ; shift += 7 {
   167  		b := s[0]
   168  		s = s[1:]
   169  		v |= (uint32(b) & 0x7F) << shift
   170  		if b&0x80 == 0 {
   171  			break
   172  		}
   173  	}
   174  	*p = s
   175  	return v
   176  }
   177  
   178  // We treat the whole object file as the text section.
   179  func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
   180  	var info os.FileInfo
   181  	info, err = f.f.Stat()
   182  	if err != nil {
   183  		return
   184  	}
   185  	text = make([]byte, info.Size())
   186  	_, err = f.f.ReadAt(text, 0)
   187  	return
   188  }
   189  
   190  func (f *goobjFile) goarch() string {
   191  	return f.goobj.Arch
   192  }
   193  
   194  func (f *goobjFile) loadAddress() (uint64, error) {
   195  	return 0, fmt.Errorf("unknown load address")
   196  }
   197  
   198  func (f *goobjFile) dwarf() (*dwarf.Data, error) {
   199  	return nil, errors.New("no DWARF data in go object file")
   200  }