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