cmd/splitdwarf: copy debug/macho
The splitdwarf command will need a modified version of debug/macho. This is a verbatim copy from std as of Go 1.11, which we'll modify in the next CL. Change-Id: Ia9ded870d1ba91dad21f9f6dc5bb38f9f6cc0e80 Reviewed-on: https://go-review.googlesource.com/c/152240 Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
98df4c70be
commit
d674b4ad67
|
@ -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) (*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
|
||||||
|
}
|
|
@ -0,0 +1,688 @@
|
||||||
|
// Copyright 2009 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 implements access to Mach-O object files.
|
||||||
|
package macho
|
||||||
|
|
||||||
|
// High level access to low level data structures.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/zlib"
|
||||||
|
"debug/dwarf"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A File represents an open Mach-O file.
|
||||||
|
type File struct {
|
||||||
|
FileHeader
|
||||||
|
ByteOrder binary.ByteOrder
|
||||||
|
Loads []Load
|
||||||
|
Sections []*Section
|
||||||
|
|
||||||
|
Symtab *Symtab
|
||||||
|
Dysymtab *Dysymtab
|
||||||
|
|
||||||
|
closer io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Load represents any Mach-O load command.
|
||||||
|
type Load interface {
|
||||||
|
Raw() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LoadBytes is the uninterpreted bytes of a Mach-O load command.
|
||||||
|
type LoadBytes []byte
|
||||||
|
|
||||||
|
func (b LoadBytes) Raw() []byte { return b }
|
||||||
|
|
||||||
|
// A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command.
|
||||||
|
type SegmentHeader struct {
|
||||||
|
Cmd LoadCmd
|
||||||
|
Len uint32
|
||||||
|
Name string
|
||||||
|
Addr uint64
|
||||||
|
Memsz uint64
|
||||||
|
Offset uint64
|
||||||
|
Filesz uint64
|
||||||
|
Maxprot uint32
|
||||||
|
Prot uint32
|
||||||
|
Nsect uint32
|
||||||
|
Flag uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Segment represents a Mach-O 32-bit or 64-bit load segment command.
|
||||||
|
type Segment struct {
|
||||||
|
LoadBytes
|
||||||
|
SegmentHeader
|
||||||
|
|
||||||
|
// Embed ReaderAt for ReadAt method.
|
||||||
|
// Do not embed SectionReader directly
|
||||||
|
// to avoid having Read and Seek.
|
||||||
|
// If a client wants Read and Seek it must use
|
||||||
|
// Open() to avoid fighting over the seek offset
|
||||||
|
// with other clients.
|
||||||
|
io.ReaderAt
|
||||||
|
sr *io.SectionReader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data reads and returns the contents of the segment.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open returns a new ReadSeeker reading the segment.
|
||||||
|
func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
|
||||||
|
|
||||||
|
type SectionHeader struct {
|
||||||
|
Name string
|
||||||
|
Seg string
|
||||||
|
Addr uint64
|
||||||
|
Size uint64
|
||||||
|
Offset uint32
|
||||||
|
Align uint32
|
||||||
|
Reloff uint32
|
||||||
|
Nreloc uint32
|
||||||
|
Flags uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Reloc represents a Mach-O relocation.
|
||||||
|
type Reloc struct {
|
||||||
|
Addr uint32
|
||||||
|
Value uint32
|
||||||
|
// when Scattered == false && Extern == true, Value is the symbol number.
|
||||||
|
// when Scattered == false && Extern == false, Value is the section number.
|
||||||
|
// when Scattered == true, Value is the value that this reloc refers to.
|
||||||
|
Type uint8
|
||||||
|
Len uint8 // 0=byte, 1=word, 2=long, 3=quad
|
||||||
|
Pcrel bool
|
||||||
|
Extern bool // valid if Scattered == false
|
||||||
|
Scattered bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Section struct {
|
||||||
|
SectionHeader
|
||||||
|
Relocs []Reloc
|
||||||
|
|
||||||
|
// Embed ReaderAt for ReadAt method.
|
||||||
|
// Do not embed SectionReader directly
|
||||||
|
// to avoid having Read and Seek.
|
||||||
|
// If a client wants Read and Seek it must use
|
||||||
|
// Open() to avoid fighting over the seek offset
|
||||||
|
// with other clients.
|
||||||
|
io.ReaderAt
|
||||||
|
sr *io.SectionReader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data reads and returns the contents of the Mach-O section.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open returns a new ReadSeeker reading the Mach-O section.
|
||||||
|
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
|
||||||
|
|
||||||
|
// A Dylib represents a Mach-O load dynamic library command.
|
||||||
|
type Dylib struct {
|
||||||
|
LoadBytes
|
||||||
|
Name string
|
||||||
|
Time uint32
|
||||||
|
CurrentVersion uint32
|
||||||
|
CompatVersion uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Symtab represents a Mach-O symbol table command.
|
||||||
|
type Symtab struct {
|
||||||
|
LoadBytes
|
||||||
|
SymtabCmd
|
||||||
|
Syms []Symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Dysymtab represents a Mach-O dynamic symbol table command.
|
||||||
|
type Dysymtab struct {
|
||||||
|
LoadBytes
|
||||||
|
DysymtabCmd
|
||||||
|
IndirectSyms []uint32 // indices into Symtab.Syms
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Rpath represents a Mach-O rpath command.
|
||||||
|
type Rpath struct {
|
||||||
|
LoadBytes
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Symbol is a Mach-O 32-bit or 64-bit symbol table entry.
|
||||||
|
type Symbol struct {
|
||||||
|
Name string
|
||||||
|
Type uint8
|
||||||
|
Sect uint8
|
||||||
|
Desc uint16
|
||||||
|
Value uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mach-O reader
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FormatError is returned by some operations if the data does
|
||||||
|
// not have the correct format for an object file.
|
||||||
|
type FormatError struct {
|
||||||
|
off int64
|
||||||
|
msg string
|
||||||
|
val interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FormatError) Error() string {
|
||||||
|
msg := e.msg
|
||||||
|
if e.val != nil {
|
||||||
|
msg += fmt.Sprintf(" '%v'", e.val)
|
||||||
|
}
|
||||||
|
msg += fmt.Sprintf(" in record at byte %#x", e.off)
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open opens the named file using os.Open and prepares it for use as a Mach-O binary.
|
||||||
|
func Open(name string) (*File, error) {
|
||||||
|
f, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ff, err := NewFile(f)
|
||||||
|
if err != nil {
|
||||||
|
f.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ff.closer = f
|
||||||
|
return ff, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the File.
|
||||||
|
// If the File was created using NewFile directly instead of Open,
|
||||||
|
// Close has no effect.
|
||||||
|
func (f *File) Close() error {
|
||||||
|
var err error
|
||||||
|
if f.closer != nil {
|
||||||
|
err = f.closer.Close()
|
||||||
|
f.closer = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFile creates a new File for accessing a Mach-O binary in an underlying reader.
|
||||||
|
// The Mach-O binary is expected to start at position 0 in the ReaderAt.
|
||||||
|
func NewFile(r io.ReaderAt) (*File, error) {
|
||||||
|
f := new(File)
|
||||||
|
sr := io.NewSectionReader(r, 0, 1<<63-1)
|
||||||
|
|
||||||
|
// Read and decode Mach magic to determine byte order, size.
|
||||||
|
// Magic32 and Magic64 differ only in the bottom bit.
|
||||||
|
var ident [4]byte
|
||||||
|
if _, err := r.ReadAt(ident[0:], 0); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
be := binary.BigEndian.Uint32(ident[0:])
|
||||||
|
le := binary.LittleEndian.Uint32(ident[0:])
|
||||||
|
switch Magic32 &^ 1 {
|
||||||
|
case be &^ 1:
|
||||||
|
f.ByteOrder = binary.BigEndian
|
||||||
|
f.Magic = be
|
||||||
|
case le &^ 1:
|
||||||
|
f.ByteOrder = binary.LittleEndian
|
||||||
|
f.Magic = le
|
||||||
|
default:
|
||||||
|
return nil, &FormatError{0, "invalid magic number", nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read entire file header.
|
||||||
|
if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then load commands.
|
||||||
|
offset := int64(fileHeaderSize32)
|
||||||
|
if f.Magic == Magic64 {
|
||||||
|
offset = fileHeaderSize64
|
||||||
|
}
|
||||||
|
dat := make([]byte, f.Cmdsz)
|
||||||
|
if _, err := r.ReadAt(dat, offset); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f.Loads = make([]Load, f.Ncmd)
|
||||||
|
bo := f.ByteOrder
|
||||||
|
for i := range f.Loads {
|
||||||
|
// Each load command begins with uint32 command and length.
|
||||||
|
if len(dat) < 8 {
|
||||||
|
return nil, &FormatError{offset, "command block too small", nil}
|
||||||
|
}
|
||||||
|
cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
|
||||||
|
if siz < 8 || siz > uint32(len(dat)) {
|
||||||
|
return nil, &FormatError{offset, "invalid command block size", nil}
|
||||||
|
}
|
||||||
|
var cmddat []byte
|
||||||
|
cmddat, dat = dat[0:siz], dat[siz:]
|
||||||
|
offset += int64(siz)
|
||||||
|
var s *Segment
|
||||||
|
switch cmd {
|
||||||
|
default:
|
||||||
|
f.Loads[i] = LoadBytes(cmddat)
|
||||||
|
|
||||||
|
case LoadCmdRpath:
|
||||||
|
var hdr RpathCmd
|
||||||
|
b := bytes.NewReader(cmddat)
|
||||||
|
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l := new(Rpath)
|
||||||
|
if hdr.Path >= uint32(len(cmddat)) {
|
||||||
|
return nil, &FormatError{offset, "invalid path in rpath command", hdr.Path}
|
||||||
|
}
|
||||||
|
l.Path = cstring(cmddat[hdr.Path:])
|
||||||
|
l.LoadBytes = LoadBytes(cmddat)
|
||||||
|
f.Loads[i] = l
|
||||||
|
|
||||||
|
case LoadCmdDylib:
|
||||||
|
var hdr DylibCmd
|
||||||
|
b := bytes.NewReader(cmddat)
|
||||||
|
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l := new(Dylib)
|
||||||
|
if hdr.Name >= uint32(len(cmddat)) {
|
||||||
|
return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
|
||||||
|
}
|
||||||
|
l.Name = cstring(cmddat[hdr.Name:])
|
||||||
|
l.Time = hdr.Time
|
||||||
|
l.CurrentVersion = hdr.CurrentVersion
|
||||||
|
l.CompatVersion = hdr.CompatVersion
|
||||||
|
l.LoadBytes = LoadBytes(cmddat)
|
||||||
|
f.Loads[i] = l
|
||||||
|
|
||||||
|
case LoadCmdSymtab:
|
||||||
|
var hdr SymtabCmd
|
||||||
|
b := bytes.NewReader(cmddat)
|
||||||
|
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
strtab := make([]byte, hdr.Strsize)
|
||||||
|
if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var symsz int
|
||||||
|
if f.Magic == Magic64 {
|
||||||
|
symsz = 16
|
||||||
|
} else {
|
||||||
|
symsz = 12
|
||||||
|
}
|
||||||
|
symdat := make([]byte, int(hdr.Nsyms)*symsz)
|
||||||
|
if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f.Loads[i] = st
|
||||||
|
f.Symtab = st
|
||||||
|
|
||||||
|
case LoadCmdDysymtab:
|
||||||
|
var hdr DysymtabCmd
|
||||||
|
b := bytes.NewReader(cmddat)
|
||||||
|
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dat := make([]byte, hdr.Nindirectsyms*4)
|
||||||
|
if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := make([]uint32, hdr.Nindirectsyms)
|
||||||
|
if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
st := new(Dysymtab)
|
||||||
|
st.LoadBytes = LoadBytes(cmddat)
|
||||||
|
st.DysymtabCmd = hdr
|
||||||
|
st.IndirectSyms = x
|
||||||
|
f.Loads[i] = st
|
||||||
|
f.Dysymtab = st
|
||||||
|
|
||||||
|
case LoadCmdSegment:
|
||||||
|
var seg32 Segment32
|
||||||
|
b := bytes.NewReader(cmddat)
|
||||||
|
if err := binary.Read(b, bo, &seg32); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s = new(Segment)
|
||||||
|
s.LoadBytes = cmddat
|
||||||
|
s.Cmd = cmd
|
||||||
|
s.Len = siz
|
||||||
|
s.Name = cstring(seg32.Name[0:])
|
||||||
|
s.Addr = uint64(seg32.Addr)
|
||||||
|
s.Memsz = uint64(seg32.Memsz)
|
||||||
|
s.Offset = uint64(seg32.Offset)
|
||||||
|
s.Filesz = uint64(seg32.Filesz)
|
||||||
|
s.Maxprot = seg32.Maxprot
|
||||||
|
s.Prot = seg32.Prot
|
||||||
|
s.Nsect = seg32.Nsect
|
||||||
|
s.Flag = seg32.Flag
|
||||||
|
f.Loads[i] = s
|
||||||
|
for i := 0; i < int(s.Nsect); i++ {
|
||||||
|
var sh32 Section32
|
||||||
|
if err := binary.Read(b, bo, &sh32); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sh := new(Section)
|
||||||
|
sh.Name = cstring(sh32.Name[0:])
|
||||||
|
sh.Seg = cstring(sh32.Seg[0:])
|
||||||
|
sh.Addr = uint64(sh32.Addr)
|
||||||
|
sh.Size = uint64(sh32.Size)
|
||||||
|
sh.Offset = sh32.Offset
|
||||||
|
sh.Align = sh32.Align
|
||||||
|
sh.Reloff = sh32.Reloff
|
||||||
|
sh.Nreloc = sh32.Nreloc
|
||||||
|
sh.Flags = sh32.Flags
|
||||||
|
if err := f.pushSection(sh, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case LoadCmdSegment64:
|
||||||
|
var seg64 Segment64
|
||||||
|
b := bytes.NewReader(cmddat)
|
||||||
|
if err := binary.Read(b, bo, &seg64); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s = new(Segment)
|
||||||
|
s.LoadBytes = cmddat
|
||||||
|
s.Cmd = cmd
|
||||||
|
s.Len = siz
|
||||||
|
s.Name = cstring(seg64.Name[0:])
|
||||||
|
s.Addr = seg64.Addr
|
||||||
|
s.Memsz = seg64.Memsz
|
||||||
|
s.Offset = seg64.Offset
|
||||||
|
s.Filesz = seg64.Filesz
|
||||||
|
s.Maxprot = seg64.Maxprot
|
||||||
|
s.Prot = seg64.Prot
|
||||||
|
s.Nsect = seg64.Nsect
|
||||||
|
s.Flag = seg64.Flag
|
||||||
|
f.Loads[i] = s
|
||||||
|
for i := 0; i < int(s.Nsect); i++ {
|
||||||
|
var sh64 Section64
|
||||||
|
if err := binary.Read(b, bo, &sh64); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sh := new(Section)
|
||||||
|
sh.Name = cstring(sh64.Name[0:])
|
||||||
|
sh.Seg = cstring(sh64.Seg[0:])
|
||||||
|
sh.Addr = sh64.Addr
|
||||||
|
sh.Size = sh64.Size
|
||||||
|
sh.Offset = sh64.Offset
|
||||||
|
sh.Align = sh64.Align
|
||||||
|
sh.Reloff = sh64.Reloff
|
||||||
|
sh.Nreloc = sh64.Nreloc
|
||||||
|
sh.Flags = sh64.Flags
|
||||||
|
if err := f.pushSection(sh, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s != nil {
|
||||||
|
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
|
||||||
|
s.ReaderAt = s.sr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
|
||||||
|
bo := f.ByteOrder
|
||||||
|
symtab := make([]Symbol, hdr.Nsyms)
|
||||||
|
b := bytes.NewReader(symdat)
|
||||||
|
for i := range symtab {
|
||||||
|
var n Nlist64
|
||||||
|
if f.Magic == Magic64 {
|
||||||
|
if err := binary.Read(b, bo, &n); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var n32 Nlist32
|
||||||
|
if err := binary.Read(b, bo, &n32); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n.Name = n32.Name
|
||||||
|
n.Type = n32.Type
|
||||||
|
n.Sect = n32.Sect
|
||||||
|
n.Desc = n32.Desc
|
||||||
|
n.Value = uint64(n32.Value)
|
||||||
|
}
|
||||||
|
sym := &symtab[i]
|
||||||
|
if n.Name >= uint32(len(strtab)) {
|
||||||
|
return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
|
||||||
|
}
|
||||||
|
sym.Name = cstring(strtab[n.Name:])
|
||||||
|
sym.Type = n.Type
|
||||||
|
sym.Sect = n.Sect
|
||||||
|
sym.Desc = n.Desc
|
||||||
|
sym.Value = n.Value
|
||||||
|
}
|
||||||
|
st := new(Symtab)
|
||||||
|
st.LoadBytes = LoadBytes(cmddat)
|
||||||
|
st.Syms = symtab
|
||||||
|
return st, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type relocInfo struct {
|
||||||
|
Addr uint32
|
||||||
|
Symnum uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
|
||||||
|
f.Sections = append(f.Sections, sh)
|
||||||
|
sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
|
||||||
|
sh.ReaderAt = sh.sr
|
||||||
|
|
||||||
|
if sh.Nreloc > 0 {
|
||||||
|
reldat := make([]byte, int(sh.Nreloc)*8)
|
||||||
|
if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b := bytes.NewReader(reldat)
|
||||||
|
|
||||||
|
bo := f.ByteOrder
|
||||||
|
|
||||||
|
sh.Relocs = make([]Reloc, sh.Nreloc)
|
||||||
|
for i := range sh.Relocs {
|
||||||
|
rel := &sh.Relocs[i]
|
||||||
|
|
||||||
|
var ri relocInfo
|
||||||
|
if err := binary.Read(b, bo, &ri); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ri.Addr&(1<<31) != 0 { // scattered
|
||||||
|
rel.Addr = ri.Addr & (1<<24 - 1)
|
||||||
|
rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
|
||||||
|
rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
|
||||||
|
rel.Pcrel = ri.Addr&(1<<30) != 0
|
||||||
|
rel.Value = ri.Symnum
|
||||||
|
rel.Scattered = true
|
||||||
|
} else {
|
||||||
|
switch bo {
|
||||||
|
case binary.LittleEndian:
|
||||||
|
rel.Addr = ri.Addr
|
||||||
|
rel.Value = ri.Symnum & (1<<24 - 1)
|
||||||
|
rel.Pcrel = ri.Symnum&(1<<24) != 0
|
||||||
|
rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
|
||||||
|
rel.Extern = ri.Symnum&(1<<27) != 0
|
||||||
|
rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
|
||||||
|
case binary.BigEndian:
|
||||||
|
rel.Addr = ri.Addr
|
||||||
|
rel.Value = ri.Symnum >> 8
|
||||||
|
rel.Pcrel = ri.Symnum&(1<<7) != 0
|
||||||
|
rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
|
||||||
|
rel.Extern = ri.Symnum&(1<<4) != 0
|
||||||
|
rel.Type = uint8(ri.Symnum & (1<<4 - 1))
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cstring(b []byte) string {
|
||||||
|
i := bytes.IndexByte(b, 0)
|
||||||
|
if i == -1 {
|
||||||
|
i = len(b)
|
||||||
|
}
|
||||||
|
return string(b[0:i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Segment returns the first Segment with the given name, or nil if no such segment exists.
|
||||||
|
func (f *File) Segment(name string) *Segment {
|
||||||
|
for _, l := range f.Loads {
|
||||||
|
if s, ok := l.(*Segment); ok && s.Name == name {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section returns the first section with the given name, or nil if no such
|
||||||
|
// section exists.
|
||||||
|
func (f *File) Section(name string) *Section {
|
||||||
|
for _, s := range f.Sections {
|
||||||
|
if s.Name == name {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DWARF returns the DWARF debug information for the Mach-O file.
|
||||||
|
func (f *File) DWARF() (*dwarf.Data, error) {
|
||||||
|
dwarfSuffix := func(s *Section) string {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(s.Name, "__debug_"):
|
||||||
|
return s.Name[8:]
|
||||||
|
case strings.HasPrefix(s.Name, "__zdebug_"):
|
||||||
|
return s.Name[9:]
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
sectionData := func(s *Section) ([]byte, error) {
|
||||||
|
b, err := s.Data()
|
||||||
|
if err != nil && uint64(len(b)) < s.Size {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) >= 12 && string(b[:4]) == "ZLIB" {
|
||||||
|
dlen := binary.BigEndian.Uint64(b[4:12])
|
||||||
|
dbuf := make([]byte, dlen)
|
||||||
|
r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(r, dbuf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := r.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b = dbuf
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are many other DWARF sections, but these
|
||||||
|
// are the ones the debug/dwarf package uses.
|
||||||
|
// Don't bother loading others.
|
||||||
|
var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
|
||||||
|
for _, s := range f.Sections {
|
||||||
|
suffix := dwarfSuffix(s)
|
||||||
|
if suffix == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := dat[suffix]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b, err := sectionData(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dat[suffix] = b
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for DWARF4 .debug_types sections.
|
||||||
|
for i, s := range f.Sections {
|
||||||
|
suffix := dwarfSuffix(s)
|
||||||
|
if suffix != "types" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := sectionData(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportedSymbols returns the names of all symbols
|
||||||
|
// referred to by the binary f that are expected to be
|
||||||
|
// satisfied by other libraries at dynamic load time.
|
||||||
|
func (f *File) ImportedSymbols() ([]string, error) {
|
||||||
|
if f.Dysymtab == nil || f.Symtab == nil {
|
||||||
|
return nil, &FormatError{0, "missing symbol table", nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
st := f.Symtab
|
||||||
|
dt := f.Dysymtab
|
||||||
|
var all []string
|
||||||
|
for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
|
||||||
|
all = append(all, s.Name)
|
||||||
|
}
|
||||||
|
return all, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportedLibraries returns the paths of all libraries
|
||||||
|
// referred to by the binary f that are expected to be
|
||||||
|
// linked with the binary at dynamic link time.
|
||||||
|
func (f *File) ImportedLibraries() ([]string, error) {
|
||||||
|
var all []string
|
||||||
|
for _, l := range f.Loads {
|
||||||
|
if lib, ok := l.(*Dylib); ok {
|
||||||
|
all = append(all, lib.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all, nil
|
||||||
|
}
|
|
@ -0,0 +1,379 @@
|
||||||
|
// Copyright 2009 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 (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileTest struct {
|
||||||
|
file string
|
||||||
|
hdr FileHeader
|
||||||
|
loads []interface{}
|
||||||
|
sections []*SectionHeader
|
||||||
|
relocations map[string][]Reloc
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileTests = []fileTest{
|
||||||
|
{
|
||||||
|
"testdata/gcc-386-darwin-exec",
|
||||||
|
FileHeader{0xfeedface, Cpu386, 0x3, 0x2, 0xc, 0x3c0, 0x85},
|
||||||
|
[]interface{}{
|
||||||
|
&SegmentHeader{LoadCmdSegment, 0x38, "__PAGEZERO", 0x0, 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
&SegmentHeader{LoadCmdSegment, 0xc0, "__TEXT", 0x1000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x2, 0x0},
|
||||||
|
&SegmentHeader{LoadCmdSegment, 0xc0, "__DATA", 0x2000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x2, 0x0},
|
||||||
|
&SegmentHeader{LoadCmdSegment, 0x7c, "__IMPORT", 0x3000, 0x1000, 0x2000, 0x1000, 0x7, 0x7, 0x1, 0x0},
|
||||||
|
&SegmentHeader{LoadCmdSegment, 0x38, "__LINKEDIT", 0x4000, 0x1000, 0x3000, 0x12c, 0x7, 0x1, 0x0, 0x0},
|
||||||
|
nil, // LC_SYMTAB
|
||||||
|
nil, // LC_DYSYMTAB
|
||||||
|
nil, // LC_LOAD_DYLINKER
|
||||||
|
nil, // LC_UUID
|
||||||
|
nil, // LC_UNIXTHREAD
|
||||||
|
&Dylib{nil, "/usr/lib/libgcc_s.1.dylib", 0x2, 0x10000, 0x10000},
|
||||||
|
&Dylib{nil, "/usr/lib/libSystem.B.dylib", 0x2, 0x6f0104, 0x10000},
|
||||||
|
},
|
||||||
|
[]*SectionHeader{
|
||||||
|
{"__text", "__TEXT", 0x1f68, 0x88, 0xf68, 0x2, 0x0, 0x0, 0x80000400},
|
||||||
|
{"__cstring", "__TEXT", 0x1ff0, 0xd, 0xff0, 0x0, 0x0, 0x0, 0x2},
|
||||||
|
{"__data", "__DATA", 0x2000, 0x14, 0x1000, 0x2, 0x0, 0x0, 0x0},
|
||||||
|
{"__dyld", "__DATA", 0x2014, 0x1c, 0x1014, 0x2, 0x0, 0x0, 0x0},
|
||||||
|
{"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"testdata/gcc-amd64-darwin-exec",
|
||||||
|
FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0xb, 0x568, 0x85},
|
||||||
|
[]interface{}{
|
||||||
|
&SegmentHeader{LoadCmdSegment64, 0x48, "__PAGEZERO", 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
&SegmentHeader{LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x5, 0x0},
|
||||||
|
&SegmentHeader{LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x3, 0x0},
|
||||||
|
&SegmentHeader{LoadCmdSegment64, 0x48, "__LINKEDIT", 0x100002000, 0x1000, 0x2000, 0x140, 0x7, 0x1, 0x0, 0x0},
|
||||||
|
nil, // LC_SYMTAB
|
||||||
|
nil, // LC_DYSYMTAB
|
||||||
|
nil, // LC_LOAD_DYLINKER
|
||||||
|
nil, // LC_UUID
|
||||||
|
nil, // LC_UNIXTHREAD
|
||||||
|
&Dylib{nil, "/usr/lib/libgcc_s.1.dylib", 0x2, 0x10000, 0x10000},
|
||||||
|
&Dylib{nil, "/usr/lib/libSystem.B.dylib", 0x2, 0x6f0104, 0x10000},
|
||||||
|
},
|
||||||
|
[]*SectionHeader{
|
||||||
|
{"__text", "__TEXT", 0x100000f14, 0x6d, 0xf14, 0x2, 0x0, 0x0, 0x80000400},
|
||||||
|
{"__symbol_stub1", "__TEXT", 0x100000f81, 0xc, 0xf81, 0x0, 0x0, 0x0, 0x80000408},
|
||||||
|
{"__stub_helper", "__TEXT", 0x100000f90, 0x18, 0xf90, 0x2, 0x0, 0x0, 0x0},
|
||||||
|
{"__cstring", "__TEXT", 0x100000fa8, 0xd, 0xfa8, 0x0, 0x0, 0x0, 0x2},
|
||||||
|
{"__eh_frame", "__TEXT", 0x100000fb8, 0x48, 0xfb8, 0x3, 0x0, 0x0, 0x6000000b},
|
||||||
|
{"__data", "__DATA", 0x100001000, 0x1c, 0x1000, 0x3, 0x0, 0x0, 0x0},
|
||||||
|
{"__dyld", "__DATA", 0x100001020, 0x38, 0x1020, 0x3, 0x0, 0x0, 0x0},
|
||||||
|
{"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"testdata/gcc-amd64-darwin-exec-debug",
|
||||||
|
FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0xa, 0x4, 0x5a0, 0},
|
||||||
|
[]interface{}{
|
||||||
|
nil, // LC_UUID
|
||||||
|
&SegmentHeader{LoadCmdSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x0, 0x7, 0x5, 0x5, 0x0},
|
||||||
|
&SegmentHeader{LoadCmdSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x0, 0x0, 0x7, 0x3, 0x3, 0x0},
|
||||||
|
&SegmentHeader{LoadCmdSegment64, 0x278, "__DWARF", 0x100002000, 0x1000, 0x1000, 0x1bc, 0x7, 0x3, 0x7, 0x0},
|
||||||
|
},
|
||||||
|
[]*SectionHeader{
|
||||||
|
{"__text", "__TEXT", 0x100000f14, 0x0, 0x0, 0x2, 0x0, 0x0, 0x80000400},
|
||||||
|
{"__symbol_stub1", "__TEXT", 0x100000f81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000408},
|
||||||
|
{"__stub_helper", "__TEXT", 0x100000f90, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0},
|
||||||
|
{"__cstring", "__TEXT", 0x100000fa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2},
|
||||||
|
{"__eh_frame", "__TEXT", 0x100000fb8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x6000000b},
|
||||||
|
{"__data", "__DATA", 0x100001000, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0},
|
||||||
|
{"__dyld", "__DATA", 0x100001020, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0},
|
||||||
|
{"__la_symbol_ptr", "__DATA", 0x100001058, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7},
|
||||||
|
{"__debug_abbrev", "__DWARF", 0x100002000, 0x36, 0x1000, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
{"__debug_aranges", "__DWARF", 0x100002036, 0x30, 0x1036, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
{"__debug_frame", "__DWARF", 0x100002066, 0x40, 0x1066, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
{"__debug_info", "__DWARF", 0x1000020a6, 0x54, 0x10a6, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
{"__debug_line", "__DWARF", 0x1000020fa, 0x47, 0x10fa, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
{"__debug_pubnames", "__DWARF", 0x100002141, 0x1b, 0x1141, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
{"__debug_str", "__DWARF", 0x10000215c, 0x60, 0x115c, 0x0, 0x0, 0x0, 0x0},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"testdata/clang-386-darwin-exec-with-rpath",
|
||||||
|
FileHeader{0xfeedface, Cpu386, 0x3, 0x2, 0x10, 0x42c, 0x1200085},
|
||||||
|
[]interface{}{
|
||||||
|
nil, // LC_SEGMENT
|
||||||
|
nil, // LC_SEGMENT
|
||||||
|
nil, // LC_SEGMENT
|
||||||
|
nil, // LC_SEGMENT
|
||||||
|
nil, // LC_DYLD_INFO_ONLY
|
||||||
|
nil, // LC_SYMTAB
|
||||||
|
nil, // LC_DYSYMTAB
|
||||||
|
nil, // LC_LOAD_DYLINKER
|
||||||
|
nil, // LC_UUID
|
||||||
|
nil, // LC_VERSION_MIN_MACOSX
|
||||||
|
nil, // LC_SOURCE_VERSION
|
||||||
|
nil, // LC_MAIN
|
||||||
|
nil, // LC_LOAD_DYLIB
|
||||||
|
&Rpath{nil, "/my/rpath"},
|
||||||
|
nil, // LC_FUNCTION_STARTS
|
||||||
|
nil, // LC_DATA_IN_CODE
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"testdata/clang-amd64-darwin-exec-with-rpath",
|
||||||
|
FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0x10, 0x4c8, 0x200085},
|
||||||
|
[]interface{}{
|
||||||
|
nil, // LC_SEGMENT
|
||||||
|
nil, // LC_SEGMENT
|
||||||
|
nil, // LC_SEGMENT
|
||||||
|
nil, // LC_SEGMENT
|
||||||
|
nil, // LC_DYLD_INFO_ONLY
|
||||||
|
nil, // LC_SYMTAB
|
||||||
|
nil, // LC_DYSYMTAB
|
||||||
|
nil, // LC_LOAD_DYLINKER
|
||||||
|
nil, // LC_UUID
|
||||||
|
nil, // LC_VERSION_MIN_MACOSX
|
||||||
|
nil, // LC_SOURCE_VERSION
|
||||||
|
nil, // LC_MAIN
|
||||||
|
nil, // LC_LOAD_DYLIB
|
||||||
|
&Rpath{nil, "/my/rpath"},
|
||||||
|
nil, // LC_FUNCTION_STARTS
|
||||||
|
nil, // LC_DATA_IN_CODE
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"testdata/clang-386-darwin.obj",
|
||||||
|
FileHeader{0xfeedface, Cpu386, 0x3, 0x1, 0x4, 0x138, 0x2000},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
map[string][]Reloc{
|
||||||
|
"__text": []Reloc{
|
||||||
|
{
|
||||||
|
Addr: 0x1d,
|
||||||
|
Type: uint8(GENERIC_RELOC_VANILLA),
|
||||||
|
Len: 2,
|
||||||
|
Pcrel: true,
|
||||||
|
Extern: true,
|
||||||
|
Value: 1,
|
||||||
|
Scattered: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addr: 0xe,
|
||||||
|
Type: uint8(GENERIC_RELOC_LOCAL_SECTDIFF),
|
||||||
|
Len: 2,
|
||||||
|
Pcrel: false,
|
||||||
|
Value: 0x2d,
|
||||||
|
Scattered: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addr: 0x0,
|
||||||
|
Type: uint8(GENERIC_RELOC_PAIR),
|
||||||
|
Len: 2,
|
||||||
|
Pcrel: false,
|
||||||
|
Value: 0xb,
|
||||||
|
Scattered: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"testdata/clang-amd64-darwin.obj",
|
||||||
|
FileHeader{0xfeedfacf, CpuAmd64, 0x3, 0x1, 0x4, 0x200, 0x2000},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
map[string][]Reloc{
|
||||||
|
"__text": []Reloc{
|
||||||
|
{
|
||||||
|
Addr: 0x19,
|
||||||
|
Type: uint8(X86_64_RELOC_BRANCH),
|
||||||
|
Len: 2,
|
||||||
|
Pcrel: true,
|
||||||
|
Extern: true,
|
||||||
|
Value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Addr: 0xb,
|
||||||
|
Type: uint8(X86_64_RELOC_SIGNED),
|
||||||
|
Len: 2,
|
||||||
|
Pcrel: true,
|
||||||
|
Extern: false,
|
||||||
|
Value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"__compact_unwind": []Reloc{
|
||||||
|
{
|
||||||
|
Addr: 0x0,
|
||||||
|
Type: uint8(X86_64_RELOC_UNSIGNED),
|
||||||
|
Len: 3,
|
||||||
|
Pcrel: false,
|
||||||
|
Extern: false,
|
||||||
|
Value: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpen(t *testing.T) {
|
||||||
|
for i := range fileTests {
|
||||||
|
tt := &fileTests[i]
|
||||||
|
|
||||||
|
f, err := Open(tt.file)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(f.FileHeader, tt.hdr) {
|
||||||
|
t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for i, l := range f.Loads {
|
||||||
|
if len(l.Raw()) < 8 {
|
||||||
|
t.Errorf("open %s, command %d:\n\tload command %T don't have enough data\n", tt.file, i, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tt.loads != nil {
|
||||||
|
for i, l := range f.Loads {
|
||||||
|
if i >= len(tt.loads) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
want := tt.loads[i]
|
||||||
|
if want == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch l := l.(type) {
|
||||||
|
case *Segment:
|
||||||
|
have := &l.SegmentHeader
|
||||||
|
if !reflect.DeepEqual(have, want) {
|
||||||
|
t.Errorf("open %s, command %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
|
||||||
|
}
|
||||||
|
case *Dylib:
|
||||||
|
have := l
|
||||||
|
have.LoadBytes = nil
|
||||||
|
if !reflect.DeepEqual(have, want) {
|
||||||
|
t.Errorf("open %s, command %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
|
||||||
|
}
|
||||||
|
case *Rpath:
|
||||||
|
have := l
|
||||||
|
have.LoadBytes = nil
|
||||||
|
if !reflect.DeepEqual(have, want) {
|
||||||
|
t.Errorf("open %s, command %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Errorf("open %s, command %d: unknown load command\n\thave %#v\n\twant %#v\n", tt.file, i, l, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tn := len(tt.loads)
|
||||||
|
fn := len(f.Loads)
|
||||||
|
if tn != fn {
|
||||||
|
t.Errorf("open %s: len(Loads) = %d, want %d", tt.file, fn, tn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.sections != nil {
|
||||||
|
for i, sh := range f.Sections {
|
||||||
|
if i >= len(tt.sections) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
have := &sh.SectionHeader
|
||||||
|
want := tt.sections[i]
|
||||||
|
if !reflect.DeepEqual(have, want) {
|
||||||
|
t.Errorf("open %s, section %d:\n\thave %#v\n\twant %#v\n", tt.file, i, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tn := len(tt.sections)
|
||||||
|
fn := len(f.Sections)
|
||||||
|
if tn != fn {
|
||||||
|
t.Errorf("open %s: len(Sections) = %d, want %d", tt.file, fn, tn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.relocations != nil {
|
||||||
|
for i, sh := range f.Sections {
|
||||||
|
have := sh.Relocs
|
||||||
|
want := tt.relocations[sh.Name]
|
||||||
|
if !reflect.DeepEqual(have, want) {
|
||||||
|
t.Errorf("open %s, relocations in section %d (%s):\n\thave %#v\n\twant %#v\n", tt.file, i, sh.Name, have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenFailure(t *testing.T) {
|
||||||
|
filename := "file.go" // not a Mach-O file
|
||||||
|
_, err := Open(filename) // don't crash
|
||||||
|
if err == nil {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRelocTypeString(t *testing.T) {
|
||||||
|
if X86_64_RELOC_BRANCH.String() != "X86_64_RELOC_BRANCH" {
|
||||||
|
t.Errorf("got %v, want %v", X86_64_RELOC_BRANCH.String(), "X86_64_RELOC_BRANCH")
|
||||||
|
}
|
||||||
|
if X86_64_RELOC_BRANCH.GoString() != "macho.X86_64_RELOC_BRANCH" {
|
||||||
|
t.Errorf("got %v, want %v", X86_64_RELOC_BRANCH.GoString(), "macho.X86_64_RELOC_BRANCH")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTypeString(t *testing.T) {
|
||||||
|
if TypeExec.String() != "Exec" {
|
||||||
|
t.Errorf("got %v, want %v", TypeExec.String(), "Exec")
|
||||||
|
}
|
||||||
|
if TypeExec.GoString() != "macho.Exec" {
|
||||||
|
t.Errorf("got %v, want %v", TypeExec.GoString(), "macho.Exec")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,336 @@
|
||||||
|
// Copyright 2009 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.
|
||||||
|
|
||||||
|
// Mach-O header data structures
|
||||||
|
// http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
|
||||||
|
|
||||||
|
package macho
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// A FileHeader represents a Mach-O file header.
|
||||||
|
type FileHeader struct {
|
||||||
|
Magic uint32
|
||||||
|
Cpu Cpu
|
||||||
|
SubCpu uint32
|
||||||
|
Type Type
|
||||||
|
Ncmd uint32
|
||||||
|
Cmdsz uint32
|
||||||
|
Flags uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileHeaderSize32 = 7 * 4
|
||||||
|
fileHeaderSize64 = 8 * 4
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Magic32 uint32 = 0xfeedface
|
||||||
|
Magic64 uint32 = 0xfeedfacf
|
||||||
|
MagicFat uint32 = 0xcafebabe
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
TypeDylib Type = 6
|
||||||
|
TypeBundle Type = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
var typeStrings = []intName{
|
||||||
|
{uint32(TypeObj), "Obj"},
|
||||||
|
{uint32(TypeExec), "Exec"},
|
||||||
|
{uint32(TypeDylib), "Dylib"},
|
||||||
|
{uint32(TypeBundle), "Bundle"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Type) String() string { return stringName(uint32(t), typeStrings, false) }
|
||||||
|
func (t Type) GoString() string { return stringName(uint32(t), typeStrings, true) }
|
||||||
|
|
||||||
|
// A Cpu is a Mach-O cpu type.
|
||||||
|
type Cpu uint32
|
||||||
|
|
||||||
|
const cpuArch64 = 0x01000000
|
||||||
|
|
||||||
|
const (
|
||||||
|
Cpu386 Cpu = 7
|
||||||
|
CpuAmd64 Cpu = Cpu386 | cpuArch64
|
||||||
|
CpuArm Cpu = 12
|
||||||
|
CpuArm64 Cpu = CpuArm | cpuArch64
|
||||||
|
CpuPpc Cpu = 18
|
||||||
|
CpuPpc64 Cpu = CpuPpc | cpuArch64
|
||||||
|
)
|
||||||
|
|
||||||
|
var cpuStrings = []intName{
|
||||||
|
{uint32(Cpu386), "Cpu386"},
|
||||||
|
{uint32(CpuAmd64), "CpuAmd64"},
|
||||||
|
{uint32(CpuArm), "CpuArm"},
|
||||||
|
{uint32(CpuArm64), "CpuArm64"},
|
||||||
|
{uint32(CpuPpc), "CpuPpc"},
|
||||||
|
{uint32(CpuPpc64), "CpuPpc64"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Cpu) String() string { return stringName(uint32(i), cpuStrings, false) }
|
||||||
|
func (i Cpu) GoString() string { return stringName(uint32(i), cpuStrings, true) }
|
||||||
|
|
||||||
|
// A LoadCmd is a Mach-O load command.
|
||||||
|
type LoadCmd uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
LoadCmdSegment LoadCmd = 0x1
|
||||||
|
LoadCmdSymtab LoadCmd = 0x2
|
||||||
|
LoadCmdThread LoadCmd = 0x4
|
||||||
|
LoadCmdUnixThread LoadCmd = 0x5 // thread+stack
|
||||||
|
LoadCmdDysymtab LoadCmd = 0xb
|
||||||
|
LoadCmdDylib LoadCmd = 0xc // load dylib command
|
||||||
|
LoadCmdDylinker LoadCmd = 0xf // id dylinker command (not load dylinker command)
|
||||||
|
LoadCmdSegment64 LoadCmd = 0x19
|
||||||
|
LoadCmdRpath LoadCmd = 0x8000001c
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdStrings = []intName{
|
||||||
|
{uint32(LoadCmdSegment), "LoadCmdSegment"},
|
||||||
|
{uint32(LoadCmdThread), "LoadCmdThread"},
|
||||||
|
{uint32(LoadCmdUnixThread), "LoadCmdUnixThread"},
|
||||||
|
{uint32(LoadCmdDylib), "LoadCmdDylib"},
|
||||||
|
{uint32(LoadCmdSegment64), "LoadCmdSegment64"},
|
||||||
|
{uint32(LoadCmdRpath), "LoadCmdRpath"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) }
|
||||||
|
func (i LoadCmd) GoString() string { return stringName(uint32(i), cmdStrings, true) }
|
||||||
|
|
||||||
|
type (
|
||||||
|
// A Segment32 is a 32-bit Mach-O segment load command.
|
||||||
|
Segment32 struct {
|
||||||
|
Cmd LoadCmd
|
||||||
|
Len uint32
|
||||||
|
Name [16]byte
|
||||||
|
Addr uint32
|
||||||
|
Memsz uint32
|
||||||
|
Offset uint32
|
||||||
|
Filesz uint32
|
||||||
|
Maxprot uint32
|
||||||
|
Prot uint32
|
||||||
|
Nsect uint32
|
||||||
|
Flag uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Segment64 is a 64-bit Mach-O segment load command.
|
||||||
|
Segment64 struct {
|
||||||
|
Cmd LoadCmd
|
||||||
|
Len uint32
|
||||||
|
Name [16]byte
|
||||||
|
Addr uint64
|
||||||
|
Memsz uint64
|
||||||
|
Offset uint64
|
||||||
|
Filesz uint64
|
||||||
|
Maxprot uint32
|
||||||
|
Prot uint32
|
||||||
|
Nsect uint32
|
||||||
|
Flag uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SymtabCmd is a Mach-O symbol table command.
|
||||||
|
SymtabCmd struct {
|
||||||
|
Cmd LoadCmd
|
||||||
|
Len uint32
|
||||||
|
Symoff uint32
|
||||||
|
Nsyms uint32
|
||||||
|
Stroff uint32
|
||||||
|
Strsize uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A DysymtabCmd is a Mach-O dynamic symbol table command.
|
||||||
|
DysymtabCmd struct {
|
||||||
|
Cmd LoadCmd
|
||||||
|
Len uint32
|
||||||
|
Ilocalsym uint32
|
||||||
|
Nlocalsym uint32
|
||||||
|
Iextdefsym uint32
|
||||||
|
Nextdefsym uint32
|
||||||
|
Iundefsym uint32
|
||||||
|
Nundefsym uint32
|
||||||
|
Tocoffset uint32
|
||||||
|
Ntoc uint32
|
||||||
|
Modtaboff uint32
|
||||||
|
Nmodtab uint32
|
||||||
|
Extrefsymoff uint32
|
||||||
|
Nextrefsyms uint32
|
||||||
|
Indirectsymoff uint32
|
||||||
|
Nindirectsyms uint32
|
||||||
|
Extreloff uint32
|
||||||
|
Nextrel uint32
|
||||||
|
Locreloff uint32
|
||||||
|
Nlocrel uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A DylibCmd is a Mach-O load dynamic library command.
|
||||||
|
DylibCmd struct {
|
||||||
|
Cmd LoadCmd
|
||||||
|
Len uint32
|
||||||
|
Name uint32
|
||||||
|
Time uint32
|
||||||
|
CurrentVersion uint32
|
||||||
|
CompatVersion uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A RpathCmd is a Mach-O rpath command.
|
||||||
|
RpathCmd struct {
|
||||||
|
Cmd LoadCmd
|
||||||
|
Len uint32
|
||||||
|
Path uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Thread is a Mach-O thread state command.
|
||||||
|
Thread struct {
|
||||||
|
Cmd LoadCmd
|
||||||
|
Len uint32
|
||||||
|
Type uint32
|
||||||
|
Data []uint32
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FlagNoUndefs uint32 = 0x1
|
||||||
|
FlagIncrLink uint32 = 0x2
|
||||||
|
FlagDyldLink uint32 = 0x4
|
||||||
|
FlagBindAtLoad uint32 = 0x8
|
||||||
|
FlagPrebound uint32 = 0x10
|
||||||
|
FlagSplitSegs uint32 = 0x20
|
||||||
|
FlagLazyInit uint32 = 0x40
|
||||||
|
FlagTwoLevel uint32 = 0x80
|
||||||
|
FlagForceFlat uint32 = 0x100
|
||||||
|
FlagNoMultiDefs uint32 = 0x200
|
||||||
|
FlagNoFixPrebinding uint32 = 0x400
|
||||||
|
FlagPrebindable uint32 = 0x800
|
||||||
|
FlagAllModsBound uint32 = 0x1000
|
||||||
|
FlagSubsectionsViaSymbols uint32 = 0x2000
|
||||||
|
FlagCanonical uint32 = 0x4000
|
||||||
|
FlagWeakDefines uint32 = 0x8000
|
||||||
|
FlagBindsToWeak uint32 = 0x10000
|
||||||
|
FlagAllowStackExecution uint32 = 0x20000
|
||||||
|
FlagRootSafe uint32 = 0x40000
|
||||||
|
FlagSetuidSafe uint32 = 0x80000
|
||||||
|
FlagNoReexportedDylibs uint32 = 0x100000
|
||||||
|
FlagPIE uint32 = 0x200000
|
||||||
|
FlagDeadStrippableDylib uint32 = 0x400000
|
||||||
|
FlagHasTLVDescriptors uint32 = 0x800000
|
||||||
|
FlagNoHeapExecution uint32 = 0x1000000
|
||||||
|
FlagAppExtensionSafe uint32 = 0x2000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Section32 is a 32-bit Mach-O section header.
|
||||||
|
type Section32 struct {
|
||||||
|
Name [16]byte
|
||||||
|
Seg [16]byte
|
||||||
|
Addr uint32
|
||||||
|
Size uint32
|
||||||
|
Offset uint32
|
||||||
|
Align uint32
|
||||||
|
Reloff uint32
|
||||||
|
Nreloc uint32
|
||||||
|
Flags uint32
|
||||||
|
Reserve1 uint32
|
||||||
|
Reserve2 uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Section64 is a 64-bit Mach-O section header.
|
||||||
|
type Section64 struct {
|
||||||
|
Name [16]byte
|
||||||
|
Seg [16]byte
|
||||||
|
Addr uint64
|
||||||
|
Size uint64
|
||||||
|
Offset uint32
|
||||||
|
Align uint32
|
||||||
|
Reloff uint32
|
||||||
|
Nreloc uint32
|
||||||
|
Flags uint32
|
||||||
|
Reserve1 uint32
|
||||||
|
Reserve2 uint32
|
||||||
|
Reserve3 uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Nlist32 is a Mach-O 32-bit symbol table entry.
|
||||||
|
type Nlist32 struct {
|
||||||
|
Name uint32
|
||||||
|
Type uint8
|
||||||
|
Sect uint8
|
||||||
|
Desc uint16
|
||||||
|
Value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Nlist64 is a Mach-O 64-bit symbol table entry.
|
||||||
|
type Nlist64 struct {
|
||||||
|
Name uint32
|
||||||
|
Type uint8
|
||||||
|
Sect uint8
|
||||||
|
Desc uint16
|
||||||
|
Value uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regs386 is the Mach-O 386 register structure.
|
||||||
|
type Regs386 struct {
|
||||||
|
AX uint32
|
||||||
|
BX uint32
|
||||||
|
CX uint32
|
||||||
|
DX uint32
|
||||||
|
DI uint32
|
||||||
|
SI uint32
|
||||||
|
BP uint32
|
||||||
|
SP uint32
|
||||||
|
SS uint32
|
||||||
|
FLAGS uint32
|
||||||
|
IP uint32
|
||||||
|
CS uint32
|
||||||
|
DS uint32
|
||||||
|
ES uint32
|
||||||
|
FS uint32
|
||||||
|
GS uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegsAMD64 is the Mach-O AMD64 register structure.
|
||||||
|
type RegsAMD64 struct {
|
||||||
|
AX uint64
|
||||||
|
BX uint64
|
||||||
|
CX uint64
|
||||||
|
DX uint64
|
||||||
|
DI uint64
|
||||||
|
SI uint64
|
||||||
|
BP uint64
|
||||||
|
SP uint64
|
||||||
|
R8 uint64
|
||||||
|
R9 uint64
|
||||||
|
R10 uint64
|
||||||
|
R11 uint64
|
||||||
|
R12 uint64
|
||||||
|
R13 uint64
|
||||||
|
R14 uint64
|
||||||
|
R15 uint64
|
||||||
|
IP uint64
|
||||||
|
FLAGS uint64
|
||||||
|
CS uint64
|
||||||
|
FS uint64
|
||||||
|
GS uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type intName struct {
|
||||||
|
i uint32
|
||||||
|
s string
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringName(i uint32, names []intName, goSyntax bool) string {
|
||||||
|
for _, n := range names {
|
||||||
|
if n.i == i {
|
||||||
|
if goSyntax {
|
||||||
|
return "macho." + n.s
|
||||||
|
}
|
||||||
|
return n.s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strconv.FormatUint(uint64(i), 10)
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2017 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
|
||||||
|
|
||||||
|
//go:generate stringer -type=RelocTypeGeneric,RelocTypeX86_64,RelocTypeARM,RelocTypeARM64 -output reloctype_string.go
|
||||||
|
|
||||||
|
type RelocTypeGeneric int
|
||||||
|
|
||||||
|
const (
|
||||||
|
GENERIC_RELOC_VANILLA RelocTypeGeneric = 0
|
||||||
|
GENERIC_RELOC_PAIR RelocTypeGeneric = 1
|
||||||
|
GENERIC_RELOC_SECTDIFF RelocTypeGeneric = 2
|
||||||
|
GENERIC_RELOC_PB_LA_PTR RelocTypeGeneric = 3
|
||||||
|
GENERIC_RELOC_LOCAL_SECTDIFF RelocTypeGeneric = 4
|
||||||
|
GENERIC_RELOC_TLV RelocTypeGeneric = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r RelocTypeGeneric) GoString() string { return "macho." + r.String() }
|
||||||
|
|
||||||
|
type RelocTypeX86_64 int
|
||||||
|
|
||||||
|
const (
|
||||||
|
X86_64_RELOC_UNSIGNED RelocTypeX86_64 = 0
|
||||||
|
X86_64_RELOC_SIGNED RelocTypeX86_64 = 1
|
||||||
|
X86_64_RELOC_BRANCH RelocTypeX86_64 = 2
|
||||||
|
X86_64_RELOC_GOT_LOAD RelocTypeX86_64 = 3
|
||||||
|
X86_64_RELOC_GOT RelocTypeX86_64 = 4
|
||||||
|
X86_64_RELOC_SUBTRACTOR RelocTypeX86_64 = 5
|
||||||
|
X86_64_RELOC_SIGNED_1 RelocTypeX86_64 = 6
|
||||||
|
X86_64_RELOC_SIGNED_2 RelocTypeX86_64 = 7
|
||||||
|
X86_64_RELOC_SIGNED_4 RelocTypeX86_64 = 8
|
||||||
|
X86_64_RELOC_TLV RelocTypeX86_64 = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r RelocTypeX86_64) GoString() string { return "macho." + r.String() }
|
||||||
|
|
||||||
|
type RelocTypeARM int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ARM_RELOC_VANILLA RelocTypeARM = 0
|
||||||
|
ARM_RELOC_PAIR RelocTypeARM = 1
|
||||||
|
ARM_RELOC_SECTDIFF RelocTypeARM = 2
|
||||||
|
ARM_RELOC_LOCAL_SECTDIFF RelocTypeARM = 3
|
||||||
|
ARM_RELOC_PB_LA_PTR RelocTypeARM = 4
|
||||||
|
ARM_RELOC_BR24 RelocTypeARM = 5
|
||||||
|
ARM_THUMB_RELOC_BR22 RelocTypeARM = 6
|
||||||
|
ARM_THUMB_32BIT_BRANCH RelocTypeARM = 7
|
||||||
|
ARM_RELOC_HALF RelocTypeARM = 8
|
||||||
|
ARM_RELOC_HALF_SECTDIFF RelocTypeARM = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r RelocTypeARM) GoString() string { return "macho." + r.String() }
|
||||||
|
|
||||||
|
type RelocTypeARM64 int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ARM64_RELOC_UNSIGNED RelocTypeARM64 = 0
|
||||||
|
ARM64_RELOC_SUBTRACTOR RelocTypeARM64 = 1
|
||||||
|
ARM64_RELOC_BRANCH26 RelocTypeARM64 = 2
|
||||||
|
ARM64_RELOC_PAGE21 RelocTypeARM64 = 3
|
||||||
|
ARM64_RELOC_PAGEOFF12 RelocTypeARM64 = 4
|
||||||
|
ARM64_RELOC_GOT_LOAD_PAGE21 RelocTypeARM64 = 5
|
||||||
|
ARM64_RELOC_GOT_LOAD_PAGEOFF12 RelocTypeARM64 = 6
|
||||||
|
ARM64_RELOC_POINTER_TO_GOT RelocTypeARM64 = 7
|
||||||
|
ARM64_RELOC_TLVP_LOAD_PAGE21 RelocTypeARM64 = 8
|
||||||
|
ARM64_RELOC_TLVP_LOAD_PAGEOFF12 RelocTypeARM64 = 9
|
||||||
|
ARM64_RELOC_ADDEND RelocTypeARM64 = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r RelocTypeARM64) GoString() string { return "macho." + r.String() }
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Code generated by "stringer -type=RelocTypeGeneric,RelocTypeX86_64,RelocTypeARM,RelocTypeARM64 -output reloctype_string.go"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package macho
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
const _RelocTypeGeneric_name = "GENERIC_RELOC_VANILLAGENERIC_RELOC_PAIRGENERIC_RELOC_SECTDIFFGENERIC_RELOC_PB_LA_PTRGENERIC_RELOC_LOCAL_SECTDIFFGENERIC_RELOC_TLV"
|
||||||
|
|
||||||
|
var _RelocTypeGeneric_index = [...]uint8{0, 21, 39, 61, 84, 112, 129}
|
||||||
|
|
||||||
|
func (i RelocTypeGeneric) String() string {
|
||||||
|
if i < 0 || i >= RelocTypeGeneric(len(_RelocTypeGeneric_index)-1) {
|
||||||
|
return "RelocTypeGeneric(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _RelocTypeGeneric_name[_RelocTypeGeneric_index[i]:_RelocTypeGeneric_index[i+1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _RelocTypeX86_64_name = "X86_64_RELOC_UNSIGNEDX86_64_RELOC_SIGNEDX86_64_RELOC_BRANCHX86_64_RELOC_GOT_LOADX86_64_RELOC_GOTX86_64_RELOC_SUBTRACTORX86_64_RELOC_SIGNED_1X86_64_RELOC_SIGNED_2X86_64_RELOC_SIGNED_4X86_64_RELOC_TLV"
|
||||||
|
|
||||||
|
var _RelocTypeX86_64_index = [...]uint8{0, 21, 40, 59, 80, 96, 119, 140, 161, 182, 198}
|
||||||
|
|
||||||
|
func (i RelocTypeX86_64) String() string {
|
||||||
|
if i < 0 || i >= RelocTypeX86_64(len(_RelocTypeX86_64_index)-1) {
|
||||||
|
return "RelocTypeX86_64(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _RelocTypeX86_64_name[_RelocTypeX86_64_index[i]:_RelocTypeX86_64_index[i+1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _RelocTypeARM_name = "ARM_RELOC_VANILLAARM_RELOC_PAIRARM_RELOC_SECTDIFFARM_RELOC_LOCAL_SECTDIFFARM_RELOC_PB_LA_PTRARM_RELOC_BR24ARM_THUMB_RELOC_BR22ARM_THUMB_32BIT_BRANCHARM_RELOC_HALFARM_RELOC_HALF_SECTDIFF"
|
||||||
|
|
||||||
|
var _RelocTypeARM_index = [...]uint8{0, 17, 31, 49, 73, 92, 106, 126, 148, 162, 185}
|
||||||
|
|
||||||
|
func (i RelocTypeARM) String() string {
|
||||||
|
if i < 0 || i >= RelocTypeARM(len(_RelocTypeARM_index)-1) {
|
||||||
|
return "RelocTypeARM(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _RelocTypeARM_name[_RelocTypeARM_index[i]:_RelocTypeARM_index[i+1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _RelocTypeARM64_name = "ARM64_RELOC_UNSIGNEDARM64_RELOC_SUBTRACTORARM64_RELOC_BRANCH26ARM64_RELOC_PAGE21ARM64_RELOC_PAGEOFF12ARM64_RELOC_GOT_LOAD_PAGE21ARM64_RELOC_GOT_LOAD_PAGEOFF12ARM64_RELOC_POINTER_TO_GOTARM64_RELOC_TLVP_LOAD_PAGE21ARM64_RELOC_TLVP_LOAD_PAGEOFF12ARM64_RELOC_ADDEND"
|
||||||
|
|
||||||
|
var _RelocTypeARM64_index = [...]uint16{0, 20, 42, 62, 80, 101, 128, 158, 184, 212, 243, 261}
|
||||||
|
|
||||||
|
func (i RelocTypeARM64) String() string {
|
||||||
|
if i < 0 || i >= RelocTypeARM64(len(_RelocTypeARM64_index)-1) {
|
||||||
|
return "RelocTypeARM64(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _RelocTypeARM64_name[_RelocTypeARM64_index[i]:_RelocTypeARM64_index[i+1]]
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,8 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
printf("hello, world\n");
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue