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  }