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 }