github.com/pkujhd/goloader@v0.0.0-20240411034752-1a28096bd7bd/ld.go (about) 1 package goloader 2 3 import ( 4 "cmd/objfile/sys" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "runtime" 9 "strings" 10 "sync" 11 "unsafe" 12 13 "github.com/pkujhd/goloader/constants" 14 "github.com/pkujhd/goloader/obj" 15 "github.com/pkujhd/goloader/objabi/funcalign" 16 "github.com/pkujhd/goloader/objabi/symkind" 17 "github.com/pkujhd/goloader/stackobject" 18 ) 19 20 // ourself defined struct 21 // code segment 22 type codeSeg struct { 23 codeByte []byte 24 codeBase int 25 length int 26 maxLen int 27 codeOff int 28 } 29 30 // data segment 31 type dataSeg struct { 32 dataByte []byte 33 dataBase int 34 length int 35 maxLen int 36 dataLen int 37 noptrdataLen int 38 bssLen int 39 noptrbssLen int 40 dataOff int 41 } 42 43 type segment struct { 44 codeSeg 45 dataSeg 46 } 47 48 type gcData struct { 49 gcdata []byte 50 gcbss []byte 51 } 52 53 type CodeModule struct { 54 segment 55 gcData 56 Syms map[string]uintptr 57 stringMap map[string]*string 58 module *moduledata 59 } 60 61 type LinkerData struct { 62 Code []byte 63 Data []byte 64 Noptrdata []byte 65 Bss []byte 66 Noptrbss []byte 67 } 68 69 type Linker struct { 70 LinkerData 71 SymMap map[string]*obj.Sym 72 ObjSymbolMap map[string]*obj.ObjSymbol 73 NameMap map[string]int 74 StringMap map[string]*string 75 Filetab []uint32 76 Pclntable []byte 77 Funcs []*_func 78 Packages []*obj.Pkg 79 Arch *sys.Arch 80 CUOffset int32 81 AdaptedOffset bool 82 } 83 84 var ( 85 modules = make(map[interface{}]bool) 86 modulesLock sync.Mutex 87 ) 88 89 // initialize Linker 90 func initLinker() *Linker { 91 linker := &Linker{ 92 SymMap: make(map[string]*obj.Sym), 93 ObjSymbolMap: make(map[string]*obj.ObjSymbol), 94 NameMap: make(map[string]int), 95 StringMap: make(map[string]*string), 96 CUOffset: 0, 97 AdaptedOffset: false, 98 } 99 linker.Pclntable = make([]byte, PCHeaderSize) 100 return linker 101 } 102 103 func (linker *Linker) initPcHeader() { 104 pcheader := (*pcHeader)(unsafe.Pointer(&linker.Pclntable[0])) 105 pcheader.magic = magic 106 pcheader.minLC = uint8(linker.Arch.MinLC) 107 pcheader.ptrSize = uint8(linker.Arch.PtrSize) 108 } 109 110 func (linker *Linker) addFiles(files []string) { 111 linker.CUOffset += int32(len(files)) 112 for _, fileName := range files { 113 if offset, ok := linker.NameMap[fileName]; !ok { 114 linker.Filetab = append(linker.Filetab, (uint32)(len(linker.Pclntable))) 115 linker.NameMap[fileName] = len(linker.Pclntable) 116 fileName = strings.TrimPrefix(fileName, FileSymPrefix) 117 linker.Pclntable = append(linker.Pclntable, []byte(fileName)...) 118 linker.Pclntable = append(linker.Pclntable, ZeroByte) 119 } else { 120 linker.Filetab = append(linker.Filetab, uint32(offset)) 121 } 122 } 123 } 124 125 func (linker *Linker) addSymbols() error { 126 //static_tmp is 0, golang compile not allocate memory. 127 linker.Noptrdata = append(linker.Noptrdata, make([]byte, IntSize)...) 128 for _, objSym := range linker.ObjSymbolMap { 129 if objSym.Kind == symkind.STEXT && objSym.DupOK == false { 130 if _, err := linker.addSymbol(objSym.Name, nil); err != nil { 131 return err 132 } 133 } 134 } 135 for _, pkg := range linker.Packages { 136 initFuncName := getInitFuncName(pkg.PkgPath) 137 if _, ok := linker.ObjSymbolMap[initFuncName]; ok { 138 if _, err := linker.addSymbol(initFuncName, nil); err != nil { 139 return err 140 } 141 } 142 } 143 return nil 144 } 145 146 func (linker *Linker) adaptSymbolOffset() { 147 if linker.AdaptedOffset == false { 148 for _, sym := range linker.SymMap { 149 offset := 0 150 switch sym.Kind { 151 case symkind.SNOPTRDATA, symkind.SRODATA: 152 if !strings.HasPrefix(sym.Name, constants.TypeStringPrefix) { 153 offset += len(linker.Data) 154 } 155 case symkind.SBSS: 156 offset += len(linker.Data) + len(linker.Noptrdata) 157 case symkind.SNOPTRBSS: 158 offset += len(linker.Data) + len(linker.Noptrdata) + len(linker.Bss) 159 } 160 if sym.Offset != InvalidOffset { 161 sym.Offset += offset 162 } 163 if offset != 0 { 164 for index := range sym.Reloc { 165 if sym.Reloc[index].Offset != InvalidOffset { 166 sym.Reloc[index].Offset += offset 167 } 168 } 169 } 170 } 171 linker.AdaptedOffset = true 172 } 173 } 174 175 func (linker *Linker) addSymbol(name string, symPtr map[string]uintptr) (symbol *obj.Sym, err error) { 176 if symbol, ok := linker.SymMap[name]; ok { 177 return symbol, nil 178 } 179 objsym := linker.ObjSymbolMap[name] 180 symbol = &obj.Sym{Name: objsym.Name, Kind: objsym.Kind} 181 linker.SymMap[symbol.Name] = symbol 182 if symPtr != nil { 183 if _, ok := symPtr[name]; ok { 184 symbol.Offset = InvalidOffset 185 return symbol, nil 186 } 187 } 188 switch symbol.Kind { 189 case symkind.STEXT: 190 symbol.Offset = len(linker.Code) 191 linker.Code = append(linker.Code, objsym.Data...) 192 expandFunc(linker, objsym, symbol) 193 if len(linker.Code)-symbol.Offset < minfunc { 194 linker.Code = append(linker.Code, createArchNops(linker.Arch, minfunc-(len(linker.Code)-symbol.Offset))...) 195 } 196 bytearrayAlignNops(linker.Arch, &linker.Code, funcalign.GetFuncAlign(linker.Arch)) 197 symbol.Func = &obj.Func{} 198 if err := linker.readFuncData(linker.ObjSymbolMap[name], symbol.Offset, symPtr); err != nil { 199 return nil, err 200 } 201 case symkind.SDATA: 202 symbol.Offset = len(linker.Data) 203 linker.Data = append(linker.Data, objsym.Data...) 204 bytearrayAlign(&linker.Data, PtrSize) 205 case symkind.SNOPTRDATA, symkind.SRODATA: 206 //because golang string assignment is pointer assignment, so store go.string constants in heap. 207 if strings.HasPrefix(symbol.Name, constants.TypeStringPrefix) { 208 data := make([]byte, len(objsym.Data)) 209 copy(data, objsym.Data) 210 stringVal := string(data) 211 linker.StringMap[symbol.Name] = &stringVal 212 } else { 213 symbol.Offset = len(linker.Noptrdata) 214 linker.Noptrdata = append(linker.Noptrdata, objsym.Data...) 215 bytearrayAlign(&linker.Noptrdata, PtrSize) 216 } 217 case symkind.SBSS: 218 symbol.Offset = len(linker.Bss) 219 linker.Bss = append(linker.Bss, objsym.Data...) 220 bytearrayAlign(&linker.Bss, PtrSize) 221 case symkind.SNOPTRBSS: 222 symbol.Offset = len(linker.Noptrbss) 223 linker.Noptrbss = append(linker.Noptrbss, objsym.Data...) 224 bytearrayAlign(&linker.Noptrbss, PtrSize) 225 default: 226 return nil, fmt.Errorf("invalid symbol:%s kind:%d", symbol.Name, symbol.Kind) 227 } 228 229 for _, loc := range objsym.Reloc { 230 reloc := loc 231 reloc.Offset += symbol.Offset 232 if reloc.Epilogue.Offset != 0 { 233 reloc.Epilogue.Offset += symbol.Offset 234 } 235 if _, ok := linker.ObjSymbolMap[reloc.SymName]; ok { 236 relocSym, err := linker.addSymbol(reloc.SymName, symPtr) 237 if err != nil { 238 return nil, err 239 } 240 if relocSym != nil && len(linker.ObjSymbolMap[reloc.SymName].Data) == 0 && reloc.Size > 0 { 241 //static_tmp is 0, golang compile not allocate memory. 242 //goloader add IntSize bytes on linker.Noptrdata[0] 243 if reloc.Size <= IntSize { 244 relocSym.Offset = 0 245 } else { 246 return nil, fmt.Errorf("Symbol:%s size:%d>IntSize:%d\n", relocSym.Name, reloc.Size, IntSize) 247 } 248 } 249 } else { 250 if _, ok := linker.SymMap[reloc.SymName]; !ok && reloc.SymName != EmptyString { 251 relocSym := &obj.Sym{Name: reloc.SymName, Offset: InvalidOffset} 252 if strings.HasPrefix(reloc.SymName, constants.TypeImportPathPrefix) { 253 path := strings.Trim(strings.TrimPrefix(reloc.SymName, constants.TypeImportPathPrefix), ".") 254 relocSym.Kind = symkind.SNOPTRDATA 255 relocSym.Offset = len(linker.Noptrdata) 256 //name memory layout 257 //name { tagLen(byte), len(uint16), str*} 258 nameLen := []byte{0, 0, 0} 259 binary.BigEndian.PutUint16(nameLen[1:], uint16(len(path))) 260 linker.Noptrdata = append(linker.Noptrdata, nameLen...) 261 linker.Noptrdata = append(linker.Noptrdata, path...) 262 linker.Noptrdata = append(linker.Noptrdata, ZeroByte) 263 bytearrayAlign(&linker.Noptrbss, PtrSize) 264 } 265 if ispreprocesssymbol(reloc.SymName) { 266 bytes := make([]byte, UInt64Size) 267 if err := preprocesssymbol(linker.Arch.ByteOrder, reloc.SymName, bytes); err != nil { 268 return nil, err 269 } else { 270 relocSym.Kind = symkind.SNOPTRDATA 271 relocSym.Offset = len(linker.Noptrdata) 272 linker.Noptrdata = append(linker.Noptrdata, bytes...) 273 bytearrayAlign(&linker.Noptrbss, PtrSize) 274 } 275 } 276 if reloc.Size > 0 { 277 linker.SymMap[reloc.SymName] = relocSym 278 } 279 } 280 } 281 symbol.Reloc = append(symbol.Reloc, reloc) 282 } 283 284 if objsym.Type != EmptyString { 285 if _, ok := linker.SymMap[objsym.Type]; !ok { 286 if _, ok := linker.ObjSymbolMap[objsym.Type]; !ok { 287 linker.SymMap[objsym.Type] = &obj.Sym{Name: objsym.Type, Offset: InvalidOffset} 288 } else { 289 linker.addSymbol(objsym.Type, symPtr) 290 } 291 } 292 } 293 return symbol, nil 294 } 295 296 func (linker *Linker) readFuncData(symbol *obj.ObjSymbol, codeLen int, symPtr map[string]uintptr) (err error) { 297 nameOff := len(linker.Pclntable) 298 if offset, ok := linker.NameMap[symbol.Name]; !ok { 299 linker.NameMap[symbol.Name] = len(linker.Pclntable) 300 linker.Pclntable = append(linker.Pclntable, []byte(symbol.Name)...) 301 linker.Pclntable = append(linker.Pclntable, ZeroByte) 302 } else { 303 nameOff = offset 304 } 305 306 adaptePCFile(linker, symbol) 307 for _, reloc := range symbol.Reloc { 308 if reloc.Epilogue.Size > 0 { 309 patchPCValues(linker, &symbol.Func.PCSP, reloc) 310 patchPCValues(linker, &symbol.Func.PCFile, reloc) 311 patchPCValues(linker, &symbol.Func.PCLine, reloc) 312 for i := range symbol.Func.PCData { 313 patchPCValues(linker, &symbol.Func.PCData[i], reloc) 314 } 315 } 316 } 317 pcspOff := len(linker.Pclntable) 318 linker.Pclntable = append(linker.Pclntable, symbol.Func.PCSP...) 319 320 pcfileOff := len(linker.Pclntable) 321 linker.Pclntable = append(linker.Pclntable, symbol.Func.PCFile...) 322 323 pclnOff := len(linker.Pclntable) 324 linker.Pclntable = append(linker.Pclntable, symbol.Func.PCLine...) 325 326 _func := initfunc(symbol, nameOff, pcspOff, pcfileOff, pclnOff, int(symbol.Func.CUOffset)) 327 linker.Funcs = append(linker.Funcs, &_func) 328 Func := linker.SymMap[symbol.Name].Func 329 for _, pcdata := range symbol.Func.PCData { 330 if len(pcdata) == 0 { 331 Func.PCData = append(Func.PCData, 0) 332 } else { 333 Func.PCData = append(Func.PCData, uint32(len(linker.Pclntable))) 334 linker.Pclntable = append(linker.Pclntable, pcdata...) 335 } 336 } 337 338 for _, name := range symbol.Func.FuncData { 339 if name == EmptyString { 340 Func.FuncData = append(Func.FuncData, (uintptr)(0)) 341 } else { 342 if _, ok := linker.SymMap[name]; !ok { 343 if _, ok := linker.ObjSymbolMap[name]; ok { 344 if _, err = linker.addSymbol(name, symPtr); err != nil { 345 return err 346 } 347 } else { 348 return errors.New("unknown gcobj:" + name) 349 } 350 } 351 if sym, ok := linker.SymMap[name]; ok { 352 Func.FuncData = append(Func.FuncData, (uintptr)(sym.Offset)) 353 } else { 354 Func.FuncData = append(Func.FuncData, (uintptr)(0)) 355 } 356 } 357 } 358 359 if err = linker.addInlineTree(&_func, symbol); err != nil { 360 return err 361 } 362 363 grow(&linker.Pclntable, alignof(len(linker.Pclntable), PtrSize)) 364 return 365 } 366 367 func (linker *Linker) addSymbolMap(symPtr map[string]uintptr, codeModule *CodeModule) (symbolMap map[string]uintptr, err error) { 368 symbolMap = make(map[string]uintptr) 369 segment := &codeModule.segment 370 for name, sym := range linker.SymMap { 371 if sym.Offset == InvalidOffset { 372 if ptr, ok := symPtr[sym.Name]; ok { 373 symbolMap[name] = ptr 374 } else if addr, ok := symPtr[strings.TrimSuffix(name, GOTPCRELSuffix)]; ok && strings.HasSuffix(name, GOTPCRELSuffix) { 375 symbolMap[name] = uintptr(segment.dataBase) + uintptr(segment.dataOff) 376 putAddressAddOffset(linker.Arch.ByteOrder, segment.dataByte, &segment.dataOff, uint64(addr)) 377 } else { 378 symbolMap[name] = InvalidHandleValue 379 return nil, fmt.Errorf("unresolve external:%s", sym.Name) 380 } 381 } else if sym.Kind == symkind.STEXT { 382 symbolMap[name] = uintptr(sym.Offset + segment.codeBase) 383 codeModule.Syms[sym.Name] = symbolMap[name] 384 } else if strings.HasPrefix(name, constants.TypeStringPrefix) { 385 symbolMap[name] = (*stringHeader)(unsafe.Pointer(linker.StringMap[name])).Data 386 } else if name == getInitFuncName(DefaultPkgPath) || 387 strings.HasPrefix(name, constants.TypePrefix) { 388 symbolMap[name] = uintptr(sym.Offset + segment.dataBase) 389 } else if _, ok := symPtr[name]; ok { 390 symbolMap[name] = symPtr[name] 391 } else { 392 symbolMap[name] = uintptr(sym.Offset + segment.dataBase) 393 } 394 //fill itablinks 395 if strings.HasPrefix(name, constants.ItabPrefix) { 396 codeModule.module.itablinks = append(codeModule.module.itablinks, (*itab)(adduintptr(symbolMap[name], 0))) 397 } 398 } 399 return symbolMap, err 400 } 401 402 func (linker *Linker) addFuncTab(module *moduledata, _func *_func, symbolMap map[string]uintptr) (err error) { 403 funcname := getfuncname(_func, module) 404 setfuncentry(_func, symbolMap[funcname], module.text) 405 Func := linker.SymMap[funcname].Func 406 407 if err = stackobject.AddStackObject(funcname, linker.SymMap, symbolMap, module.noptrdata); err != nil { 408 return err 409 } 410 if err = linker.addDeferReturn(_func, module); err != nil { 411 return err 412 } 413 414 append2Slice(&module.pclntable, uintptr(unsafe.Pointer(_func)), _FuncSize) 415 416 if _func.Npcdata > 0 { 417 append2Slice(&module.pclntable, uintptr(unsafe.Pointer(&(Func.PCData[0]))), Uint32Size*int(_func.Npcdata)) 418 } 419 420 if _func.Nfuncdata > 0 { 421 addfuncdata(module, Func, _func) 422 } 423 424 return err 425 } 426 427 func (linker *Linker) buildModule(codeModule *CodeModule, symbolMap map[string]uintptr) (err error) { 428 segment := &codeModule.segment 429 module := codeModule.module 430 module.pclntable = append(module.pclntable, linker.Pclntable...) 431 module.minpc = uintptr(segment.codeBase) 432 module.maxpc = uintptr(segment.codeBase + segment.codeOff) 433 module.text = uintptr(segment.codeBase) 434 module.etext = module.maxpc 435 module.data = uintptr(segment.dataBase) 436 module.edata = uintptr(segment.dataBase) + uintptr(segment.dataLen) 437 module.noptrdata = module.edata 438 module.enoptrdata = module.noptrdata + uintptr(segment.noptrdataLen) 439 module.bss = module.enoptrdata 440 module.ebss = module.bss + uintptr(segment.bssLen) 441 module.noptrbss = module.ebss 442 module.enoptrbss = module.noptrbss + uintptr(segment.noptrbssLen) 443 module.end = module.enoptrbss 444 module.types = module.data 445 module.etypes = module.enoptrbss 446 initmodule(codeModule.module, linker) 447 448 module.ftab = append(module.ftab, initfunctab(module.minpc, uintptr(len(module.pclntable)), module.text)) 449 for index, _func := range linker.Funcs { 450 funcname := getfuncname(_func, module) 451 module.ftab = append(module.ftab, initfunctab(symbolMap[funcname], uintptr(len(module.pclntable)), module.text)) 452 if err = linker.addFuncTab(module, linker.Funcs[index], symbolMap); err != nil { 453 return err 454 } 455 } 456 module.ftab = append(module.ftab, initfunctab(module.maxpc, uintptr(len(module.pclntable)), module.text)) 457 458 // see:^src/cmd/link/internal/ld/pcln.go findfunctab 459 funcbucket := []findfuncbucket{} 460 for k := 0; k < len(linker.Funcs); k++ { 461 lEntry := int(getfuncentry(linker.Funcs[k], module.text) - module.text) 462 lb := lEntry / pcbucketsize 463 li := lEntry % pcbucketsize / (pcbucketsize / nsub) 464 465 entry := int(module.maxpc - module.text) 466 if k < len(linker.Funcs)-1 { 467 entry = int(getfuncentry(linker.Funcs[k+1], module.text) - module.text) 468 } 469 b := entry / pcbucketsize 470 i := entry % pcbucketsize / (pcbucketsize / nsub) 471 472 for m := b - len(funcbucket); m >= 0; m-- { 473 funcbucket = append(funcbucket, findfuncbucket{idx: uint32(k)}) 474 } 475 if lb < b { 476 i = nsub - 1 477 } 478 for n := li + 1; n <= i; n++ { 479 if funcbucket[lb].subbuckets[n] == 0 { 480 funcbucket[lb].subbuckets[n] = byte(k - int(funcbucket[lb].idx)) 481 } 482 } 483 } 484 length := len(funcbucket) * FindFuncBucketSize 485 append2Slice(&module.pclntable, uintptr(unsafe.Pointer(&funcbucket[0])), length) 486 module.findfunctab = (uintptr)(unsafe.Pointer(&module.pclntable[len(module.pclntable)-length])) 487 488 if err = linker.addgcdata(codeModule, symbolMap); err != nil { 489 return err 490 } 491 for name, addr := range symbolMap { 492 if strings.HasPrefix(name, constants.TypePrefix) && 493 !strings.HasPrefix(name, constants.TypeDoubleDotPrefix) && 494 addr >= module.types && addr < module.etypes { 495 module.typelinks = append(module.typelinks, int32(addr-module.types)) 496 } 497 } 498 499 modulesLock.Lock() 500 addModule(codeModule.module) 501 modulesLock.Unlock() 502 moduledataverify1(codeModule.module) 503 modulesinit() 504 typelinksinit() 505 additabs(codeModule.module) 506 507 return err 508 } 509 510 func Load(linker *Linker, symPtr map[string]uintptr) (codeModule *CodeModule, err error) { 511 codeModule = &CodeModule{ 512 Syms: make(map[string]uintptr), 513 module: &moduledata{typemap: nil}, 514 } 515 516 //init code segment 517 codeSeg := &codeModule.segment.codeSeg 518 codeSeg.length = len(linker.Code) 519 codeSeg.maxLen = alignof((codeSeg.length)*2, PageSize) 520 codeByte, err := Mmap(codeSeg.maxLen) 521 if err != nil { 522 return nil, err 523 } 524 codeSeg.codeByte = codeByte 525 codeSeg.codeBase = int((*sliceHeader)(unsafe.Pointer(&codeByte)).Data) 526 copy(codeSeg.codeByte, linker.Code) 527 codeSeg.codeOff = codeSeg.length 528 529 //init data segment 530 dataSeg := &codeModule.segment.dataSeg 531 dataSeg.dataLen = len(linker.Data) 532 dataSeg.noptrdataLen = len(linker.Noptrdata) 533 dataSeg.bssLen = len(linker.Bss) 534 dataSeg.noptrbssLen = len(linker.Noptrbss) 535 dataSeg.length = dataSeg.dataLen + dataSeg.noptrdataLen + dataSeg.bssLen + dataSeg.noptrbssLen 536 dataSeg.maxLen = alignof((dataSeg.length)*2, PageSize) 537 dataSeg.dataOff = 0 538 dataByte, err := MmapData(dataSeg.maxLen) 539 if err != nil { 540 Munmap(dataSeg.dataByte) 541 return nil, err 542 } 543 dataSeg.dataByte = dataByte 544 dataSeg.dataBase = int((*sliceHeader)(unsafe.Pointer(&dataByte)).Data) 545 copy(dataSeg.dataByte[dataSeg.dataOff:], linker.Data) 546 dataSeg.dataOff = dataSeg.dataLen 547 copy(dataSeg.dataByte[dataSeg.dataOff:], linker.Noptrdata) 548 dataSeg.dataOff += dataSeg.noptrdataLen 549 copy(dataSeg.dataByte[dataSeg.dataOff:], linker.Bss) 550 dataSeg.dataOff += dataSeg.bssLen 551 copy(dataSeg.dataByte[dataSeg.dataOff:], linker.Noptrbss) 552 dataSeg.dataOff += dataSeg.noptrbssLen 553 554 codeModule.stringMap = linker.StringMap 555 556 linker.adaptSymbolOffset() 557 558 var symbolMap map[string]uintptr 559 if symbolMap, err = linker.addSymbolMap(symPtr, codeModule); err == nil { 560 if err = linker.relocate(codeModule, symbolMap, symPtr); err == nil { 561 if err = linker.buildModule(codeModule, symbolMap); err == nil { 562 MakeThreadJITCodeExecutable(uintptr(codeModule.codeBase), codeSeg.maxLen) 563 if err = linker.doInitialize(symPtr, symbolMap); err == nil { 564 return codeModule, err 565 } 566 } 567 } 568 } 569 return nil, err 570 } 571 572 func UnresolvedSymbols(linker *Linker, symPtr map[string]uintptr) []string { 573 unresolvedSymbols := make([]string, 0) 574 for name, sym := range linker.SymMap { 575 if sym.Offset == InvalidOffset { 576 if _, ok := symPtr[sym.Name]; !ok { 577 nName := strings.TrimSuffix(name, GOTPCRELSuffix) 578 if name != nName { 579 if _, ok := symPtr[nName]; !ok { 580 unresolvedSymbols = append(unresolvedSymbols, nName) 581 } 582 } else { 583 unresolvedSymbols = append(unresolvedSymbols, name) 584 } 585 } 586 } 587 } 588 return unresolvedSymbols 589 } 590 591 func (cm *CodeModule) Unload() { 592 removeitabs(cm.module) 593 runtime.GC() 594 modulesLock.Lock() 595 removeModule(cm.module) 596 modulesLock.Unlock() 597 modulesinit() 598 Munmap(cm.codeByte) 599 Munmap(cm.dataByte) 600 }