Source file src/debug/macho/fat.go
     1	
     2	
     3	
     4	
     5	package macho
     6	
     7	import (
     8		"encoding/binary"
     9		"fmt"
    10		"io"
    11		"os"
    12	)
    13	
    14	
    15	type FatFile struct {
    16		Magic  uint32
    17		Arches []FatArch
    18		closer io.Closer
    19	}
    20	
    21	
    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	
    33	type FatArch struct {
    34		FatArchHeader
    35		*File
    36	}
    37	
    38	
    39	
    40	var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil}
    41	
    42	
    43	
    44	
    45	func NewFatFile(r io.ReaderAt) (*FatFile, error) {
    46		var ff FatFile
    47		sr := io.NewSectionReader(r, 0, 1<<63-1)
    48	
    49		
    50		
    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			
    56			
    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		
    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		
    81		
    82		seenArches := make(map[uint64]bool, narch)
    83		
    84		var machoType Type
    85	
    86		
    87		
    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			
   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			
   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	
   124	
   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