├── go.sum ├── go.mod ├── testdata ├── main_macho └── main.go ├── .gitignore ├── type.go ├── file_test.go ├── pe.go ├── elf.go ├── README.md ├── LICENSE ├── macho_test.go ├── file.go └── macho.go /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/goccy/go-execbin 2 | 3 | go 1.16 4 | -------------------------------------------------------------------------------- /testdata/main_macho: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goccy/go-execbin/HEAD/testdata/main_macho -------------------------------------------------------------------------------- /testdata/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Iface interface { 4 | F(int) string 5 | } 6 | 7 | func f(v Iface) { 8 | 9 | } 10 | 11 | func main() {} 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /type.go: -------------------------------------------------------------------------------- 1 | package execbin 2 | 3 | type InterfaceType struct { 4 | Name string 5 | Implemented string 6 | PkgPath string 7 | Methods []*Method 8 | } 9 | 10 | type Method struct { 11 | Name string 12 | Signature string 13 | In []*Type 14 | Out []*Type 15 | } 16 | 17 | type Type struct { 18 | Name string 19 | PkgPath string 20 | IsPointer bool 21 | } 22 | -------------------------------------------------------------------------------- /file_test.go: -------------------------------------------------------------------------------- 1 | package execbin_test 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/goccy/go-execbin" 8 | ) 9 | 10 | func TestFile(t *testing.T) { 11 | path := filepath.Join("testdata", "main_macho") 12 | f, err := execbin.Open(path) 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | if f.Type() != execbin.MachO { 17 | t.Fatal("failed to read macho file") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pe.go: -------------------------------------------------------------------------------- 1 | package execbin 2 | 3 | import ( 4 | "debug/pe" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func NewPEFile(f *os.File) (*PEFile, error) { 10 | exec, err := pe.NewFile(f) 11 | if err != nil { 12 | return nil, err 13 | } 14 | return &PEFile{ 15 | File: exec, 16 | rawFile: f, 17 | }, nil 18 | } 19 | 20 | type PEFile struct { 21 | File *pe.File 22 | rawFile *os.File 23 | } 24 | 25 | func (f *PEFile) Type() FileType { return PE } 26 | 27 | func (f *PEFile) DefinedInterfaceTypes() ([]*InterfaceType, error) { 28 | return nil, fmt.Errorf("execbin: not yet supported by pe") 29 | } 30 | -------------------------------------------------------------------------------- /elf.go: -------------------------------------------------------------------------------- 1 | package execbin 2 | 3 | import ( 4 | "debug/elf" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func NewELFFile(f *os.File) (*ELFFile, error) { 10 | exec, err := elf.NewFile(f) 11 | if err != nil { 12 | return nil, err 13 | } 14 | return &ELFFile{ 15 | File: exec, 16 | rawFile: f, 17 | }, nil 18 | } 19 | 20 | type ELFFile struct { 21 | File *elf.File 22 | rawFile *os.File 23 | } 24 | 25 | func (f *ELFFile) Type() FileType { return ELF } 26 | 27 | func (f *ELFFile) DefinedInterfaceTypes() ([]*InterfaceType, error) { 28 | return nil, fmt.Errorf("execbin: not yet supported by elf") 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-execbin 2 | 3 | [![GoDoc](https://godoc.org/github.com/goccy/go-execbin?status.svg)](https://pkg.go.dev/github.com/goccy/go-execbin?tab=doc) 4 | 5 | Analyze the binary outputted by `go build` to get type information etc. 6 | 7 | # Status 8 | 9 | UNDER DEVELOPMENT 10 | 11 | # Synopsis 12 | 13 | ```go 14 | package main 15 | 16 | import ( 17 | "github.com/goccy/go-execbin" 18 | ) 19 | 20 | func main() { 21 | file, err := execbin.Open("path/to/binary") 22 | if err != nil { 23 | panic(err) 24 | } 25 | types, err := file.DefinedInterfaceTypes() 26 | if err != nil { 27 | panic(err) 28 | } 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Masaaki Goshima 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /macho_test.go: -------------------------------------------------------------------------------- 1 | package execbin_test 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/goccy/go-execbin" 10 | ) 11 | 12 | func TestMachO(t *testing.T) { 13 | path := filepath.Join("testdata", "main_macho") 14 | file, err := os.Open(path) 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | f, err := execbin.NewMachOFile(file) 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | ifaceTypes, err := f.DefinedInterfaceTypes() 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | if len(ifaceTypes) != 1 { 27 | t.Fatalf("failed to get defined interface types. got types are %d", len(ifaceTypes)) 28 | } 29 | if !reflect.DeepEqual(ifaceTypes[0], &execbin.InterfaceType{ 30 | Name: "error", 31 | Implemented: "runtime.errorString", 32 | PkgPath: "", 33 | Methods: []*execbin.Method{ 34 | &execbin.Method{ 35 | Name: "Error", 36 | Signature: "func() string", 37 | In: []*execbin.Type{}, 38 | Out: []*execbin.Type{ 39 | &execbin.Type{ 40 | Name: "string", 41 | PkgPath: "", 42 | IsPointer: false, 43 | }, 44 | }, 45 | }, 46 | }, 47 | }) { 48 | t.Fatalf("failed to get defined interface types. got %+v", ifaceTypes[0]) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /file.go: -------------------------------------------------------------------------------- 1 | package execbin 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | type FileType int 11 | 12 | const ( 13 | UnknownFormat FileType = iota 14 | ELF 15 | MachO 16 | PE 17 | ) 18 | 19 | func (t FileType) String() string { 20 | switch t { 21 | case ELF: 22 | return "elf" 23 | case MachO: 24 | return "macho" 25 | case PE: 26 | return "pe" 27 | default: 28 | // fallthrough 29 | } 30 | return "" 31 | } 32 | 33 | type File interface { 34 | Type() FileType 35 | DefinedInterfaceTypes() ([]*InterfaceType, error) 36 | } 37 | 38 | func Open(path string) (File, error) { 39 | f, err := os.Open(path) 40 | if err != nil { 41 | return nil, err 42 | } 43 | data := make([]byte, 16) 44 | if _, err := io.ReadFull(f, data); err != nil { 45 | return nil, err 46 | } 47 | f.Seek(0, 0) 48 | if bytes.HasPrefix(data, []byte("\x7FELF")) { 49 | exec, err := NewELFFile(f) 50 | if err != nil { 51 | f.Close() 52 | return nil, err 53 | } 54 | return exec, nil 55 | } 56 | if bytes.HasPrefix(data, []byte("MZ")) { 57 | exec, err := NewPEFile(f) 58 | if err != nil { 59 | f.Close() 60 | return nil, err 61 | } 62 | return exec, nil 63 | } 64 | if bytes.HasPrefix(data, []byte("\xFE\xED\xFA")) || bytes.HasPrefix(data[1:], []byte("\xFA\xED\xFE")) { 65 | exec, err := NewMachOFile(f) 66 | if err != nil { 67 | f.Close() 68 | return nil, err 69 | } 70 | return exec, nil 71 | } 72 | return nil, fmt.Errorf("execbin: unknown format") 73 | } 74 | -------------------------------------------------------------------------------- /macho.go: -------------------------------------------------------------------------------- 1 | package execbin 2 | 3 | import ( 4 | "bytes" 5 | "debug/macho" 6 | "encoding/binary" 7 | "fmt" 8 | "os" 9 | "reflect" 10 | "strings" 11 | "unsafe" 12 | ) 13 | 14 | const uintptrSize = 4 << (^uintptr(0) >> 63) 15 | 16 | func NewMachOFile(f *os.File) (*MachOFile, error) { 17 | exec, err := macho.NewFile(f) 18 | if err != nil { 19 | return nil, err 20 | } 21 | return &MachOFile{ 22 | File: exec, 23 | rawFile: f, 24 | }, nil 25 | } 26 | 27 | type MachOFile struct { 28 | File *macho.File 29 | rawFile *os.File 30 | } 31 | 32 | func (f *MachOFile) Type() FileType { return MachO } 33 | 34 | func (f *MachOFile) detectByteOrder() (bo binary.ByteOrder, e error) { 35 | dat, err := f.File.Section("__typelink").Data() 36 | if err != nil { 37 | e = err 38 | return 39 | } 40 | if len(dat) < 16 { 41 | e = fmt.Errorf("failed to detect byte order. data length (%d) is smaller than 16", len(dat)) 42 | return 43 | } 44 | bigEndian := dat[15] != 0 45 | if bigEndian { 46 | return binary.BigEndian, nil 47 | } 48 | return binary.LittleEndian, nil 49 | } 50 | 51 | func (f *MachOFile) DefinedInterfaceTypes() ([]*InterfaceType, error) { 52 | var itabSyms []*macho.Symbol 53 | for _, sym := range f.File.Symtab.Syms { 54 | sym := sym 55 | if strings.HasPrefix(sym.Name, "go.itab.") { 56 | itabSyms = append(itabSyms, &sym) 57 | } 58 | } 59 | bo, err := f.detectByteOrder() 60 | if err != nil { 61 | return nil, err 62 | } 63 | rodataSect := f.File.Section("__rodata") 64 | rodata, err := rodataSect.Data() 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | var ret []*InterfaceType 70 | for _, itabSym := range itabSyms { 71 | splittedNames := strings.Split(itabSym.Name, ",") 72 | if len(splittedNames) < 2 { 73 | return nil, fmt.Errorf("failed to get symbol name for itab %s", itabSym.Name) 74 | } 75 | itabV, err := f.decodeItab(rodataSect.Addr, rodata, itabSym.Value, bo) 76 | if err != nil { 77 | return nil, err 78 | } 79 | methods := make([]*Method, 0, len(itabV.inter.mhdr)) 80 | for _, hdr := range itabV.inter.mhdr { 81 | if int(hdr.name) == int(hdr.ityp) || 82 | hdr.name <= 0 || int(hdr.name) > len(rodata) || 83 | hdr.ityp <= 0 || int(hdr.ityp) > len(rodata) { 84 | continue 85 | } 86 | text, err := f.nameOffToText(hdr.name, rodata, bo) 87 | if err != nil { 88 | return nil, err 89 | } 90 | ftype, err := f.decodeFuncType(rodata, uint64(hdr.ityp), bo) 91 | if err != nil { 92 | return nil, err 93 | } 94 | signature, err := f.nameOffToText(ftype.str, rodata, bo) 95 | if err != nil { 96 | return nil, err 97 | } 98 | if signature[0] == '*' { 99 | signature = signature[1:] 100 | } 101 | inTypes, err := ftype.in(rodataSect.Addr, rodata, uint64(hdr.ityp), bo) 102 | if err != nil { 103 | return nil, err 104 | } 105 | in := make([]*Type, 0, len(inTypes)) 106 | for _, t := range inTypes { 107 | text, err := f.nameOffToText(t.str, rodata, bo) 108 | if err != nil { 109 | return nil, err 110 | } 111 | var isPointer bool 112 | if t.kind&kindMask != uint8(reflect.Ptr) { 113 | text = removePointerFromName(text) 114 | } else { 115 | isPointer = true 116 | } 117 | pkgPath, typeName := splitPkgPathAndName(text) 118 | in = append(in, &Type{ 119 | Name: typeName, 120 | PkgPath: pkgPath, 121 | IsPointer: isPointer, 122 | }) 123 | } 124 | outTypes, err := ftype.out(rodataSect.Addr, rodata, uint64(hdr.ityp), bo) 125 | if err != nil { 126 | return nil, err 127 | } 128 | out := make([]*Type, 0, len(outTypes)) 129 | for _, t := range outTypes { 130 | text, err := f.nameOffToText(t.str, rodata, bo) 131 | if err != nil { 132 | return nil, err 133 | } 134 | var isPointer bool 135 | if t.kind&kindMask != uint8(reflect.Ptr) { 136 | text = removePointerFromName(text) 137 | } else { 138 | isPointer = true 139 | } 140 | pkgPath, typeName := splitPkgPathAndName(text) 141 | out = append(out, &Type{ 142 | Name: typeName, 143 | PkgPath: pkgPath, 144 | IsPointer: isPointer, 145 | }) 146 | } 147 | methods = append(methods, &Method{ 148 | Name: text, 149 | Signature: signature, 150 | In: in, 151 | Out: out, 152 | }) 153 | } 154 | typeName := splittedNames[1] 155 | pkgPath, ifaceName := splitPkgPathAndName(typeName) 156 | implemented, err := f.nameOffToText(itabV._type.str, rodata, bo) 157 | if err != nil { 158 | return nil, err 159 | } 160 | implemented = removePointerFromName(implemented) 161 | ret = append(ret, &InterfaceType{ 162 | Name: ifaceName, 163 | Implemented: implemented, 164 | PkgPath: pkgPath, 165 | Methods: methods, 166 | }) 167 | } 168 | return ret, nil 169 | } 170 | 171 | func splitPkgPathAndName(name string) (string, string) { 172 | splittedName := strings.Split(name, ".") 173 | var ( 174 | pkgPath string 175 | typeName string 176 | ) 177 | if len(splittedName) == 1 { 178 | typeName = splittedName[0] 179 | } else { 180 | pkgPath = splittedName[0] 181 | typeName = splittedName[1] 182 | } 183 | return pkgPath, typeName 184 | } 185 | 186 | func removePointerFromName(name string) string { 187 | if name[0] == '*' { 188 | return name[1:] 189 | } 190 | return name 191 | } 192 | 193 | type sliceHeader struct { 194 | data unsafe.Pointer 195 | len int 196 | cap int 197 | } 198 | 199 | // FYI: https://github.com/golang/go/blob/b8ca6e59eda969c1d3aed9b0c5bd9e99cf0e7dfe/src/runtime/runtime2.go#L893-L899 200 | type itab struct { 201 | inter *interfacetype 202 | _type *_type 203 | hash uint32 204 | _ [4]byte 205 | fun [1]uintptr 206 | } 207 | 208 | // itabVA is type represented by virtual address about itab 209 | type itabVA struct { 210 | inter unsafe.Pointer 211 | _type unsafe.Pointer 212 | hash uint32 213 | _ [4]byte 214 | fun [1]uintptr 215 | } 216 | 217 | // FYI: https://github.com/golang/go/blob/b8ca6e59eda969c1d3aed9b0c5bd9e99cf0e7dfe/src/runtime/type.go#L366-L370 218 | type interfacetype struct { 219 | typ _type 220 | pkgpath name 221 | mhdr []imethod 222 | } 223 | 224 | // interfacetypeVA is type represented by virtual address about interfacetype 225 | type interfacetypeVA struct { 226 | typ _type 227 | pkgpath name 228 | mhdr sliceHeader 229 | } 230 | 231 | // FYI: https://github.com/golang/go/blob/b8ca6e59eda969c1d3aed9b0c5bd9e99cf0e7dfe/src/runtime/type.go#L361-L364 232 | type imethod struct { 233 | name nameOff 234 | ityp typeOff 235 | } 236 | 237 | type tflag uint8 238 | type nameOff int32 239 | type typeOff int32 240 | type textOff int32 241 | 242 | const ( 243 | kindMask = (1 << 5) - 1 244 | ) 245 | 246 | const ( 247 | tflagUncommon tflag = 1 << 0 248 | tflagExtraStar tflag = 1 << 1 249 | tflagNamed tflag = 1 << 2 250 | tflagRegularMemory tflag = 1 << 3 251 | ) 252 | 253 | type name struct { 254 | bytes *byte 255 | } 256 | 257 | type uncommonType struct { 258 | pkgPath nameOff 259 | mcount uint16 260 | xcount uint16 261 | moff uint32 262 | _ uint32 263 | } 264 | 265 | // FYI: https://github.com/golang/go/blob/b8ca6e59eda969c1d3aed9b0c5bd9e99cf0e7dfe/src/runtime/type.go#L31-L48 266 | type _type struct { 267 | size uintptr 268 | ptrdata uintptr 269 | hash uint32 270 | tflag tflag 271 | align uint8 272 | fieldAlign uint8 273 | kind uint8 274 | equal unsafe.Pointer 275 | gcdata *byte 276 | str nameOff 277 | ptrToThis typeOff 278 | } 279 | 280 | type funcType struct { 281 | _type 282 | inCount uint16 283 | outCount uint16 284 | } 285 | 286 | func (t *funcType) in(rodataAddr uint64, rodata []byte, funcAddr uint64, bo binary.ByteOrder) ([]*_type, error) { 287 | uadd := functypeSize 288 | if t.tflag&tflagUncommon != 0 { 289 | uadd += uint64(unsafe.Sizeof(uncommonType{})) 290 | } 291 | if t.inCount == 0 { 292 | return nil, nil 293 | } 294 | var ret []*_type 295 | addr := funcAddr + uint64(uadd) 296 | for i := 0; i < int(t.inCount); i++ { 297 | offset := uint64(i * uintptrSize) 298 | var typeAddr uint64 299 | if err := binary.Read(bytes.NewReader(rodata[addr+offset:addr+offset+uintptrSize]), bo, &typeAddr); err != nil { 300 | return nil, err 301 | } 302 | typ, err := decodeType(rodataAddr, rodata, typeAddr, bo) 303 | if err != nil { 304 | return nil, err 305 | } 306 | ret = append(ret, typ) 307 | } 308 | return ret, nil 309 | } 310 | 311 | func (t *funcType) out(rodataAddr uint64, rodata []byte, funcAddr uint64, bo binary.ByteOrder) ([]*_type, error) { 312 | uadd := functypeSize 313 | if t.tflag&tflagUncommon != 0 { 314 | uadd += uint64(unsafe.Sizeof(uncommonType{})) 315 | } 316 | outCount := t.outCount & (1<<15 - 1) 317 | if outCount == 0 { 318 | return nil, nil 319 | } 320 | var ret []*_type 321 | addr := funcAddr + uint64(uadd) + uint64(t.inCount)*uintptrSize 322 | for i := 0; i < int(outCount); i++ { 323 | offset := uint64(i * uintptrSize) 324 | var typeAddr uint64 325 | if err := binary.Read(bytes.NewReader(rodata[addr+offset:addr+offset+uintptrSize]), bo, &typeAddr); err != nil { 326 | return nil, err 327 | } 328 | typ, err := decodeType(rodataAddr, rodata, typeAddr, bo) 329 | if err != nil { 330 | return nil, err 331 | } 332 | ret = append(ret, typ) 333 | } 334 | return ret, nil 335 | } 336 | 337 | var ( 338 | itabSize = uint64(unsafe.Sizeof(itab{})) 339 | interfacetypeSize = uint64(unsafe.Sizeof(interfacetype{})) 340 | imethodSize = uint64(unsafe.Sizeof(imethod{})) 341 | typeSize = uint64(unsafe.Sizeof(_type{})) 342 | functypeSize = uint64(unsafe.Sizeof(funcType{})) 343 | ) 344 | 345 | func (f *MachOFile) decodeFuncType(rodata []byte, funcAddr uint64, bo binary.ByteOrder) (*funcType, error) { 346 | var v [7]int64 347 | if err := binary.Read(bytes.NewReader(rodata[funcAddr:funcAddr+functypeSize]), bo, &v); err != nil { 348 | return nil, err 349 | } 350 | return (*funcType)(unsafe.Pointer(&v)), nil 351 | } 352 | 353 | func (f *MachOFile) decodeItab(rodataAddr uint64, rodata []byte, itabAddr uint64, bo binary.ByteOrder) (*itab, error) { 354 | va, err := f.decodeItabVA(rodata, itabAddr-rodataAddr, bo) 355 | if err != nil { 356 | return nil, err 357 | } 358 | inter, err := f.decodeInterfacetype(rodataAddr, rodata, uint64(uintptr(va.inter)), bo) 359 | if err != nil { 360 | return nil, err 361 | } 362 | _type, err := decodeType(rodataAddr, rodata, uint64(uintptr(va._type)), bo) 363 | if err != nil { 364 | return nil, err 365 | } 366 | return &itab{ 367 | inter: inter, 368 | _type: _type, 369 | hash: va.hash, 370 | fun: va.fun, 371 | }, nil 372 | } 373 | 374 | func (f *MachOFile) decodeItabVA(rodata []byte, offset uint64, bo binary.ByteOrder) (*itabVA, error) { 375 | var v [4]uint64 376 | if err := binary.Read(bytes.NewReader(rodata[offset:offset+itabSize]), bo, &v); err != nil { 377 | return nil, err 378 | } 379 | return (*itabVA)(unsafe.Pointer(&v)), nil 380 | } 381 | 382 | func decodeType(rodataAddr uint64, rodata []byte, typeAddr uint64, bo binary.ByteOrder) (*_type, error) { 383 | offset := typeAddr - rodataAddr 384 | var v [6]uint64 385 | if err := binary.Read(bytes.NewReader(rodata[offset:offset+typeSize]), bo, &v); err != nil { 386 | return nil, err 387 | } 388 | return (*_type)(unsafe.Pointer(&v)), nil 389 | } 390 | 391 | func (f *MachOFile) decodeInterfacetype(rodataAddr uint64, rodata []byte, interAddr uint64, bo binary.ByteOrder) (*interfacetype, error) { 392 | va, err := f.decodeInterfacetypeVA(rodata, interAddr-rodataAddr, bo) 393 | if err != nil { 394 | return nil, err 395 | } 396 | mhdr, err := f.decodeIMethods(rodataAddr, rodata, va.mhdr, bo) 397 | if err != nil { 398 | return nil, err 399 | } 400 | return &interfacetype{ 401 | typ: va.typ, 402 | pkgpath: va.pkgpath, 403 | mhdr: mhdr, 404 | }, nil 405 | } 406 | 407 | func (f *MachOFile) decodeInterfacetypeVA(rodata []byte, offset uint64, bo binary.ByteOrder) (*interfacetypeVA, error) { 408 | var v [10]int64 409 | if err := binary.Read(bytes.NewReader(rodata[offset:offset+interfacetypeSize]), bo, &v); err != nil { 410 | return nil, err 411 | } 412 | return (*interfacetypeVA)(unsafe.Pointer(&v)), nil 413 | } 414 | 415 | func (f *MachOFile) decodeIMethods(rodataAddr uint64, rodata []byte, mhdr sliceHeader, bo binary.ByteOrder) ([]imethod, error) { 416 | start := uint64(uintptr(mhdr.data) - uintptr(rodataAddr)) 417 | end := start 418 | var methods []imethod 419 | for i := uint64(0); i < uint64(mhdr.len); i++ { 420 | start += i * imethodSize 421 | end += (i + 1) * imethodSize 422 | var hdr uint64 423 | if err := binary.Read(bytes.NewReader(rodata[start:end]), bo, &hdr); err != nil { 424 | return nil, err 425 | } 426 | methods = append(methods, *(*imethod)(unsafe.Pointer(&hdr))) 427 | } 428 | return methods, nil 429 | } 430 | 431 | func (f *MachOFile) nameOffToText(name nameOff, data []byte, bo binary.ByteOrder) (string, error) { 432 | var hdr [4]byte 433 | if err := binary.Read(bytes.NewReader(data[name:name+4]), bo, &hdr); err != nil { 434 | return "", err 435 | } 436 | var text string 437 | textHeader := (*sliceHeader)(unsafe.Pointer(&text)) 438 | textHeader.data = unsafe.Pointer(&data[name+3]) 439 | textHeader.len = int(hdr[1])<<8 | int(hdr[2]) 440 | return text, nil 441 | } 442 | --------------------------------------------------------------------------------