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  	"internal/saferio"
    11  	"io"
    12  	"os"
    13  )
    14  
    15  // A FatFile is a Mach-O universal binary that contains at least one architecture.
    16  type FatFile struct {
    17  	Magic  uint32
    18  	Arches []FatArch
    19  	closer io.Closer
    20  }
    21  
    22  // A FatArchHeader represents a fat header for a specific image architecture.
    23  type FatArchHeader struct {
    24  	Cpu    Cpu
    25  	SubCpu uint32
    26  	Offset uint32
    27  	Size   uint32
    28  	Align  uint32
    29  }
    30  
    31  const fatArchHeaderSize = 5 * 4
    32  
    33  // A FatArch is a Mach-O File inside a FatFile.
    34  type FatArch struct {
    35  	FatArchHeader
    36  	*File
    37  }
    38  
    39  // ErrNotFat is returned from [NewFatFile] or [OpenFat] when the file is not a
    40  // universal binary but may be a thin binary, based on its magic number.
    41  var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil}
    42  
    43  // NewFatFile creates a new [FatFile] for accessing all the Mach-O images in a
    44  // universal binary. The Mach-O binary is expected to start at position 0 in
    45  // the ReaderAt.
    46  func NewFatFile(r io.ReaderAt) (*FatFile, error) {
    47  	var ff FatFile
    48  	sr := io.NewSectionReader(r, 0, 1<<63-1)
    49  
    50  	// Read the fat_header struct, which is always in big endian.
    51  	// Start with the magic number.
    52  	err := binary.Read(sr, binary.BigEndian, &ff.Magic)
    53  	if err != nil {
    54  		return nil, &FormatError{0, "error reading magic number", nil}
    55  	} else if ff.Magic != MagicFat {
    56  		// See if this is a Mach-O file via its magic number. The magic
    57  		// must be converted to little endian first though.
    58  		var buf [4]byte
    59  		binary.BigEndian.PutUint32(buf[:], ff.Magic)
    60  		leMagic := binary.LittleEndian.Uint32(buf[:])
    61  		if leMagic == Magic32 || leMagic == Magic64 {
    62  			return nil, ErrNotFat
    63  		} else {
    64  			return nil, &FormatError{0, "invalid magic number", nil}
    65  		}
    66  	}
    67  	offset := int64(4)
    68  
    69  	// Read the number of FatArchHeaders that come after the fat_header.
    70  	var narch uint32
    71  	err = binary.Read(sr, binary.BigEndian, &narch)
    72  	if err != nil {
    73  		return nil, &FormatError{offset, "invalid fat_header", nil}
    74  	}
    75  	offset += 4
    76  
    77  	if narch < 1 {
    78  		return nil, &FormatError{offset, "file contains no images", nil}
    79  	}
    80  
    81  	// Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure
    82  	// there are not duplicate architectures.
    83  	seenArches := make(map[uint64]bool)
    84  	// Make sure that all images are for the same MH_ type.
    85  	var machoType Type
    86  
    87  	// Following the fat_header comes narch fat_arch structs that index
    88  	// Mach-O images further in the file.
    89  	c := saferio.SliceCap[FatArch](uint64(narch))
    90  	if c < 0 {
    91  		return nil, &FormatError{offset, "too many images", nil}
    92  	}
    93  	ff.Arches = make([]FatArch, 0, c)
    94  	for i := uint32(0); i < narch; i++ {
    95  		var fa FatArch
    96  		err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader)
    97  		if err != nil {
    98  			return nil, &FormatError{offset, "invalid fat_arch header", nil}
    99  		}
   100  		offset += fatArchHeaderSize
   101  
   102  		fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size))
   103  		fa.File, err = NewFile(fr)
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  
   108  		// Make sure the architecture for this image is not duplicate.
   109  		seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu)
   110  		if o, k := seenArches[seenArch]; o || k {
   111  			return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil}
   112  		}
   113  		seenArches[seenArch] = true
   114  
   115  		// Make sure the Mach-O type matches that of the first image.
   116  		if i == 0 {
   117  			machoType = fa.Type
   118  		} else {
   119  			if fa.Type != machoType {
   120  				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}
   121  			}
   122  		}
   123  
   124  		ff.Arches = append(ff.Arches, fa)
   125  	}
   126  
   127  	return &ff, nil
   128  }
   129  
   130  // OpenFat opens the named file using [os.Open] and prepares it for use as a Mach-O
   131  // universal binary.
   132  func OpenFat(name string) (*FatFile, error) {
   133  	f, err := os.Open(name)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	ff, err := NewFatFile(f)
   138  	if err != nil {
   139  		f.Close()
   140  		return nil, err
   141  	}
   142  	ff.closer = f
   143  	return ff, nil
   144  }
   145  
   146  func (ff *FatFile) Close() error {
   147  	var err error
   148  	if ff.closer != nil {
   149  		err = ff.closer.Close()
   150  		ff.closer = nil
   151  	}
   152  	return err
   153  }
   154  

View as plain text