Source file src/debug/macho/file.go

     1  // Copyright 2009 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  /*
     6  Package macho implements access to Mach-O object files.
     7  
     8  # Security
     9  
    10  This package is not designed to be hardened against adversarial inputs, and is
    11  outside the scope of https://go.dev/security/policy. In particular, only basic
    12  validation is done when parsing object files. As such, care should be taken when
    13  parsing untrusted inputs, as parsing malformed files may consume significant
    14  resources, or cause panics.
    15  */
    16  package macho
    17  
    18  // High level access to low level data structures.
    19  
    20  import (
    21  	"bytes"
    22  	"compress/zlib"
    23  	"debug/dwarf"
    24  	"encoding/binary"
    25  	"fmt"
    26  	"internal/saferio"
    27  	"io"
    28  	"os"
    29  	"strings"
    30  )
    31  
    32  // A File represents an open Mach-O file.
    33  type File struct {
    34  	FileHeader
    35  	ByteOrder binary.ByteOrder
    36  	Loads     []Load
    37  	Sections  []*Section
    38  
    39  	Symtab   *Symtab
    40  	Dysymtab *Dysymtab
    41  
    42  	closer io.Closer
    43  }
    44  
    45  // A Load represents any Mach-O load command.
    46  type Load interface {
    47  	Raw() []byte
    48  }
    49  
    50  // A LoadBytes is the uninterpreted bytes of a Mach-O load command.
    51  type LoadBytes []byte
    52  
    53  func (b LoadBytes) Raw() []byte { return b }
    54  
    55  // A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command.
    56  type SegmentHeader struct {
    57  	Cmd     LoadCmd
    58  	Len     uint32
    59  	Name    string
    60  	Addr    uint64
    61  	Memsz   uint64
    62  	Offset  uint64
    63  	Filesz  uint64
    64  	Maxprot uint32
    65  	Prot    uint32
    66  	Nsect   uint32
    67  	Flag    uint32
    68  }
    69  
    70  // A Segment represents a Mach-O 32-bit or 64-bit load segment command.
    71  type Segment struct {
    72  	LoadBytes
    73  	SegmentHeader
    74  
    75  	// Embed ReaderAt for ReadAt method.
    76  	// Do not embed SectionReader directly
    77  	// to avoid having Read and Seek.
    78  	// If a client wants Read and Seek it must use
    79  	// Open() to avoid fighting over the seek offset
    80  	// with other clients.
    81  	io.ReaderAt
    82  	sr *io.SectionReader
    83  }
    84  
    85  // Data reads and returns the contents of the segment.
    86  func (s *Segment) Data() ([]byte, error) {
    87  	return saferio.ReadDataAt(s.sr, s.Filesz, 0)
    88  }
    89  
    90  // Open returns a new ReadSeeker reading the segment.
    91  func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
    92  
    93  type SectionHeader struct {
    94  	Name   string
    95  	Seg    string
    96  	Addr   uint64
    97  	Size   uint64
    98  	Offset uint32
    99  	Align  uint32
   100  	Reloff uint32
   101  	Nreloc uint32
   102  	Flags  uint32
   103  }
   104  
   105  // A Reloc represents a Mach-O relocation.
   106  type Reloc struct {
   107  	Addr  uint32
   108  	Value uint32
   109  	// when Scattered == false && Extern == true, Value is the symbol number.
   110  	// when Scattered == false && Extern == false, Value is the section number.
   111  	// when Scattered == true, Value is the value that this reloc refers to.
   112  	Type      uint8
   113  	Len       uint8 // 0=byte, 1=word, 2=long, 3=quad
   114  	Pcrel     bool
   115  	Extern    bool // valid if Scattered == false
   116  	Scattered bool
   117  }
   118  
   119  type Section struct {
   120  	SectionHeader
   121  	Relocs []Reloc
   122  
   123  	// Embed ReaderAt for ReadAt method.
   124  	// Do not embed SectionReader directly
   125  	// to avoid having Read and Seek.
   126  	// If a client wants Read and Seek it must use
   127  	// Open() to avoid fighting over the seek offset
   128  	// with other clients.
   129  	io.ReaderAt
   130  	sr *io.SectionReader
   131  }
   132  
   133  // Data reads and returns the contents of the Mach-O section.
   134  func (s *Section) Data() ([]byte, error) {
   135  	return saferio.ReadDataAt(s.sr, s.Size, 0)
   136  }
   137  
   138  // Open returns a new ReadSeeker reading the Mach-O section.
   139  func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
   140  
   141  // A Dylib represents a Mach-O load dynamic library command.
   142  type Dylib struct {
   143  	LoadBytes
   144  	Name           string
   145  	Time           uint32
   146  	CurrentVersion uint32
   147  	CompatVersion  uint32
   148  }
   149  
   150  // A Symtab represents a Mach-O symbol table command.
   151  type Symtab struct {
   152  	LoadBytes
   153  	SymtabCmd
   154  	Syms []Symbol
   155  }
   156  
   157  // A Dysymtab represents a Mach-O dynamic symbol table command.
   158  type Dysymtab struct {
   159  	LoadBytes
   160  	DysymtabCmd
   161  	IndirectSyms []uint32 // indices into Symtab.Syms
   162  }
   163  
   164  // A Rpath represents a Mach-O rpath command.
   165  type Rpath struct {
   166  	LoadBytes
   167  	Path string
   168  }
   169  
   170  // A Symbol is a Mach-O 32-bit or 64-bit symbol table entry.
   171  type Symbol struct {
   172  	Name  string
   173  	Type  uint8
   174  	Sect  uint8
   175  	Desc  uint16
   176  	Value uint64
   177  }
   178  
   179  /*
   180   * Mach-O reader
   181   */
   182  
   183  // FormatError is returned by some operations if the data does
   184  // not have the correct format for an object file.
   185  type FormatError struct {
   186  	off int64
   187  	msg string
   188  	val any
   189  }
   190  
   191  func (e *FormatError) Error() string {
   192  	msg := e.msg
   193  	if e.val != nil {
   194  		msg += fmt.Sprintf(" '%v'", e.val)
   195  	}
   196  	msg += fmt.Sprintf(" in record at byte %#x", e.off)
   197  	return msg
   198  }
   199  
   200  // Open opens the named file using os.Open and prepares it for use as a Mach-O binary.
   201  func Open(name string) (*File, error) {
   202  	f, err := os.Open(name)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	ff, err := NewFile(f)
   207  	if err != nil {
   208  		f.Close()
   209  		return nil, err
   210  	}
   211  	ff.closer = f
   212  	return ff, nil
   213  }
   214  
   215  // Close closes the File.
   216  // If the File was created using NewFile directly instead of Open,
   217  // Close has no effect.
   218  func (f *File) Close() error {
   219  	var err error
   220  	if f.closer != nil {
   221  		err = f.closer.Close()
   222  		f.closer = nil
   223  	}
   224  	return err
   225  }
   226  
   227  // NewFile creates a new File for accessing a Mach-O binary in an underlying reader.
   228  // The Mach-O binary is expected to start at position 0 in the ReaderAt.
   229  func NewFile(r io.ReaderAt) (*File, error) {
   230  	f := new(File)
   231  	sr := io.NewSectionReader(r, 0, 1<<63-1)
   232  
   233  	// Read and decode Mach magic to determine byte order, size.
   234  	// Magic32 and Magic64 differ only in the bottom bit.
   235  	var ident [4]byte
   236  	if _, err := r.ReadAt(ident[0:], 0); err != nil {
   237  		return nil, err
   238  	}
   239  	be := binary.BigEndian.Uint32(ident[0:])
   240  	le := binary.LittleEndian.Uint32(ident[0:])
   241  	switch Magic32 &^ 1 {
   242  	case be &^ 1:
   243  		f.ByteOrder = binary.BigEndian
   244  		f.Magic = be
   245  	case le &^ 1:
   246  		f.ByteOrder = binary.LittleEndian
   247  		f.Magic = le
   248  	default:
   249  		return nil, &FormatError{0, "invalid magic number", nil}
   250  	}
   251  
   252  	// Read entire file header.
   253  	if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil {
   254  		return nil, err
   255  	}
   256  
   257  	// Then load commands.
   258  	offset := int64(fileHeaderSize32)
   259  	if f.Magic == Magic64 {
   260  		offset = fileHeaderSize64
   261  	}
   262  	dat, err := saferio.ReadDataAt(r, uint64(f.Cmdsz), offset)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	c := saferio.SliceCap((*Load)(nil), uint64(f.Ncmd))
   267  	if c < 0 {
   268  		return nil, &FormatError{offset, "too many load commands", nil}
   269  	}
   270  	f.Loads = make([]Load, 0, c)
   271  	bo := f.ByteOrder
   272  	for i := uint32(0); i < f.Ncmd; i++ {
   273  		// Each load command begins with uint32 command and length.
   274  		if len(dat) < 8 {
   275  			return nil, &FormatError{offset, "command block too small", nil}
   276  		}
   277  		cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
   278  		if siz < 8 || siz > uint32(len(dat)) {
   279  			return nil, &FormatError{offset, "invalid command block size", nil}
   280  		}
   281  		var cmddat []byte
   282  		cmddat, dat = dat[0:siz], dat[siz:]
   283  		offset += int64(siz)
   284  		var s *Segment
   285  		switch cmd {
   286  		default:
   287  			f.Loads = append(f.Loads, LoadBytes(cmddat))
   288  
   289  		case LoadCmdRpath:
   290  			var hdr RpathCmd
   291  			b := bytes.NewReader(cmddat)
   292  			if err := binary.Read(b, bo, &hdr); err != nil {
   293  				return nil, err
   294  			}
   295  			l := new(Rpath)
   296  			if hdr.Path >= uint32(len(cmddat)) {
   297  				return nil, &FormatError{offset, "invalid path in rpath command", hdr.Path}
   298  			}
   299  			l.Path = cstring(cmddat[hdr.Path:])
   300  			l.LoadBytes = LoadBytes(cmddat)
   301  			f.Loads = append(f.Loads, l)
   302  
   303  		case LoadCmdDylib:
   304  			var hdr DylibCmd
   305  			b := bytes.NewReader(cmddat)
   306  			if err := binary.Read(b, bo, &hdr); err != nil {
   307  				return nil, err
   308  			}
   309  			l := new(Dylib)
   310  			if hdr.Name >= uint32(len(cmddat)) {
   311  				return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
   312  			}
   313  			l.Name = cstring(cmddat[hdr.Name:])
   314  			l.Time = hdr.Time
   315  			l.CurrentVersion = hdr.CurrentVersion
   316  			l.CompatVersion = hdr.CompatVersion
   317  			l.LoadBytes = LoadBytes(cmddat)
   318  			f.Loads = append(f.Loads, l)
   319  
   320  		case LoadCmdSymtab:
   321  			var hdr SymtabCmd
   322  			b := bytes.NewReader(cmddat)
   323  			if err := binary.Read(b, bo, &hdr); err != nil {
   324  				return nil, err
   325  			}
   326  			strtab := make([]byte, hdr.Strsize)
   327  			if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil {
   328  				return nil, err
   329  			}
   330  			var symsz int
   331  			if f.Magic == Magic64 {
   332  				symsz = 16
   333  			} else {
   334  				symsz = 12
   335  			}
   336  			symdat, err := saferio.ReadDataAt(r, uint64(hdr.Nsyms)*uint64(symsz), int64(hdr.Symoff))
   337  			if err != nil {
   338  				return nil, err
   339  			}
   340  			st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
   341  			if err != nil {
   342  				return nil, err
   343  			}
   344  			f.Loads = append(f.Loads, st)
   345  			f.Symtab = st
   346  
   347  		case LoadCmdDysymtab:
   348  			var hdr DysymtabCmd
   349  			b := bytes.NewReader(cmddat)
   350  			if err := binary.Read(b, bo, &hdr); err != nil {
   351  				return nil, err
   352  			}
   353  			if hdr.Iundefsym > uint32(len(f.Symtab.Syms)) {
   354  				return nil, &FormatError{offset, fmt.Sprintf(
   355  					"undefined symbols index in dynamic symbol table command is greater than symbol table length (%d > %d)",
   356  					hdr.Iundefsym, len(f.Symtab.Syms)), nil}
   357  			} else if hdr.Iundefsym+hdr.Nundefsym > uint32(len(f.Symtab.Syms)) {
   358  				return nil, &FormatError{offset, fmt.Sprintf(
   359  					"number of undefined symbols after index in dynamic symbol table command is greater than symbol table length (%d > %d)",
   360  					hdr.Iundefsym+hdr.Nundefsym, len(f.Symtab.Syms)), nil}
   361  			}
   362  			dat := make([]byte, hdr.Nindirectsyms*4)
   363  			if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
   364  				return nil, err
   365  			}
   366  			x := make([]uint32, hdr.Nindirectsyms)
   367  			if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
   368  				return nil, err
   369  			}
   370  			st := new(Dysymtab)
   371  			st.LoadBytes = LoadBytes(cmddat)
   372  			st.DysymtabCmd = hdr
   373  			st.IndirectSyms = x
   374  			f.Loads = append(f.Loads, st)
   375  			f.Dysymtab = st
   376  
   377  		case LoadCmdSegment:
   378  			var seg32 Segment32
   379  			b := bytes.NewReader(cmddat)
   380  			if err := binary.Read(b, bo, &seg32); err != nil {
   381  				return nil, err
   382  			}
   383  			s = new(Segment)
   384  			s.LoadBytes = cmddat
   385  			s.Cmd = cmd
   386  			s.Len = siz
   387  			s.Name = cstring(seg32.Name[0:])
   388  			s.Addr = uint64(seg32.Addr)
   389  			s.Memsz = uint64(seg32.Memsz)
   390  			s.Offset = uint64(seg32.Offset)
   391  			s.Filesz = uint64(seg32.Filesz)
   392  			s.Maxprot = seg32.Maxprot
   393  			s.Prot = seg32.Prot
   394  			s.Nsect = seg32.Nsect
   395  			s.Flag = seg32.Flag
   396  			f.Loads = append(f.Loads, s)
   397  			for i := 0; i < int(s.Nsect); i++ {
   398  				var sh32 Section32
   399  				if err := binary.Read(b, bo, &sh32); err != nil {
   400  					return nil, err
   401  				}
   402  				sh := new(Section)
   403  				sh.Name = cstring(sh32.Name[0:])
   404  				sh.Seg = cstring(sh32.Seg[0:])
   405  				sh.Addr = uint64(sh32.Addr)
   406  				sh.Size = uint64(sh32.Size)
   407  				sh.Offset = sh32.Offset
   408  				sh.Align = sh32.Align
   409  				sh.Reloff = sh32.Reloff
   410  				sh.Nreloc = sh32.Nreloc
   411  				sh.Flags = sh32.Flags
   412  				if err := f.pushSection(sh, r); err != nil {
   413  					return nil, err
   414  				}
   415  			}
   416  
   417  		case LoadCmdSegment64:
   418  			var seg64 Segment64
   419  			b := bytes.NewReader(cmddat)
   420  			if err := binary.Read(b, bo, &seg64); err != nil {
   421  				return nil, err
   422  			}
   423  			s = new(Segment)
   424  			s.LoadBytes = cmddat
   425  			s.Cmd = cmd
   426  			s.Len = siz
   427  			s.Name = cstring(seg64.Name[0:])
   428  			s.Addr = seg64.Addr
   429  			s.Memsz = seg64.Memsz
   430  			s.Offset = seg64.Offset
   431  			s.Filesz = seg64.Filesz
   432  			s.Maxprot = seg64.Maxprot
   433  			s.Prot = seg64.Prot
   434  			s.Nsect = seg64.Nsect
   435  			s.Flag = seg64.Flag
   436  			f.Loads = append(f.Loads, s)
   437  			for i := 0; i < int(s.Nsect); i++ {
   438  				var sh64 Section64
   439  				if err := binary.Read(b, bo, &sh64); err != nil {
   440  					return nil, err
   441  				}
   442  				sh := new(Section)
   443  				sh.Name = cstring(sh64.Name[0:])
   444  				sh.Seg = cstring(sh64.Seg[0:])
   445  				sh.Addr = sh64.Addr
   446  				sh.Size = sh64.Size
   447  				sh.Offset = sh64.Offset
   448  				sh.Align = sh64.Align
   449  				sh.Reloff = sh64.Reloff
   450  				sh.Nreloc = sh64.Nreloc
   451  				sh.Flags = sh64.Flags
   452  				if err := f.pushSection(sh, r); err != nil {
   453  					return nil, err
   454  				}
   455  			}
   456  		}
   457  		if s != nil {
   458  			if int64(s.Offset) < 0 {
   459  				return nil, &FormatError{offset, "invalid section offset", s.Offset}
   460  			}
   461  			if int64(s.Filesz) < 0 {
   462  				return nil, &FormatError{offset, "invalid section file size", s.Filesz}
   463  			}
   464  			s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
   465  			s.ReaderAt = s.sr
   466  		}
   467  	}
   468  	return f, nil
   469  }
   470  
   471  func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
   472  	bo := f.ByteOrder
   473  	c := saferio.SliceCap((*Symbol)(nil), uint64(hdr.Nsyms))
   474  	if c < 0 {
   475  		return nil, &FormatError{offset, "too many symbols", nil}
   476  	}
   477  	symtab := make([]Symbol, 0, c)
   478  	b := bytes.NewReader(symdat)
   479  	for i := 0; i < int(hdr.Nsyms); i++ {
   480  		var n Nlist64
   481  		if f.Magic == Magic64 {
   482  			if err := binary.Read(b, bo, &n); err != nil {
   483  				return nil, err
   484  			}
   485  		} else {
   486  			var n32 Nlist32
   487  			if err := binary.Read(b, bo, &n32); err != nil {
   488  				return nil, err
   489  			}
   490  			n.Name = n32.Name
   491  			n.Type = n32.Type
   492  			n.Sect = n32.Sect
   493  			n.Desc = n32.Desc
   494  			n.Value = uint64(n32.Value)
   495  		}
   496  		if n.Name >= uint32(len(strtab)) {
   497  			return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
   498  		}
   499  		// We add "_" to Go symbols. Strip it here. See issue 33808.
   500  		name := cstring(strtab[n.Name:])
   501  		if strings.Contains(name, ".") && name[0] == '_' {
   502  			name = name[1:]
   503  		}
   504  		symtab = append(symtab, Symbol{
   505  			Name:  name,
   506  			Type:  n.Type,
   507  			Sect:  n.Sect,
   508  			Desc:  n.Desc,
   509  			Value: n.Value,
   510  		})
   511  	}
   512  	st := new(Symtab)
   513  	st.LoadBytes = LoadBytes(cmddat)
   514  	st.Syms = symtab
   515  	return st, nil
   516  }
   517  
   518  type relocInfo struct {
   519  	Addr   uint32
   520  	Symnum uint32
   521  }
   522  
   523  func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
   524  	f.Sections = append(f.Sections, sh)
   525  	sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
   526  	sh.ReaderAt = sh.sr
   527  
   528  	if sh.Nreloc > 0 {
   529  		reldat, err := saferio.ReadDataAt(r, uint64(sh.Nreloc)*8, int64(sh.Reloff))
   530  		if err != nil {
   531  			return err
   532  		}
   533  		b := bytes.NewReader(reldat)
   534  
   535  		bo := f.ByteOrder
   536  
   537  		sh.Relocs = make([]Reloc, sh.Nreloc)
   538  		for i := range sh.Relocs {
   539  			rel := &sh.Relocs[i]
   540  
   541  			var ri relocInfo
   542  			if err := binary.Read(b, bo, &ri); err != nil {
   543  				return err
   544  			}
   545  
   546  			if ri.Addr&(1<<31) != 0 { // scattered
   547  				rel.Addr = ri.Addr & (1<<24 - 1)
   548  				rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
   549  				rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
   550  				rel.Pcrel = ri.Addr&(1<<30) != 0
   551  				rel.Value = ri.Symnum
   552  				rel.Scattered = true
   553  			} else {
   554  				switch bo {
   555  				case binary.LittleEndian:
   556  					rel.Addr = ri.Addr
   557  					rel.Value = ri.Symnum & (1<<24 - 1)
   558  					rel.Pcrel = ri.Symnum&(1<<24) != 0
   559  					rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
   560  					rel.Extern = ri.Symnum&(1<<27) != 0
   561  					rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
   562  				case binary.BigEndian:
   563  					rel.Addr = ri.Addr
   564  					rel.Value = ri.Symnum >> 8
   565  					rel.Pcrel = ri.Symnum&(1<<7) != 0
   566  					rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
   567  					rel.Extern = ri.Symnum&(1<<4) != 0
   568  					rel.Type = uint8(ri.Symnum & (1<<4 - 1))
   569  				default:
   570  					panic("unreachable")
   571  				}
   572  			}
   573  		}
   574  	}
   575  
   576  	return nil
   577  }
   578  
   579  func cstring(b []byte) string {
   580  	i := bytes.IndexByte(b, 0)
   581  	if i == -1 {
   582  		i = len(b)
   583  	}
   584  	return string(b[0:i])
   585  }
   586  
   587  // Segment returns the first Segment with the given name, or nil if no such segment exists.
   588  func (f *File) Segment(name string) *Segment {
   589  	for _, l := range f.Loads {
   590  		if s, ok := l.(*Segment); ok && s.Name == name {
   591  			return s
   592  		}
   593  	}
   594  	return nil
   595  }
   596  
   597  // Section returns the first section with the given name, or nil if no such
   598  // section exists.
   599  func (f *File) Section(name string) *Section {
   600  	for _, s := range f.Sections {
   601  		if s.Name == name {
   602  			return s
   603  		}
   604  	}
   605  	return nil
   606  }
   607  
   608  // DWARF returns the DWARF debug information for the Mach-O file.
   609  func (f *File) DWARF() (*dwarf.Data, error) {
   610  	dwarfSuffix := func(s *Section) string {
   611  		switch {
   612  		case strings.HasPrefix(s.Name, "__debug_"):
   613  			return s.Name[8:]
   614  		case strings.HasPrefix(s.Name, "__zdebug_"):
   615  			return s.Name[9:]
   616  		default:
   617  			return ""
   618  		}
   619  
   620  	}
   621  	sectionData := func(s *Section) ([]byte, error) {
   622  		b, err := s.Data()
   623  		if err != nil && uint64(len(b)) < s.Size {
   624  			return nil, err
   625  		}
   626  
   627  		if len(b) >= 12 && string(b[:4]) == "ZLIB" {
   628  			dlen := binary.BigEndian.Uint64(b[4:12])
   629  			dbuf := make([]byte, dlen)
   630  			r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
   631  			if err != nil {
   632  				return nil, err
   633  			}
   634  			if _, err := io.ReadFull(r, dbuf); err != nil {
   635  				return nil, err
   636  			}
   637  			if err := r.Close(); err != nil {
   638  				return nil, err
   639  			}
   640  			b = dbuf
   641  		}
   642  		return b, nil
   643  	}
   644  
   645  	// There are many other DWARF sections, but these
   646  	// are the ones the debug/dwarf package uses.
   647  	// Don't bother loading others.
   648  	var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
   649  	for _, s := range f.Sections {
   650  		suffix := dwarfSuffix(s)
   651  		if suffix == "" {
   652  			continue
   653  		}
   654  		if _, ok := dat[suffix]; !ok {
   655  			continue
   656  		}
   657  		b, err := sectionData(s)
   658  		if err != nil {
   659  			return nil, err
   660  		}
   661  		dat[suffix] = b
   662  	}
   663  
   664  	d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
   665  	if err != nil {
   666  		return nil, err
   667  	}
   668  
   669  	// Look for DWARF4 .debug_types sections and DWARF5 sections.
   670  	for i, s := range f.Sections {
   671  		suffix := dwarfSuffix(s)
   672  		if suffix == "" {
   673  			continue
   674  		}
   675  		if _, ok := dat[suffix]; ok {
   676  			// Already handled.
   677  			continue
   678  		}
   679  
   680  		b, err := sectionData(s)
   681  		if err != nil {
   682  			return nil, err
   683  		}
   684  
   685  		if suffix == "types" {
   686  			err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
   687  		} else {
   688  			err = d.AddSection(".debug_"+suffix, b)
   689  		}
   690  		if err != nil {
   691  			return nil, err
   692  		}
   693  	}
   694  
   695  	return d, nil
   696  }
   697  
   698  // ImportedSymbols returns the names of all symbols
   699  // referred to by the binary f that are expected to be
   700  // satisfied by other libraries at dynamic load time.
   701  func (f *File) ImportedSymbols() ([]string, error) {
   702  	if f.Dysymtab == nil || f.Symtab == nil {
   703  		return nil, &FormatError{0, "missing symbol table", nil}
   704  	}
   705  
   706  	st := f.Symtab
   707  	dt := f.Dysymtab
   708  	var all []string
   709  	for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
   710  		all = append(all, s.Name)
   711  	}
   712  	return all, nil
   713  }
   714  
   715  // ImportedLibraries returns the paths of all libraries
   716  // referred to by the binary f that are expected to be
   717  // linked with the binary at dynamic link time.
   718  func (f *File) ImportedLibraries() ([]string, error) {
   719  	var all []string
   720  	for _, l := range f.Loads {
   721  		if lib, ok := l.(*Dylib); ok {
   722  			all = append(all, lib.Name)
   723  		}
   724  	}
   725  	return all, nil
   726  }
   727  

View as plain text