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