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](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, err := saferio.ReadDataAt(r, uint64(hdr.Strsize), int64(hdr.Stroff))
   327  			if 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 f.Symtab == nil {
   354  				return nil, &FormatError{offset, "dynamic symbol table seen before any ordinary symbol table", nil}
   355  			} else if hdr.Iundefsym > uint32(len(f.Symtab.Syms)) {
   356  				return nil, &FormatError{offset, fmt.Sprintf(
   357  					"undefined symbols index in dynamic symbol table command is greater than symbol table length (%d > %d)",
   358  					hdr.Iundefsym, len(f.Symtab.Syms)), nil}
   359  			} else if hdr.Iundefsym+hdr.Nundefsym > uint32(len(f.Symtab.Syms)) {
   360  				return nil, &FormatError{offset, fmt.Sprintf(
   361  					"number of undefined symbols after index in dynamic symbol table command is greater than symbol table length (%d > %d)",
   362  					hdr.Iundefsym+hdr.Nundefsym, len(f.Symtab.Syms)), nil}
   363  			}
   364  			dat, err := saferio.ReadDataAt(r, uint64(hdr.Nindirectsyms)*4, int64(hdr.Indirectsymoff))
   365  			if err != nil {
   366  				return nil, err
   367  			}
   368  			x := make([]uint32, hdr.Nindirectsyms)
   369  			if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
   370  				return nil, err
   371  			}
   372  			st := new(Dysymtab)
   373  			st.LoadBytes = LoadBytes(cmddat)
   374  			st.DysymtabCmd = hdr
   375  			st.IndirectSyms = x
   376  			f.Loads = append(f.Loads, st)
   377  			f.Dysymtab = st
   378  
   379  		case LoadCmdSegment:
   380  			var seg32 Segment32
   381  			b := bytes.NewReader(cmddat)
   382  			if err := binary.Read(b, bo, &seg32); err != nil {
   383  				return nil, err
   384  			}
   385  			s = new(Segment)
   386  			s.LoadBytes = cmddat
   387  			s.Cmd = cmd
   388  			s.Len = siz
   389  			s.Name = cstring(seg32.Name[0:])
   390  			s.Addr = uint64(seg32.Addr)
   391  			s.Memsz = uint64(seg32.Memsz)
   392  			s.Offset = uint64(seg32.Offset)
   393  			s.Filesz = uint64(seg32.Filesz)
   394  			s.Maxprot = seg32.Maxprot
   395  			s.Prot = seg32.Prot
   396  			s.Nsect = seg32.Nsect
   397  			s.Flag = seg32.Flag
   398  			f.Loads = append(f.Loads, s)
   399  			for i := 0; i < int(s.Nsect); i++ {
   400  				var sh32 Section32
   401  				if err := binary.Read(b, bo, &sh32); err != nil {
   402  					return nil, err
   403  				}
   404  				sh := new(Section)
   405  				sh.Name = cstring(sh32.Name[0:])
   406  				sh.Seg = cstring(sh32.Seg[0:])
   407  				sh.Addr = uint64(sh32.Addr)
   408  				sh.Size = uint64(sh32.Size)
   409  				sh.Offset = sh32.Offset
   410  				sh.Align = sh32.Align
   411  				sh.Reloff = sh32.Reloff
   412  				sh.Nreloc = sh32.Nreloc
   413  				sh.Flags = sh32.Flags
   414  				if err := f.pushSection(sh, r); err != nil {
   415  					return nil, err
   416  				}
   417  			}
   418  
   419  		case LoadCmdSegment64:
   420  			var seg64 Segment64
   421  			b := bytes.NewReader(cmddat)
   422  			if err := binary.Read(b, bo, &seg64); err != nil {
   423  				return nil, err
   424  			}
   425  			s = new(Segment)
   426  			s.LoadBytes = cmddat
   427  			s.Cmd = cmd
   428  			s.Len = siz
   429  			s.Name = cstring(seg64.Name[0:])
   430  			s.Addr = seg64.Addr
   431  			s.Memsz = seg64.Memsz
   432  			s.Offset = seg64.Offset
   433  			s.Filesz = seg64.Filesz
   434  			s.Maxprot = seg64.Maxprot
   435  			s.Prot = seg64.Prot
   436  			s.Nsect = seg64.Nsect
   437  			s.Flag = seg64.Flag
   438  			f.Loads = append(f.Loads, s)
   439  			for i := 0; i < int(s.Nsect); i++ {
   440  				var sh64 Section64
   441  				if err := binary.Read(b, bo, &sh64); err != nil {
   442  					return nil, err
   443  				}
   444  				sh := new(Section)
   445  				sh.Name = cstring(sh64.Name[0:])
   446  				sh.Seg = cstring(sh64.Seg[0:])
   447  				sh.Addr = sh64.Addr
   448  				sh.Size = sh64.Size
   449  				sh.Offset = sh64.Offset
   450  				sh.Align = sh64.Align
   451  				sh.Reloff = sh64.Reloff
   452  				sh.Nreloc = sh64.Nreloc
   453  				sh.Flags = sh64.Flags
   454  				if err := f.pushSection(sh, r); err != nil {
   455  					return nil, err
   456  				}
   457  			}
   458  		}
   459  		if s != nil {
   460  			if int64(s.Offset) < 0 {
   461  				return nil, &FormatError{offset, "invalid section offset", s.Offset}
   462  			}
   463  			if int64(s.Filesz) < 0 {
   464  				return nil, &FormatError{offset, "invalid section file size", s.Filesz}
   465  			}
   466  			s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
   467  			s.ReaderAt = s.sr
   468  		}
   469  	}
   470  	return f, nil
   471  }
   472  
   473  func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
   474  	bo := f.ByteOrder
   475  	c := saferio.SliceCap[Symbol](uint64(hdr.Nsyms))
   476  	if c < 0 {
   477  		return nil, &FormatError{offset, "too many symbols", nil}
   478  	}
   479  	symtab := make([]Symbol, 0, c)
   480  	b := bytes.NewReader(symdat)
   481  	for i := 0; i < int(hdr.Nsyms); i++ {
   482  		var n Nlist64
   483  		if f.Magic == Magic64 {
   484  			if err := binary.Read(b, bo, &n); err != nil {
   485  				return nil, err
   486  			}
   487  		} else {
   488  			var n32 Nlist32
   489  			if err := binary.Read(b, bo, &n32); err != nil {
   490  				return nil, err
   491  			}
   492  			n.Name = n32.Name
   493  			n.Type = n32.Type
   494  			n.Sect = n32.Sect
   495  			n.Desc = n32.Desc
   496  			n.Value = uint64(n32.Value)
   497  		}
   498  		if n.Name >= uint32(len(strtab)) {
   499  			return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
   500  		}
   501  		// We add "_" to Go symbols. Strip it here. See issue 33808.
   502  		name := cstring(strtab[n.Name:])
   503  		if strings.Contains(name, ".") && name[0] == '_' {
   504  			name = name[1:]
   505  		}
   506  		symtab = append(symtab, Symbol{
   507  			Name:  name,
   508  			Type:  n.Type,
   509  			Sect:  n.Sect,
   510  			Desc:  n.Desc,
   511  			Value: n.Value,
   512  		})
   513  	}
   514  	st := new(Symtab)
   515  	st.LoadBytes = LoadBytes(cmddat)
   516  	st.Syms = symtab
   517  	return st, nil
   518  }
   519  
   520  type relocInfo struct {
   521  	Addr   uint32
   522  	Symnum uint32
   523  }
   524  
   525  func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
   526  	f.Sections = append(f.Sections, sh)
   527  	sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
   528  	sh.ReaderAt = sh.sr
   529  
   530  	if sh.Nreloc > 0 {
   531  		reldat, err := saferio.ReadDataAt(r, uint64(sh.Nreloc)*8, int64(sh.Reloff))
   532  		if err != nil {
   533  			return err
   534  		}
   535  		b := bytes.NewReader(reldat)
   536  
   537  		bo := f.ByteOrder
   538  
   539  		sh.Relocs = make([]Reloc, sh.Nreloc)
   540  		for i := range sh.Relocs {
   541  			rel := &sh.Relocs[i]
   542  
   543  			var ri relocInfo
   544  			if err := binary.Read(b, bo, &ri); err != nil {
   545  				return err
   546  			}
   547  
   548  			if ri.Addr&(1<<31) != 0 { // scattered
   549  				rel.Addr = ri.Addr & (1<<24 - 1)
   550  				rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
   551  				rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
   552  				rel.Pcrel = ri.Addr&(1<<30) != 0
   553  				rel.Value = ri.Symnum
   554  				rel.Scattered = true
   555  			} else {
   556  				switch bo {
   557  				case binary.LittleEndian:
   558  					rel.Addr = ri.Addr
   559  					rel.Value = ri.Symnum & (1<<24 - 1)
   560  					rel.Pcrel = ri.Symnum&(1<<24) != 0
   561  					rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
   562  					rel.Extern = ri.Symnum&(1<<27) != 0
   563  					rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
   564  				case binary.BigEndian:
   565  					rel.Addr = ri.Addr
   566  					rel.Value = ri.Symnum >> 8
   567  					rel.Pcrel = ri.Symnum&(1<<7) != 0
   568  					rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
   569  					rel.Extern = ri.Symnum&(1<<4) != 0
   570  					rel.Type = uint8(ri.Symnum & (1<<4 - 1))
   571  				default:
   572  					panic("unreachable")
   573  				}
   574  			}
   575  		}
   576  	}
   577  
   578  	return nil
   579  }
   580  
   581  func cstring(b []byte) string {
   582  	i := bytes.IndexByte(b, 0)
   583  	if i == -1 {
   584  		i = len(b)
   585  	}
   586  	return string(b[0:i])
   587  }
   588  
   589  // Segment returns the first Segment with the given name, or nil if no such segment exists.
   590  func (f *File) Segment(name string) *Segment {
   591  	for _, l := range f.Loads {
   592  		if s, ok := l.(*Segment); ok && s.Name == name {
   593  			return s
   594  		}
   595  	}
   596  	return nil
   597  }
   598  
   599  // Section returns the first section with the given name, or nil if no such
   600  // section exists.
   601  func (f *File) Section(name string) *Section {
   602  	for _, s := range f.Sections {
   603  		if s.Name == name {
   604  			return s
   605  		}
   606  	}
   607  	return nil
   608  }
   609  
   610  // DWARF returns the DWARF debug information for the Mach-O file.
   611  func (f *File) DWARF() (*dwarf.Data, error) {
   612  	dwarfSuffix := func(s *Section) string {
   613  		sectname := s.Name
   614  		var pfx int
   615  		switch {
   616  		case strings.HasPrefix(sectname, "__debug_"):
   617  			pfx = 8
   618  		case strings.HasPrefix(sectname, "__zdebug_"):
   619  			pfx = 9
   620  		default:
   621  			return ""
   622  		}
   623  		// Mach-O executables truncate section names to 16 characters, mangling some DWARF sections.
   624  		// As of DWARFv5 these are the only problematic section names (see DWARFv5 Appendix G).
   625  		for _, longname := range []string{
   626  			"__debug_str_offsets",
   627  			"__zdebug_line_str",
   628  			"__zdebug_loclists",
   629  			"__zdebug_pubnames",
   630  			"__zdebug_pubtypes",
   631  			"__zdebug_rnglists",
   632  			"__zdebug_str_offsets",
   633  		} {
   634  			if sectname == longname[:16] {
   635  				sectname = longname
   636  				break
   637  			}
   638  		}
   639  		return sectname[pfx:]
   640  	}
   641  	sectionData := func(s *Section) ([]byte, error) {
   642  		b, err := s.Data()
   643  		if err != nil && uint64(len(b)) < s.Size {
   644  			return nil, err
   645  		}
   646  
   647  		if len(b) >= 12 && string(b[:4]) == "ZLIB" {
   648  			dlen := binary.BigEndian.Uint64(b[4:12])
   649  			dbuf := make([]byte, dlen)
   650  			r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
   651  			if err != nil {
   652  				return nil, err
   653  			}
   654  			if _, err := io.ReadFull(r, dbuf); err != nil {
   655  				return nil, err
   656  			}
   657  			if err := r.Close(); err != nil {
   658  				return nil, err
   659  			}
   660  			b = dbuf
   661  		}
   662  		return b, nil
   663  	}
   664  
   665  	// There are many other DWARF sections, but these
   666  	// are the ones the debug/dwarf package uses.
   667  	// Don't bother loading others.
   668  	var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
   669  	for _, s := range f.Sections {
   670  		suffix := dwarfSuffix(s)
   671  		if suffix == "" {
   672  			continue
   673  		}
   674  		if _, ok := dat[suffix]; !ok {
   675  			continue
   676  		}
   677  		b, err := sectionData(s)
   678  		if err != nil {
   679  			return nil, err
   680  		}
   681  		dat[suffix] = b
   682  	}
   683  
   684  	d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
   685  	if err != nil {
   686  		return nil, err
   687  	}
   688  
   689  	// Look for DWARF4 .debug_types sections and DWARF5 sections.
   690  	for i, s := range f.Sections {
   691  		suffix := dwarfSuffix(s)
   692  		if suffix == "" {
   693  			continue
   694  		}
   695  		if _, ok := dat[suffix]; ok {
   696  			// Already handled.
   697  			continue
   698  		}
   699  
   700  		b, err := sectionData(s)
   701  		if err != nil {
   702  			return nil, err
   703  		}
   704  
   705  		if suffix == "types" {
   706  			err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
   707  		} else {
   708  			err = d.AddSection(".debug_"+suffix, b)
   709  		}
   710  		if err != nil {
   711  			return nil, err
   712  		}
   713  	}
   714  
   715  	return d, nil
   716  }
   717  
   718  // ImportedSymbols returns the names of all symbols
   719  // referred to by the binary f that are expected to be
   720  // satisfied by other libraries at dynamic load time.
   721  func (f *File) ImportedSymbols() ([]string, error) {
   722  	if f.Dysymtab == nil || f.Symtab == nil {
   723  		return nil, &FormatError{0, "missing symbol table", nil}
   724  	}
   725  
   726  	st := f.Symtab
   727  	dt := f.Dysymtab
   728  	var all []string
   729  	for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
   730  		all = append(all, s.Name)
   731  	}
   732  	return all, nil
   733  }
   734  
   735  // ImportedLibraries returns the paths of all libraries
   736  // referred to by the binary f that are expected to be
   737  // linked with the binary at dynamic link time.
   738  func (f *File) ImportedLibraries() ([]string, error) {
   739  	var all []string
   740  	for _, l := range f.Loads {
   741  		if lib, ok := l.(*Dylib); ok {
   742  			all = append(all, lib.Name)
   743  		}
   744  	}
   745  	return all, nil
   746  }
   747  

View as plain text