Skip to content

Commit

Permalink
splitdwarf: initial working commit
Browse files Browse the repository at this point in the history
splitdwarf osxMachoFile [ osxDsymFile ]

splitdwarf takes an executable produced by go build as input,
and uncompresses and copies the DWARF segment into a separate
file in the way that is expected by OSX-hosted tools
(lldb and ports of gdb).
If osxDsymFile is not named explicitly, the default of
"<osxMachoFile>.dSYM/Contents/Resources/DWARF/<osxMachoFile>"
is used instead, with directories created as needed.

If the input file contains no UUID, then one is created by
hashing non-DWARF segment contents, and added to the
executable. This is necessary because gdb and lldb both
expect matching UUIDs to be present in the executable
and its debugging symbols.

Includes a modified version of debug/macho, with additional
definitions and the ability to write segments, sections, and
some MachO load commands added.

Change-Id: Ia5b0e289260f72bbca392cdf2c7c0a75e3ca40e5
Reviewed-on: https://go-review.googlesource.com/c/143357
Reviewed-by: Austin Clements <austin@google.com>
  • Loading branch information
dr2chase committed Jan 9, 2019
1 parent d674b4a commit d30e00c
Show file tree
Hide file tree
Showing 6 changed files with 1,418 additions and 253 deletions.
19 changes: 19 additions & 0 deletions cmd/splitdwarf/doc.go
@@ -0,0 +1,19 @@
// 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.

/*
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"
27 changes: 11 additions & 16 deletions cmd/splitdwarf/internal/macho/fat.go
Expand Up @@ -6,7 +6,6 @@ package macho

import (
"encoding/binary"
"fmt"
"io"
"os"
)
Expand Down Expand Up @@ -35,10 +34,6 @@ type FatArch struct {
*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.
Expand All @@ -50,17 +45,17 @@ func NewFatFile(r io.ReaderAt) (*FatFile, error) {
// 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}
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, ErrNotFat
return nil, formatError(0, "not a fat Mach-O file, leMagic=0x%x", leMagic)
} else {
return nil, &FormatError{0, "invalid magic number", nil}
return nil, formatError(0, "invalid magic number, leMagic=0x%x", leMagic)
}
}
offset := int64(4)
Expand All @@ -69,19 +64,19 @@ func NewFatFile(r io.ReaderAt) (*FatFile, error) {
var narch uint32
err = binary.Read(sr, binary.BigEndian, &narch)
if err != nil {
return nil, &FormatError{offset, "invalid fat_header", nil}
return nil, formatError(offset, "invalid fat_header %v", err)
}
offset += 4

if narch < 1 {
return nil, &FormatError{offset, "file contains no images", nil}
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 Type
var machoType HdrType

// Following the fat_header comes narch fat_arch structs that index
// Mach-O images further in the file.
Expand All @@ -90,7 +85,7 @@ func NewFatFile(r io.ReaderAt) (*FatFile, error) {
fa := &ff.Arches[i]
err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader)
if err != nil {
return nil, &FormatError{offset, "invalid fat_arch header", nil}
return nil, formatError(offset, "invalid fat_arch header, %v", err)
}
offset += fatArchHeaderSize

Expand All @@ -103,16 +98,16 @@ func NewFatFile(r io.ReaderAt) (*FatFile, error) {
// 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}
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 = fa.Type
machoType = HdrType(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}
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)
}
}
}
Expand Down

0 comments on commit d30e00c

Please sign in to comment.