...

Source file src/debug/macho/fat.go

     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) (*FatFile, 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 ff, nil
   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	}
   147	

View as plain text