github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/register.go (about) 1 package goloader 2 3 import ( 4 "cmd/objfile/objfile" 5 "debug/elf" 6 "fmt" 7 "github.com/eh-steve/goloader/obj" 8 "github.com/eh-steve/goloader/objabi/dataindex" 9 "log" 10 "os" 11 "reflect" 12 "runtime" 13 "strings" 14 "unsafe" 15 ) 16 17 //go:linkname typelinksinit runtime.typelinksinit 18 func typelinksinit() 19 20 func registerType(t *_type, symPtr map[string]uintptr, pkgSet map[string]struct{}) { 21 if t.Kind() == reflect.Invalid { 22 panic("Unexpected invalid kind during registration!") 23 } 24 25 pkgpath := t.PkgPath() 26 pkgSet[pkgpath] = struct{}{} 27 name := resolveFullyQualifiedSymbolName(t) 28 29 if _, ok := symPtr[TypePrefix+name]; ok { 30 return 31 } 32 33 symPtr[TypePrefix+name] = uintptr(unsafe.Pointer(t)) 34 35 switch t.Kind() { 36 case reflect.Ptr, reflect.Chan, reflect.Array, reflect.Slice: 37 element := t.Elem() 38 registerType(element, symPtr, pkgSet) 39 case reflect.Func: 40 typ := AsType(t) 41 for i := 0; i < typ.NumIn(); i++ { 42 registerType(toType(typ.In(i)), symPtr, pkgSet) 43 } 44 for i := 0; i < typ.NumOut(); i++ { 45 registerType(toType(typ.Out(i)), symPtr, pkgSet) 46 } 47 case reflect.Struct: 48 typ := AsType(t) 49 for i := 0; i < typ.NumField(); i++ { 50 registerType(toType(typ.Field(i).Type), symPtr, pkgSet) 51 } 52 case reflect.Map: 53 mt := (*mapType)(unsafe.Pointer(t)) 54 registerType(mt.key, symPtr, pkgSet) 55 registerType(mt.elem, symPtr, pkgSet) 56 case reflect.Bool, reflect.Int, reflect.Uint, reflect.Int64, reflect.Uint64, reflect.Int32, reflect.Uint32, reflect.Int16, reflect.Uint16, reflect.Int8, reflect.Uint8, reflect.Float64, reflect.Float32, reflect.String, reflect.UnsafePointer, reflect.Uintptr, reflect.Complex64, reflect.Complex128, reflect.Interface: 57 // Already added above 58 default: 59 panic(fmt.Sprintf("typelinksregister found unexpected type (kind %s): ", t.Kind())) 60 } 61 } 62 63 // !IMPORTANT: only init firstmodule type, avoid load multiple objs but unload non-sequence errors 64 func typelinksregister(symPtr map[string]uintptr, pkgSet map[string]struct{}) { 65 md := activeModules()[0] 66 for _, tl := range md.typelinks { 67 t := (*_type)(adduintptr(md.types, int(tl))) 68 if _, ok := md.typemap[typeOff(tl)]; ok { 69 t = md.typemap[typeOff(tl)] 70 } 71 registerType(t, symPtr, pkgSet) 72 } 73 // register function 74 for _, f := range md.ftab { 75 if int(f.funcoff) < len(md.pclntable) { 76 77 _func := (*_func)(unsafe.Pointer(&(md.pclntable[f.funcoff]))) 78 name := getfuncname(_func, md) 79 if name != EmptyString { 80 if _, ok := symPtr[name]; !ok { 81 pkgpath := funcPkgPath(name) 82 if name != pkgpath+_InitTaskSuffix { 83 // Don't add to the package list if the only thing included is the init func 84 pkgSet[pkgpath] = struct{}{} 85 } 86 symPtr[name] = getfuncentry(_func, md.text) 87 88 // Asm function ABI wrappers will usually be inlined away into the caller's code, but it may be 89 // useful to know that certain functions are ABI0 and so cannot be called from Go directly 90 if _func.flag&funcFlag_ASM > 0 && _func.args == dataindex.ArgsSizeUnknown { 91 // Make clear that the ASM func uses ABI0 not ABIInternal by storing another suffixed copy 92 93 symPtr[name+obj.ABI0Suffix] = symPtr[name] 94 } 95 } 96 } 97 } 98 } 99 } 100 101 func RegSymbolWithSo(symPtr map[string]uintptr, pkgSet map[string]struct{}, path string) error { 102 return regSymbol(symPtr, pkgSet, path) 103 } 104 105 func RegSymbol(symPtr map[string]uintptr, pkgSet map[string]struct{}) error { 106 path, err := os.Executable() 107 if err != nil { 108 return err 109 } 110 return regSymbol(symPtr, pkgSet, path) 111 } 112 113 var resolvedTlsG uintptr = 0 114 115 func regSymbol(symPtr map[string]uintptr, pkgSet map[string]struct{}, path string) error { 116 f, err := objfile.Open(path) 117 if err != nil { 118 return err 119 } 120 defer f.Close() 121 122 typelinksregister(symPtr, pkgSet) 123 syms, err := f.Symbols() 124 if err != nil { 125 return fmt.Errorf("could not get symbols of file %s: %w", path, err) 126 } 127 128 for _, sym := range syms { 129 if sym.Name == OsStdout { 130 symPtr[sym.Name] = uintptr(sym.Addr) 131 } 132 } 133 // Address space layout randomization(ASLR) 134 // golang 1.15 symbol address has offset, before 1.15 offset is 0 135 addroff := int64(uintptr(unsafe.Pointer(&os.Stdout))) - int64(symPtr[OsStdout]) 136 for _, sym := range syms { 137 code := strings.ToUpper(string(sym.Code)) 138 if code == "B" || code == "D" || code == "R" { 139 symPtr[sym.Name] = uintptr(int64(sym.Addr) + addroff) 140 } 141 if strings.HasPrefix(sym.Name, ItabPrefix) { 142 symPtr[sym.Name] = uintptr(int64(sym.Addr) + addroff) 143 } 144 if strings.HasPrefix(sym.Name, "__cgo_") { 145 symPtr[sym.Name] = uintptr(int64(sym.Addr) + addroff) 146 } 147 } 148 149 for pkg := range pkgSet { 150 if _, ok := symPtr[pkg+_InitTaskSuffix]; !ok { 151 // If we haven't seen a real inittask in the host symtab but we have symbols from that package, 152 // the inittask was probably eliminated (due to being a no-op) so avoid rebuilding that package 153 // by providing a nil ptr as the inittask, and check it later before doInit() 154 symPtr[pkg+_InitTaskSuffix] = 0 155 } 156 } 157 158 tlsG, x86Found := symPtr["runtime.tlsg"] 159 tls_G, arm64Found := symPtr["runtime.tls_g"] 160 161 if resolvedTlsG != 0 { 162 symPtr[TLSNAME] = resolvedTlsG 163 } else { 164 if x86Found || arm64Found { 165 // If this is an ELF file, try to relocate the tls G as created by the external linker 166 var typeFound []string 167 if x86Found { 168 typeFound = append(typeFound, "runtime.tlsg") 169 } 170 if arm64Found { 171 typeFound = append(typeFound, "runtime.tls_g") 172 } 173 if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { 174 log.Printf("Got a TLS symbol %s emitted in the main binary (value 0x%x or 0x%x), but not sure what to do with it\n", typeFound, tlsG, tls_G) 175 return nil 176 } 177 path, err := os.Executable() 178 if err != nil { 179 return fmt.Errorf("found '%s' and so expected elf file (macho not yet supported), but failed to find executable: %w", typeFound, err) 180 } 181 elfFile, err := elf.Open(path) 182 if err != nil { 183 return fmt.Errorf("found '%s' and so expected elf file (macho not yet supported), but failed to open ELF executable: %w", typeFound, err) 184 } 185 defer elfFile.Close() 186 187 var tls *elf.Prog 188 for _, prog := range elfFile.Progs { 189 if prog.Type == elf.PT_TLS { 190 tls = prog 191 break 192 } 193 } 194 if tls == nil { 195 tlsG = uintptr(^uint64(PtrSize) + 1) // -ptrSize 196 } else { 197 // Copied from delve/pkg/proc/bininfo.go 198 switch elfFile.Machine { 199 case elf.EM_X86_64, elf.EM_386: 200 201 // According to https://reviews.llvm.org/D61824, linkers must pad the actual 202 // size of the TLS segment to ensure that (tlsoffset%align) == (vaddr%align). 203 // This formula, copied from the lld code, matches that. 204 // https://github.com/llvm-mirror/lld/blob/9aef969544981d76bea8e4d1961d3a6980980ef9/ELF/InputSection.cpp#L643 205 memsz := uintptr(tls.Memsz + (-tls.Vaddr-tls.Memsz)&(tls.Align-1)) 206 207 // The TLS register points to the end of the TLS block, which is 208 // tls.Memsz long. runtime.tlsg is an offset from the beginning of that block. 209 tlsG = ^(memsz) + 1 + tlsG // -tls.Memsz + tlsg.Value 210 211 case elf.EM_AARCH64: 212 if !arm64Found || tls == nil { 213 tlsG = uintptr(2 * uint64(PtrSize)) 214 } else { 215 tlsG = tls_G + uintptr(PtrSize*2) + ((uintptr(tls.Vaddr) - uintptr(PtrSize*2)) & uintptr(tls.Align-1)) 216 } 217 218 default: 219 // we should never get here 220 return fmt.Errorf("found 'runtime.tlsg' but got unsupported architecture: %s", elfFile.Machine) 221 } 222 } 223 resolvedTlsG = resolvedTlsG 224 symPtr[TLSNAME] = tlsG 225 } 226 } 227 228 return nil 229 } 230 231 func getFunctionPtr(function interface{}) uintptr { 232 return *(*uintptr)((*emptyInterface)(unsafe.Pointer(&function)).data) 233 }