| | |
| | |
| | |
| |
|
| | package macho |
| |
|
| | import ( |
| | "encoding/binary" |
| | "fmt" |
| | "internal/saferio" |
| | "io" |
| | "os" |
| | ) |
| |
|
| | |
| | type FatFile struct { |
| | Magic uint32 |
| | Arches []FatArch |
| | closer io.Closer |
| | } |
| |
|
| | |
| | type FatArchHeader struct { |
| | Cpu Cpu |
| | SubCpu uint32 |
| | Offset uint32 |
| | Size uint32 |
| | Align uint32 |
| | } |
| |
|
| | const fatArchHeaderSize = 5 * 4 |
| |
|
| | |
| | type FatArch struct { |
| | FatArchHeader |
| | *File |
| | } |
| |
|
| | |
| | |
| | var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil} |
| |
|
| | |
| | |
| | |
| | func NewFatFile(r io.ReaderAt) (*FatFile, error) { |
| | var ff FatFile |
| | sr := io.NewSectionReader(r, 0, 1<<63-1) |
| |
|
| | |
| | |
| | err := binary.Read(sr, binary.BigEndian, &ff.Magic) |
| | if err != nil { |
| | return nil, &FormatError{0, "error reading magic number", nil} |
| | } else if ff.Magic != MagicFat { |
| | |
| | |
| | var buf [4]byte |
| | binary.BigEndian.PutUint32(buf[:], ff.Magic) |
| | leMagic := binary.LittleEndian.Uint32(buf[:]) |
| | if leMagic == Magic32 || leMagic == Magic64 { |
| | return nil, ErrNotFat |
| | } else { |
| | return nil, &FormatError{0, "invalid magic number", nil} |
| | } |
| | } |
| | offset := int64(4) |
| |
|
| | |
| | var narch uint32 |
| | err = binary.Read(sr, binary.BigEndian, &narch) |
| | if err != nil { |
| | return nil, &FormatError{offset, "invalid fat_header", nil} |
| | } |
| | offset += 4 |
| |
|
| | if narch < 1 { |
| | return nil, &FormatError{offset, "file contains no images", nil} |
| | } |
| |
|
| | |
| | |
| | seenArches := make(map[uint64]bool) |
| | |
| | var machoType Type |
| |
|
| | |
| | |
| | c := saferio.SliceCap[FatArch](uint64(narch)) |
| | if c < 0 { |
| | return nil, &FormatError{offset, "too many images", nil} |
| | } |
| | ff.Arches = make([]FatArch, 0, c) |
| | for i := uint32(0); i < narch; i++ { |
| | var fa FatArch |
| | err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader) |
| | if err != nil { |
| | return nil, &FormatError{offset, "invalid fat_arch header", nil} |
| | } |
| | offset += fatArchHeaderSize |
| |
|
| | fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size)) |
| | fa.File, err = NewFile(fr) |
| | if err != nil { |
| | return nil, err |
| | } |
| |
|
| | |
| | seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu) |
| | if o, k := seenArches[seenArch]; o || k { |
| | return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil} |
| | } |
| | seenArches[seenArch] = true |
| |
|
| | |
| | if i == 0 { |
| | machoType = fa.Type |
| | } else { |
| | if fa.Type != machoType { |
| | 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} |
| | } |
| | } |
| |
|
| | ff.Arches = append(ff.Arches, fa) |
| | } |
| |
|
| | return &ff, nil |
| | } |
| |
|
| | |
| | |
| | func OpenFat(name string) (*FatFile, error) { |
| | f, err := os.Open(name) |
| | if err != nil { |
| | return nil, err |
| | } |
| | ff, err := NewFatFile(f) |
| | if err != nil { |
| | f.Close() |
| | return nil, err |
| | } |
| | ff.closer = f |
| | return ff, nil |
| | } |
| |
|
| | func (ff *FatFile) Close() error { |
| | var err error |
| | if ff.closer != nil { |
| | err = ff.closer.Close() |
| | ff.closer = nil |
| | } |
| | return err |
| | } |
| |
|