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  }