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