| // Copyright 2014 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package macho | 
 |  | 
 | import ( | 
 | 	"encoding/binary" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"os" | 
 | ) | 
 |  | 
 | // A FatFile is a Mach-O universal binary that contains at least one architecture. | 
 | type FatFile struct { | 
 | 	Magic  uint32 | 
 | 	Arches []FatArch | 
 | 	closer io.Closer | 
 | } | 
 |  | 
 | // A FatArchHeader represents a fat header for a specific image architecture. | 
 | type FatArchHeader struct { | 
 | 	Cpu    Cpu | 
 | 	SubCpu uint32 | 
 | 	Offset uint32 | 
 | 	Size   uint32 | 
 | 	Align  uint32 | 
 | } | 
 |  | 
 | const fatArchHeaderSize = 5 * 4 | 
 |  | 
 | // A FatArch is a Mach-O File inside a FatFile. | 
 | type FatArch struct { | 
 | 	FatArchHeader | 
 | 	*File | 
 | } | 
 |  | 
 | // ErrNotFat is returned from NewFatFile or OpenFat when the file is not a | 
 | // universal binary but may be a thin binary, based on its magic number. | 
 | var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil} | 
 |  | 
 | // NewFatFile creates a new FatFile for accessing all the Mach-O images in a | 
 | // universal binary. The Mach-O binary is expected to start at position 0 in | 
 | // the ReaderAt. | 
 | func NewFatFile(r io.ReaderAt) (*FatFile, error) { | 
 | 	var ff FatFile | 
 | 	sr := io.NewSectionReader(r, 0, 1<<63-1) | 
 |  | 
 | 	// Read the fat_header struct, which is always in big endian. | 
 | 	// Start with the magic number. | 
 | 	err := binary.Read(sr, binary.BigEndian, &ff.Magic) | 
 | 	if err != nil { | 
 | 		return nil, &FormatError{0, "error reading magic number", nil} | 
 | 	} else if ff.Magic != MagicFat { | 
 | 		// See if this is a Mach-O file via its magic number. The magic | 
 | 		// must be converted to little endian first though. | 
 | 		var buf [4]byte | 
 | 		binary.BigEndian.PutUint32(buf[:], ff.Magic) | 
 | 		leMagic := binary.LittleEndian.Uint32(buf[:]) | 
 | 		if leMagic == Magic32 || leMagic == Magic64 { | 
 | 			return nil, ErrNotFat | 
 | 		} else { | 
 | 			return nil, &FormatError{0, "invalid magic number", nil} | 
 | 		} | 
 | 	} | 
 | 	offset := int64(4) | 
 |  | 
 | 	// Read the number of FatArchHeaders that come after the fat_header. | 
 | 	var narch uint32 | 
 | 	err = binary.Read(sr, binary.BigEndian, &narch) | 
 | 	if err != nil { | 
 | 		return nil, &FormatError{offset, "invalid fat_header", nil} | 
 | 	} | 
 | 	offset += 4 | 
 |  | 
 | 	if narch < 1 { | 
 | 		return nil, &FormatError{offset, "file contains no images", nil} | 
 | 	} | 
 |  | 
 | 	// Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure | 
 | 	// there are not duplicate architectures. | 
 | 	seenArches := make(map[uint64]bool, narch) | 
 | 	// Make sure that all images are for the same MH_ type. | 
 | 	var machoType Type | 
 |  | 
 | 	// Following the fat_header comes narch fat_arch structs that index | 
 | 	// Mach-O images further in the file. | 
 | 	ff.Arches = make([]FatArch, narch) | 
 | 	for i := uint32(0); i < narch; i++ { | 
 | 		fa := &ff.Arches[i] | 
 | 		err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader) | 
 | 		if err != nil { | 
 | 			return nil, &FormatError{offset, "invalid fat_arch header", nil} | 
 | 		} | 
 | 		offset += fatArchHeaderSize | 
 |  | 
 | 		fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size)) | 
 | 		fa.File, err = NewFile(fr) | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 |  | 
 | 		// Make sure the architecture for this image is not duplicate. | 
 | 		seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu) | 
 | 		if o, k := seenArches[seenArch]; o || k { | 
 | 			return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil} | 
 | 		} | 
 | 		seenArches[seenArch] = true | 
 |  | 
 | 		// Make sure the Mach-O type matches that of the first image. | 
 | 		if i == 0 { | 
 | 			machoType = fa.Type | 
 | 		} else { | 
 | 			if fa.Type != machoType { | 
 | 				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} | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return &ff, nil | 
 | } | 
 |  | 
 | // OpenFat opens the named file using os.Open and prepares it for use as a Mach-O | 
 | // universal binary. | 
 | func OpenFat(name string) (*FatFile, error) { | 
 | 	f, err := os.Open(name) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	ff, err := NewFatFile(f) | 
 | 	if err != nil { | 
 | 		f.Close() | 
 | 		return nil, err | 
 | 	} | 
 | 	ff.closer = f | 
 | 	return ff, nil | 
 | } | 
 |  | 
 | func (ff *FatFile) Close() error { | 
 | 	var err error | 
 | 	if ff.closer != nil { | 
 | 		err = ff.closer.Close() | 
 | 		ff.closer = nil | 
 | 	} | 
 | 	return err | 
 | } |