Source file src/cmd/internal/objfile/macho.go

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Parsing of Mach-O executables (OS X).
     6  
     7  package objfile
     8  
     9  import (
    10  	"debug/dwarf"
    11  	"debug/macho"
    12  	"fmt"
    13  	"io"
    14  	"slices"
    15  	"sort"
    16  )
    17  
    18  const stabTypeMask = 0xe0
    19  
    20  type machoFile struct {
    21  	macho *macho.File
    22  }
    23  
    24  func openMacho(r io.ReaderAt) (rawFile, error) {
    25  	f, err := macho.NewFile(r)
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  	return &machoFile{f}, nil
    30  }
    31  
    32  func (f *machoFile) symbols() ([]Sym, error) {
    33  	if f.macho.Symtab == nil {
    34  		return nil, nil
    35  	}
    36  
    37  	// Build sorted list of addresses of all symbols.
    38  	// We infer the size of a symbol by looking at where the next symbol begins.
    39  	var addrs []uint64
    40  	for _, s := range f.macho.Symtab.Syms {
    41  		// Skip stab debug info.
    42  		if s.Type&stabTypeMask == 0 {
    43  			addrs = append(addrs, s.Value)
    44  		}
    45  	}
    46  	slices.Sort(addrs)
    47  
    48  	var syms []Sym
    49  	for _, s := range f.macho.Symtab.Syms {
    50  		if s.Type&stabTypeMask != 0 {
    51  			// Skip stab debug info.
    52  			continue
    53  		}
    54  		sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
    55  		i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
    56  		if i < len(addrs) {
    57  			sym.Size = int64(addrs[i] - s.Value)
    58  		}
    59  		if s.Sect == 0 {
    60  			sym.Code = 'U'
    61  		} else if int(s.Sect) <= len(f.macho.Sections) {
    62  			sect := f.macho.Sections[s.Sect-1]
    63  			switch sect.Seg {
    64  			case "__TEXT", "__DATA_CONST":
    65  				sym.Code = 'R'
    66  			case "__DATA":
    67  				sym.Code = 'D'
    68  			}
    69  			switch sect.Seg + " " + sect.Name {
    70  			case "__TEXT __text":
    71  				sym.Code = 'T'
    72  			case "__DATA __bss", "__DATA __noptrbss":
    73  				sym.Code = 'B'
    74  			}
    75  		}
    76  		syms = append(syms, sym)
    77  	}
    78  
    79  	return syms, nil
    80  }
    81  
    82  func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
    83  	if sect := f.macho.Section("__text"); sect != nil {
    84  		textStart = sect.Addr
    85  	}
    86  	if sect := f.macho.Section("__gosymtab"); sect != nil {
    87  		if symtab, err = sect.Data(); err != nil {
    88  			return 0, nil, nil, err
    89  		}
    90  	}
    91  	if sect := f.macho.Section("__gopclntab"); sect != nil {
    92  		if pclntab, err = sect.Data(); err != nil {
    93  			return 0, nil, nil, err
    94  		}
    95  	}
    96  	return textStart, symtab, pclntab, nil
    97  }
    98  
    99  func (f *machoFile) text() (textStart uint64, text []byte, err error) {
   100  	sect := f.macho.Section("__text")
   101  	if sect == nil {
   102  		return 0, nil, fmt.Errorf("text section not found")
   103  	}
   104  	textStart = sect.Addr
   105  	text, err = sect.Data()
   106  	return
   107  }
   108  
   109  func (f *machoFile) goarch() string {
   110  	switch f.macho.Cpu {
   111  	case macho.Cpu386:
   112  		return "386"
   113  	case macho.CpuAmd64:
   114  		return "amd64"
   115  	case macho.CpuArm:
   116  		return "arm"
   117  	case macho.CpuArm64:
   118  		return "arm64"
   119  	case macho.CpuPpc64:
   120  		return "ppc64"
   121  	}
   122  	return ""
   123  }
   124  
   125  func (f *machoFile) loadAddress() (uint64, error) {
   126  	if seg := f.macho.Segment("__TEXT"); seg != nil {
   127  		return seg.Addr, nil
   128  	}
   129  	return 0, fmt.Errorf("unknown load address")
   130  }
   131  
   132  func (f *machoFile) dwarf() (*dwarf.Data, error) {
   133  	return f.macho.DWARF()
   134  }
   135  

View as plain text