//============================================================================ // main : Entry point. //---------------------------------------------------------------------------- func main() { var dwarfData *dwarf.Data var theFile *macho.File var theErr os.Error var relativeAddress uint64 var runtimeAddress uint64 var loadAddress uint64 var segmentAddress uint64 var pathMacho string var pathDsym string // Parse our arguments flag.Uint64Var(&runtimeAddress, "raddr", 0, "") flag.Uint64Var(&loadAddress, "laddr", 0, "") flag.StringVar(&pathMacho, "macho", "", "") flag.StringVar(&pathDsym, "dsym", "", "") flag.Parse() if runtimeAddress == 0 || loadAddress == 0 || pathMacho == "" || pathDsym == "" { printHelp() } // Find the text segment address theFile, theErr = macho.Open(pathMacho) if theErr != nil { fatalError("Can't open Mach-O file: " + theErr.String()) } segmentAddress = theFile.Segment("__TEXT").Addr theFile.Close() // Calculate the target address relativeAddress = runtimeAddress - loadAddress gTargetAddress = segmentAddress + relativeAddress // Find the target theFile, theErr = macho.Open(pathDsym) if theErr != nil { fatalError("Can't open .dsym file: " + theErr.String()) } dwarfData, theErr = theFile.DWARF() if theErr != nil { fatalError("Can't find DWARF info: " + theErr.String()) } processChildren(dwarfData.Reader(), 0, false) theFile.Close() }
func openBinary(name string) (Binary, error) { f, err := os.Open(name) if err != nil { return nil, err } magic := make([]byte, 4) if _, err := f.ReadAt(magic[:], 0); err != nil { return nil, err } if bytes.HasPrefix(magic, []byte{0x7f, 'E', 'L', 'F'}) { e, err := elf.NewFile(f) if err != nil { return nil, err } return &elfBinary{File: e}, nil } else if bytes.HasPrefix(magic, []byte{'M', 'Z'}) { p, err := pe.Open(name) if err != nil { return nil, err } return &peBinary{File: p}, nil } else if bytes.HasPrefix(magic, []byte{0xcf, 0xfa, 0xed, 0xfe}) { m, err := macho.Open(name) if err != nil { return nil, err } return &machoBinary{File: m}, nil } return nil, fmt.Errorf("unsupported binary format") }
func TestMachO(filename, expectedArch, expectedOs string, isVerbose bool) error { file, err := macho.Open(filename) if err != nil { log.Printf("File '%s' is not a Mach-O file: %v\n", filename, err) return err } defer file.Close() if isVerbose { log.Printf("File '%s' is a Mach-O file (arch: %s)\n", filename, file.FileHeader.Cpu.String()) } if expectedArch == platforms.X86 { if file.FileHeader.Cpu != macho.Cpu386 { return errors.New("Not a 386 executable") } } if expectedArch == platforms.AMD64 { if file.FileHeader.Cpu != macho.CpuAmd64 { return errors.New("Not an AMD64 executable") } } return nil }
func getDwarf(execname string) *dwarf.Data { e, err := elf.Open(execname) if err == nil { defer e.Close() d, err := e.DWARF() if err == nil { return d } } m, err := macho.Open(execname) if err == nil { defer m.Close() d, err := m.DWARF() if err == nil { return d } } p, err := pe.Open(execname) if err == nil { defer p.Close() d, err := p.DWARF() if err == nil { return d } } log.Fatal("can't get dwarf info from executable", err) return nil }
func IsMACHO(fPath string) error { if _, err := macho.Open(fPath); err != nil { return err } return nil }
// darwinRelink makes paths of linked libraries relative to executable. // // /usr/local/Cellar/qt5/5.3.0/lib/QtWidgets.framework/Versions/5/QtWidgets // /usr/local/opt/qt5/lib/QtWidgets.framework/Versions/5/QtWidgets // -> // @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets func darwinRelink(qlib, name string, strict bool) (err error) { file, err := macho.Open(name) if err != nil { return } defer file.Close() libs, err := file.ImportedLibraries() if err != nil { return } var qlib2 string // detect alternative qlib (homebrew symlinks Qt to /usr/local/opt) for _, lib := range libs { idx := strings.Index(lib, "QtCore") if idx > 0 { qlib2 = lib[:idx-1] // drop sep break } } replacer := strings.NewReplacer(qlib, relinkBase, qlib2, relinkBase) if len(qlib2) < 1 && strict { return fmt.Errorf("darwin relink: corrupt binary: %s", name) } else if !strict { replacer = strings.NewReplacer(qlib, relinkBase) } // replace qlib/qlib2 to relinkBase for _, lib := range libs { rlib := replacer.Replace(lib) cmd := exec.Command("install_name_tool", "-change", lib, rlib, name) if err = cmd.Run(); err != nil { return fmt.Errorf("darwin relink: %v", err) } } return }
func main() { file, err := macho.Open("bash.macho") if err != nil { log.Fatalf("failed opening file: %s", err) } defer file.Close() printFileInformation(file) }
func dynimport(obj string) { if f, err := elf.Open(obj); err == nil { sym, err := f.ImportedSymbols() if err != nil { fatalf("cannot load imported symbols from ELF file %s: %v", obj, err) } for _, s := range sym { targ := s.Name if s.Version != "" { targ += "@" + s.Version } fmt.Printf("#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) } for _, l := range lib { fmt.Printf("#pragma dynimport _ _ %q\n", l) } return } if f, err := macho.Open(obj); err == nil { sym, err := f.ImportedSymbols() if err != nil { fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err) } for _, s := range sym { if len(s) > 0 && s[0] == '_' { s = s[1:] } fmt.Printf("#pragma dynimport %s %s %q\n", s, s, "") } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) } for _, l := range lib { fmt.Printf("#pragma dynimport _ _ %q\n", l) } return } if f, err := pe.Open(obj); err == nil { sym, err := f.ImportedSymbols() if err != nil { fatalf("cannot load imported symbols from PE file %s: %v", obj, err) } for _, s := range sym { ss := strings.Split(s, ":", -1) fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) } return } fatalf("cannot parse %s as ELF, Mach-O or PE", obj) }
func MACHOAnal(input string, symbolsDump bool) ([][]int, error) { // An array of arrays for storing the section offsets var sectionData [][]int fmt.Printf("[+] Analyzing binary: %s\n", input) // Check for executable type machoFmt, err := macho.Open(input) if err != nil { return sectionData, err } defer machoFmt.Close() sections := machoFmt.Sections sectionCount := len(sections) fmt.Printf("[+] Number of sections: %d\n", sectionCount) for k := range sections { sec := sections[k] secName := sec.Name secSize := sec.Size secOffset := sec.Offset + 1 secEnd := int(secOffset) + int(secSize) - 1 fmt.Printf("\t Name: %s\n", secName) fmt.Printf("\t Size: %d\n", secSize) fmt.Printf("\t Offset: %d\n", secOffset) fmt.Printf("\t Section end: %d\n", secEnd) fmt.Println("") sectionData = append(sectionData, []int{int(secOffset), int(secEnd)}) } symbols, err := machoFmt.ImportedSymbols() if err != nil { return sectionData, err } numberOfSymbols := len(symbols) fmt.Printf("[+] Found %d symbols\n", numberOfSymbols) if numberOfSymbols > 0 && symbolsDump { for k := range symbols { symName := symbols[k] fmt.Printf("\t Name: %s\n", symName) } fmt.Println("") } return sectionData, nil }
func machoData(t *testing.T, name string) *Data { f, err := macho.Open(name) if err != nil { t.Fatal(err) } d, err := f.DWARF() if err != nil { t.Fatal(err) } return d }
func (b *Binutils) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) { of, err := macho.Open(name) if err != nil { return nil, fmt.Errorf("Parsing %s: %v", name, err) } defer of.Close() if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) { return &fileNM{file: file{b: b, name: name}}, nil } return &fileAddr2Line{file: file{b: b, name: name}}, nil }
// gccDebug runs gcc -gdwarf-2 over the C program stdin and // returns the corresponding DWARF data and, if present, debug data block. func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) { runGcc(stdin, p.gccCmd()) if f, err := macho.Open(gccTmp); err == nil { d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp, err) } var data []byte if f.Symtab != nil { for i := range f.Symtab.Syms { s := &f.Symtab.Syms[i] // Mach-O still uses a leading _ to denote non-assembly symbols. if s.Name == "_"+"__cgodebug_data" { // Found it. Now find data section. if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { data = sdat[s.Value-sect.Addr:] } } } } } } return d, f.ByteOrder, data } // Can skip debug data block in ELF and PE for now. // The DWARF information is complete. if f, err := elf.Open(gccTmp); err == nil { d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp, err) } return d, f.ByteOrder, nil } if f, err := pe.Open(gccTmp); err == nil { d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp, err) } return d, binary.LittleEndian, nil } fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp) panic("not reached") }
func newmacho(path string) (tabler, error) { f, err := macho.Open(path) if err != nil { return nil, err } tbl := machotbl{f, nil} switch tbl.Cpu { case macho.Cpu386: tbl.typ = PlatformDarwin386 case macho.CpuAmd64: tbl.typ = PlatformDarwinAMD64 } return tbl, nil }
// Open an input file. func open(name string) (*file, error) { efile, err := Open(name) var mfile *macho.File if err != nil { var merr error mfile, merr = macho.Open(name) if merr != nil { return nil, err } } r := &file{elf: efile, macho: mfile} if efile != nil { r.dwarf, err = efile.DWARF() } else { r.dwarf, err = mfile.DWARF() } if err != nil { return nil, err } var syms []sym if efile != nil { esyms, err := efile.Symbols() if err != nil { return nil, err } syms = make([]sym, 0, len(esyms)) for _, s := range esyms { if ST_TYPE(s.Info) == STT_FUNC { syms = append(syms, sym{s.Name, uintptr(s.Value)}) } } } else { syms = make([]sym, 0, len(mfile.Symtab.Syms)) for _, s := range mfile.Symtab.Syms { syms = append(syms, sym{s.Name, uintptr(s.Value)}) } } r.symsByName = make([]sym, len(syms)) copy(r.symsByName, syms) sort.Sort(symsByName(r.symsByName)) r.symsByAddr = syms sort.Sort(symsByAddr(r.symsByAddr)) return r, nil }
func (dbp *Process) findExecutable(path string) (*macho.File, error) { if path == "" { path = C.GoString(C.find_executable(C.int(dbp.Pid))) } exe, err := macho.Open(path) if err != nil { return nil, err } data, err := exe.DWARF() if err != nil { return nil, err } dbp.dwarf = data return exe, nil }
func Open(name string) (binaryx.File, error) { elfBinary, err := elf.Open(name) if err == nil { return newFile(&elfx.File{elfBinary}) } machoBinary, err := macho.Open(name) if err == nil { return newFile(&machox.File{machoBinary}) } peBinary, err := pe.Open(name) if err == nil { return newFile(&pex.File{peBinary}) } return nil, err }
func MACHOAnal(input string) ([]SectionData, []string, []string, error) { // An array of arrays for storing the section offsets var sectionData []SectionData // Check for executable type machoFmt, err := macho.Open(input) if err != nil { return sectionData, []string{}, []string{}, NotValidMACHOFileError } defer machoFmt.Close() // Extract sections sections := machoFmt.Sections for k := range sections { sec := sections[k] secName := sec.Name secSize := sec.Size secOffset := sec.Offset + 1 secEnd := int(secOffset) + int(secSize) - 1 sd := SectionData{ Name: secName, Size: int(secSize), Offset: int(secOffset), End: int(secEnd), } sectionData = append(sectionData, sd) } // Get imported symbols symbolsArr, err := machoFmt.ImportedSymbols() if err != nil { return sectionData, []string{}, []string{}, err } // Get imported libraries libraries, err := machoFmt.ImportedLibraries() if err != nil { return sectionData, []string{}, []string{}, err } return sectionData, libraries, symbolsArr, nil }
// gccDebug runs gcc -gdwarf-2 over the C program stdin and // returns the corresponding DWARF data and any messages // printed to standard error. func (p *Prog) gccDebug(stdin []byte) (*dwarf.Data, string) { machine := "-m32" if p.PtrSize == 8 { machine = "-m64" } tmp := "_cgo_.o" base := []string{ "gcc", machine, "-Wall", // many warnings "-Werror", // warnings are errors "-o" + tmp, // write object to tmp "-gdwarf-2", // generate DWARF v2 debugging symbols "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise "-c", // do not link "-xc", // input language is C "-", // read input from standard input } _, stderr, ok := run(stdin, concat(base, p.GccOptions)) if !ok { return nil, string(stderr) } // Try to parse f as ELF and Mach-O and hope one works. var f interface { DWARF() (*dwarf.Data, os.Error) } var err os.Error if f, err = elf.Open(tmp); err != nil { if f, err = macho.Open(tmp); err != nil { fatal("cannot parse gcc output %s as ELF or Mach-O object", tmp) } } d, err := f.DWARF() if err != nil { fatal("cannot load DWARF debug information from %s: %s", tmp, err) } return d, "" }
// gccDebug runs gcc -gdwarf-2 over the C program stdin and // returns the corresponding DWARF data and any messages // printed to standard error. func (p *Package) gccDebug(stdin []byte) *dwarf.Data { runGcc(stdin, p.gccCmd()) // Try to parse f as ELF and Mach-O and hope one works. var f interface { DWARF() (*dwarf.Data, os.Error) } var err os.Error if f, err = elf.Open(gccTmp); err != nil { if f, err = macho.Open(gccTmp); err != nil { if f, err = pe.Open(gccTmp); err != nil { fatalf("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp) } } } d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF debug information from %s: %s", gccTmp, err) } return d }
// Check that aranges are valid even when lldb isn't installed. func TestDwarfAranges(t *testing.T) { testenv.MustHaveGoBuild(t) dir, err := ioutil.TempDir("", "go-build") if err != nil { t.Fatalf("failed to create temp directory: %v", err) } defer os.RemoveAll(dir) src := filepath.Join(dir, "main.go") err = ioutil.WriteFile(src, []byte(lldbHelloSource), 0644) if err != nil { t.Fatalf("failed to create file: %v", err) } cmd := exec.Command("go", "build", "-o", "a.exe") cmd.Dir = dir out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("building source %v\n%s", err, out) } filename := filepath.Join(dir, "a.exe") if f, err := elf.Open(filename); err == nil { sect := f.Section(".debug_aranges") if sect == nil { t.Fatal("Missing aranges section") } verifyAranges(t, f.ByteOrder, sect.Open()) } else if f, err := macho.Open(filename); err == nil { sect := f.Section("__debug_aranges") if sect == nil { t.Fatal("Missing aranges section") } verifyAranges(t, f.ByteOrder, sect.Open()) } else { t.Skip("Not an elf or macho binary.") } }
func pclnMacho() (textStart uint64, symtab, pclntab []byte, err error) { f, err := macho.Open(*flagCompiler) if err != nil { return } defer f.Close() if sect := f.Section("__text"); sect != nil { textStart = sect.Addr } if sect := f.Section("__gosymtab"); sect != nil { if symtab, err = sect.Data(); err != nil { return } } if sect := f.Section("__gopclntab"); sect != nil { if pclntab, err = sect.Data(); err != nil { return } } return }
func macho_symsizes(path string) map[string]float64 { m := make(map[string]float64) f, err := macho.Open(path) if err != nil { panic(err.Error()) } syms := make([]macho.Symbol, len(f.Symtab.Syms)) copy(syms, f.Symtab.Syms) sort.Sort(bySectionThenOffset(syms)) for i, sym := range syms { if sym.Sect == 0 { continue } var nextOffset uint64 if i == len(syms)-1 || syms[i+1].Sect != sym.Sect { nextOffset = f.Sections[sym.Sect-1].Size } else { nextOffset = syms[i+1].Value } m[sym.Name] = float64(nextOffset - sym.Value) } return m }
func dynimport(obj string) { stdout := os.Stdout if *dynout != "" { f, err := os.Create(*dynout) if err != nil { fatalf("%s", err) } stdout = f } fmt.Fprintf(stdout, "package %s\n", *dynpackage) if f, err := elf.Open(obj); err == nil { if *dynlinker { // Emit the cgo_dynamic_linker line. if sec := f.Section(".interp"); sec != nil { if data, err := sec.Data(); err == nil && len(data) > 1 { // skip trailing \0 in data fmt.Fprintf(stdout, "//go:cgo_dynamic_linker %q\n", string(data[:len(data)-1])) } } } sym, err := f.ImportedSymbols() if err != nil { fatalf("cannot load imported symbols from ELF file %s: %v", obj, err) } for _, s := range sym { targ := s.Name if s.Version != "" { targ += "#" + s.Version } fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library) } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) } for _, l := range lib { fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l) } return } if f, err := macho.Open(obj); err == nil { sym, err := f.ImportedSymbols() if err != nil { fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err) } for _, s := range sym { if len(s) > 0 && s[0] == '_' { s = s[1:] } fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "") } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) } for _, l := range lib { fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l) } return } if f, err := pe.Open(obj); err == nil { sym, err := f.ImportedSymbols() if err != nil { fatalf("cannot load imported symbols from PE file %s: %v", obj, err) } for _, s := range sym { ss := strings.Split(s, ":") name := strings.Split(ss[0], "@")[0] fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1])) } return } fatalf("cannot parse %s as ELF, Mach-O or PE", obj) }
// gccDebug runs gcc -gdwarf-2 over the C program stdin and // returns the corresponding DWARF data and, if present, debug data block. func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) { runGcc(stdin, p.gccCmd()) isDebugData := func(s string) bool { // Some systems use leading _ to denote non-assembly symbols. return s == "__cgodebug_data" || s == "___cgodebug_data" } if f, err := macho.Open(gccTmp()); err == nil { defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } var data []byte if f.Symtab != nil { for i := range f.Symtab.Syms { s := &f.Symtab.Syms[i] if isDebugData(s.Name) { // Found it. Now find data section. if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { data = sdat[s.Value-sect.Addr:] } } } } } } return d, f.ByteOrder, data } if f, err := elf.Open(gccTmp()); err == nil { defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } var data []byte symtab, err := f.Symbols() if err == nil { for i := range symtab { s := &symtab[i] if isDebugData(s.Name) { // Found it. Now find data section. if i := int(s.Section); 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { if sdat, err := sect.Data(); err == nil { data = sdat[s.Value-sect.Addr:] } } } } } } return d, f.ByteOrder, data } if f, err := pe.Open(gccTmp()); err == nil { defer f.Close() d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } var data []byte for _, s := range f.Symbols { if isDebugData(s.Name) { if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { sect := f.Sections[i] if s.Value < sect.Size { if sdat, err := sect.Data(); err == nil { data = sdat[s.Value:] } } } } } return d, binary.LittleEndian, data } fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp()) panic("not reached") }