github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/debug/macho/fat.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package macho 6 7 import ( 8 "encoding/binary" 9 "fmt" 10 "internal/saferio" 11 "io" 12 "os" 13 ) 14 15 // A FatFile is a Mach-O universal binary that contains at least one architecture. 16 type FatFile struct { 17 Magic uint32 18 Arches []FatArch 19 closer io.Closer 20 } 21 22 // A FatArchHeader represents a fat header for a specific image architecture. 23 type FatArchHeader struct { 24 Cpu Cpu 25 SubCpu uint32 26 Offset uint32 27 Size uint32 28 Align uint32 29 } 30 31 const fatArchHeaderSize = 5 * 4 32 33 // A FatArch is a Mach-O File inside a FatFile. 34 type FatArch struct { 35 FatArchHeader 36 *File 37 } 38 39 // ErrNotFat is returned from [NewFatFile] or [OpenFat] when the file is not a 40 // universal binary but may be a thin binary, based on its magic number. 41 var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil} 42 43 // NewFatFile creates a new [FatFile] for accessing all the Mach-O images in a 44 // universal binary. The Mach-O binary is expected to start at position 0 in 45 // the ReaderAt. 46 func NewFatFile(r io.ReaderAt) (*FatFile, error) { 47 var ff FatFile 48 sr := io.NewSectionReader(r, 0, 1<<63-1) 49 50 // Read the fat_header struct, which is always in big endian. 51 // Start with the magic number. 52 err := binary.Read(sr, binary.BigEndian, &ff.Magic) 53 if err != nil { 54 return nil, &FormatError{0, "error reading magic number", nil} 55 } else if ff.Magic != MagicFat { 56 // See if this is a Mach-O file via its magic number. The magic 57 // must be converted to little endian first though. 58 var buf [4]byte 59 binary.BigEndian.PutUint32(buf[:], ff.Magic) 60 leMagic := binary.LittleEndian.Uint32(buf[:]) 61 if leMagic == Magic32 || leMagic == Magic64 { 62 return nil, ErrNotFat 63 } else { 64 return nil, &FormatError{0, "invalid magic number", nil} 65 } 66 } 67 offset := int64(4) 68 69 // Read the number of FatArchHeaders that come after the fat_header. 70 var narch uint32 71 err = binary.Read(sr, binary.BigEndian, &narch) 72 if err != nil { 73 return nil, &FormatError{offset, "invalid fat_header", nil} 74 } 75 offset += 4 76 77 if narch < 1 { 78 return nil, &FormatError{offset, "file contains no images", nil} 79 } 80 81 // Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure 82 // there are not duplicate architectures. 83 seenArches := make(map[uint64]bool) 84 // Make sure that all images are for the same MH_ type. 85 var machoType Type 86 87 // Following the fat_header comes narch fat_arch structs that index 88 // Mach-O images further in the file. 89 c := saferio.SliceCap[FatArch](uint64(narch)) 90 if c < 0 { 91 return nil, &FormatError{offset, "too many images", nil} 92 } 93 ff.Arches = make([]FatArch, 0, c) 94 for i := uint32(0); i < narch; i++ { 95 var fa FatArch 96 err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader) 97 if err != nil { 98 return nil, &FormatError{offset, "invalid fat_arch header", nil} 99 } 100 offset += fatArchHeaderSize 101 102 fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size)) 103 fa.File, err = NewFile(fr) 104 if err != nil { 105 return nil, err 106 } 107 108 // Make sure the architecture for this image is not duplicate. 109 seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu) 110 if o, k := seenArches[seenArch]; o || k { 111 return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil} 112 } 113 seenArches[seenArch] = true 114 115 // Make sure the Mach-O type matches that of the first image. 116 if i == 0 { 117 machoType = fa.Type 118 } else { 119 if fa.Type != machoType { 120 return nil, &FormatError{offset, fmt.Sprintf("Mach-O type for architecture #%d (type=%#x) does not match first (type=%#x)", i, fa.Type, machoType), nil} 121 } 122 } 123 124 ff.Arches = append(ff.Arches, fa) 125 } 126 127 return &ff, nil 128 } 129 130 // OpenFat opens the named file using [os.Open] and prepares it for use as a Mach-O 131 // universal binary. 132 func OpenFat(name string) (*FatFile, error) { 133 f, err := os.Open(name) 134 if err != nil { 135 return nil, err 136 } 137 ff, err := NewFatFile(f) 138 if err != nil { 139 f.Close() 140 return nil, err 141 } 142 ff.closer = f 143 return ff, nil 144 } 145 146 func (ff *FatFile) Close() error { 147 var err error 148 if ff.closer != nil { 149 err = ff.closer.Close() 150 ff.closer = nil 151 } 152 return err 153 }