...

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

     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 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{&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	}
   222	

View as plain text