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