From 70df8813f93d91529c95e3f4ba0c3317a2dbf97d Mon Sep 17 00:00:00 2001 From: pkujhd Date: Mon, 15 Jun 2020 17:20:46 +0800 Subject: [PATCH] fix #15, support inline function on traceback --- const.go | 2 ++ dymcode.1.12.go | 31 +++++++++++++++++- dymcode.1.14.go | 29 ++++++++++++++++- dymcode.1.8.go | 8 ++++- dymcode.1.9.go | 26 ++++++++++++++- dymcode.go | 31 ++++++++++-------- inlinetree.go | 59 +++++++++++++++++++++++++++++++++ module.1.8.go | 7 ++-- module.1.9.go | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ module.go | 2 ++ 10 files changed, 262 insertions(+), 20 deletions(-) create mode 100644 inlinetree.go create mode 100644 module.1.9.go diff --git a/const.go b/const.go index a4b7bce9..5115c44a 100644 --- a/const.go +++ b/const.go @@ -11,6 +11,7 @@ const ( _FuncSize = int(unsafe.Sizeof(_func{})) ItabSize = int(unsafe.Sizeof(itab{})) FindFuncBucketSize = int(unsafe.Sizeof(findfuncbucket{})) + InlinedCallSize = int(unsafe.Sizeof(inlinedCall{})) INVALID_HANDLE_VALUE = ^uintptr(0) INVALID_OFFSET = int(-1) ) @@ -49,4 +50,5 @@ const ( ITAB_PREFIX = "go.itab." RUNTIME_PREFIX = "runtime." STKOBJ_SUFFIX = ".stkobj" + INLINETREE_SUFFIX = ".inlinetree" ) diff --git a/dymcode.1.12.go b/dymcode.1.12.go index 10409f6c..670a05c2 100644 --- a/dymcode.1.12.go +++ b/dymcode.1.12.go @@ -3,6 +3,10 @@ package goloader +import ( + "cmd/objfile/goobj" +) + const ( R_PCREL = 15 // R_TLS_LE, used on 386, amd64, and ARM, resolves to the offset of the @@ -51,10 +55,35 @@ const ( // Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values. ) +// inlinedCall is the encoding of entries in the FUNCDATA_InlTree table. +type inlinedCall struct { + parent int16 // index of parent in the inltree, or < 0 + funcID funcID // type of the called function + _ byte + file int32 // fileno index into filetab + line int32 // line number of the call site + func_ int32 // offset into pclntab for name of called function + parentPc int32 // position of an instruction whose source position is the call site (offset from entry) +} + +func initInlinedCall(codereloc *CodeReloc, inl goobj.InlinedCall, _func *_func) inlinedCall { + return inlinedCall{ + parent: int16(inl.Parent), + funcID: _func.funcID, + file: int32(findFileTab(codereloc, inl.File)), + line: int32(inl.Line), + func_: int32(findFuncNameOff(codereloc, inl.Func.Name)), + parentPc: int32(inl.ParentPC)} +} + +func addInlineTree(codereloc *CodeReloc, _func *_func, funcdata *[]uintptr, pcdata *[]uint32, inlineOffset uint32) (err error) { + return _addInlineTree(codereloc, _func, funcdata, pcdata, inlineOffset) +} + func addStackObject(codereloc *CodeReloc, funcname string, symbolMap map[string]uintptr) (err error) { return _addStackObject(codereloc, funcname, symbolMap) } -func addDeferReturn(codereloc *CodeReloc, _func *_func, funcname string) (err error) { +func addDeferReturn(codereloc *CodeReloc, _func *_func) (err error) { return nil } diff --git a/dymcode.1.14.go b/dymcode.1.14.go index d5e42132..3124ee3f 100644 --- a/dymcode.1.14.go +++ b/dymcode.1.14.go @@ -4,6 +4,7 @@ package goloader import ( + "cmd/objfile/goobj" "errors" "fmt" ) @@ -59,11 +60,37 @@ const ( ) +// inlinedCall is the encoding of entries in the FUNCDATA_InlTree table. +type inlinedCall struct { + parent int16 // index of parent in the inltree, or < 0 + funcID funcID // type of the called function + _ byte + file int32 // fileno index into filetab + line int32 // line number of the call site + func_ int32 // offset into pclntab for name of called function + parentPc int32 // position of an instruction whose source position is the call site (offset from entry) +} + +func initInlinedCall(codereloc *CodeReloc, inl goobj.InlinedCall, _func *_func) inlinedCall { + return inlinedCall{ + parent: int16(inl.Parent), + funcID: _func.funcID, + file: int32(findFileTab(codereloc, inl.File)), + line: int32(inl.Line), + func_: int32(findFuncNameOff(codereloc, inl.Func.Name)), + parentPc: int32(inl.ParentPC)} +} + +func addInlineTree(codereloc *CodeReloc, _func *_func, funcdata *[]uintptr, pcdata *[]uint32, inlineOffset uint32) (err error) { + return _addInlineTree(codereloc, _func, funcdata, pcdata, inlineOffset) +} + func addStackObject(codereloc *CodeReloc, funcname string, symbolMap map[string]uintptr) (err error) { return _addStackObject(codereloc, funcname, symbolMap) } -func addDeferReturn(codereloc *CodeReloc, _func *_func, funcname string) (err error) { +func addDeferReturn(codereloc *CodeReloc, _func *_func) (err error) { + funcname := gostringnocopy(&codereloc.pclntable[_func.nameoff]) Func := codereloc.symMap[funcname].Func if Func != nil && len(Func.FuncData) > _FUNCDATA_OpenCodedDeferInfo { sym := codereloc.symMap[funcname] diff --git a/dymcode.1.8.go b/dymcode.1.8.go index 6b294106..3986b724 100644 --- a/dymcode.1.8.go +++ b/dymcode.1.8.go @@ -67,10 +67,16 @@ const ( SDWARFINFO ) +type inlinedCall struct{} + +func addInlineTree(codereloc *CodeReloc, _func *_func, funcdata *[]uintptr, pcdata *[]uint32, inlineOffset uint32) (err error) { + return nil +} + func addStackObject(codereloc *CodeReloc, funcname string, symbolMap map[string]uintptr) (err error) { return nil } -func addDeferReturn(codereloc *CodeReloc, _func *_func, funcname string) (err error) { +func addDeferReturn(codereloc *CodeReloc, _func *_func) (err error) { return nil } diff --git a/dymcode.1.9.go b/dymcode.1.9.go index 67aa5e7d..3204e293 100644 --- a/dymcode.1.9.go +++ b/dymcode.1.9.go @@ -3,6 +3,10 @@ package goloader +import ( + "cmd/objfile/goobj" +) + const ( R_PCREL = 15 // R_TLS_LE, used on 386, amd64, and ARM, resolves to the offset of the @@ -44,10 +48,30 @@ const ( // Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values. ) +// inlinedCall is the encoding of entries in the FUNCDATA_InlTree table. +type inlinedCall struct { + parent int32 // index of parent in the inltree, or < 0 + file int32 // fileno index into filetab + line int32 // line number of the call site + func_ int32 // offset into pclntab for name of called function +} + +func initInlinedCall(codereloc *CodeReloc, inl goobj.InlinedCall, _func *_func) inlinedCall { + return inlinedCall{ + parent: int32(inl.Parent), + file: int32(findFileTab(codereloc, inl.File)), + line: int32(inl.Line), + func_: int32(findFuncNameOff(codereloc, inl.Func.Name))} +} + +func addInlineTree(codereloc *CodeReloc, _func *_func, funcdata *[]uintptr, pcdata *[]uint32, inlineOffset uint32) (err error) { + return _addInlineTree(codereloc, _func, funcdata, pcdata, inlineOffset) +} + func addStackObject(codereloc *CodeReloc, funcname string, symbolMap map[string]uintptr) (err error) { return nil } -func addDeferReturn(codereloc *CodeReloc, _func *_func, funcname string) (err error) { +func addDeferReturn(codereloc *CodeReloc, _func *_func) (err error) { return nil } diff --git a/dymcode.go b/dymcode.go index 3c47f3fb..7178db52 100644 --- a/dymcode.go +++ b/dymcode.go @@ -435,36 +435,41 @@ func addFuncTab(module *moduledata, index int, pclnOff *int, codereloc *CodeRelo _func := codereloc._func[index] _func.entry = module.ftab[index].entry - data := make([]uintptr, len(Func.FuncData)) + funcdata := make([]uintptr, len(Func.FuncData)) for k, symbol := range Func.FuncData { if codereloc.stkmaps[symbol.Sym.Name] != nil { - data[k] = (uintptr)(unsafe.Pointer(&(codereloc.stkmaps[symbol.Sym.Name][0]))) + funcdata[k] = (uintptr)(unsafe.Pointer(&(codereloc.stkmaps[symbol.Sym.Name][0]))) } else { - data[k] = (uintptr)(0) + funcdata[k] = (uintptr)(0) } } - + pcdata := []uint32{} + pcln := uint32(_func.pcln) + uint32(Func.PCLine.Size) + for k := 0; k < len(Func.PCData); k++ { + pcdata = append(pcdata, pcln) + pcln += uint32(Func.PCData[k].Size) + } + if err = addInlineTree(codereloc, &_func, &funcdata, &pcdata, pcln); err != nil { + return err + } if err = addStackObject(codereloc, funcname, symbolMap); err != nil { return err } - if err = addDeferReturn(codereloc, &_func, funcname); err != nil { + if err = addDeferReturn(codereloc, &_func); err != nil { return err } copy2Slice(module.pclntable[offset:], uintptr(unsafe.Pointer(&_func)), _FuncSize) offset += _FuncSize - pcln := uint32(_func.pcln) + uint32(Func.PCLine.Size) - for k := 0; k < len(Func.PCData); k++ { - binary.LittleEndian.PutUint32(module.pclntable[offset:], pcln) - pcln += uint32(Func.PCData[k].Size) - offset += Uint32Size + if len(pcdata) > 0 { + copy2Slice(module.pclntable[offset:], uintptr(unsafe.Pointer(&(pcdata[0]))), Uint32Size*len(pcdata)) + offset += Uint32Size * len(pcdata) } offset = alignof(offset, PtrSize) - funcDataSize := int(PtrSize * _func.nfuncdata) - copy2Slice(module.pclntable[offset:], uintptr(unsafe.Pointer(&data[0])), funcDataSize) - offset += funcDataSize + copy2Slice(module.pclntable[offset:], uintptr(unsafe.Pointer(&funcdata[0])), int(PtrSize*_func.nfuncdata)) + offset += int(PtrSize * _func.nfuncdata) *pclnOff = offset return err diff --git a/inlinetree.go b/inlinetree.go new file mode 100644 index 00000000..8bf47ccb --- /dev/null +++ b/inlinetree.go @@ -0,0 +1,59 @@ +// +build go1.9 +// +build !go1.15 + +package goloader + +import ( + "cmd/objfile/goobj" + "strings" + "unsafe" +) + +func readPCInline(codeReloc *CodeReloc, symbol *goobj.Sym, fd *readAtSeeker) { + fd.ReadAtWithSize(&(codeReloc.pclntable), symbol.Func.PCInline.Size, symbol.Func.PCInline.Offset) +} + +func findFuncNameOff(codereloc *CodeReloc, funcname string) int32 { + for index, _ := range codereloc._func { + name := gostringnocopy(&codereloc.pclntable[codereloc._func[index].nameoff]) + if name == funcname { + return codereloc._func[index].nameoff + } + } + return -1 +} + +func findFileTab(codereloc *CodeReloc, filename string) int32 { + tab := codereloc.fileMap[strings.TrimLeft(filename, FILE_SYM_PREFIX)] + for index, value := range codereloc.filetab { + if uint32(tab) == value { + return int32(index) + } + } + return -1 +} + +func _addInlineTree(codereloc *CodeReloc, _func *_func, funcdata *[]uintptr, pcdata *[]uint32, inlineOffset uint32) (err error) { + funcname := gostringnocopy(&codereloc.pclntable[_func.nameoff]) + Func := codereloc.symMap[funcname].Func + if Func != nil && len(Func.InlTree) != 0 { + name := funcname + INLINETREE_SUFFIX + bytes := make([]byte, len(Func.InlTree)*InlinedCallSize) + for k, inl := range Func.InlTree { + inlinedcall := initInlinedCall(codereloc, inl, _func) + copy2Slice(bytes[k*InlinedCallSize:], uintptr(unsafe.Pointer(&inlinedcall)), InlinedCallSize) + } + codereloc.stkmaps[name] = bytes + for _func.nfuncdata <= _FUNCDATA_InlTree { + *funcdata = append(*funcdata, uintptr(0)) + _func.nfuncdata++ + } + (*funcdata)[_FUNCDATA_InlTree] = (uintptr)(unsafe.Pointer(&(codereloc.stkmaps[name][0]))) + for _func.npcdata <= _PCDATA_InlTreeIndex { + *pcdata = append(*pcdata, uint32(0)) + _func.npcdata++ + } + (*pcdata)[_PCDATA_InlTreeIndex] = inlineOffset + } + return err +} diff --git a/module.1.8.go b/module.1.8.go index 0d048a17..32ed1fef 100644 --- a/module.1.8.go +++ b/module.1.8.go @@ -1,5 +1,5 @@ // +build go1.8 -// +build !go1.10,!go1.11,!go1.12,!go1.13,!go1.14,!go1.15 +// +build !go1.9,!go1.10,!go1.11,!go1.12,!go1.13,!go1.14,!go1.15 package goloader @@ -12,10 +12,8 @@ import ( // See funcdata.h and ../cmd/internal/obj/funcdata.go. const ( _PCDATA_StackMapIndex = 0 - _PCDATA_InlTreeIndex = 1 _FUNCDATA_ArgsPointerMaps = 0 _FUNCDATA_LocalsPointerMaps = 1 - _FUNCDATA_InlTree = 2 _ArgsSizeUnknown = -0x80000000 ) @@ -85,3 +83,6 @@ func init_func(symbol *goobj.Sym, nameOff, spOff, pcfileOff, pclnOff int) _func } return fdata } + +func readPCInline(codeReloc *CodeReloc, symbol *goobj.Sym, fd *readAtSeeker) { +} diff --git a/module.1.9.go b/module.1.9.go new file mode 100644 index 00000000..0ad0c01f --- /dev/null +++ b/module.1.9.go @@ -0,0 +1,87 @@ +// +build go1.9 +// +build !go1.10,!go1.11,!go1.12,!go1.13,!go1.14,!go1.15 + +package goloader + +import ( + "cmd/objfile/goobj" +) + +// PCDATA and FUNCDATA table indexes. +// +// See funcdata.h and ../cmd/internal/obj/funcdata.go. +const ( + _PCDATA_StackMapIndex = 0 + _PCDATA_InlTreeIndex = 1 + _FUNCDATA_ArgsPointerMaps = 0 + _FUNCDATA_LocalsPointerMaps = 1 + _FUNCDATA_InlTree = 2 + _ArgsSizeUnknown = -0x80000000 +) + +// moduledata records information about the layout of the executable +// image. It is written by the linker. Any changes here must be +// matched changes to the code in cmd/internal/ld/symtab.go:symtab. +// moduledata is stored in read-only memory; none of the pointers here +// are visible to the garbage collector. +type moduledata struct { + pclntable []byte + ftab []functab + filetab []uint32 + findfunctab uintptr + minpc, maxpc uintptr + + text, etext uintptr + noptrdata, enoptrdata uintptr + data, edata uintptr + bss, ebss uintptr + noptrbss, enoptrbss uintptr + end, gcdata, gcbss uintptr + types, etypes uintptr + + textsectmap []textsect + typelinks []int32 // offsets from types + itablinks []*itab + + ptab []ptabEntry + + pluginpath string + pkghashes []modulehash + + modulename string + modulehashes []modulehash + + gcdatamask, gcbssmask bitvector + + typemap map[typeOff]uintptr // offset to *_rtype in previous module + + next *moduledata +} + +type _func struct { + entry uintptr // start pc + nameoff int32 // function name + + args int32 // in/out args size + _ int32 // previously legacy frame size; kept for layout compatibility + + pcsp int32 + pcfile int32 + pcln int32 + npcdata int32 + nfuncdata int32 +} + +func init_func(symbol *goobj.Sym, nameOff, spOff, pcfileOff, pclnOff int) _func { + fdata := _func{ + entry: uintptr(0), + nameoff: int32(nameOff), + args: int32(symbol.Func.Args), + pcsp: int32(spOff), + pcfile: int32(pcfileOff), + pcln: int32(pclnOff), + npcdata: int32(len(symbol.Func.PCData)), + nfuncdata: int32(len(symbol.Func.FuncData)), + } + return fdata +} diff --git a/module.go b/module.go index 0df6234e..dab8b9d2 100644 --- a/module.go +++ b/module.go @@ -138,6 +138,8 @@ func readFuncData(codeReloc *CodeReloc, objsym objSym, objSymMap map[string]objS fd.ReadAtWithSize(&(codeReloc.pclntable), data.Size, data.Offset) } + readPCInline(codeReloc, symbol, &fd) + for _, data := range symbol.Func.FuncData { if _, ok := codeReloc.stkmaps[data.Sym.Name]; !ok { if gcobj, ok := objSymMap[data.Sym.Name]; ok {