From d674b4ad678f67fe02b23ad40ae3d9ad309336f7 Mon Sep 17 00:00:00 2001 From: David Chase Date: Tue, 11 Dec 2018 16:11:12 -0500 Subject: [PATCH] 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 TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements Reviewed-by: David Chase --- cmd/splitdwarf/internal/macho/fat.go | 146 ++++ cmd/splitdwarf/internal/macho/file.go | 688 ++++++++++++++++++ cmd/splitdwarf/internal/macho/file_test.go | 379 ++++++++++ cmd/splitdwarf/internal/macho/macho.go | 336 +++++++++ cmd/splitdwarf/internal/macho/reloctype.go | 72 ++ .../internal/macho/reloctype_string.go | 49 ++ .../testdata/clang-386-darwin-exec-with-rpath | Bin 0 -> 8416 bytes .../macho/testdata/clang-386-darwin.obj | Bin 0 -> 464 bytes .../clang-amd64-darwin-exec-with-rpath | Bin 0 -> 8432 bytes .../macho/testdata/clang-amd64-darwin.obj | Bin 0 -> 768 bytes .../testdata/fat-gcc-386-amd64-darwin-exec | Bin 0 -> 28992 bytes .../macho/testdata/gcc-386-darwin-exec | Bin 0 -> 12588 bytes .../macho/testdata/gcc-amd64-darwin-exec | Bin 0 -> 8512 bytes .../testdata/gcc-amd64-darwin-exec-debug | Bin 0 -> 4540 bytes .../internal/macho/testdata/hello.c | 8 + 15 files changed, 1678 insertions(+) create mode 100644 cmd/splitdwarf/internal/macho/fat.go create mode 100644 cmd/splitdwarf/internal/macho/file.go create mode 100644 cmd/splitdwarf/internal/macho/file_test.go create mode 100644 cmd/splitdwarf/internal/macho/macho.go create mode 100644 cmd/splitdwarf/internal/macho/reloctype.go create mode 100644 cmd/splitdwarf/internal/macho/reloctype_string.go create mode 100644 cmd/splitdwarf/internal/macho/testdata/clang-386-darwin-exec-with-rpath create mode 100644 cmd/splitdwarf/internal/macho/testdata/clang-386-darwin.obj create mode 100644 cmd/splitdwarf/internal/macho/testdata/clang-amd64-darwin-exec-with-rpath create mode 100644 cmd/splitdwarf/internal/macho/testdata/clang-amd64-darwin.obj create mode 100644 cmd/splitdwarf/internal/macho/testdata/fat-gcc-386-amd64-darwin-exec create mode 100644 cmd/splitdwarf/internal/macho/testdata/gcc-386-darwin-exec create mode 100644 cmd/splitdwarf/internal/macho/testdata/gcc-amd64-darwin-exec create mode 100644 cmd/splitdwarf/internal/macho/testdata/gcc-amd64-darwin-exec-debug create mode 100644 cmd/splitdwarf/internal/macho/testdata/hello.c diff --git a/cmd/splitdwarf/internal/macho/fat.go b/cmd/splitdwarf/internal/macho/fat.go new file mode 100644 index 00000000000..6bd730dc0bb --- /dev/null +++ b/cmd/splitdwarf/internal/macho/fat.go @@ -0,0 +1,146 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package macho + +import ( + "encoding/binary" + "fmt" + "io" + "os" +) + +// A FatFile is a Mach-O universal binary that contains at least one architecture. +type FatFile struct { + Magic uint32 + Arches []FatArch + closer io.Closer +} + +// A FatArchHeader represents a fat header for a specific image architecture. +type FatArchHeader struct { + Cpu Cpu + SubCpu uint32 + Offset uint32 + Size uint32 + Align uint32 +} + +const fatArchHeaderSize = 5 * 4 + +// A FatArch is a Mach-O File inside a FatFile. +type FatArch struct { + FatArchHeader + *File +} + +// ErrNotFat is returned from NewFatFile or OpenFat when the file is not a +// universal binary but may be a thin binary, based on its magic number. +var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil} + +// NewFatFile creates a new FatFile for accessing all the Mach-O images in a +// universal binary. The Mach-O binary is expected to start at position 0 in +// the ReaderAt. +func NewFatFile(r io.ReaderAt) (*FatFile, error) { + var ff FatFile + sr := io.NewSectionReader(r, 0, 1<<63-1) + + // Read the fat_header struct, which is always in big endian. + // Start with the magic number. + err := binary.Read(sr, binary.BigEndian, &ff.Magic) + if err != nil { + return nil, &FormatError{0, "error reading magic number", nil} + } else if ff.Magic != MagicFat { + // See if this is a Mach-O file via its magic number. The magic + // must be converted to little endian first though. + var buf [4]byte + binary.BigEndian.PutUint32(buf[:], ff.Magic) + leMagic := binary.LittleEndian.Uint32(buf[:]) + if leMagic == Magic32 || leMagic == Magic64 { + return nil, ErrNotFat + } else { + return nil, &FormatError{0, "invalid magic number", nil} + } + } + offset := int64(4) + + // Read the number of FatArchHeaders that come after the fat_header. + var narch uint32 + err = binary.Read(sr, binary.BigEndian, &narch) + if err != nil { + return nil, &FormatError{offset, "invalid fat_header", nil} + } + offset += 4 + + if narch < 1 { + return nil, &FormatError{offset, "file contains no images", nil} + } + + // Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure + // there are not duplicate architectures. + seenArches := make(map[uint64]bool, narch) + // Make sure that all images are for the same MH_ type. + var machoType Type + + // Following the fat_header comes narch fat_arch structs that index + // Mach-O images further in the file. + ff.Arches = make([]FatArch, narch) + for i := uint32(0); i < narch; i++ { + fa := &ff.Arches[i] + err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader) + if err != nil { + return nil, &FormatError{offset, "invalid fat_arch header", nil} + } + offset += fatArchHeaderSize + + fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size)) + fa.File, err = NewFile(fr) + if err != nil { + return nil, err + } + + // Make sure the architecture for this image is not duplicate. + seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu) + if o, k := seenArches[seenArch]; o || k { + return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil} + } + seenArches[seenArch] = true + + // Make sure the Mach-O type matches that of the first image. + if i == 0 { + machoType = fa.Type + } else { + if fa.Type != machoType { + return nil, &FormatError{offset, fmt.Sprintf("Mach-O type for architecture #%d (type=%#x) does not match first (type=%#x)", i, fa.Type, machoType), nil} + } + } + } + + return &ff, nil +} + +// OpenFat opens the named file using os.Open and prepares it for use as a Mach-O +// universal binary. +func OpenFat(name string) (*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 +} diff --git a/cmd/splitdwarf/internal/macho/file.go b/cmd/splitdwarf/internal/macho/file.go new file mode 100644 index 00000000000..16708e5247f --- /dev/null +++ b/cmd/splitdwarf/internal/macho/file.go @@ -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 +} diff --git a/cmd/splitdwarf/internal/macho/file_test.go b/cmd/splitdwarf/internal/macho/file_test.go new file mode 100644 index 00000000000..003c14e69b1 --- /dev/null +++ b/cmd/splitdwarf/internal/macho/file_test.go @@ -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") + } +} diff --git a/cmd/splitdwarf/internal/macho/macho.go b/cmd/splitdwarf/internal/macho/macho.go new file mode 100644 index 00000000000..7bc1950bfdb --- /dev/null +++ b/cmd/splitdwarf/internal/macho/macho.go @@ -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) +} diff --git a/cmd/splitdwarf/internal/macho/reloctype.go b/cmd/splitdwarf/internal/macho/reloctype.go new file mode 100644 index 00000000000..496dfce7ec1 --- /dev/null +++ b/cmd/splitdwarf/internal/macho/reloctype.go @@ -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() } diff --git a/cmd/splitdwarf/internal/macho/reloctype_string.go b/cmd/splitdwarf/internal/macho/reloctype_string.go new file mode 100644 index 00000000000..9c2b13186e7 --- /dev/null +++ b/cmd/splitdwarf/internal/macho/reloctype_string.go @@ -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]] +} diff --git a/cmd/splitdwarf/internal/macho/testdata/clang-386-darwin-exec-with-rpath b/cmd/splitdwarf/internal/macho/testdata/clang-386-darwin-exec-with-rpath new file mode 100644 index 0000000000000000000000000000000000000000..a8720feb923693232bce4fa9642988c86a28c372 GIT binary patch literal 8416 zcmeHM&r1|h9Dl284Q8nwN-%O14GVJ%f;rQo=QFb}r?L72;(gigy!ZY2zV9<{Vc&k&zkU9( zQwY%rZUDD|2ckmU5@sZV`4Cvj@sYFf>+vg>At<8bUx;yc@Q`vcK0O(5VvLHz$aS0Q zOfM;2Yr*Brr*QmCmk>u`nrRgRC2mnvEGj|DVmX`4+p_4El2C?oEF;Gb=$Da$bt*EN z{2a6)2ZCkVvI~~u$-nPo1v&OXzk(dZ>=)H@P?mDG=sKC)&AKsM$0~B{fo&Bzf)var zWhpnG$)#i_H<)dNWfrXuHHxeU8plaFDY}t(1)_% zI)98e=!n>0&BRaPDy5zHR}RDU-=h)Aq0S@IGnan`FOFTh5FZ^==jj*>h!ds|8yFa) z*2Prx2(d^P+UeGYu>+%`e$&?vVH|*xd!RiHnP=+}My|UHJPr;LeH91w!TEhNbVln& zzVid%7I3UobYgZU8B3Mz6y#1YYv_D;@9X-tleZu5KmX)``Q~XablihzY4-StlBomr zW3P);ax#(Ki!83!XA(QlK^KyChWy>+AkZ{w8KLlnC zDbwHA;krz`G-(7h0vZ90fJQ(gpb^jrXaqC@J_1vf_e-BTHYrTsT|AB9W&As(O0;)# zH^$?yEAcOtS4%HCZalBn`V13ykSHFtK3-j_{Ov71e5}=KxQ^QS0dqd@*r{gkuETo- zDLC?XKJNki-B0E{m&7^mcP)r)^P;KUqKCLkYSIX31T+E~0gZr0KqH_L&+L^w1_lOZAZ7$&79h3&F%%&D13)G=U=0)u0jdMhApIcB1jHZ^A77GMQ37E@ z_#v(lAqYN*r3=I${UG}$0BMl@Ku!aQ0)hDWT|%)E367sy971Eil1h#P@=kpavN zATbpn7637LxIiS>eG))=1yB!2-2osClII5E03b#NAY)+|S)3SlXy?=BHxe&EN+Wt3 zlz`N6*FPX$CyUC9`~Uy{H$2(t`lIti^AU;I!x^bLIr%yY<@rT9DL`k+0+j*n6X&9mRy#6Y0Wp+gUP^kPWg#3orZLXs77!9;$qMRYZV; z2AwKj;P>;%tYw!h2SKg*)>Xbfm4KMM*JaoE=F6^=E8JB9t@)m-dYIfb}2hwt&9T-R-=l;g3PSjdC*Yg*S}eM9StJy*{yWx_qKcI-0548DPp$D93SsaA;?wZ}jcs|Ov{Ltkgj3bx!TOU*Tym98M zQKyu0vv8e;vfnw==k=_@Gy|Fe&46a$UomhivHCHw^2JE3T^dE7 zuj4=1^9!+k0A^%sHU52dGqLi@n0;9rSVq;v+VOiZvFX}C3U;0Ew&L@rTCIjpO1n5{ zE*2d-)8&0r;9c00u;sfn-?h)fQXi@JZkG8y(~Xvf*BYSt)5NQIg7}D6(htpmW{uc&%`;95irFV>%ecolbOyhnSm)BE{SQn1bJ0?rG zfOZ#vAQ6fC_%@l(N^8lQuecWOx>F24nNQ^kVt_v&+Qo+xjD-XRR`*2wn?e3xIOJjf nUj;G$VZhdZJ@8&JjQzBz6k?2J6F0=W#~{5i1moQ`BeeSkgV?^% literal 0 HcmV?d00001 diff --git a/cmd/splitdwarf/internal/macho/testdata/clang-amd64-darwin.obj b/cmd/splitdwarf/internal/macho/testdata/clang-amd64-darwin.obj new file mode 100644 index 0000000000000000000000000000000000000000..23cc3c1bcb18a3256f515a1fe69a8bd199a6ef53 GIT binary patch literal 768 zcma)4ze_?<6h6;Hqs-n=P}J~-rl_HsuOI$>sDdwxw1{E$@2%d1>9UGjPb!{Z#9^QtOb`Bef{yL)O!Uk5&t1CA%yHb4xGFkVJ%l5D)@FKnMr{ zAs_^VfDjM@LO=)zfi*_p>np!qH3NTp5`Y8z8O#D4MUL^m+K!}ksPF0i1N{SgVK~BR zf@=u-%^+z#)IU7rYQ#QHKjggbO-P)4plDr)O%&>S-Co>3zFDa^Q4w!%^s!reRLRcq zp>;H0$R*OFrl2{7dHC3Xx_S6;ixUIO8u9Pw8|rhl3P5+hgNYxgT=9>D3*kn4t(W%q zvl@Nf*K71({3Daekw!_g4}Ub}#ukd+YPI&Eyka(?njPz_Z;z3NK(WuUM z_jDt)Lt$1==WJ_V<-%b3qTen&I9?iUL+27S_4XXkvvF>Nkj+;0U;m+gn$qb0zyCt2mv7=1cZPP zSaSp}FUXn;T>=&YLO=)z0U;m+gn$qb0zyCt2mv7=1g?_+*B@`f^5f%JtIYM!)IWjV z4ZRJEt$hKddZD*N=URZE(YYodWOS|zI0U_g3kL8nYs$H1V3Im~o`uiXjLvn@N1l1PtWMZd<{g={QmmdNWP#dK6BGnp5Q*?L=@3PnbCAO6@#9N>)1prS}epC+Q-u4iCiYF;3bh(`9e5XP-v9E zXSP_#BP0OZ^=){_MT(a724i zd~WB^S3A$(xbep`4JfqT((GI7SX>{;aT>&y=KFEd_~SjN(8jZ|Ab37rSTLaQzHYbg zYtvZ=xPOc8t8;%{_mzGf-#GVR<+BrvKknVP1-8@IDRSqlouj~QF^q4!=|p>Ff61`( zsALPAgj)Q4hmAjEw$OI7=X30~8}C)qISGL#Zm=o6Hp~^yO$gNA-5KO1wC8&qLZa}x zOpM_cqa#~ZP6W(h4o$T@Hvv`4o}0j?&e;i%b)3w#^^bpd--nkfCuX-C=DW-7M?4^6 zE7FY?=l_S*;^6zvXM^u){l4)Atb9fT8rXc?{p6emQV|gXLO=)z0U;m+guwrsz?ceF zE(Eb#KRETOH#lANRj!o3ux5W~30l7gE9ZXo1k1DDVEIv1{DWHcUVNR(m(>T5UG5v) zHGR(q^R-&vV6F3Wl(cVFwN4(fzmHC*pLh^zm7_KORjkg;)jH21uXTP)Qz`VGMfUT@ zwOS4Or__%_Z{D}>XJd%<@_@1?e3q|uz4sXYMCGXg93#DrK9t+ph~nYjuWi0J)_FM0 z+2D*^sdwfFcyb~41B@B}{GFb*^Sc}u+B^FKpxXNf+%|u|0A;iEn~rQZ0G*Z`qVig9wLzHYSh%SRk2iCagkClT4p+zbRBzp*v1|w5!=Qd&EoYy zzDEB8;fZilmIm8<2Dg8Y8)ZqEh*>eR9FelMK0qPVHlOFF*B0!tm`OA|d-n)vLYzf7 z@Q*#H|NBrW2Zy7hV~u~{0-UH5nzIsrQpzWha?Z{cq!o*24ClVJ{x}6RA&%1OicbiY zO!_ZrFXx>Z8XUP185s0V??-^nMh`tmYCad<8jn+CAWrY#GptXro`6Q7B*dDOYN?OE zF+@l_M*V*APQ-{GozZI-&E8Bp-s>Vi10VG;eQB!i>3ID0&oA9q&cFD!PR~U>(6jlW z2Nil>_Y*q%B9zW@dZe9|MnC8&sq3;B>IwO)h_7AC+1 zm;e)C0!)AjFaajO1pYsPIZ@s$ecmo@`75{8@@!D74ExGMLAA7|y#3Use1OYWX)mp* z%d7Tx+;miX&}4ZaD5_KV=GduH%agk=bu5*oB(A#~j<2$Gt9Ey@yg6Rl3e=o4E2YzL zmepWYL-i}zqO=~EdAF7_GMRjjb}w%>e`QkK75c#OZ#?or>0_ru6T$~op@Ni!2`~XB zzyz286JP>NfC(@GCcp%k024TL1a|6j=vwE7nE(@D0!)AjFaajO1egF5U;<2l2{3^J z5}^6xZj2v4$E-5VKa>3ib_jMShOPaY5dE;bVACwXb=RhufT(NJT)-^sb{ZJKui*M= zW?+$Q?D-yhK5%WCi(ZDU2&FduTxqgtuA64QH9Q#S)Y>G8s0Ly1UM!NEXa|G8fAlu(K&?EEu|N8TEW4H=j21 YIe}f$IZ?D?rX`S+#wLgJDVnj z(nx4wDS_bGyP>BXJmg}Vw1?7D5j}X879vs!A|7&Z`uokkm&{Cq9&>pgy#4;X_x*VD zdo#;?^Zm8+@5@dhtPUX-4+$ZTV9dNB#1GJvcQ9-WDaSLPWxvW^`jXM8SW$Ohk2nvp zP|C^d)MRu?ke`aigz~XBf$ao?E2Q+Cd5>0G;&{h;a50#wsCoLlu3brp#a$bQl#PW- zzE+kEubDsl|NY%icwH)qepr$U@30C24?uFpDc2nrvH1R06ke}dPd~E1FnEw(Xn1a^ zI<4Yyyj6vFRL#>5OB`EDXGUIka}}q(Dvr0V@Ob7F`nWfC1^36bT?YowbtpL3;ml+v zG=uYtoU1FPcH$E&efr?2HrxsxEG1zrPcn1)Drry#w+La zty1?~oNIJ{Qwk3%5c*;6c^`-Uz2|ClWbBLV@Q8jU(lOrPQ$7jE6oNR)4C2HL?xl45 z+1-F%M2yezI0l7lIGFpUvZ3~swX_h4eP+55!<682FxYIkgXL0wFhudYh;ttoPOZD$ z*|`U&e}39p+kF2DpWSr~VxGY`Xwm-o^TAR7Z`2S7t9 zk6B7QFJaXXjpj`(G(4v=@M$D5$TmNQ0mFb{z%XDKFbo(53QWx9&Yj*h`y9d+EGrzM!@B0XEtCW9vhx=b4Go<>R-veLpkd z_x*}U&q?9;{Q~1ML6)`&)@yNd(eL0eY!#*dlTyM9pCq1-=)0@8wF*# z)}Q{S=C-SvV_XcezP%)@`IMFF>P)WS6}2BR)<`#zLwTEvC<(s5cBg(klwg^2RM)GV zdZnZ`r2oATNu{}#NWHq=Q97wK>vqoTI`@{UCz)r&Fkl!k3>XFs1BL;^fMLKeU>GnA z7zPXjhJk}&fW55~DA>nUoz6ZU{i5;*;P>ETfv)#IQhrL+`5!A^*ZrTv@1O(WcYv!H z%-7!s*vIa{#H_++pPs(%%QfIz!fLnNU@ydc_AlSU&^j&Hglsq6^QCGLopRlLZ?}Nu z^=7po%eC6q&AO0dQyF)Wab6o6;!qzO$335^7(8-p4F62z?(LIDXH6{xyF2o+jH#neumA8_o*c2K%2 zLV|5?0IP~4u;31C;SelAA`9S+J>xizqvQY{X=Xg{=bPs@ww0G}!=Has2w@SSqeX;J z0{W*U46H4~d>;Cx-0(;8`McDgmmfdeEZs6Mfb2U)h>)EJdppdSO&~@nKQ7}d*b7lS z-+~ZF8#r7$$t#|L;6`fqN(inK=qR`(#6Ik*rcP|9udYq<-bV0({%OWrU_#&lh_p4m zr&%Bi-|uk*Z!Eqb-1V>9j@2<*K7TxapCfodM3_y8)+S9$>1v);IKD3tJUDB=We_^> z7jQw(b=^JJ`?Z}~Km+GF>s%?9vk7S^jxzEmWD0L&;`;_f{w44Z^@hKMa~S7IJid|1 z44$s=N2TXj6TH0$UNHCcIS#IA-Uc9mxOQt#9*&}rLV08F#aB0(2vH8r8J%f^`_$Ba zizupUX>X?Us-F4O_l&E3o~0Nq&7O#_DtYm(GKVUQkNbyb&f||<_qa;|{S z;yl#7!V1b{?t%y^lQO!O6?3;UcQTi=+e_49gZ@i!4Z2tTq+RGMR%M=x+5%)n=>MQ$ zga`R-Dt*kVpA}{w_D@iKF8^Lg(Awzq!ebcOeweCo35M&?xnr=b7ZS-`P>J{jwG?rb z9e##3K`S8oyB*`-zZPNi&H* literal 0 HcmV?d00001 diff --git a/cmd/splitdwarf/internal/macho/testdata/hello.c b/cmd/splitdwarf/internal/macho/testdata/hello.c new file mode 100644 index 00000000000..a689d3644e1 --- /dev/null +++ b/cmd/splitdwarf/internal/macho/testdata/hello.c @@ -0,0 +1,8 @@ +#include + +int +main(void) +{ + printf("hello, world\n"); + return 0; +}