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.
13 // A FatFile is a Mach-O universal binary that contains at least one architecture.
20 // A FatArchHeader represents a fat header for a specific image architecture.
21 type FatArchHeader struct {
29 const fatArchHeaderSize = 5 * 4
31 // A FatArch is a Mach-O File inside a FatFile.
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
40 func NewFatFile(r io.ReaderAt) (*FatFile, error) {
42 sr := io.NewSectionReader(r, 0, 1<<63-1)
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)
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.
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)
58 return nil, formatError(0, "invalid magic number, leMagic=0x%x", leMagic)
63 // Read the number of FatArchHeaders that come after the fat_header.
65 err = binary.Read(sr, binary.BigEndian, &narch)
67 return nil, formatError(offset, "invalid fat_header %v", err)
72 return nil, formatError(offset, "file contains no images, narch=%d", narch)
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.
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++ {
86 err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader)
88 return nil, formatError(offset, "invalid fat_arch header, %v", err)
90 offset += fatArchHeaderSize
92 fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size))
93 fa.File, err = NewFile(fr)
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)
103 seenArches[seenArch] = true
105 // Make sure the Mach-O type matches that of the first image.
107 machoType = HdrType(fa.Type)
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)
118 // OpenFat opens the named file using os.Open and prepares it for use as a Mach-O
120 func OpenFat(name string) (*FatFile, error) {
121 f, err := os.Open(name)
125 ff, err := NewFatFile(f)
134 func (ff *FatFile) Close() error {
136 if ff.closer != nil {
137 err = ff.closer.Close()