summaryrefslogtreecommitdiff
path: root/src/pkg/debug/macho
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/debug/macho')
-rw-r--r--src/pkg/debug/macho/fat.go146
-rw-r--r--src/pkg/debug/macho/file.go23
-rw-r--r--src/pkg/debug/macho/file_test.go43
-rw-r--r--src/pkg/debug/macho/macho.go23
-rw-r--r--src/pkg/debug/macho/testdata/fat-gcc-386-amd64-darwin-execbin0 -> 28992 bytes
5 files changed, 220 insertions, 15 deletions
diff --git a/src/pkg/debug/macho/fat.go b/src/pkg/debug/macho/fat.go
new file mode 100644
index 000000000..93b831526
--- /dev/null
+++ b/src/pkg/debug/macho/fat.go
@@ -0,0 +1,146 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package macho
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "os"
+)
+
+// A FatFile is a Mach-O universal binary that contains at least one architecture.
+type FatFile struct {
+ Magic uint32
+ Arches []FatArch
+ closer io.Closer
+}
+
+// A FatArchHeader represents a fat header for a specific image architecture.
+type FatArchHeader struct {
+ Cpu Cpu
+ SubCpu uint32
+ Offset uint32
+ Size uint32
+ Align uint32
+}
+
+const fatArchHeaderSize = 5 * 4
+
+// A FatArch is a Mach-O File inside a FatFile.
+type FatArch struct {
+ FatArchHeader
+ *File
+}
+
+// ErrNotFat is returned from NewFatFile or OpenFat when the file is not a
+// universal binary but may be a thin binary, based on its magic number.
+var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil}
+
+// NewFatFile creates a new FatFile for accessing all the Mach-O images in a
+// universal binary. The Mach-O binary is expected to start at position 0 in
+// the ReaderAt.
+func NewFatFile(r io.ReaderAt) (*FatFile, error) {
+ var ff FatFile
+ sr := io.NewSectionReader(r, 0, 1<<63-1)
+
+ // Read the fat_header struct, which is always in big endian.
+ // Start with the magic number.
+ 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 {
+ // See if this is a Mach-O file via its magic number. The magic
+ // must be converted to little endian first though.
+ 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)
+
+ // Read the number of FatArchHeaders that come after the fat_header.
+ 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}
+ }
+
+ // Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure
+ // there are not duplicate architectures.
+ seenArches := make(map[uint64]bool, narch)
+ // Make sure that all images are for the same MH_ type.
+ var machoType Type
+
+ // Following the fat_header comes narch fat_arch structs that index
+ // Mach-O images further in the file.
+ ff.Arches = make([]FatArch, narch)
+ for i := uint32(0); i < narch; i++ {
+ fa := &ff.Arches[i]
+ 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
+ }
+
+ // Make sure the architecture for this image is not duplicate.
+ 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
+
+ // Make sure the Mach-O type matches that of the first image.
+ 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}
+ }
+ }
+ }
+
+ return &ff, nil
+}
+
+// OpenFat opens the named file using os.Open and prepares it for use as a Mach-O
+// universal binary.
+func OpenFat(name string) (ff *FatFile, err 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
+}
+
+func (ff *FatFile) Close() error {
+ var err error
+ if ff.closer != nil {
+ err = ff.closer.Close()
+ ff.closer = nil
+ }
+ return err
+}
diff --git a/src/pkg/debug/macho/file.go b/src/pkg/debug/macho/file.go
index f5f0dedb7..eefb74444 100644
--- a/src/pkg/debug/macho/file.go
+++ b/src/pkg/debug/macho/file.go
@@ -11,7 +11,6 @@ import (
"bytes"
"debug/dwarf"
"encoding/binary"
- "errors"
"fmt"
"io"
"os"
@@ -74,6 +73,9 @@ type Segment struct {
func (s *Segment) Data() ([]byte, error) {
dat := make([]byte, s.sr.Size())
n, err := s.sr.ReadAt(dat, 0)
+ if n == len(dat) {
+ err = nil
+ }
return dat[0:n], err
}
@@ -109,6 +111,9 @@ type Section struct {
func (s *Section) Data() ([]byte, error) {
dat := make([]byte, s.sr.Size())
n, err := s.sr.ReadAt(dat, 0)
+ if n == len(dat) {
+ err = nil
+ }
return dat[0:n], err
}
@@ -246,7 +251,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
case LoadCmdDylib:
var hdr DylibCmd
- b := bytes.NewBuffer(cmddat)
+ b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &hdr); err != nil {
return nil, err
}
@@ -263,7 +268,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
case LoadCmdSymtab:
var hdr SymtabCmd
- b := bytes.NewBuffer(cmddat)
+ b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &hdr); err != nil {
return nil, err
}
@@ -290,7 +295,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
case LoadCmdDysymtab:
var hdr DysymtabCmd
- b := bytes.NewBuffer(cmddat)
+ b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &hdr); err != nil {
return nil, err
}
@@ -299,7 +304,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
return nil, err
}
x := make([]uint32, hdr.Nindirectsyms)
- if err := binary.Read(bytes.NewBuffer(dat), bo, x); err != nil {
+ if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
return nil, err
}
st := new(Dysymtab)
@@ -311,7 +316,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
case LoadCmdSegment:
var seg32 Segment32
- b := bytes.NewBuffer(cmddat)
+ b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &seg32); err != nil {
return nil, err
}
@@ -349,7 +354,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
case LoadCmdSegment64:
var seg64 Segment64
- b := bytes.NewBuffer(cmddat)
+ b := bytes.NewReader(cmddat)
if err := binary.Read(b, bo, &seg64); err != nil {
return nil, err
}
@@ -396,7 +401,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
bo := f.ByteOrder
symtab := make([]Symbol, hdr.Nsyms)
- b := bytes.NewBuffer(symdat)
+ b := bytes.NewReader(symdat)
for i := range symtab {
var n Nlist64
if f.Magic == Magic64 {
@@ -475,7 +480,7 @@ func (f *File) DWARF() (*dwarf.Data, error) {
name = "__debug_" + name
s := f.Section(name)
if s == nil {
- return nil, errors.New("missing Mach-O section " + name)
+ continue
}
b, err := s.Data()
if err != nil && uint64(len(b)) < s.Size {
diff --git a/src/pkg/debug/macho/file_test.go b/src/pkg/debug/macho/file_test.go
index 640225b32..4797780ce 100644
--- a/src/pkg/debug/macho/file_test.go
+++ b/src/pkg/debug/macho/file_test.go
@@ -165,3 +165,46 @@ func TestOpenFailure(t *testing.T) {
t.Errorf("open %s: succeeded unexpectedly", filename)
}
}
+
+func TestOpenFat(t *testing.T) {
+ ff, err := OpenFat("testdata/fat-gcc-386-amd64-darwin-exec")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if ff.Magic != MagicFat {
+ t.Errorf("OpenFat: got magic number %#x, want %#x", ff.Magic, MagicFat)
+ }
+ if len(ff.Arches) != 2 {
+ t.Errorf("OpenFat: got %d architectures, want 2", len(ff.Arches))
+ }
+
+ for i := range ff.Arches {
+ arch := &ff.Arches[i]
+ ftArch := &fileTests[i]
+
+ if arch.Cpu != ftArch.hdr.Cpu || arch.SubCpu != ftArch.hdr.SubCpu {
+ t.Errorf("OpenFat: architecture #%d got cpu=%#x subtype=%#x, expected cpu=%#x, subtype=%#x", i, arch.Cpu, arch.SubCpu, ftArch.hdr.Cpu, ftArch.hdr.SubCpu)
+ }
+
+ if !reflect.DeepEqual(arch.FileHeader, ftArch.hdr) {
+ t.Errorf("OpenFat header:\n\tgot %#v\n\twant %#v\n", arch.FileHeader, ftArch.hdr)
+ }
+ }
+}
+
+func TestOpenFatFailure(t *testing.T) {
+ filename := "file.go" // not a Mach-O file
+ if _, err := OpenFat(filename); err == nil {
+ t.Errorf("OpenFat %s: succeeded unexpectedly", filename)
+ }
+
+ filename = "testdata/gcc-386-darwin-exec" // not a fat Mach-O
+ ff, err := OpenFat(filename)
+ if err != ErrNotFat {
+ t.Errorf("OpenFat %s: got %v, want ErrNotFat", filename, err)
+ }
+ if ff != nil {
+ t.Errorf("OpenFat %s: got %v, want nil", filename, ff)
+ }
+}
diff --git a/src/pkg/debug/macho/macho.go b/src/pkg/debug/macho/macho.go
index bc14226c5..d9678c8ed 100644
--- a/src/pkg/debug/macho/macho.go
+++ b/src/pkg/debug/macho/macho.go
@@ -26,29 +26,40 @@ const (
)
const (
- Magic32 uint32 = 0xfeedface
- Magic64 uint32 = 0xfeedfacf
+ Magic32 uint32 = 0xfeedface
+ Magic64 uint32 = 0xfeedfacf
+ MagicFat uint32 = 0xcafebabe
)
-// A Type is a Mach-O file type, either an object or an executable.
+// A Type is the Mach-O file type, e.g. an object file, executable, or dynamic library.
type Type uint32
const (
- TypeObj Type = 1
- TypeExec Type = 2
+ TypeObj Type = 1
+ TypeExec Type = 2
+ TypeDylib Type = 6
+ TypeBundle Type = 8
)
// A Cpu is a Mach-O cpu type.
type Cpu uint32
+const cpuArch64 = 0x01000000
+
const (
Cpu386 Cpu = 7
- CpuAmd64 Cpu = Cpu386 + 1<<24
+ CpuAmd64 Cpu = Cpu386 | cpuArch64
+ CpuArm Cpu = 12
+ CpuPpc Cpu = 18
+ CpuPpc64 Cpu = CpuPpc | cpuArch64
)
var cpuStrings = []intName{
{uint32(Cpu386), "Cpu386"},
{uint32(CpuAmd64), "CpuAmd64"},
+ {uint32(CpuArm), "CpuArm"},
+ {uint32(CpuPpc), "CpuPpc"},
+ {uint32(CpuPpc64), "CpuPpc64"},
}
func (i Cpu) String() string { return stringName(uint32(i), cpuStrings, false) }
diff --git a/src/pkg/debug/macho/testdata/fat-gcc-386-amd64-darwin-exec b/src/pkg/debug/macho/testdata/fat-gcc-386-amd64-darwin-exec
new file mode 100644
index 000000000..7efd19300
--- /dev/null
+++ b/src/pkg/debug/macho/testdata/fat-gcc-386-amd64-darwin-exec
Binary files differ