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  }