| | |
| | |
| | |
| |
|
| | package pkgbits |
| |
|
| | import ( |
| | "encoding/binary" |
| | "errors" |
| | "fmt" |
| | "go/constant" |
| | "go/token" |
| | "io" |
| | "math/big" |
| | "os" |
| | "runtime" |
| | "strings" |
| | ) |
| |
|
| | |
| | |
| | type PkgDecoder struct { |
| | |
| | version Version |
| |
|
| | |
| | sync bool |
| |
|
| | |
| | |
| | |
| | pkgPath string |
| |
|
| | |
| | |
| | |
| | |
| | elemData string |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | elemEnds []uint32 |
| |
|
| | |
| | |
| | |
| | |
| | |
| | elemEndsEnds [numRelocs]uint32 |
| |
|
| | scratchRelocEnt []RefTableEntry |
| | } |
| |
|
| | |
| | |
| | |
| | func (pr *PkgDecoder) PkgPath() string { return pr.pkgPath } |
| |
|
| | |
| | func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync } |
| |
|
| | |
| | |
| | |
| | func NewPkgDecoder(pkgPath, input string) PkgDecoder { |
| | pr := PkgDecoder{ |
| | pkgPath: pkgPath, |
| | } |
| |
|
| | |
| | |
| |
|
| | r := strings.NewReader(input) |
| |
|
| | var ver uint32 |
| | assert(binary.Read(r, binary.LittleEndian, &ver) == nil) |
| | pr.version = Version(ver) |
| |
|
| | if pr.version >= numVersions { |
| | panic(fmt.Errorf("cannot decode %q, export data version %d is greater than maximum supported version %d", pkgPath, pr.version, numVersions-1)) |
| | } |
| |
|
| | if pr.version.Has(Flags) { |
| | var flags uint32 |
| | assert(binary.Read(r, binary.LittleEndian, &flags) == nil) |
| | pr.sync = flags&flagSyncMarkers != 0 |
| | } |
| |
|
| | assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil) |
| |
|
| | pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1]) |
| | assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil) |
| |
|
| | pos, err := r.Seek(0, io.SeekCurrent) |
| | assert(err == nil) |
| |
|
| | pr.elemData = input[pos:] |
| |
|
| | const fingerprintSize = 8 |
| | assert(len(pr.elemData)-fingerprintSize == int(pr.elemEnds[len(pr.elemEnds)-1])) |
| |
|
| | return pr |
| | } |
| |
|
| | |
| | func (pr *PkgDecoder) NumElems(k SectionKind) int { |
| | count := int(pr.elemEndsEnds[k]) |
| | if k > 0 { |
| | count -= int(pr.elemEndsEnds[k-1]) |
| | } |
| | return count |
| | } |
| |
|
| | |
| | func (pr *PkgDecoder) TotalElems() int { |
| | return len(pr.elemEnds) |
| | } |
| |
|
| | |
| | func (pr *PkgDecoder) Fingerprint() [8]byte { |
| | var fp [8]byte |
| | copy(fp[:], pr.elemData[len(pr.elemData)-8:]) |
| | return fp |
| | } |
| |
|
| | |
| | |
| | func (pr *PkgDecoder) AbsIdx(k SectionKind, idx RelElemIdx) int { |
| | absIdx := int(idx) |
| | if k > 0 { |
| | absIdx += int(pr.elemEndsEnds[k-1]) |
| | } |
| | if absIdx >= int(pr.elemEndsEnds[k]) { |
| | panicf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds) |
| | } |
| | return absIdx |
| | } |
| |
|
| | |
| | |
| | func (pr *PkgDecoder) DataIdx(k SectionKind, idx RelElemIdx) string { |
| | absIdx := pr.AbsIdx(k, idx) |
| |
|
| | var start uint32 |
| | if absIdx > 0 { |
| | start = pr.elemEnds[absIdx-1] |
| | } |
| | end := pr.elemEnds[absIdx] |
| |
|
| | return pr.elemData[start:end] |
| | } |
| |
|
| | |
| | func (pr *PkgDecoder) StringIdx(idx RelElemIdx) string { |
| | return pr.DataIdx(SectionString, idx) |
| | } |
| |
|
| | |
| | |
| | func (pr *PkgDecoder) NewDecoder(k SectionKind, idx RelElemIdx, marker SyncMarker) Decoder { |
| | r := pr.NewDecoderRaw(k, idx) |
| | r.Sync(marker) |
| | return r |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func (pr *PkgDecoder) TempDecoder(k SectionKind, idx RelElemIdx, marker SyncMarker) Decoder { |
| | r := pr.TempDecoderRaw(k, idx) |
| | r.Sync(marker) |
| | return r |
| | } |
| |
|
| | func (pr *PkgDecoder) RetireDecoder(d *Decoder) { |
| | pr.scratchRelocEnt = d.Relocs |
| | d.Relocs = nil |
| | } |
| |
|
| | |
| | |
| | |
| | func (pr *PkgDecoder) NewDecoderRaw(k SectionKind, idx RelElemIdx) Decoder { |
| | r := Decoder{ |
| | common: pr, |
| | k: k, |
| | Idx: idx, |
| | } |
| |
|
| | r.Data.Reset(pr.DataIdx(k, idx)) |
| | r.Sync(SyncRelocs) |
| | r.Relocs = make([]RefTableEntry, r.Len()) |
| | for i := range r.Relocs { |
| | r.Sync(SyncReloc) |
| | r.Relocs[i] = RefTableEntry{SectionKind(r.Len()), RelElemIdx(r.Len())} |
| | } |
| |
|
| | return r |
| | } |
| |
|
| | func (pr *PkgDecoder) TempDecoderRaw(k SectionKind, idx RelElemIdx) Decoder { |
| | r := Decoder{ |
| | common: pr, |
| | k: k, |
| | Idx: idx, |
| | } |
| |
|
| | r.Data.Reset(pr.DataIdx(k, idx)) |
| | r.Sync(SyncRelocs) |
| | l := r.Len() |
| | if cap(pr.scratchRelocEnt) >= l { |
| | r.Relocs = pr.scratchRelocEnt[:l] |
| | pr.scratchRelocEnt = nil |
| | } else { |
| | r.Relocs = make([]RefTableEntry, l) |
| | } |
| | for i := range r.Relocs { |
| | r.Sync(SyncReloc) |
| | r.Relocs[i] = RefTableEntry{SectionKind(r.Len()), RelElemIdx(r.Len())} |
| | } |
| |
|
| | return r |
| | } |
| |
|
| | |
| | |
| | type Decoder struct { |
| | common *PkgDecoder |
| |
|
| | Relocs []RefTableEntry |
| | Data strings.Reader |
| |
|
| | k SectionKind |
| | Idx RelElemIdx |
| | } |
| |
|
| | func (r *Decoder) checkErr(err error) { |
| | if err != nil { |
| | panicf("unexpected decoding error: %w", err) |
| | } |
| | } |
| |
|
| | func (r *Decoder) rawUvarint() uint64 { |
| | x, err := readUvarint(&r.Data) |
| | r.checkErr(err) |
| | return x |
| | } |
| |
|
| | |
| | |
| | |
| | func readUvarint(r *strings.Reader) (uint64, error) { |
| | var x uint64 |
| | var s uint |
| | for i := 0; i < binary.MaxVarintLen64; i++ { |
| | b, err := r.ReadByte() |
| | if err != nil { |
| | if i > 0 && err == io.EOF { |
| | err = io.ErrUnexpectedEOF |
| | } |
| | return x, err |
| | } |
| | if b < 0x80 { |
| | if i == binary.MaxVarintLen64-1 && b > 1 { |
| | return x, overflow |
| | } |
| | return x | uint64(b)<<s, nil |
| | } |
| | x |= uint64(b&0x7f) << s |
| | s += 7 |
| | } |
| | return x, overflow |
| | } |
| |
|
| | var overflow = errors.New("pkgbits: readUvarint overflows a 64-bit integer") |
| |
|
| | func (r *Decoder) rawVarint() int64 { |
| | ux := r.rawUvarint() |
| |
|
| | |
| | x := int64(ux >> 1) |
| | if ux&1 != 0 { |
| | x = ^x |
| | } |
| | return x |
| | } |
| |
|
| | func (r *Decoder) rawReloc(k SectionKind, idx int) RelElemIdx { |
| | e := r.Relocs[idx] |
| | assert(e.Kind == k) |
| | return e.Idx |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | func (r *Decoder) Sync(mWant SyncMarker) { |
| | if !r.common.sync { |
| | return |
| | } |
| |
|
| | pos, _ := r.Data.Seek(0, io.SeekCurrent) |
| | mHave := SyncMarker(r.rawUvarint()) |
| | writerPCs := make([]int, r.rawUvarint()) |
| | for i := range writerPCs { |
| | writerPCs[i] = int(r.rawUvarint()) |
| | } |
| |
|
| | if mHave == mWant { |
| | return |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.Idx, pos) |
| |
|
| | fmt.Printf("\nfound %v, written at:\n", mHave) |
| | if len(writerPCs) == 0 { |
| | fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath) |
| | } |
| | for _, pc := range writerPCs { |
| | fmt.Printf("\t%s\n", r.common.StringIdx(r.rawReloc(SectionString, pc))) |
| | } |
| |
|
| | fmt.Printf("\nexpected %v, reading at:\n", mWant) |
| | var readerPCs [32]uintptr |
| | n := runtime.Callers(2, readerPCs[:]) |
| | for _, pc := range fmtFrames(readerPCs[:n]...) { |
| | fmt.Printf("\t%s\n", pc) |
| | } |
| |
|
| | |
| | |
| | |
| | os.Exit(1) |
| | } |
| |
|
| | |
| | func (r *Decoder) Bool() bool { |
| | r.Sync(SyncBool) |
| | x, err := r.Data.ReadByte() |
| | r.checkErr(err) |
| | assert(x < 2) |
| | return x != 0 |
| | } |
| |
|
| | |
| | func (r *Decoder) Int64() int64 { |
| | r.Sync(SyncInt64) |
| | return r.rawVarint() |
| | } |
| |
|
| | |
| | func (r *Decoder) Uint64() uint64 { |
| | r.Sync(SyncUint64) |
| | return r.rawUvarint() |
| | } |
| |
|
| | |
| | func (r *Decoder) Len() int { x := r.Uint64(); v := int(x); assert(uint64(v) == x); return v } |
| |
|
| | |
| | func (r *Decoder) Int() int { x := r.Int64(); v := int(x); assert(int64(v) == x); return v } |
| |
|
| | |
| | func (r *Decoder) Uint() uint { x := r.Uint64(); v := uint(x); assert(uint64(v) == x); return v } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func (r *Decoder) Code(mark SyncMarker) int { |
| | r.Sync(mark) |
| | return r.Len() |
| | } |
| |
|
| | |
| | |
| | func (r *Decoder) Reloc(k SectionKind) RelElemIdx { |
| | r.Sync(SyncUseReloc) |
| | return r.rawReloc(k, r.Len()) |
| | } |
| |
|
| | |
| | |
| | func (r *Decoder) String() string { |
| | r.Sync(SyncString) |
| | return r.common.StringIdx(r.Reloc(SectionString)) |
| | } |
| |
|
| | |
| | |
| | func (r *Decoder) Strings() []string { |
| | res := make([]string, r.Len()) |
| | for i := range res { |
| | res[i] = r.String() |
| | } |
| | return res |
| | } |
| |
|
| | |
| | |
| | func (r *Decoder) Value() constant.Value { |
| | r.Sync(SyncValue) |
| | isComplex := r.Bool() |
| | val := r.scalar() |
| | if isComplex { |
| | val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar())) |
| | } |
| | return val |
| | } |
| |
|
| | func (r *Decoder) scalar() constant.Value { |
| | switch tag := CodeVal(r.Code(SyncVal)); tag { |
| | default: |
| | panic(fmt.Errorf("unexpected scalar tag: %v", tag)) |
| |
|
| | case ValBool: |
| | return constant.MakeBool(r.Bool()) |
| | case ValString: |
| | return constant.MakeString(r.String()) |
| | case ValInt64: |
| | return constant.MakeInt64(r.Int64()) |
| | case ValBigInt: |
| | return constant.Make(r.bigInt()) |
| | case ValBigRat: |
| | num := r.bigInt() |
| | denom := r.bigInt() |
| | return constant.Make(new(big.Rat).SetFrac(num, denom)) |
| | case ValBigFloat: |
| | return constant.Make(r.bigFloat()) |
| | } |
| | } |
| |
|
| | func (r *Decoder) bigInt() *big.Int { |
| | v := new(big.Int).SetBytes([]byte(r.String())) |
| | if r.Bool() { |
| | v.Neg(v) |
| | } |
| | return v |
| | } |
| |
|
| | func (r *Decoder) bigFloat() *big.Float { |
| | v := new(big.Float).SetPrec(512) |
| | assert(v.UnmarshalText([]byte(r.String())) == nil) |
| | return v |
| | } |
| |
|
| | |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | func (pr *PkgDecoder) PeekPkgPath(idx RelElemIdx) string { |
| | var path string |
| | { |
| | r := pr.TempDecoder(SectionPkg, idx, SyncPkgDef) |
| | path = r.String() |
| | pr.RetireDecoder(&r) |
| | } |
| | if path == "" { |
| | path = pr.pkgPath |
| | } |
| | return path |
| | } |
| |
|
| | |
| | |
| | func (pr *PkgDecoder) PeekObj(idx RelElemIdx) (string, string, CodeObj) { |
| | var ridx RelElemIdx |
| | var name string |
| | var rcode int |
| | { |
| | r := pr.TempDecoder(SectionName, idx, SyncObject1) |
| | r.Sync(SyncSym) |
| | r.Sync(SyncPkg) |
| | ridx = r.Reloc(SectionPkg) |
| | name = r.String() |
| | rcode = r.Code(SyncCodeObj) |
| | pr.RetireDecoder(&r) |
| | } |
| |
|
| | path := pr.PeekPkgPath(ridx) |
| | assert(name != "") |
| |
|
| | tag := CodeObj(rcode) |
| |
|
| | return path, name, tag |
| | } |
| |
|
| | |
| | func (w *Decoder) Version() Version { return w.common.version } |
| |
|