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