| | |
| | |
| | |
| |
|
| | |
| |
|
| | package objfile |
| |
|
| | import ( |
| | "cmd/internal/archive" |
| | "cmd/internal/goobj" |
| | "cmd/internal/objabi" |
| | "cmd/internal/sys" |
| | "debug/dwarf" |
| | "debug/gosym" |
| | "errors" |
| | "fmt" |
| | "io" |
| | "os" |
| | ) |
| |
|
| | type goobjFile struct { |
| | goobj *archive.GoObj |
| | r *goobj.Reader |
| | f *os.File |
| | arch *sys.Arch |
| | } |
| |
|
| | func openGoFile(f *os.File) (*File, error) { |
| | a, err := archive.Parse(f, false) |
| | if err != nil { |
| | return nil, err |
| | } |
| | entries := make([]*Entry, 0, len(a.Entries)) |
| | L: |
| | for _, e := range a.Entries { |
| | switch e.Type { |
| | case archive.EntryPkgDef, archive.EntrySentinelNonObj: |
| | continue |
| | case archive.EntryGoObj: |
| | o := e.Obj |
| | b := make([]byte, o.Size) |
| | _, err := f.ReadAt(b, o.Offset) |
| | if err != nil { |
| | return nil, err |
| | } |
| | r := goobj.NewReaderFromBytes(b, false) |
| | var arch *sys.Arch |
| | for _, a := range sys.Archs { |
| | if a.Name == e.Obj.Arch { |
| | arch = a |
| | break |
| | } |
| | } |
| | entries = append(entries, &Entry{ |
| | name: e.Name, |
| | raw: &goobjFile{e.Obj, r, f, arch}, |
| | }) |
| | continue |
| | case archive.EntryNativeObj: |
| | nr := io.NewSectionReader(f, e.Offset, e.Size) |
| | for _, try := range openers { |
| | if raw, err := try(nr); err == nil { |
| | entries = append(entries, &Entry{ |
| | name: e.Name, |
| | raw: raw, |
| | }) |
| | continue L |
| | } |
| | } |
| | } |
| | return nil, fmt.Errorf("open %s: unrecognized archive member %s", f.Name(), e.Name) |
| | } |
| | return &File{f, entries}, nil |
| | } |
| |
|
| | func goobjName(name string, ver int) string { |
| | if ver == 0 { |
| | return name |
| | } |
| | return fmt.Sprintf("%s<%d>", name, ver) |
| | } |
| |
|
| | type goobjReloc struct { |
| | Off int32 |
| | Size uint8 |
| | Type objabi.RelocType |
| | Add int64 |
| | Sym string |
| | } |
| |
|
| | func (r goobjReloc) String(insnOffset uint64) string { |
| | delta := int64(r.Off) - int64(insnOffset) |
| | s := fmt.Sprintf("[%d:%d]%s", delta, delta+int64(r.Size), r.Type) |
| | if r.Sym != "" { |
| | if r.Add != 0 { |
| | return fmt.Sprintf("%s:%s+%d", s, r.Sym, r.Add) |
| | } |
| | return fmt.Sprintf("%s:%s", s, r.Sym) |
| | } |
| | if r.Add != 0 { |
| | return fmt.Sprintf("%s:%d", s, r.Add) |
| | } |
| | return s |
| | } |
| |
|
| | func (f *goobjFile) symbols() ([]Sym, error) { |
| | r := f.r |
| | var syms []Sym |
| |
|
| | |
| | nrefName := r.NRefName() |
| | refNames := make(map[goobj.SymRef]string, nrefName) |
| | for i := 0; i < nrefName; i++ { |
| | rn := r.RefName(i) |
| | refNames[rn.Sym()] = rn.Name(r) |
| | } |
| |
|
| | abiToVer := func(abi uint16) int { |
| | var ver int |
| | if abi == goobj.SymABIstatic { |
| | |
| | ver = 1 |
| | } |
| | return ver |
| | } |
| |
|
| | resolveSymRef := func(s goobj.SymRef) string { |
| | var i uint32 |
| | switch p := s.PkgIdx; p { |
| | case goobj.PkgIdxInvalid: |
| | if s.SymIdx != 0 { |
| | panic("bad sym ref") |
| | } |
| | return "" |
| | case goobj.PkgIdxHashed64: |
| | i = s.SymIdx + uint32(r.NSym()) |
| | case goobj.PkgIdxHashed: |
| | i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()) |
| | case goobj.PkgIdxNone: |
| | i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef()) |
| | case goobj.PkgIdxBuiltin: |
| | name, abi := goobj.BuiltinName(int(s.SymIdx)) |
| | return goobjName(name, abi) |
| | case goobj.PkgIdxSelf: |
| | i = s.SymIdx |
| | default: |
| | return refNames[s] |
| | } |
| | sym := r.Sym(i) |
| | return goobjName(sym.Name(r), abiToVer(sym.ABI())) |
| | } |
| |
|
| | |
| | ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) |
| | for i := uint32(0); i < ndef; i++ { |
| | osym := r.Sym(i) |
| | if osym.Name(r) == "" { |
| | continue |
| | } |
| | name := osym.Name(r) |
| | ver := osym.ABI() |
| | name = goobjName(name, abiToVer(ver)) |
| | typ := objabi.SymKind(osym.Type()) |
| | var code rune = '?' |
| | switch typ { |
| | case objabi.STEXT, objabi.STEXTFIPS: |
| | code = 'T' |
| | case objabi.SRODATA, objabi.SRODATAFIPS: |
| | code = 'R' |
| | case objabi.SNOPTRDATA, objabi.SNOPTRDATAFIPS, |
| | objabi.SDATA, objabi.SDATAFIPS: |
| | code = 'D' |
| | case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS: |
| | code = 'B' |
| | } |
| | if ver >= goobj.SymABIstatic { |
| | code += 'a' - 'A' |
| | } |
| |
|
| | sym := Sym{ |
| | Name: name, |
| | Addr: uint64(r.DataOff(i)), |
| | Size: int64(osym.Siz()), |
| | Code: code, |
| | } |
| |
|
| | relocs := r.Relocs(i) |
| | sym.Relocs = make([]Reloc, len(relocs)) |
| | for j := range relocs { |
| | rel := &relocs[j] |
| | sym.Relocs[j] = Reloc{ |
| | Addr: uint64(r.DataOff(i)) + uint64(rel.Off()), |
| | Size: uint64(rel.Siz()), |
| | Stringer: goobjReloc{ |
| | Off: rel.Off(), |
| | Size: rel.Siz(), |
| | Type: objabi.RelocType(rel.Type()), |
| | Add: rel.Add(), |
| | Sym: resolveSymRef(rel.Sym()), |
| | }, |
| | } |
| | } |
| |
|
| | syms = append(syms, sym) |
| | } |
| |
|
| | |
| | n := ndef + uint32(r.NNonpkgref()) |
| | for i := ndef; i < n; i++ { |
| | osym := r.Sym(i) |
| | sym := Sym{Name: osym.Name(r), Code: 'U'} |
| | syms = append(syms, sym) |
| | } |
| | for i := 0; i < nrefName; i++ { |
| | rn := r.RefName(i) |
| | sym := Sym{Name: rn.Name(r), Code: 'U'} |
| | syms = append(syms, sym) |
| | } |
| |
|
| | return syms, nil |
| | } |
| |
|
| | func (f *goobjFile) pcln() (textStart uint64, pclntab []byte, err error) { |
| | |
| | |
| | return 0, nil, fmt.Errorf("pcln not available in go object file") |
| | } |
| |
|
| | |
| | |
| | |
| | func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { |
| | r := f.r |
| | if f.arch == nil { |
| | return "", 0, nil |
| | } |
| | getSymData := func(s goobj.SymRef) []byte { |
| | if s.PkgIdx != goobj.PkgIdxHashed { |
| | |
| | panic("not supported") |
| | } |
| | i := s.SymIdx + uint32(r.NSym()+r.NHashed64def()) |
| | return r.BytesAt(r.DataOff(i), r.DataSize(i)) |
| | } |
| |
|
| | ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) |
| | for i := uint32(0); i < ndef; i++ { |
| | osym := r.Sym(i) |
| | addr := uint64(r.DataOff(i)) |
| | if pc < addr || pc >= addr+uint64(osym.Siz()) { |
| | continue |
| | } |
| | var pcfileSym, pclineSym goobj.SymRef |
| | for _, a := range r.Auxs(i) { |
| | switch a.Type() { |
| | case goobj.AuxPcfile: |
| | pcfileSym = a.Sym() |
| | case goobj.AuxPcline: |
| | pclineSym = a.Sym() |
| | } |
| | } |
| | if pcfileSym.IsZero() || pclineSym.IsZero() { |
| | continue |
| | } |
| | pcline := getSymData(pclineSym) |
| | line := int(pcValue(pcline, pc-addr, f.arch)) |
| | pcfile := getSymData(pcfileSym) |
| | fileID := pcValue(pcfile, pc-addr, f.arch) |
| | fileName := r.File(int(fileID)) |
| | |
| | |
| | return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: osym.Name(r)}} |
| | } |
| | return "", 0, nil |
| | } |
| |
|
| | |
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | 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 |
| | } |
| |
|
| | |
| | func (f *goobjFile) text() (textStart uint64, text []byte, err error) { |
| | text = make([]byte, f.goobj.Size) |
| | _, err = f.f.ReadAt(text, f.goobj.Offset) |
| | 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") |
| | } |
| |
|