Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/internal/objfile/goobj.go

Documentation: cmd/internal/objfile

     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/archive"
    11  	"cmd/internal/goobj"
    12  	"cmd/internal/objabi"
    13  	"cmd/internal/sys"
    14  	"debug/dwarf"
    15  	"debug/gosym"
    16  	"errors"
    17  	"fmt"
    18  	"io"
    19  	"os"
    20  )
    21  
    22  type goobjFile struct {
    23  	goobj *archive.GoObj
    24  	r     *goobj.Reader
    25  	f     *os.File
    26  	arch  *sys.Arch
    27  }
    28  
    29  func openGoFile(f *os.File) (*File, error) {
    30  	a, err := archive.Parse(f, false)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  	entries := make([]*Entry, 0, len(a.Entries))
    35  L:
    36  	for _, e := range a.Entries {
    37  		switch e.Type {
    38  		case archive.EntryPkgDef:
    39  			continue
    40  		case archive.EntryGoObj:
    41  			o := e.Obj
    42  			b := make([]byte, o.Size)
    43  			_, err := f.ReadAt(b, o.Offset)
    44  			if err != nil {
    45  				return nil, err
    46  			}
    47  			r := goobj.NewReaderFromBytes(b, false)
    48  			var arch *sys.Arch
    49  			for _, a := range sys.Archs {
    50  				if a.Name == e.Obj.Arch {
    51  					arch = a
    52  					break
    53  				}
    54  			}
    55  			entries = append(entries, &Entry{
    56  				name: e.Name,
    57  				raw:  &goobjFile{e.Obj, r, f, arch},
    58  			})
    59  			continue
    60  		case archive.EntryNativeObj:
    61  			nr := io.NewSectionReader(f, e.Offset, e.Size)
    62  			for _, try := range openers {
    63  				if raw, err := try(nr); err == nil {
    64  					entries = append(entries, &Entry{
    65  						name: e.Name,
    66  						raw:  raw,
    67  					})
    68  					continue L
    69  				}
    70  			}
    71  		}
    72  		return nil, fmt.Errorf("open %s: unrecognized archive member %s", f.Name(), e.Name)
    73  	}
    74  	return &File{f, entries}, nil
    75  }
    76  
    77  func goobjName(name string, ver int) string {
    78  	if ver == 0 {
    79  		return name
    80  	}
    81  	return fmt.Sprintf("%s<%d>", name, ver)
    82  }
    83  
    84  type goobjReloc struct {
    85  	Off  int32
    86  	Size uint8
    87  	Type objabi.RelocType
    88  	Add  int64
    89  	Sym  string
    90  }
    91  
    92  func (r goobjReloc) String(insnOffset uint64) string {
    93  	delta := int64(r.Off) - int64(insnOffset)
    94  	s := fmt.Sprintf("[%d:%d]%s", delta, delta+int64(r.Size), r.Type)
    95  	if r.Sym != "" {
    96  		if r.Add != 0 {
    97  			return fmt.Sprintf("%s:%s+%d", s, r.Sym, r.Add)
    98  		}
    99  		return fmt.Sprintf("%s:%s", s, r.Sym)
   100  	}
   101  	if r.Add != 0 {
   102  		return fmt.Sprintf("%s:%d", s, r.Add)
   103  	}
   104  	return s
   105  }
   106  
   107  func (f *goobjFile) symbols() ([]Sym, error) {
   108  	r := f.r
   109  	var syms []Sym
   110  
   111  	// Name of referenced indexed symbols.
   112  	nrefName := r.NRefName()
   113  	refNames := make(map[goobj.SymRef]string, nrefName)
   114  	for i := 0; i < nrefName; i++ {
   115  		rn := r.RefName(i)
   116  		refNames[rn.Sym()] = rn.Name(r)
   117  	}
   118  
   119  	abiToVer := func(abi uint16) int {
   120  		var ver int
   121  		if abi == goobj.SymABIstatic {
   122  			// Static symbol
   123  			ver = 1
   124  		}
   125  		return ver
   126  	}
   127  
   128  	resolveSymRef := func(s goobj.SymRef) string {
   129  		var i uint32
   130  		switch p := s.PkgIdx; p {
   131  		case goobj.PkgIdxInvalid:
   132  			if s.SymIdx != 0 {
   133  				panic("bad sym ref")
   134  			}
   135  			return ""
   136  		case goobj.PkgIdxHashed64:
   137  			i = s.SymIdx + uint32(r.NSym())
   138  		case goobj.PkgIdxHashed:
   139  			i = s.SymIdx + uint32(r.NSym()+r.NHashed64def())
   140  		case goobj.PkgIdxNone:
   141  			i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef())
   142  		case goobj.PkgIdxBuiltin:
   143  			name, abi := goobj.BuiltinName(int(s.SymIdx))
   144  			return goobjName(name, abi)
   145  		case goobj.PkgIdxSelf:
   146  			i = s.SymIdx
   147  		default:
   148  			return refNames[s]
   149  		}
   150  		sym := r.Sym(i)
   151  		return goobjName(sym.Name(r), abiToVer(sym.ABI()))
   152  	}
   153  
   154  	// Defined symbols
   155  	ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef())
   156  	for i := uint32(0); i < ndef; i++ {
   157  		osym := r.Sym(i)
   158  		if osym.Name(r) == "" {
   159  			continue // not a real symbol
   160  		}
   161  		name := osym.Name(r)
   162  		ver := osym.ABI()
   163  		name = goobjName(name, abiToVer(ver))
   164  		typ := objabi.SymKind(osym.Type())
   165  		var code rune = '?'
   166  		switch typ {
   167  		case objabi.STEXT:
   168  			code = 'T'
   169  		case objabi.SRODATA:
   170  			code = 'R'
   171  		case objabi.SNOPTRDATA, objabi.SDATA:
   172  			code = 'D'
   173  		case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
   174  			code = 'B'
   175  		}
   176  		if ver >= goobj.SymABIstatic {
   177  			code += 'a' - 'A'
   178  		}
   179  
   180  		sym := Sym{
   181  			Name: name,
   182  			Addr: uint64(r.DataOff(i)),
   183  			Size: int64(osym.Siz()),
   184  			Code: code,
   185  		}
   186  
   187  		relocs := r.Relocs(i)
   188  		sym.Relocs = make([]Reloc, len(relocs))
   189  		for j := range relocs {
   190  			rel := &relocs[j]
   191  			sym.Relocs[j] = Reloc{
   192  				Addr: uint64(r.DataOff(i)) + uint64(rel.Off()),
   193  				Size: uint64(rel.Siz()),
   194  				Stringer: goobjReloc{
   195  					Off:  rel.Off(),
   196  					Size: rel.Siz(),
   197  					Type: objabi.RelocType(rel.Type()),
   198  					Add:  rel.Add(),
   199  					Sym:  resolveSymRef(rel.Sym()),
   200  				},
   201  			}
   202  		}
   203  
   204  		syms = append(syms, sym)
   205  	}
   206  
   207  	// Referenced symbols
   208  	n := ndef + uint32(r.NNonpkgref())
   209  	for i := ndef; i < n; i++ {
   210  		osym := r.Sym(i)
   211  		sym := Sym{Name: osym.Name(r), Code: 'U'}
   212  		syms = append(syms, sym)
   213  	}
   214  	for i := 0; i < nrefName; i++ {
   215  		rn := r.RefName(i)
   216  		sym := Sym{Name: rn.Name(r), Code: 'U'}
   217  		syms = append(syms, sym)
   218  	}
   219  
   220  	return syms, nil
   221  }
   222  
   223  func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
   224  	// Should never be called. We implement Liner below, callers
   225  	// should use that instead.
   226  	return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
   227  }
   228  
   229  // Find returns the file name, line, and function data for the given pc.
   230  // Returns "",0,nil if unknown.
   231  // This function implements the Liner interface in preference to pcln() above.
   232  func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
   233  	r := f.r
   234  	if f.arch == nil {
   235  		return "", 0, nil
   236  	}
   237  	getSymData := func(s goobj.SymRef) []byte {
   238  		if s.PkgIdx != goobj.PkgIdxHashed {
   239  			// We don't need the data for non-hashed symbols, yet.
   240  			panic("not supported")
   241  		}
   242  		i := uint32(s.SymIdx + uint32(r.NSym()+r.NHashed64def()))
   243  		return r.BytesAt(r.DataOff(i), r.DataSize(i))
   244  	}
   245  
   246  	ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef())
   247  	for i := uint32(0); i < ndef; i++ {
   248  		osym := r.Sym(i)
   249  		addr := uint64(r.DataOff(i))
   250  		if pc < addr || pc >= addr+uint64(osym.Siz()) {
   251  			continue
   252  		}
   253  		isym := ^uint32(0)
   254  		auxs := r.Auxs(i)
   255  		for j := range auxs {
   256  			a := &auxs[j]
   257  			if a.Type() != goobj.AuxFuncInfo {
   258  				continue
   259  			}
   260  			if a.Sym().PkgIdx != goobj.PkgIdxSelf {
   261  				panic("funcinfo symbol not defined in current package")
   262  			}
   263  			isym = a.Sym().SymIdx
   264  		}
   265  		if isym == ^uint32(0) {
   266  			continue
   267  		}
   268  		b := r.BytesAt(r.DataOff(isym), r.DataSize(isym))
   269  		var info *goobj.FuncInfo
   270  		pcline := getSymData(info.ReadPcline(b))
   271  		line := int(pcValue(pcline, pc-addr, f.arch))
   272  		pcfile := getSymData(info.ReadPcfile(b))
   273  		fileID := pcValue(pcfile, pc-addr, f.arch)
   274  		fileName := r.File(int(fileID))
   275  		// Note: we provide only the name in the Func structure.
   276  		// We could provide more if needed.
   277  		return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: osym.Name(r)}}
   278  	}
   279  	return "", 0, nil
   280  }
   281  
   282  // pcValue looks up the given PC in a pc value table. target is the
   283  // offset of the pc from the entry point.
   284  func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 {
   285  	val := int32(-1)
   286  	var pc uint64
   287  	for step(&tab, &pc, &val, pc == 0, arch) {
   288  		if target < pc {
   289  			return val
   290  		}
   291  	}
   292  	return -1
   293  }
   294  
   295  // step advances to the next pc, value pair in the encoded table.
   296  func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool {
   297  	uvdelta := readvarint(p)
   298  	if uvdelta == 0 && !first {
   299  		return false
   300  	}
   301  	if uvdelta&1 != 0 {
   302  		uvdelta = ^(uvdelta >> 1)
   303  	} else {
   304  		uvdelta >>= 1
   305  	}
   306  	vdelta := int32(uvdelta)
   307  	pcdelta := readvarint(p) * uint32(arch.MinLC)
   308  	*pc += uint64(pcdelta)
   309  	*val += vdelta
   310  	return true
   311  }
   312  
   313  // readvarint reads, removes, and returns a varint from *p.
   314  func readvarint(p *[]byte) uint32 {
   315  	var v, shift uint32
   316  	s := *p
   317  	for shift = 0; ; shift += 7 {
   318  		b := s[0]
   319  		s = s[1:]
   320  		v |= (uint32(b) & 0x7F) << shift
   321  		if b&0x80 == 0 {
   322  			break
   323  		}
   324  	}
   325  	*p = s
   326  	return v
   327  }
   328  
   329  // We treat the whole object file as the text section.
   330  func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
   331  	text = make([]byte, f.goobj.Size)
   332  	_, err = f.f.ReadAt(text, int64(f.goobj.Offset))
   333  	return
   334  }
   335  
   336  func (f *goobjFile) goarch() string {
   337  	return f.goobj.Arch
   338  }
   339  
   340  func (f *goobjFile) loadAddress() (uint64, error) {
   341  	return 0, fmt.Errorf("unknown load address")
   342  }
   343  
   344  func (f *goobjFile) dwarf() (*dwarf.Data, error) {
   345  	return nil, errors.New("no DWARF data in go object file")
   346  }
   347  

View as plain text