Black Lives Matter. Support the Equal Justice Initiative.

Source file src/debug/macho/file.go

Documentation: debug/macho

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

View as plain text