You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

157 lines
4.4 KiB

  1. // Copyright 2018 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package macho
  15. import (
  16. "encoding/binary"
  17. "fmt"
  18. "io"
  19. "os"
  20. )
  21. // A FatFile is a Mach-O universal binary that contains at least one architecture.
  22. type FatFile struct {
  23. Magic uint32
  24. Arches []FatArch
  25. closer io.Closer
  26. }
  27. // A FatArchHeader represents a fat header for a specific image architecture.
  28. type FatArchHeader struct {
  29. Cpu Cpu
  30. SubCpu uint32
  31. Offset uint32
  32. Size uint32
  33. Align uint32
  34. }
  35. const fatArchHeaderSize = 5 * 4
  36. // A FatArch is a Mach-O File inside a FatFile.
  37. type FatArch struct {
  38. FatArchHeader
  39. *File
  40. }
  41. // ErrNotFat is returned from NewFatFile or OpenFat when the file is not a
  42. // universal binary but may be a thin binary, based on its magic number.
  43. var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil}
  44. // NewFatFile creates a new FatFile for accessing all the Mach-O images in a
  45. // universal binary. The Mach-O binary is expected to start at position 0 in
  46. // the ReaderAt.
  47. func NewFatFile(r io.ReaderAt) (*FatFile, error) {
  48. var ff FatFile
  49. sr := io.NewSectionReader(r, 0, 1<<63-1)
  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. // 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. if narch < 1 {
  76. return nil, &FormatError{offset, "file contains no images", nil}
  77. }
  78. // Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure
  79. // there are not duplicate architectures.
  80. seenArches := make(map[uint64]bool, narch)
  81. // Make sure that all images are for the same MH_ type.
  82. var machoType Type
  83. // Following the fat_header comes narch fat_arch structs that index
  84. // Mach-O images further in the file.
  85. ff.Arches = make([]FatArch, narch)
  86. for i := uint32(0); i < narch; i++ {
  87. fa := &ff.Arches[i]
  88. err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader)
  89. if err != nil {
  90. return nil, &FormatError{offset, "invalid fat_arch header", nil}
  91. }
  92. offset += fatArchHeaderSize
  93. fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size))
  94. fa.File, err = NewFile(fr)
  95. if err != nil {
  96. return nil, err
  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, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil}
  102. }
  103. seenArches[seenArch] = true
  104. // Make sure the Mach-O type matches that of the first image.
  105. if i == 0 {
  106. machoType = fa.Type
  107. } else {
  108. if fa.Type != machoType {
  109. 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}
  110. }
  111. }
  112. }
  113. return &ff, nil
  114. }
  115. // OpenFat opens the named file using os.Open and prepares it for use as a Mach-O
  116. // universal binary.
  117. func OpenFat(name string) (ff *FatFile, err error) {
  118. f, err := os.Open(name)
  119. if err != nil {
  120. return nil, err
  121. }
  122. ff, err = NewFatFile(f)
  123. if err != nil {
  124. f.Close()
  125. return nil, err
  126. }
  127. ff.closer = f
  128. return
  129. }
  130. func (ff *FatFile) Close() error {
  131. var err error
  132. if ff.closer != nil {
  133. err = ff.closer.Close()
  134. ff.closer = nil
  135. }
  136. return err
  137. }