github.com/dearplain/goloader@v0.0.0-20190107071432-2b1e47d74273/module.go (about) 1 package goloader 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 "unsafe" 8 ) 9 10 //go:linkname firstmoduledata runtime.firstmoduledata 11 var firstmoduledata moduledata 12 13 const PtrSize = 4 << (^uintptr(0) >> 63) 14 const _funcSize = int(unsafe.Sizeof(_func{})) 15 16 type functab struct { 17 entry uintptr 18 funcoff uintptr 19 } 20 21 // findfunctab is an array of these structures. 22 // Each bucket represents 4096 bytes of the text segment. 23 // Each subbucket represents 256 bytes of the text segment. 24 // To find a function given a pc, locate the bucket and subbucket for 25 // that pc. Add together the idx and subbucket value to obtain a 26 // function index. Then scan the functab array starting at that 27 // index to find the target function. 28 // This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead. 29 type findfuncbucket struct { 30 idx uint32 31 subbuckets [16]byte 32 } 33 34 // Mapping information for secondary text sections 35 type textsect struct { 36 vaddr uintptr // prelinked section vaddr 37 length uintptr // section length 38 baseaddr uintptr // relocated section address 39 } 40 41 type itab struct { 42 inter uintptr 43 _type uintptr 44 link uintptr 45 hash uint32 // copy of _type.hash. Used for type switches. 46 bad bool // type does not implement interface 47 inhash bool // has this itab been added to hash? 48 unused [2]byte 49 fun [1]uintptr // variable sized 50 } 51 52 type nameOff int32 53 type typeOff int32 54 type textOff int32 55 56 // A ptabEntry is generated by the compiler for each exported function 57 // and global variable in the main package of a plugin. It is used to 58 // initialize the plugin module's symbol map. 59 type ptabEntry struct { 60 name nameOff 61 typ typeOff 62 } 63 64 type modulehash struct { 65 modulename string 66 linktimehash string 67 runtimehash *string 68 } 69 70 type bitvector struct { 71 n int32 // # of bits 72 bytedata *uint8 73 } 74 75 type moduledata struct { 76 pclntable []byte 77 ftab []functab 78 filetab []uint32 79 findfunctab uintptr 80 minpc, maxpc uintptr 81 82 text, etext uintptr 83 noptrdata, enoptrdata uintptr 84 data, edata uintptr 85 bss, ebss uintptr 86 noptrbss, enoptrbss uintptr 87 end, gcdata, gcbss uintptr 88 types, etypes uintptr 89 90 textsectmap []textsect 91 typelinks []int32 // offsets from types 92 itablinks []*itab 93 94 ptab []ptabEntry 95 96 pluginpath string 97 pkghashes []modulehash 98 99 modulename string 100 modulehashes []modulehash 101 102 gcdatamask, gcbssmask bitvector 103 104 typemap map[typeOff]uintptr // offset to *_rtype in previous module 105 106 next *moduledata 107 } 108 109 type _func struct { 110 entry uintptr // start pc 111 nameoff int32 // function name 112 113 args int32 // in/out args size 114 _ int32 // previously legacy frame size; kept for layout compatibility 115 116 pcsp int32 117 pcfile int32 118 pcln int32 119 npcdata int32 120 nfuncdata int32 121 } 122 123 type funcInfoData struct { 124 _func 125 pcdata []uint32 126 funcdata []uintptr 127 } 128 129 type stackmap struct { 130 n int32 // number of bitmaps 131 nbit int32 // number of bits in each bitmap 132 bytedata [1]byte // bitmaps, each starting on a byte boundary 133 } 134 135 type Module struct { 136 pclntable []byte 137 pcfunc []findfuncbucket 138 funcinfo []funcInfoData 139 ftab []functab // entry need reloc 140 filetab []uint32 141 stkmaps [][]byte 142 } 143 144 const minfunc = 16 // minimum function size 145 const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table 146 const nsub = len(findfuncbucket{}.subbuckets) 147 148 func readFuncData(module *Module, curSymFile symFile, 149 allSyms map[string]symFile, gcObjs map[string]uintptr, 150 fileTabOffsetMap map[string]int, curSymOffset, curCodeLen int) { 151 152 fs := readAtSeeker{ReadSeeker: curSymFile.file} 153 curSym := curSymFile.sym 154 155 { 156 x := curCodeLen 157 b := x / pcbucketsize 158 i := x % pcbucketsize / (pcbucketsize / nsub) 159 for lb := b - len(module.pcfunc); lb >= 0; lb-- { 160 module.pcfunc = append(module.pcfunc, findfuncbucket{ 161 idx: uint32(256 * len(module.pcfunc))}) 162 } 163 bucket := &module.pcfunc[b] 164 if len(module.ftab) <= 0 { 165 module.ftab = append(module.ftab, functab{}) 166 } 167 bucket.subbuckets[i] = byte(len(module.ftab) - int(bucket.idx)) 168 } 169 170 var fileTabOffset = len(module.filetab) 171 var fileOffsets []uint32 172 var fullFile string 173 for _, fileName := range curSym.Func.File { 174 fileOffsets = append(fileOffsets, uint32(len(fullFile)+len(module.pclntable))) 175 fileName = strings.TrimLeft(curSym.Func.File[0], "gofile..") 176 fullFile += fileName + "\x00" 177 } 178 if tabOffset, ok := fileTabOffsetMap[fullFile]; !ok { 179 module.pclntable = append(module.pclntable, []byte(fullFile)...) 180 fileTabOffsetMap[fullFile] = fileTabOffset 181 module.filetab = append(module.filetab, fileOffsets...) 182 } else { 183 fileTabOffset = tabOffset 184 } 185 var pcFileHead [2]byte 186 if fileTabOffset > 128 { 187 fmt.Println("filetab overflow!") 188 } 189 pcFileHead[0] = byte(fileTabOffset << 1) 190 191 nameOff := len(module.pclntable) 192 nameByte := make([]byte, len(curSym.Name)+1) 193 copy(nameByte, []byte(curSym.Name)) 194 module.pclntable = append(module.pclntable, nameByte...) 195 196 spOff := len(module.pclntable) 197 var fb = make([]byte, curSym.Func.PCSP.Size) 198 fs.ReadAt(fb, curSym.Func.PCSP.Offset) 199 // fmt.Println("sp val:", fb) 200 module.pclntable = append(module.pclntable, fb...) 201 202 pcfileOff := len(module.pclntable) 203 fb = make([]byte, curSym.Func.PCFile.Size) 204 fs.ReadAt(fb, curSym.Func.PCFile.Offset) 205 // dumpPCData(fb, "pcfile") 206 module.pclntable = append(module.pclntable, pcFileHead[:]...) 207 module.pclntable = append(module.pclntable, fb...) 208 209 pclnOff := len(module.pclntable) 210 fb = make([]byte, curSym.Func.PCLine.Size) 211 fs.ReadAt(fb, curSym.Func.PCLine.Offset) 212 module.pclntable = append(module.pclntable, fb...) 213 214 fdata := _func{ 215 entry: uintptr(curSymOffset), 216 nameoff: int32(nameOff), 217 args: int32(curSym.Func.Args), 218 pcsp: int32(spOff), 219 pcfile: int32(pcfileOff), 220 pcln: int32(pclnOff), 221 npcdata: int32(len(curSym.Func.PCData)), 222 nfuncdata: int32(len(curSym.Func.FuncData)), 223 } 224 var fInfo funcInfoData 225 fInfo._func = fdata 226 for _, data := range curSym.Func.PCData { 227 fInfo.pcdata = append(fInfo.pcdata, uint32(len(module.pclntable))) 228 229 var b = make([]byte, data.Size) 230 fs.ReadAt(b, data.Offset) 231 // dumpPCData(b) 232 module.pclntable = append(module.pclntable, b...) 233 } 234 for _, data := range curSym.Func.FuncData { 235 var offset uintptr 236 if off, ok := gcObjs[data.Sym.Name]; !ok { 237 if gcobj, ok := allSyms[data.Sym.Name]; ok { 238 var b = make([]byte, gcobj.sym.Data.Size) 239 cfs := readAtSeeker{ReadSeeker: gcobj.file} 240 cfs.ReadAt(b, gcobj.sym.Data.Offset) 241 offset = uintptr(len(module.stkmaps)) 242 module.stkmaps = append(module.stkmaps, b) 243 gcObjs[data.Sym.Name] = offset 244 } else { 245 fmt.Println("unknown gcobj:", data.Sym.Name) 246 } 247 } else { 248 offset = off 249 } 250 251 fInfo.funcdata = append(fInfo.funcdata, offset) 252 } 253 254 module.ftab = append(module.ftab, functab{ 255 entry: uintptr(curSymOffset), 256 }) 257 258 module.funcinfo = append(module.funcinfo, fInfo) 259 } 260 261 func dumpPCData(b []byte, prefix string) { 262 fmt.Println(prefix, b) 263 var pc uintptr 264 val := int32(-1) 265 var ok bool 266 b, ok = step(b, &pc, &val, true) 267 for { 268 if !ok || len(b) <= 0 { 269 fmt.Println(prefix, "step end") 270 break 271 } 272 fmt.Println(prefix, "pc:", pc, "val:", val) 273 b, ok = step(b, &pc, &val, false) 274 } 275 } 276 277 //go:linkname step runtime.step 278 func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool) 279 280 //go:linkname findfunc runtime.findfunc 281 func findfunc(pc uintptr) funcInfo 282 283 //go:linkname funcdata runtime.funcdata 284 func funcdata(f funcInfo, i int32) unsafe.Pointer 285 286 //go:linkname funcname runtime.funcname 287 func funcname(f funcInfo) string 288 289 type funcInfo struct { 290 *_func 291 datap *moduledata 292 } 293 294 const ( 295 _PCDATA_StackMapIndex = 0 296 _PCDATA_InlTreeIndex = 1 297 _FUNCDATA_ArgsPointerMaps = 0 298 _FUNCDATA_LocalsPointerMaps = 1 299 _FUNCDATA_InlTree = 2 300 _ArgsSizeUnknown = -0x80000000 301 ) 302 303 func dumpStackMap(f interface{}) { 304 finfo := findfunc(getFuncPtr(f)) 305 fmt.Println(funcname(finfo)) 306 stkmap := (*stackmap)(funcdata(finfo, _FUNCDATA_LocalsPointerMaps)) 307 fmt.Printf("%v %p\n", stkmap, stkmap) 308 } 309 310 type moduledata110 struct { 311 pclntable []byte 312 ftab []functab 313 filetab []uint32 314 findfunctab uintptr 315 minpc, maxpc uintptr 316 317 text, etext uintptr 318 noptrdata, enoptrdata uintptr 319 data, edata uintptr 320 bss, ebss uintptr 321 noptrbss, enoptrbss uintptr 322 end, gcdata, gcbss uintptr 323 types, etypes uintptr 324 325 textsectmap []textsect 326 typelinks []int32 // offsets from types 327 itablinks []*itab 328 329 ptab []ptabEntry 330 331 pluginpath string 332 pkghashes []modulehash 333 334 modulename string 335 modulehashes []modulehash 336 337 hasmain uint8 // 1 if module contains the main function, 0 otherwise 338 339 gcdatamask, gcbssmask bitvector 340 341 typemap map[typeOff]uintptr // offset to *_rtype in previous module 342 343 bad bool // module failed to load and should be ignored 344 345 next *moduledata110 346 } 347 348 func moduledataTo110(m110 *moduledata110, m *moduledata) { 349 m110.pclntable = m.pclntable 350 m110.ftab = m.ftab 351 m110.filetab = m.filetab 352 m110.filetab = m.filetab 353 m110.findfunctab = m.findfunctab 354 m110.minpc = m.minpc 355 m110.maxpc = m.maxpc 356 m110.text = m.text 357 m110.etext = m.etext 358 m110.typemap = m.typemap 359 m110.types = m.types 360 m110.etypes = m.etypes 361 } 362 363 func linkModule(first uintptr, offset uintptr, newModule uintptr) { 364 for datap := first; ; { 365 p := (*uintptr)(unsafe.Pointer(datap + offset)) 366 nextdatap := *p 367 if nextdatap == 0 { 368 *p = newModule 369 break 370 } 371 datap = nextdatap 372 } 373 } 374 375 func unlinkModule(first uintptr, offset uintptr, module uintptr) { 376 prevp := first 377 for datap := first; datap != 0; { 378 p := (*uintptr)(unsafe.Pointer(datap + offset)) 379 nextdatap := *p 380 if datap == module { 381 pp := (*uintptr)(unsafe.Pointer(prevp + offset)) 382 *pp = nextdatap 383 } 384 prevp = datap 385 datap = nextdatap 386 } 387 } 388 389 func addModule(codeModule *CodeModule, m *moduledata, goVer string) { 390 switch goVer[:5] { 391 case "go1.8", "go1.9": 392 tmpModule = m 393 modules[tmpModule] = true 394 offset := uintptr(unsafe.Pointer(&m.next)) - uintptr(unsafe.Pointer(m)) 395 linkModule(uintptr(unsafe.Pointer(&firstmoduledata)), 396 offset, reflect.ValueOf(tmpModule).Pointer()) 397 case "go1.1": 398 var m110 moduledata110 399 moduledataTo110(&m110, m) 400 tmpModule = &m110 401 modules[tmpModule] = true 402 offset := uintptr(unsafe.Pointer(&m110.next)) - uintptr(unsafe.Pointer(&m110)) 403 linkModule(uintptr(unsafe.Pointer(&firstmoduledata)), 404 offset, reflect.ValueOf(tmpModule).Pointer()) 405 default: 406 panic("unsupported go version: " + goVer) 407 } 408 codeModule.Module = tmpModule 409 } 410 411 func removeModule(module interface{}, goVer string) { 412 var offset uintptr 413 switch goVer[:5] { 414 case "go1.8", "go1.9": 415 var m moduledata 416 offset = uintptr(unsafe.Pointer(&m.next)) - uintptr(unsafe.Pointer(&m)) 417 case "go1.1": 418 var m110 moduledata110 419 offset = uintptr(unsafe.Pointer(&m110.next)) - uintptr(unsafe.Pointer(&m110)) 420 default: 421 panic("unsupported go version: " + goVer) 422 } 423 unlinkModule(uintptr(unsafe.Pointer(&firstmoduledata)), offset, 424 reflect.ValueOf(module).Pointer()) 425 delete(modules, module) 426 }