...
Run Format

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

Documentation: cmd/internal/objfile

  // Copyright 2013 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  // Parsing of Go intermediate object files and archives.
  
  package objfile
  
  import (
  	"cmd/internal/goobj"
  	"cmd/internal/objabi"
  	"cmd/internal/sys"
  	"debug/dwarf"
  	"debug/gosym"
  	"errors"
  	"fmt"
  	"os"
  )
  
  type goobjFile struct {
  	goobj *goobj.Package
  	f     *os.File // the underlying .o or .a file
  }
  
  func openGoobj(r *os.File) (rawFile, error) {
  	f, err := goobj.Parse(r, `""`)
  	if err != nil {
  		return nil, err
  	}
  	return &goobjFile{goobj: f, f: r}, nil
  }
  
  func goobjName(id goobj.SymID) string {
  	if id.Version == 0 {
  		return id.Name
  	}
  	return fmt.Sprintf("%s<%d>", id.Name, id.Version)
  }
  
  func (f *goobjFile) symbols() ([]Sym, error) {
  	seen := make(map[goobj.SymID]bool)
  
  	var syms []Sym
  	for _, s := range f.goobj.Syms {
  		seen[s.SymID] = true
  		sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'}
  		switch s.Kind {
  		case objabi.STEXT:
  			sym.Code = 'T'
  		case objabi.SRODATA:
  			sym.Code = 'R'
  		case objabi.SDATA:
  			sym.Code = 'D'
  		case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS:
  			sym.Code = 'B'
  		}
  		if s.Version != 0 {
  			sym.Code += 'a' - 'A'
  		}
  		for i, r := range s.Reloc {
  			sym.Relocs = append(sym.Relocs, Reloc{Addr: uint64(s.Data.Offset) + uint64(r.Offset), Size: uint64(r.Size), Stringer: &s.Reloc[i]})
  		}
  		syms = append(syms, sym)
  	}
  
  	for _, s := range f.goobj.Syms {
  		for _, r := range s.Reloc {
  			if !seen[r.Sym] {
  				seen[r.Sym] = true
  				sym := Sym{Name: goobjName(r.Sym), Code: 'U'}
  				if s.Version != 0 {
  					// should not happen but handle anyway
  					sym.Code = 'u'
  				}
  				syms = append(syms, sym)
  			}
  		}
  	}
  
  	return syms, nil
  }
  
  func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
  	// Should never be called.  We implement Liner below, callers
  	// should use that instead.
  	return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
  }
  
  // Find returns the file name, line, and function data for the given pc.
  // Returns "",0,nil if unknown.
  // This function implements the Liner interface in preference to pcln() above.
  func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
  	// TODO: this is really inefficient.  Binary search?  Memoize last result?
  	var arch *sys.Arch
  	for _, a := range sys.Archs {
  		if a.Name == f.goobj.Arch {
  			arch = a
  			break
  		}
  	}
  	if arch == nil {
  		return "", 0, nil
  	}
  	for _, s := range f.goobj.Syms {
  		if pc < uint64(s.Data.Offset) || pc >= uint64(s.Data.Offset+s.Data.Size) {
  			continue
  		}
  		if s.Func == nil {
  			return "", 0, nil
  		}
  		pcfile := make([]byte, s.Func.PCFile.Size)
  		_, err := f.f.ReadAt(pcfile, s.Func.PCFile.Offset)
  		if err != nil {
  			return "", 0, nil
  		}
  		fileID := int(pcValue(pcfile, pc-uint64(s.Data.Offset), arch))
  		fileName := s.Func.File[fileID]
  		pcline := make([]byte, s.Func.PCLine.Size)
  		_, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset)
  		if err != nil {
  			return "", 0, nil
  		}
  		line := int(pcValue(pcline, pc-uint64(s.Data.Offset), arch))
  		// Note: we provide only the name in the Func structure.
  		// We could provide more if needed.
  		return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: s.Name}}
  	}
  	return "", 0, nil
  }
  
  // pcValue looks up the given PC in a pc value table. target is the
  // offset of the pc from the entry point.
  func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 {
  	val := int32(-1)
  	var pc uint64
  	for step(&tab, &pc, &val, pc == 0, arch) {
  		if target < pc {
  			return val
  		}
  	}
  	return -1
  }
  
  // step advances to the next pc, value pair in the encoded table.
  func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool {
  	uvdelta := readvarint(p)
  	if uvdelta == 0 && !first {
  		return false
  	}
  	if uvdelta&1 != 0 {
  		uvdelta = ^(uvdelta >> 1)
  	} else {
  		uvdelta >>= 1
  	}
  	vdelta := int32(uvdelta)
  	pcdelta := readvarint(p) * uint32(arch.MinLC)
  	*pc += uint64(pcdelta)
  	*val += vdelta
  	return true
  }
  
  // readvarint reads, removes, and returns a varint from *p.
  func readvarint(p *[]byte) uint32 {
  	var v, shift uint32
  	s := *p
  	for shift = 0; ; shift += 7 {
  		b := s[0]
  		s = s[1:]
  		v |= (uint32(b) & 0x7F) << shift
  		if b&0x80 == 0 {
  			break
  		}
  	}
  	*p = s
  	return v
  }
  
  // We treat the whole object file as the text section.
  func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
  	var info os.FileInfo
  	info, err = f.f.Stat()
  	if err != nil {
  		return
  	}
  	text = make([]byte, info.Size())
  	_, err = f.f.ReadAt(text, 0)
  	return
  }
  
  func (f *goobjFile) goarch() string {
  	return f.goobj.Arch
  }
  
  func (f *goobjFile) loadAddress() (uint64, error) {
  	return 0, fmt.Errorf("unknown load address")
  }
  
  func (f *goobjFile) dwarf() (*dwarf.Data, error) {
  	return nil, errors.New("no DWARF data in go object file")
  }
  

View as plain text