whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
// 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"
|
||||
"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
|
||||
}
|
||||
|
||||
// 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, %v", err)
|
||||
} 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, formatError(0, "not a fat Mach-O file, leMagic=0x%x", leMagic)
|
||||
} else {
|
||||
return nil, formatError(0, "invalid magic number, leMagic=0x%x", leMagic)
|
||||
}
|
||||
}
|
||||
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 %v", err)
|
||||
}
|
||||
offset += 4
|
||||
|
||||
if narch < 1 {
|
||||
return nil, formatError(offset, "file contains no images, narch=%d", narch)
|
||||
}
|
||||
|
||||
// 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 HdrType
|
||||
|
||||
// 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, %v", err)
|
||||
}
|
||||
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, "duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu)
|
||||
}
|
||||
seenArches[seenArch] = true
|
||||
|
||||
// Make sure the Mach-O type matches that of the first image.
|
||||
if i == 0 {
|
||||
machoType = HdrType(fa.Type)
|
||||
} else {
|
||||
if HdrType(fa.Type) != machoType {
|
||||
return nil, formatError(offset, "Mach-O type for architecture #%d (type=%#x) does not match first (type=%#x)", i, fa.Type, machoType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,389 @@
|
||||
// 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"
|
||||
"strings"
|
||||
"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{LcSegment, 0x38, "__PAGEZERO", 0x0, 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0},
|
||||
&SegmentHeader{LcSegment, 0xc0, "__TEXT", 0x1000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x2, 0x0, 0},
|
||||
&SegmentHeader{LcSegment, 0xc0, "__DATA", 0x2000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x2, 0x0, 2},
|
||||
&SegmentHeader{LcSegment, 0x7c, "__IMPORT", 0x3000, 0x1000, 0x2000, 0x1000, 0x7, 0x7, 0x1, 0x0, 4},
|
||||
&SegmentHeader{LcSegment, 0x38, "__LINKEDIT", 0x4000, 0x1000, 0x3000, 0x12c, 0x7, 0x1, 0x0, 0x0, 5},
|
||||
nil, // LC_SYMTAB
|
||||
nil, // LC_DYSYMTAB
|
||||
nil, // LC_LOAD_DYLINKER
|
||||
nil, // LC_UUID
|
||||
nil, // LC_UNIXTHREAD
|
||||
&Dylib{DylibCmd{}, "/usr/lib/libgcc_s.1.dylib", 0x2, 0x10000, 0x10000},
|
||||
&Dylib{DylibCmd{}, "/usr/lib/libSystem.B.dylib", 0x2, 0x6f0104, 0x10000},
|
||||
},
|
||||
[]*SectionHeader{
|
||||
{"__text", "__TEXT", 0x1f68, 0x88, 0xf68, 0x2, 0x0, 0x0, 0x80000400, 0, 0, 0},
|
||||
{"__cstring", "__TEXT", 0x1ff0, 0xd, 0xff0, 0x0, 0x0, 0x0, 0x2, 0, 0, 0},
|
||||
{"__data", "__DATA", 0x2000, 0x14, 0x1000, 0x2, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__dyld", "__DATA", 0x2014, 0x1c, 0x1014, 0x2, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__jump_table", "__IMPORT", 0x3000, 0xa, 0x2000, 0x6, 0x0, 0x0, 0x4000008, 0, 5, 0},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"testdata/gcc-amd64-darwin-exec",
|
||||
FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0x2, 0xb, 0x568, 0x85},
|
||||
[]interface{}{
|
||||
&SegmentHeader{LcSegment64, 0x48, "__PAGEZERO", 0x0, 0x100000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0},
|
||||
&SegmentHeader{LcSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x1000, 0x7, 0x5, 0x5, 0x0, 0},
|
||||
&SegmentHeader{LcSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x1000, 0x1000, 0x7, 0x3, 0x3, 0x0, 5},
|
||||
&SegmentHeader{LcSegment64, 0x48, "__LINKEDIT", 0x100002000, 0x1000, 0x2000, 0x140, 0x7, 0x1, 0x0, 0x0, 8},
|
||||
nil, // LC_SYMTAB
|
||||
nil, // LC_DYSYMTAB
|
||||
nil, // LC_LOAD_DYLINKER
|
||||
nil, // LC_UUID
|
||||
nil, // LC_UNIXTHREAD
|
||||
&Dylib{DylibCmd{}, "/usr/lib/libgcc_s.1.dylib", 0x2, 0x10000, 0x10000},
|
||||
&Dylib{DylibCmd{}, "/usr/lib/libSystem.B.dylib", 0x2, 0x6f0104, 0x10000},
|
||||
},
|
||||
[]*SectionHeader{
|
||||
{"__text", "__TEXT", 0x100000f14, 0x6d, 0xf14, 0x2, 0x0, 0x0, 0x80000400, 0, 0, 0},
|
||||
{"__symbol_stub1", "__TEXT", 0x100000f81, 0xc, 0xf81, 0x0, 0x0, 0x0, 0x80000408, 0, 6, 0},
|
||||
{"__stub_helper", "__TEXT", 0x100000f90, 0x18, 0xf90, 0x2, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__cstring", "__TEXT", 0x100000fa8, 0xd, 0xfa8, 0x0, 0x0, 0x0, 0x2, 0, 0, 0},
|
||||
{"__eh_frame", "__TEXT", 0x100000fb8, 0x48, 0xfb8, 0x3, 0x0, 0x0, 0x6000000b, 0, 0, 0},
|
||||
{"__data", "__DATA", 0x100001000, 0x1c, 0x1000, 0x3, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__dyld", "__DATA", 0x100001020, 0x38, 0x1020, 0x3, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__la_symbol_ptr", "__DATA", 0x100001058, 0x10, 0x1058, 0x2, 0x0, 0x0, 0x7, 2, 0, 0},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"testdata/gcc-amd64-darwin-exec-debug",
|
||||
FileHeader{0xfeedfacf, CpuAmd64, 0x80000003, 0xa, 0x4, 0x5a0, 0},
|
||||
[]interface{}{
|
||||
nil, // LC_UUID
|
||||
&SegmentHeader{LcSegment64, 0x1d8, "__TEXT", 0x100000000, 0x1000, 0x0, 0x0, 0x7, 0x5, 0x5, 0x0, 0},
|
||||
&SegmentHeader{LcSegment64, 0x138, "__DATA", 0x100001000, 0x1000, 0x0, 0x0, 0x7, 0x3, 0x3, 0x0, 5},
|
||||
&SegmentHeader{LcSegment64, 0x278, "__DWARF", 0x100002000, 0x1000, 0x1000, 0x1bc, 0x7, 0x3, 0x7, 0x0, 8},
|
||||
},
|
||||
[]*SectionHeader{
|
||||
{"__text", "__TEXT", 0x100000f14, 0x0, 0x0, 0x2, 0x0, 0x0, 0x80000400, 0, 0, 0},
|
||||
{"__symbol_stub1", "__TEXT", 0x100000f81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80000408, 0, 6, 0},
|
||||
{"__stub_helper", "__TEXT", 0x100000f90, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__cstring", "__TEXT", 0x100000fa8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0, 0, 0},
|
||||
{"__eh_frame", "__TEXT", 0x100000fb8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x6000000b, 0, 0, 0},
|
||||
{"__data", "__DATA", 0x100001000, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__dyld", "__DATA", 0x100001020, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__la_symbol_ptr", "__DATA", 0x100001058, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7, 2, 0, 0},
|
||||
{"__debug_abbrev", "__DWARF", 0x100002000, 0x36, 0x1000, 0x0, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__debug_aranges", "__DWARF", 0x100002036, 0x30, 0x1036, 0x0, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__debug_frame", "__DWARF", 0x100002066, 0x40, 0x1066, 0x0, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__debug_info", "__DWARF", 0x1000020a6, 0x54, 0x10a6, 0x0, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__debug_line", "__DWARF", 0x1000020fa, 0x47, 0x10fa, 0x0, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__debug_pubnames", "__DWARF", 0x100002141, 0x1b, 0x1141, 0x0, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
{"__debug_str", "__DWARF", 0x10000215c, 0x60, 0x115c, 0x0, 0x0, 0x0, 0x0, 0, 0, 0},
|
||||
},
|
||||
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{LcRpath, "/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{LcRpath, "/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 %s\n\twant %s\n", tt.file, i, have.String(), want.(*SegmentHeader).String())
|
||||
}
|
||||
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 == nil {
|
||||
t.Errorf("OpenFat %s: expected error, got nil", filename)
|
||||
}
|
||||
if _, ok := err.(*FormatError); !ok {
|
||||
t.Errorf("OpenFat %s: expected FormatError, got %v", filename, err)
|
||||
}
|
||||
|
||||
ferr := err.(*FormatError)
|
||||
if !strings.Contains(ferr.String(), "not a fat") {
|
||||
t.Errorf("OpenFat %s: expected error containing 'not a fat', got %s", filename, ferr.String())
|
||||
}
|
||||
|
||||
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 MhExecute.String() != "Exec" {
|
||||
t.Errorf("got %v, want %v", MhExecute.String(), "Exec")
|
||||
}
|
||||
if MhExecute.GoString() != "macho.Exec" {
|
||||
t.Errorf("got %v, want %v", MhExecute.GoString(), "macho.Exec")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
// 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 (
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A FileHeader represents a Mach-O file header.
|
||||
type FileHeader struct {
|
||||
Magic uint32
|
||||
Cpu Cpu
|
||||
SubCpu uint32
|
||||
Type HdrType
|
||||
NCommands uint32 // number of load commands
|
||||
SizeCommands uint32 // size of all the load commands, not including this header.
|
||||
Flags HdrFlags
|
||||
}
|
||||
|
||||
func (h *FileHeader) Put(b []byte, o binary.ByteOrder) int {
|
||||
o.PutUint32(b[0:], h.Magic)
|
||||
o.PutUint32(b[4:], uint32(h.Cpu))
|
||||
o.PutUint32(b[8:], h.SubCpu)
|
||||
o.PutUint32(b[12:], uint32(h.Type))
|
||||
o.PutUint32(b[16:], h.NCommands)
|
||||
o.PutUint32(b[20:], h.SizeCommands)
|
||||
o.PutUint32(b[24:], uint32(h.Flags))
|
||||
if h.Magic == Magic32 {
|
||||
return 28
|
||||
}
|
||||
o.PutUint32(b[28:], 0)
|
||||
return 32
|
||||
}
|
||||
|
||||
const (
|
||||
fileHeaderSize32 = 7 * 4
|
||||
fileHeaderSize64 = 8 * 4
|
||||
)
|
||||
|
||||
const (
|
||||
Magic32 uint32 = 0xfeedface
|
||||
Magic64 uint32 = 0xfeedfacf
|
||||
MagicFat uint32 = 0xcafebabe
|
||||
)
|
||||
|
||||
type HdrFlags uint32
|
||||
type SegFlags uint32
|
||||
type SecFlags uint32
|
||||
|
||||
// A HdrType is the Mach-O file type, e.g. an object file, executable, or dynamic library.
|
||||
type HdrType uint32
|
||||
|
||||
const ( // SNAKE_CASE to CamelCase translation from C names
|
||||
MhObject HdrType = 1
|
||||
MhExecute HdrType = 2
|
||||
MhCore HdrType = 4
|
||||
MhDylib HdrType = 6
|
||||
MhBundle HdrType = 8
|
||||
MhDsym HdrType = 0xa
|
||||
)
|
||||
|
||||
var typeStrings = []intName{
|
||||
{uint32(MhObject), "Obj"},
|
||||
{uint32(MhExecute), "Exec"},
|
||||
{uint32(MhDylib), "Dylib"},
|
||||
{uint32(MhBundle), "Bundle"},
|
||||
{uint32(MhDsym), "Dsym"},
|
||||
}
|
||||
|
||||
func (t HdrType) String() string { return stringName(uint32(t), typeStrings, false) }
|
||||
func (t HdrType) 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
|
||||
|
||||
func (c LoadCmd) Command() LoadCmd { return c }
|
||||
|
||||
const ( // SNAKE_CASE to CamelCase translation from C names
|
||||
// Note 3 and 8 are obsolete
|
||||
LcSegment LoadCmd = 0x1
|
||||
LcSymtab LoadCmd = 0x2
|
||||
LcThread LoadCmd = 0x4
|
||||
LcUnixthread LoadCmd = 0x5 // thread+stack
|
||||
LcDysymtab LoadCmd = 0xb
|
||||
LcDylib LoadCmd = 0xc // load dylib command
|
||||
LcIdDylib LoadCmd = 0xd // dynamically linked shared lib ident
|
||||
LcLoadDylinker LoadCmd = 0xe // load a dynamic linker
|
||||
LcIdDylinker LoadCmd = 0xf // id dylinker command (not load dylinker command)
|
||||
LcSegment64 LoadCmd = 0x19
|
||||
LcUuid LoadCmd = 0x1b
|
||||
LcCodeSignature LoadCmd = 0x1d
|
||||
LcSegmentSplitInfo LoadCmd = 0x1e
|
||||
LcRpath LoadCmd = 0x8000001c
|
||||
LcEncryptionInfo LoadCmd = 0x21
|
||||
LcDyldInfo LoadCmd = 0x22
|
||||
LcDyldInfoOnly LoadCmd = 0x80000022
|
||||
LcVersionMinMacosx LoadCmd = 0x24
|
||||
LcVersionMinIphoneos LoadCmd = 0x25
|
||||
LcFunctionStarts LoadCmd = 0x26
|
||||
LcDyldEnvironment LoadCmd = 0x27
|
||||
LcMain LoadCmd = 0x80000028 // replacement for UnixThread
|
||||
LcDataInCode LoadCmd = 0x29 // There are non-instructions in text
|
||||
LcSourceVersion LoadCmd = 0x2a // Source version used to build binary
|
||||
LcDylibCodeSignDrs LoadCmd = 0x2b
|
||||
LcEncryptionInfo64 LoadCmd = 0x2c
|
||||
LcVersionMinTvos LoadCmd = 0x2f
|
||||
LcVersionMinWatchos LoadCmd = 0x30
|
||||
)
|
||||
|
||||
var cmdStrings = []intName{
|
||||
{uint32(LcSegment), "LoadCmdSegment"},
|
||||
{uint32(LcThread), "LoadCmdThread"},
|
||||
{uint32(LcUnixthread), "LoadCmdUnixThread"},
|
||||
{uint32(LcDylib), "LoadCmdDylib"},
|
||||
{uint32(LcIdDylib), "LoadCmdIdDylib"},
|
||||
{uint32(LcLoadDylinker), "LoadCmdLoadDylinker"},
|
||||
{uint32(LcIdDylinker), "LoadCmdIdDylinker"},
|
||||
{uint32(LcSegment64), "LoadCmdSegment64"},
|
||||
{uint32(LcUuid), "LoadCmdUuid"},
|
||||
{uint32(LcRpath), "LoadCmdRpath"},
|
||||
{uint32(LcDyldEnvironment), "LoadCmdDyldEnv"},
|
||||
{uint32(LcMain), "LoadCmdMain"},
|
||||
{uint32(LcDataInCode), "LoadCmdDataInCode"},
|
||||
{uint32(LcSourceVersion), "LoadCmdSourceVersion"},
|
||||
{uint32(LcDyldInfo), "LoadCmdDyldInfo"},
|
||||
{uint32(LcDyldInfoOnly), "LoadCmdDyldInfoOnly"},
|
||||
{uint32(LcVersionMinMacosx), "LoadCmdMinOsx"},
|
||||
{uint32(LcFunctionStarts), "LoadCmdFunctionStarts"},
|
||||
}
|
||||
|
||||
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 {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
Name [16]byte
|
||||
Addr uint32
|
||||
Memsz uint32
|
||||
Offset uint32
|
||||
Filesz uint32
|
||||
Maxprot uint32
|
||||
Prot uint32
|
||||
Nsect uint32
|
||||
Flag SegFlags
|
||||
}
|
||||
|
||||
// A Segment64 is a 64-bit Mach-O segment load command.
|
||||
Segment64 struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
Name [16]byte
|
||||
Addr uint64
|
||||
Memsz uint64
|
||||
Offset uint64
|
||||
Filesz uint64
|
||||
Maxprot uint32
|
||||
Prot uint32
|
||||
Nsect uint32
|
||||
Flag SegFlags
|
||||
}
|
||||
|
||||
// A SymtabCmd is a Mach-O symbol table command.
|
||||
SymtabCmd struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
Symoff uint32
|
||||
Nsyms uint32
|
||||
Stroff uint32
|
||||
Strsize uint32
|
||||
}
|
||||
|
||||
// A DysymtabCmd is a Mach-O dynamic symbol table command.
|
||||
DysymtabCmd struct {
|
||||
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 {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
Name uint32
|
||||
Time uint32
|
||||
CurrentVersion uint32
|
||||
CompatVersion uint32
|
||||
}
|
||||
|
||||
// A DylinkerCmd is a Mach-O load dynamic linker or environment command.
|
||||
DylinkerCmd struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
Name uint32
|
||||
}
|
||||
|
||||
// A RpathCmd is a Mach-O rpath command.
|
||||
RpathCmd struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
Path uint32
|
||||
}
|
||||
|
||||
// A Thread is a Mach-O thread state command.
|
||||
Thread struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
Type uint32
|
||||
Data []uint32
|
||||
}
|
||||
|
||||
// LC_DYLD_INFO, LC_DYLD_INFO_ONLY
|
||||
DyldInfoCmd struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
RebaseOff, RebaseLen uint32 // file offset and length; data contains segment indices
|
||||
BindOff, BindLen uint32 // file offset and length; data contains segment indices
|
||||
WeakBindOff, WeakBindLen uint32 // file offset and length
|
||||
LazyBindOff, LazyBindLen uint32 // file offset and length
|
||||
ExportOff, ExportLen uint32 // file offset and length
|
||||
}
|
||||
|
||||
// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS
|
||||
LinkEditDataCmd struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
DataOff, DataLen uint32 // file offset and length
|
||||
}
|
||||
|
||||
// LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64
|
||||
EncryptionInfoCmd struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
CryptOff, CryptLen uint32 // file offset and length
|
||||
CryptId uint32
|
||||
}
|
||||
|
||||
UuidCmd struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
Id [16]byte
|
||||
}
|
||||
|
||||
// TODO Commands below not fully supported yet.
|
||||
|
||||
EntryPointCmd struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
EntryOff uint64 // file offset
|
||||
StackSize uint64 // if not zero, initial stack size
|
||||
}
|
||||
|
||||
NoteCmd struct {
|
||||
LoadCmd
|
||||
Len uint32
|
||||
Name [16]byte
|
||||
Offset, Filesz uint64 // file offset and length
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
FlagNoUndefs HdrFlags = 0x1
|
||||
FlagIncrLink HdrFlags = 0x2
|
||||
FlagDyldLink HdrFlags = 0x4
|
||||
FlagBindAtLoad HdrFlags = 0x8
|
||||
FlagPrebound HdrFlags = 0x10
|
||||
FlagSplitSegs HdrFlags = 0x20
|
||||
FlagLazyInit HdrFlags = 0x40
|
||||
FlagTwoLevel HdrFlags = 0x80
|
||||
FlagForceFlat HdrFlags = 0x100
|
||||
FlagNoMultiDefs HdrFlags = 0x200
|
||||
FlagNoFixPrebinding HdrFlags = 0x400
|
||||
FlagPrebindable HdrFlags = 0x800
|
||||
FlagAllModsBound HdrFlags = 0x1000
|
||||
FlagSubsectionsViaSymbols HdrFlags = 0x2000
|
||||
FlagCanonical HdrFlags = 0x4000
|
||||
FlagWeakDefines HdrFlags = 0x8000
|
||||
FlagBindsToWeak HdrFlags = 0x10000
|
||||
FlagAllowStackExecution HdrFlags = 0x20000
|
||||
FlagRootSafe HdrFlags = 0x40000
|
||||
FlagSetuidSafe HdrFlags = 0x80000
|
||||
FlagNoReexportedDylibs HdrFlags = 0x100000
|
||||
FlagPIE HdrFlags = 0x200000
|
||||
FlagDeadStrippableDylib HdrFlags = 0x400000
|
||||
FlagHasTLVDescriptors HdrFlags = 0x800000
|
||||
FlagNoHeapExecution HdrFlags = 0x1000000
|
||||
FlagAppExtensionSafe HdrFlags = 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 SecFlags
|
||||
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 SecFlags
|
||||
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
|
||||
}
|
||||
|
||||
func (n *Nlist64) Put64(b []byte, o binary.ByteOrder) uint32 {
|
||||
o.PutUint32(b[0:], n.Name)
|
||||
b[4] = byte(n.Type)
|
||||
b[5] = byte(n.Sect)
|
||||
o.PutUint16(b[6:], n.Desc)
|
||||
o.PutUint64(b[8:], n.Value)
|
||||
return 8 + 8
|
||||
}
|
||||
|
||||
func (n *Nlist64) Put32(b []byte, o binary.ByteOrder) uint32 {
|
||||
o.PutUint32(b[0:], n.Name)
|
||||
b[4] = byte(n.Type)
|
||||
b[5] = byte(n.Sect)
|
||||
o.PutUint16(b[6:], n.Desc)
|
||||
o.PutUint32(b[8:], uint32(n.Value))
|
||||
return 8 + 4
|
||||
}
|
||||
|
||||
// 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 "0x" + strconv.FormatUint(uint64(i), 16)
|
||||
}
|
||||
@@ -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() }
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// Code generated by "stringer -type=RelocTypeGeneric,RelocTypeX86_64,RelocTypeARM,RelocTypeARM64 -output reloctype_string.go"; DO NOT EDIT.
|
||||
|
||||
package macho
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[GENERIC_RELOC_VANILLA-0]
|
||||
_ = x[GENERIC_RELOC_PAIR-1]
|
||||
_ = x[GENERIC_RELOC_SECTDIFF-2]
|
||||
_ = x[GENERIC_RELOC_PB_LA_PTR-3]
|
||||
_ = x[GENERIC_RELOC_LOCAL_SECTDIFF-4]
|
||||
_ = x[GENERIC_RELOC_TLV-5]
|
||||
}
|
||||
|
||||
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]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[X86_64_RELOC_UNSIGNED-0]
|
||||
_ = x[X86_64_RELOC_SIGNED-1]
|
||||
_ = x[X86_64_RELOC_BRANCH-2]
|
||||
_ = x[X86_64_RELOC_GOT_LOAD-3]
|
||||
_ = x[X86_64_RELOC_GOT-4]
|
||||
_ = x[X86_64_RELOC_SUBTRACTOR-5]
|
||||
_ = x[X86_64_RELOC_SIGNED_1-6]
|
||||
_ = x[X86_64_RELOC_SIGNED_2-7]
|
||||
_ = x[X86_64_RELOC_SIGNED_4-8]
|
||||
_ = x[X86_64_RELOC_TLV-9]
|
||||
}
|
||||
|
||||
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]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ARM_RELOC_VANILLA-0]
|
||||
_ = x[ARM_RELOC_PAIR-1]
|
||||
_ = x[ARM_RELOC_SECTDIFF-2]
|
||||
_ = x[ARM_RELOC_LOCAL_SECTDIFF-3]
|
||||
_ = x[ARM_RELOC_PB_LA_PTR-4]
|
||||
_ = x[ARM_RELOC_BR24-5]
|
||||
_ = x[ARM_THUMB_RELOC_BR22-6]
|
||||
_ = x[ARM_THUMB_32BIT_BRANCH-7]
|
||||
_ = x[ARM_RELOC_HALF-8]
|
||||
_ = x[ARM_RELOC_HALF_SECTDIFF-9]
|
||||
}
|
||||
|
||||
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]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[ARM64_RELOC_UNSIGNED-0]
|
||||
_ = x[ARM64_RELOC_SUBTRACTOR-1]
|
||||
_ = x[ARM64_RELOC_BRANCH26-2]
|
||||
_ = x[ARM64_RELOC_PAGE21-3]
|
||||
_ = x[ARM64_RELOC_PAGEOFF12-4]
|
||||
_ = x[ARM64_RELOC_GOT_LOAD_PAGE21-5]
|
||||
_ = x[ARM64_RELOC_GOT_LOAD_PAGEOFF12-6]
|
||||
_ = x[ARM64_RELOC_POINTER_TO_GOT-7]
|
||||
_ = x[ARM64_RELOC_TLVP_LOAD_PAGE21-8]
|
||||
_ = x[ARM64_RELOC_TLVP_LOAD_PAGEOFF12-9]
|
||||
_ = x[ARM64_RELOC_ADDEND-10]
|
||||
}
|
||||
|
||||
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]]
|
||||
}
|
||||
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
+8
@@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
printf("hello, world\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd
|
||||
|
||||
/*
|
||||
Splitdwarf uncompresses and copies the DWARF segment of a Mach-O
|
||||
executable into the "dSYM" file expected by lldb and ports of gdb
|
||||
on OSX.
|
||||
|
||||
Usage: splitdwarf osxMachoFile [ osxDsymFile ]
|
||||
|
||||
Unless a dSYM file name is provided on the command line,
|
||||
splitdwarf will place it where the OSX tools expect it, in
|
||||
"<osxMachoFile>.dSYM/Contents/Resources/DWARF/<osxMachoFile>",
|
||||
creating directories as necessary.
|
||||
*/
|
||||
package main // import "golang.org/x/tools/cmd/splitdwarf"
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/tools/cmd/splitdwarf/internal/macho"
|
||||
)
|
||||
|
||||
const (
|
||||
pageAlign = 12 // 4096 = 1 << 12
|
||||
)
|
||||
|
||||
func note(format string, why ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", why...)
|
||||
}
|
||||
|
||||
func fail(format string, why ...interface{}) {
|
||||
note(format, why...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// splitdwarf inputexe [ outputdwarf ]
|
||||
func main() {
|
||||
if len(os.Args) < 2 || len(os.Args) > 3 {
|
||||
fmt.Printf(`
|
||||
Usage: %s input_exe [ output_dsym ]
|
||||
Reads the executable input_exe, uncompresses and copies debugging
|
||||
information into output_dsym. If output_dsym is not specified,
|
||||
the path
|
||||
input_exe.dSYM/Contents/Resources/DWARF/input_exe
|
||||
is used instead. That is the path that gdb and lldb expect
|
||||
on OSX. Input_exe needs a UUID segment; if that is missing,
|
||||
then one is created and added. In that case, the permissions
|
||||
for input_exe need to allow writing.
|
||||
`, os.Args[0])
|
||||
return
|
||||
}
|
||||
|
||||
// Read input, find DWARF, be sure it looks right
|
||||
inputExe := os.Args[1]
|
||||
exeFile, err := os.Open(inputExe)
|
||||
if err != nil {
|
||||
fail("%v", err)
|
||||
}
|
||||
exeMacho, err := macho.NewFile(exeFile)
|
||||
if err != nil {
|
||||
fail("(internal) Couldn't create macho, %v", err)
|
||||
}
|
||||
// Postpone dealing with output till input is known-good
|
||||
|
||||
// describe(&exeMacho.FileTOC)
|
||||
|
||||
// Offsets into __LINKEDIT:
|
||||
//
|
||||
// Command LC_SYMTAB =
|
||||
// (1) number of symbols at file offset (within link edit section) of 16-byte symbol table entries
|
||||
// struct {
|
||||
// StringTableIndex uint32
|
||||
// Type, SectionIndex uint8
|
||||
// Description uint16
|
||||
// Value uint64
|
||||
// }
|
||||
//
|
||||
// (2) string table offset and size. Strings are zero-byte terminated. First must be " ".
|
||||
//
|
||||
// Command LC_DYSYMTAB = indices within symtab (above), except for IndSym
|
||||
// IndSym Offset = file offset (within link edit section) of 4-byte indices within symtab.
|
||||
//
|
||||
// Section __TEXT.__symbol_stub1.
|
||||
// Offset and size (Reserved2) locate and describe a table for this section.
|
||||
// Symbols beginning at IndirectSymIndex (Reserved1) (see LC_DYSYMTAB.IndSymOffset) refer to this table.
|
||||
// (These table entries are apparently PLTs [Procedure Linkage Table/Trampoline])
|
||||
//
|
||||
// Section __DATA.__nl_symbol_ptr.
|
||||
// Reserved1 seems to be an index within the Indirect symbols (see LC_DYSYMTAB.IndSymOffset)
|
||||
// Some of these symbols appear to be duplicates of other indirect symbols appearing early
|
||||
//
|
||||
// Section __DATA.__la_symbol_ptr.
|
||||
// Reserved1 seems to be an index within the Indirect symbols (see LC_DYSYMTAB.IndSymOffset)
|
||||
// Some of these symbols appear to be duplicates of other indirect symbols appearing early
|
||||
//
|
||||
|
||||
// Create a File for the output dwarf.
|
||||
// Copy header, file type is MH_DSYM
|
||||
// Copy the relevant load commands
|
||||
|
||||
// LoadCmdUuid
|
||||
// Symtab -- very abbreviated (Use DYSYMTAB Iextdefsym, Nextdefsym to identify these).
|
||||
// Segment __PAGEZERO
|
||||
// Segment __TEXT (zero the size, zero the offset of each section)
|
||||
// Segment __DATA (zero the size, zero the offset of each section)
|
||||
// Segment __LINKEDIT (contains the symbols and strings from Symtab)
|
||||
// Segment __DWARF (uncompressed)
|
||||
|
||||
var uuid *macho.Uuid
|
||||
for _, l := range exeMacho.Loads {
|
||||
switch l.Command() {
|
||||
case macho.LcUuid:
|
||||
uuid = l.(*macho.Uuid)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure a given load is not nil
|
||||
nonnilC := func(l macho.Load, s string) {
|
||||
if l == nil {
|
||||
fail("input file %s lacks load command %s", inputExe, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Find a segment by name and ensure it is not nil
|
||||
nonnilS := func(s string) *macho.Segment {
|
||||
l := exeMacho.Segment(s)
|
||||
if l == nil {
|
||||
fail("input file %s lacks segment %s", inputExe, s)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
newtoc := exeMacho.FileTOC.DerivedCopy(macho.MhDsym, 0)
|
||||
|
||||
symtab := exeMacho.Symtab
|
||||
dysymtab := exeMacho.Dysymtab // Not appearing in output, but necessary to construct output
|
||||
nonnilC(symtab, "symtab")
|
||||
nonnilC(dysymtab, "dysymtab")
|
||||
text := nonnilS("__TEXT")
|
||||
data := nonnilS("__DATA")
|
||||
linkedit := nonnilS("__LINKEDIT")
|
||||
pagezero := nonnilS("__PAGEZERO")
|
||||
|
||||
newtext := text.CopyZeroed()
|
||||
newdata := data.CopyZeroed()
|
||||
newsymtab := symtab.Copy()
|
||||
|
||||
// Linkedit segment contain symbols and strings;
|
||||
// Symtab refers to offsets into linkedit.
|
||||
// This next bit initializes newsymtab and sets up data structures for the linkedit segment
|
||||
linkeditsyms := []macho.Nlist64{}
|
||||
linkeditstrings := []string{}
|
||||
|
||||
// Linkedit will begin at the second page, i.e., offset is one page from beginning
|
||||
// Symbols come first
|
||||
linkeditsymbase := uint32(1) << pageAlign
|
||||
|
||||
// Strings come second, offset by the number of symbols times their size.
|
||||
// Only those symbols from dysymtab.defsym are written into the debugging information.
|
||||
linkeditstringbase := linkeditsymbase + exeMacho.FileTOC.SymbolSize()*dysymtab.Nextdefsym
|
||||
|
||||
// The first two bytes of the strings are reserved for space, null (' ', \000)
|
||||
linkeditstringcur := uint32(2)
|
||||
|
||||
newsymtab.Syms = newsymtab.Syms[:0]
|
||||
newsymtab.Symoff = linkeditsymbase
|
||||
newsymtab.Stroff = linkeditstringbase
|
||||
newsymtab.Nsyms = dysymtab.Nextdefsym
|
||||
for i := uint32(0); i < dysymtab.Nextdefsym; i++ {
|
||||
ii := i + dysymtab.Iextdefsym
|
||||
oldsym := symtab.Syms[ii]
|
||||
newsymtab.Syms = append(newsymtab.Syms, oldsym)
|
||||
|
||||
linkeditsyms = append(linkeditsyms, macho.Nlist64{Name: linkeditstringcur,
|
||||
Type: oldsym.Type, Sect: oldsym.Sect, Desc: oldsym.Desc, Value: oldsym.Value})
|
||||
linkeditstringcur += uint32(len(oldsym.Name)) + 1
|
||||
linkeditstrings = append(linkeditstrings, oldsym.Name)
|
||||
}
|
||||
newsymtab.Strsize = linkeditstringcur
|
||||
|
||||
exeNeedsUuid := uuid == nil
|
||||
if exeNeedsUuid {
|
||||
uuid = &macho.Uuid{macho.UuidCmd{LoadCmd: macho.LcUuid}}
|
||||
uuid.Len = uuid.LoadSize(newtoc)
|
||||
copy(uuid.Id[0:], contentuuid(&exeMacho.FileTOC)[0:16])
|
||||
uuid.Id[6] = uuid.Id[6]&^0xf0 | 0x40 // version 4 (pseudo-random); see section 4.1.3
|
||||
uuid.Id[8] = uuid.Id[8]&^0xc0 | 0x80 // variant bits; see section 4.1.1
|
||||
}
|
||||
newtoc.AddLoad(uuid)
|
||||
|
||||
// For the specified segment (assumed to be in exeMacho) make a copy of its
|
||||
// sections with appropriate fields zeroed out, and append them to the
|
||||
// currently-last segment in newtoc.
|
||||
copyZOdSections := func(g *macho.Segment) {
|
||||
for i := g.Firstsect; i < g.Firstsect+g.Nsect; i++ {
|
||||
s := exeMacho.Sections[i].Copy()
|
||||
s.Offset = 0
|
||||
s.Reloff = 0
|
||||
s.Nreloc = 0
|
||||
newtoc.AddSection(s)
|
||||
}
|
||||
}
|
||||
|
||||
newtoc.AddLoad(newsymtab)
|
||||
newtoc.AddSegment(pagezero)
|
||||
newtoc.AddSegment(newtext)
|
||||
copyZOdSections(text)
|
||||
newtoc.AddSegment(newdata)
|
||||
copyZOdSections(data)
|
||||
|
||||
newlinkedit := linkedit.Copy()
|
||||
newlinkedit.Offset = uint64(linkeditsymbase)
|
||||
newlinkedit.Filesz = uint64(linkeditstringcur)
|
||||
newlinkedit.Addr = macho.RoundUp(newdata.Addr+newdata.Memsz, 1<<pageAlign) // Follows data sections in file
|
||||
newlinkedit.Memsz = macho.RoundUp(newlinkedit.Filesz, 1<<pageAlign)
|
||||
// The rest should copy over fine.
|
||||
newtoc.AddSegment(newlinkedit)
|
||||
|
||||
dwarf := nonnilS("__DWARF")
|
||||
newdwarf := dwarf.CopyZeroed()
|
||||
newdwarf.Offset = macho.RoundUp(newlinkedit.Offset+newlinkedit.Filesz, 1<<pageAlign)
|
||||
newdwarf.Filesz = dwarf.UncompressedSize(&exeMacho.FileTOC, 1)
|
||||
newdwarf.Addr = newlinkedit.Addr + newlinkedit.Memsz // Follows linkedit sections in file.
|
||||
newdwarf.Memsz = macho.RoundUp(newdwarf.Filesz, 1<<pageAlign)
|
||||
newtoc.AddSegment(newdwarf)
|
||||
|
||||
// Map out Dwarf sections (that is, this is section descriptors, not their contents).
|
||||
offset := uint32(newdwarf.Offset)
|
||||
for i := dwarf.Firstsect; i < dwarf.Firstsect+dwarf.Nsect; i++ {
|
||||
o := exeMacho.Sections[i]
|
||||
s := o.Copy()
|
||||
s.Offset = offset
|
||||
us := o.UncompressedSize()
|
||||
if s.Size < us {
|
||||
s.Size = uint64(us)
|
||||
s.Align = 0 // This is apparently true for debugging sections; not sure if it generalizes.
|
||||
}
|
||||
offset += uint32(us)
|
||||
if strings.HasPrefix(s.Name, "__z") {
|
||||
s.Name = "__" + s.Name[3:] // remove "z"
|
||||
}
|
||||
s.Reloff = 0
|
||||
s.Nreloc = 0
|
||||
newtoc.AddSection(s)
|
||||
}
|
||||
|
||||
// Write segments/sections.
|
||||
// Only dwarf and linkedit contain anything interesting.
|
||||
|
||||
// Memory map the output file to get the buffer directly.
|
||||
outDwarf := inputExe + ".dSYM/Contents/Resources/DWARF"
|
||||
if len(os.Args) > 2 {
|
||||
outDwarf = os.Args[2]
|
||||
} else {
|
||||
err := os.MkdirAll(outDwarf, 0755)
|
||||
if err != nil {
|
||||
fail("%v", err)
|
||||
}
|
||||
outDwarf = filepath.Join(outDwarf, filepath.Base(inputExe))
|
||||
}
|
||||
dwarfFile, buffer := CreateMmapFile(outDwarf, int64(newtoc.FileSize()))
|
||||
|
||||
// (1) Linkedit segment
|
||||
// Symbol table
|
||||
offset = uint32(newlinkedit.Offset)
|
||||
for i := range linkeditsyms {
|
||||
if exeMacho.Magic == macho.Magic64 {
|
||||
offset += linkeditsyms[i].Put64(buffer[offset:], newtoc.ByteOrder)
|
||||
} else {
|
||||
offset += linkeditsyms[i].Put32(buffer[offset:], newtoc.ByteOrder)
|
||||
}
|
||||
}
|
||||
|
||||
// Initial two bytes of string table, followed by actual zero-terminated strings.
|
||||
buffer[linkeditstringbase] = ' '
|
||||
buffer[linkeditstringbase+1] = 0
|
||||
offset = linkeditstringbase + 2
|
||||
for _, str := range linkeditstrings {
|
||||
for i := 0; i < len(str); i++ {
|
||||
buffer[offset] = str[i]
|
||||
offset++
|
||||
}
|
||||
buffer[offset] = 0
|
||||
offset++
|
||||
}
|
||||
|
||||
// (2) DWARF segment
|
||||
ioff := newdwarf.Firstsect - dwarf.Firstsect
|
||||
for i := dwarf.Firstsect; i < dwarf.Firstsect+dwarf.Nsect; i++ {
|
||||
s := exeMacho.Sections[i]
|
||||
j := i + ioff
|
||||
s.PutUncompressedData(buffer[newtoc.Sections[j].Offset:])
|
||||
}
|
||||
|
||||
// Because "text" overlaps the header and the loads, write them afterwards, just in case.
|
||||
// Write header.
|
||||
newtoc.Put(buffer)
|
||||
|
||||
err = syscall.Munmap(buffer)
|
||||
if err != nil {
|
||||
fail("Munmap %s for dwarf output failed, %v", outDwarf, err)
|
||||
}
|
||||
err = dwarfFile.Close()
|
||||
if err != nil {
|
||||
fail("Close %s for dwarf output after mmap/munmap failed, %v", outDwarf, err)
|
||||
}
|
||||
|
||||
if exeNeedsUuid { // Map the original exe, modify the header, and write the UUID command
|
||||
hdr := exeMacho.FileTOC.FileHeader
|
||||
oldCommandEnd := hdr.SizeCommands + newtoc.HdrSize()
|
||||
hdr.NCommands += 1
|
||||
hdr.SizeCommands += uuid.LoadSize(newtoc)
|
||||
|
||||
mapf, err := os.OpenFile(inputExe, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
fail("Updating UUID in binary failed, %v", err)
|
||||
}
|
||||
exebuf, err := syscall.Mmap(int(mapf.Fd()), 0, int(macho.RoundUp(uint64(hdr.SizeCommands), 1<<pageAlign)),
|
||||
syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_FILE|syscall.MAP_SHARED)
|
||||
if err != nil {
|
||||
fail("Mmap of %s for UUID update failed, %v", inputExe, err)
|
||||
}
|
||||
_ = hdr.Put(exebuf, newtoc.ByteOrder)
|
||||
_ = uuid.Put(exebuf[oldCommandEnd:], newtoc.ByteOrder)
|
||||
err = syscall.Munmap(exebuf)
|
||||
if err != nil {
|
||||
fail("Munmap of %s for UUID update failed, %v", inputExe, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateMmapFile creates the file 'outDwarf' of the specified size, mmaps that file,
|
||||
// and returns the file descriptor and mapped buffer.
|
||||
func CreateMmapFile(outDwarf string, size int64) (*os.File, []byte) {
|
||||
dwarfFile, err := os.OpenFile(outDwarf, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
fail("Open for mmap failed, %v", err)
|
||||
}
|
||||
err = os.Truncate(outDwarf, size)
|
||||
if err != nil {
|
||||
fail("Truncate/extend of %s to %d bytes failed, %v", dwarfFile, size, err)
|
||||
}
|
||||
buffer, err := syscall.Mmap(int(dwarfFile.Fd()), 0, int(size), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_FILE|syscall.MAP_SHARED)
|
||||
if err != nil {
|
||||
fail("Mmap %s for dwarf output update failed, %v", outDwarf, err)
|
||||
}
|
||||
return dwarfFile, buffer
|
||||
}
|
||||
|
||||
func describe(exem *macho.FileTOC) {
|
||||
note("Type = %s, Flags=0x%x", exem.Type, uint32(exem.Flags))
|
||||
for i, l := range exem.Loads {
|
||||
if s, ok := l.(*macho.Segment); ok {
|
||||
fmt.Printf("Load %d is Segment %s, offset=0x%x, filesz=%d, addr=0x%x, memsz=%d, nsect=%d\n", i, s.Name,
|
||||
s.Offset, s.Filesz, s.Addr, s.Memsz, s.Nsect)
|
||||
for j := uint32(0); j < s.Nsect; j++ {
|
||||
c := exem.Sections[j+s.Firstsect]
|
||||
fmt.Printf(" Section %s, offset=0x%x, size=%d, addr=0x%x, flags=0x%x, nreloc=%d, res1=%d, res2=%d, res3=%d\n", c.Name, c.Offset, c.Size, c.Addr, c.Flags, c.Nreloc, c.Reserved1, c.Reserved2, c.Reserved3)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Load %d is %v\n", i, l)
|
||||
}
|
||||
}
|
||||
if exem.SizeCommands != exem.LoadSize() {
|
||||
fail("recorded command size %d does not equal computed command size %d", exem.SizeCommands, exem.LoadSize())
|
||||
} else {
|
||||
note("recorded command size %d, computed command size %d", exem.SizeCommands, exem.LoadSize())
|
||||
}
|
||||
note("File size is %d", exem.FileSize())
|
||||
}
|
||||
|
||||
// contentuuid returns a UUID derived from (some of) the content of an executable.
|
||||
// specifically included are the non-DWARF sections, specifically excluded are things
|
||||
// that surely depend on the presence or absence of DWARF sections (e.g., section
|
||||
// numbers, positions with file, number of load commands).
|
||||
// (It was considered desirable if this was insensitive to the presence of the
|
||||
// __DWARF segment, however because it is not last, it moves other segments,
|
||||
// whose contents appear to contain file offset references.)
|
||||
func contentuuid(exem *macho.FileTOC) []byte {
|
||||
h := sha256.New()
|
||||
for _, l := range exem.Loads {
|
||||
if l.Command() == macho.LcUuid {
|
||||
continue
|
||||
}
|
||||
if s, ok := l.(*macho.Segment); ok {
|
||||
if s.Name == "__DWARF" || s.Name == "__PAGEZERO" {
|
||||
continue
|
||||
}
|
||||
for j := uint32(0); j < s.Nsect; j++ {
|
||||
c := exem.Sections[j+s.Firstsect]
|
||||
io.Copy(h, c.Open())
|
||||
}
|
||||
} // Getting dependence on other load commands right is fiddly.
|
||||
}
|
||||
return h.Sum(nil)
|
||||
}
|
||||
Reference in New Issue
Block a user