github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/utils.go (about)

     1  package goloader
     2  
     3  import (
     4  	"cmd/objfile/sys"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"path/filepath"
     8  	"runtime"
     9  	"strconv"
    10  	"unsafe"
    11  
    12  	"github.com/eh-steve/goloader/mmap"
    13  )
    14  
    15  //go:linkname add runtime.add
    16  func add(p unsafe.Pointer, x uintptr) unsafe.Pointer
    17  
    18  //go:linkname adduintptr runtime.add
    19  func adduintptr(p uintptr, x int) unsafe.Pointer
    20  
    21  func putUint24(b []byte, v uint32) {
    22  	_ = b[2] // early bounds check to guarantee safety of writes below
    23  	b[0] = byte(v)
    24  	b[1] = byte(v >> 8)
    25  	b[2] = byte(v >> 16)
    26  }
    27  
    28  func alignof(i int, align int) int {
    29  	if i%align != 0 {
    30  		i = i + (align - i%align)
    31  	}
    32  	return i
    33  }
    34  
    35  func bytearrayAlign(b *[]byte, align int) {
    36  	length := len(*b)
    37  	if length%align != 0 {
    38  		*b = append(*b, make([]byte, align-length%align)...)
    39  	}
    40  }
    41  
    42  func createArchNops(arch *sys.Arch, size int) []byte {
    43  	if arch.Name == "arm64" {
    44  		return createARM64Nops(size)
    45  	}
    46  	return createX86Nops(size)
    47  }
    48  
    49  func createX86Nops(size int) []byte {
    50  	nops := make([]byte, size)
    51  	for i := range nops {
    52  		nops[i] = x86amd64NOPcode
    53  	}
    54  	return nops
    55  }
    56  
    57  func createARM64Nops(size int) []byte {
    58  	if size%4 != 0 {
    59  		panic(fmt.Sprintf("can't make nop instruction if padding is not multiple of 4, got %d", size))
    60  	}
    61  
    62  	nops := make([]byte, size)
    63  	for i := 0; i < size; i += 4 {
    64  		copy(nops[i:], arm64NopCode)
    65  	}
    66  	return nops
    67  }
    68  
    69  func bytearrayAlignNops(arch *sys.Arch, b *[]byte, align int) {
    70  	length := len(*b)
    71  	if length%align != 0 {
    72  		*b = append(*b, createArchNops(arch, align-length%align)...)
    73  	}
    74  }
    75  
    76  func putAddressAddOffset(byteOrder binary.ByteOrder, b []byte, offset *int, addr uint64) {
    77  	if PtrSize == Uint32Size {
    78  		byteOrder.PutUint32(b[*offset:], uint32(addr))
    79  	} else {
    80  		byteOrder.PutUint64(b[*offset:], uint64(addr))
    81  	}
    82  	*offset = *offset + PtrSize
    83  }
    84  
    85  func putAddress(byteOrder binary.ByteOrder, b []byte, addr uint64) {
    86  	if PtrSize == Uint32Size {
    87  		byteOrder.PutUint32(b, uint32(addr))
    88  	} else {
    89  		byteOrder.PutUint64(b, uint64(addr))
    90  	}
    91  }
    92  
    93  // sign extend a 24-bit integer
    94  func signext24(x int64) int32 {
    95  	return (int32(x) << 8) >> 8
    96  }
    97  
    98  func copy2Slice(dst []byte, src uintptr, size int) {
    99  	s := sliceHeader{
   100  		Data: src,
   101  		Len:  size,
   102  		Cap:  size,
   103  	}
   104  	copy(dst, *(*[]byte)(unsafe.Pointer(&s)))
   105  }
   106  
   107  func append2Slice(dst *[]byte, src uintptr, size int) {
   108  	s := sliceHeader{
   109  		Data: src,
   110  		Len:  size,
   111  		Cap:  size,
   112  	}
   113  	*dst = append(*dst, *(*[]byte)(unsafe.Pointer(&s))...)
   114  }
   115  
   116  // see runtime.internal.atomic.Loadp
   117  //
   118  //go:nosplit
   119  //go:noinline
   120  func loadp(ptr unsafe.Pointer) unsafe.Pointer {
   121  	return *(*unsafe.Pointer)(ptr)
   122  }
   123  
   124  func grow(bytes *[]byte, size int) {
   125  	if len(*bytes) < size {
   126  		*bytes = append(*bytes, make([]byte, size-len(*bytes))...)
   127  	}
   128  }
   129  
   130  func getArch(archName string) *sys.Arch {
   131  	arch := &sys.Arch{}
   132  	for index := range sys.Archs {
   133  		if archName == sys.Archs[index].Name {
   134  			arch = sys.Archs[index]
   135  		}
   136  	}
   137  	return arch
   138  }
   139  
   140  func Mmap(size int) ([]byte, error) {
   141  	return mmap.Mmap(size)
   142  }
   143  
   144  func MmapData(size int) ([]byte, error) {
   145  	return mmap.MmapData(size)
   146  }
   147  
   148  func Munmap(b []byte) (err error) {
   149  	return mmap.Munmap(b)
   150  }
   151  
   152  func MakeThreadJITCodeExecutable(ptr uintptr, len int) {
   153  	mmap.MakeThreadJITCodeExecutable(ptr, len)
   154  }
   155  
   156  // see $GOROOT/src/cmd/internal/loader/loader.go:preprocess
   157  func ispreprocesssymbol(name string) bool {
   158  	if len(name) > 5 {
   159  		switch name[:5] {
   160  		case "$f32.", "$f64.", "$i64.":
   161  			return true
   162  		default:
   163  		}
   164  	}
   165  	return false
   166  }
   167  
   168  func preprocesssymbol(byteOrder binary.ByteOrder, name string, bytes []byte) error {
   169  	val, err := strconv.ParseUint(name[5:], 16, 64)
   170  	if err != nil {
   171  		return fmt.Errorf("failed to parse $-symbol %s: %v", name, err)
   172  	}
   173  	switch name[:5] {
   174  	case "$f32.":
   175  		if uint64(uint32(val)) != val {
   176  			return fmt.Errorf("$-symbol %s too large: %d", name, val)
   177  		}
   178  		byteOrder.PutUint32(bytes, uint32(val))
   179  		bytes = bytes[:4]
   180  	case "$f64.", "$i64.":
   181  		byteOrder.PutUint64(bytes, val)
   182  	default:
   183  		return fmt.Errorf("unrecognized $-symbol: %s", name)
   184  	}
   185  	return nil
   186  }
   187  
   188  func expandGoroot(s string) string {
   189  	const n = len("$GOROOT")
   190  	if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') {
   191  		return filepath.ToSlash(filepath.Join(runtime.GOROOT(), s[n:]))
   192  	}
   193  	return s
   194  }