file.mx raw

   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  )
  30  
  31  // A File represents an open Mach-O file.
  32  type File struct {
  33  	FileHeader
  34  	ByteOrder binary.ByteOrder
  35  	Loads     []Load
  36  	Sections  []*Section
  37  
  38  	Symtab   *Symtab
  39  	Dysymtab *Dysymtab
  40  
  41  	closer io.Closer
  42  }
  43  
  44  // A Load represents any Mach-O load command.
  45  type Load interface {
  46  	Raw() []byte
  47  }
  48  
  49  // A LoadBytes is the uninterpreted bytes of a Mach-O load command.
  50  type LoadBytes []byte
  51  
  52  func (b LoadBytes) Raw() []byte { return b }
  53  
  54  // A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command.
  55  type SegmentHeader struct {
  56  	Cmd     LoadCmd
  57  	Len     uint32
  58  	Name    string
  59  	Addr    uint64
  60  	Memsz   uint64
  61  	Offset  uint64
  62  	Filesz  uint64
  63  	Maxprot uint32
  64  	Prot    uint32
  65  	Nsect   uint32
  66  	Flag    uint32
  67  }
  68  
  69  // A Segment represents a Mach-O 32-bit or 64-bit load segment command.
  70  type Segment struct {
  71  	LoadBytes
  72  	SegmentHeader
  73  
  74  	// Embed ReaderAt for ReadAt method.
  75  	// Do not embed SectionReader directly
  76  	// to avoid having Read and Seek.
  77  	// If a client wants Read and Seek it must use
  78  	// Open() to avoid fighting over the seek offset
  79  	// with other clients.
  80  	io.ReaderAt
  81  	sr *io.SectionReader
  82  }
  83  
  84  // Data reads and returns the contents of the segment.
  85  func (s *Segment) Data() ([]byte, error) {
  86  	return saferio.ReadDataAt(s.sr, s.Filesz, 0)
  87  }
  88  
  89  // Open returns a new ReadSeeker reading the segment.
  90  func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
  91  
  92  type SectionHeader struct {
  93  	Name   string
  94  	Seg    string
  95  	Addr   uint64
  96  	Size   uint64
  97  	Offset uint32
  98  	Align  uint32
  99  	Reloff uint32
 100  	Nreloc uint32
 101  	Flags  uint32
 102  }
 103  
 104  // A Reloc represents a Mach-O relocation.
 105  type Reloc struct {
 106  	Addr  uint32
 107  	Value uint32
 108  	// when Scattered == false && Extern == true, Value is the symbol number.
 109  	// when Scattered == false && Extern == false, Value is the section number.
 110  	// when Scattered == true, Value is the value that this reloc refers to.
 111  	Type      uint8
 112  	Len       uint8 // 0=byte, 1=word, 2=long, 3=quad
 113  	Pcrel     bool
 114  	Extern    bool // valid if Scattered == false
 115  	Scattered bool
 116  }
 117  
 118  type Section struct {
 119  	SectionHeader
 120  	Relocs []Reloc
 121  
 122  	// Embed ReaderAt for ReadAt method.
 123  	// Do not embed SectionReader directly
 124  	// to avoid having Read and Seek.
 125  	// If a client wants Read and Seek it must use
 126  	// Open() to avoid fighting over the seek offset
 127  	// with other clients.
 128  	io.ReaderAt
 129  	sr *io.SectionReader
 130  }
 131  
 132  // Data reads and returns the contents of the Mach-O section.
 133  func (s *Section) Data() ([]byte, error) {
 134  	return saferio.ReadDataAt(s.sr, s.Size, 0)
 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 any
 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 := &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, err := saferio.ReadDataAt(r, uint64(f.Cmdsz), offset)
 262  	if err != nil {
 263  		return nil, err
 264  	}
 265  	c := saferio.SliceCap[Load](uint64(f.Ncmd))
 266  	if c < 0 {
 267  		return nil, &FormatError{offset, "too many load commands", nil}
 268  	}
 269  	f.Loads = []Load{:0:c}
 270  	bo := f.ByteOrder
 271  	for i := uint32(0); i < f.Ncmd; i++ {
 272  		// Each load command begins with uint32 command and length.
 273  		if len(dat) < 8 {
 274  			return nil, &FormatError{offset, "command block too small", nil}
 275  		}
 276  		cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
 277  		if siz < 8 || siz > uint32(len(dat)) {
 278  			return nil, &FormatError{offset, "invalid command block size", nil}
 279  		}
 280  		var cmddat []byte
 281  		cmddat, dat = dat[0:siz], dat[siz:]
 282  		offset += int64(siz)
 283  		var s *Segment
 284  		switch cmd {
 285  		default:
 286  			f.Loads = append(f.Loads, LoadBytes(cmddat))
 287  
 288  		case LoadCmdRpath:
 289  			var hdr RpathCmd
 290  			b := bytes.NewReader(cmddat)
 291  			if err := binary.Read(b, bo, &hdr); err != nil {
 292  				return nil, err
 293  			}
 294  			l := &Rpath{}
 295  			if hdr.Path >= uint32(len(cmddat)) {
 296  				return nil, &FormatError{offset, "invalid path in rpath command", hdr.Path}
 297  			}
 298  			l.Path = cstring(cmddat[hdr.Path:])
 299  			l.LoadBytes = LoadBytes(cmddat)
 300  			f.Loads = append(f.Loads, l)
 301  
 302  		case LoadCmdDylib:
 303  			var hdr DylibCmd
 304  			b := bytes.NewReader(cmddat)
 305  			if err := binary.Read(b, bo, &hdr); err != nil {
 306  				return nil, err
 307  			}
 308  			l := &Dylib{}
 309  			if hdr.Name >= uint32(len(cmddat)) {
 310  				return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
 311  			}
 312  			l.Name = cstring(cmddat[hdr.Name:])
 313  			l.Time = hdr.Time
 314  			l.CurrentVersion = hdr.CurrentVersion
 315  			l.CompatVersion = hdr.CompatVersion
 316  			l.LoadBytes = LoadBytes(cmddat)
 317  			f.Loads = append(f.Loads, l)
 318  
 319  		case LoadCmdSymtab:
 320  			var hdr SymtabCmd
 321  			b := bytes.NewReader(cmddat)
 322  			if err := binary.Read(b, bo, &hdr); err != nil {
 323  				return nil, err
 324  			}
 325  			strtab, err := saferio.ReadDataAt(r, uint64(hdr.Strsize), int64(hdr.Stroff))
 326  			if err != nil {
 327  				return nil, err
 328  			}
 329  			var symsz int
 330  			if f.Magic == Magic64 {
 331  				symsz = 16
 332  			} else {
 333  				symsz = 12
 334  			}
 335  			symdat, err := saferio.ReadDataAt(r, uint64(hdr.Nsyms)*uint64(symsz), int64(hdr.Symoff))
 336  			if err != nil {
 337  				return nil, err
 338  			}
 339  			st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
 340  			if err != nil {
 341  				return nil, err
 342  			}
 343  			f.Loads = append(f.Loads, st)
 344  			f.Symtab = st
 345  
 346  		case LoadCmdDysymtab:
 347  			var hdr DysymtabCmd
 348  			b := bytes.NewReader(cmddat)
 349  			if err := binary.Read(b, bo, &hdr); err != nil {
 350  				return nil, err
 351  			}
 352  			if f.Symtab == nil {
 353  				return nil, &FormatError{offset, "dynamic symbol table seen before any ordinary symbol table", nil}
 354  			} else if hdr.Iundefsym > uint32(len(f.Symtab.Syms)) {
 355  				return nil, &FormatError{offset, fmt.Sprintf(
 356  					"undefined symbols index in dynamic symbol table command is greater than symbol table length (%d > %d)",
 357  					hdr.Iundefsym, len(f.Symtab.Syms)), nil}
 358  			} else if hdr.Iundefsym+hdr.Nundefsym > uint32(len(f.Symtab.Syms)) {
 359  				return nil, &FormatError{offset, fmt.Sprintf(
 360  					"number of undefined symbols after index in dynamic symbol table command is greater than symbol table length (%d > %d)",
 361  					hdr.Iundefsym+hdr.Nundefsym, len(f.Symtab.Syms)), nil}
 362  			}
 363  			dat, err := saferio.ReadDataAt(r, uint64(hdr.Nindirectsyms)*4, int64(hdr.Indirectsymoff))
 364  			if err != nil {
 365  				return nil, err
 366  			}
 367  			x := []uint32{:hdr.Nindirectsyms}
 368  			if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
 369  				return nil, err
 370  			}
 371  			st := &Dysymtab{}
 372  			st.LoadBytes = LoadBytes(cmddat)
 373  			st.DysymtabCmd = hdr
 374  			st.IndirectSyms = x
 375  			f.Loads = append(f.Loads, st)
 376  			f.Dysymtab = st
 377  
 378  		case LoadCmdSegment:
 379  			var seg32 Segment32
 380  			b := bytes.NewReader(cmddat)
 381  			if err := binary.Read(b, bo, &seg32); err != nil {
 382  				return nil, err
 383  			}
 384  			s = &Segment{}
 385  			s.LoadBytes = cmddat
 386  			s.Cmd = cmd
 387  			s.Len = siz
 388  			s.Name = cstring(seg32.Name[0:])
 389  			s.Addr = uint64(seg32.Addr)
 390  			s.Memsz = uint64(seg32.Memsz)
 391  			s.Offset = uint64(seg32.Offset)
 392  			s.Filesz = uint64(seg32.Filesz)
 393  			s.Maxprot = seg32.Maxprot
 394  			s.Prot = seg32.Prot
 395  			s.Nsect = seg32.Nsect
 396  			s.Flag = seg32.Flag
 397  			f.Loads = append(f.Loads, s)
 398  			for i := 0; i < int(s.Nsect); i++ {
 399  				var sh32 Section32
 400  				if err := binary.Read(b, bo, &sh32); err != nil {
 401  					return nil, err
 402  				}
 403  				sh := &Section{}
 404  				sh.Name = cstring(sh32.Name[0:])
 405  				sh.Seg = cstring(sh32.Seg[0:])
 406  				sh.Addr = uint64(sh32.Addr)
 407  				sh.Size = uint64(sh32.Size)
 408  				sh.Offset = sh32.Offset
 409  				sh.Align = sh32.Align
 410  				sh.Reloff = sh32.Reloff
 411  				sh.Nreloc = sh32.Nreloc
 412  				sh.Flags = sh32.Flags
 413  				if err := f.pushSection(sh, r); err != nil {
 414  					return nil, err
 415  				}
 416  			}
 417  
 418  		case LoadCmdSegment64:
 419  			var seg64 Segment64
 420  			b := bytes.NewReader(cmddat)
 421  			if err := binary.Read(b, bo, &seg64); err != nil {
 422  				return nil, err
 423  			}
 424  			s = &Segment{}
 425  			s.LoadBytes = cmddat
 426  			s.Cmd = cmd
 427  			s.Len = siz
 428  			s.Name = cstring(seg64.Name[0:])
 429  			s.Addr = seg64.Addr
 430  			s.Memsz = seg64.Memsz
 431  			s.Offset = seg64.Offset
 432  			s.Filesz = seg64.Filesz
 433  			s.Maxprot = seg64.Maxprot
 434  			s.Prot = seg64.Prot
 435  			s.Nsect = seg64.Nsect
 436  			s.Flag = seg64.Flag
 437  			f.Loads = append(f.Loads, s)
 438  			for i := 0; i < int(s.Nsect); i++ {
 439  				var sh64 Section64
 440  				if err := binary.Read(b, bo, &sh64); err != nil {
 441  					return nil, err
 442  				}
 443  				sh := &Section{}
 444  				sh.Name = cstring(sh64.Name[0:])
 445  				sh.Seg = cstring(sh64.Seg[0:])
 446  				sh.Addr = sh64.Addr
 447  				sh.Size = sh64.Size
 448  				sh.Offset = sh64.Offset
 449  				sh.Align = sh64.Align
 450  				sh.Reloff = sh64.Reloff
 451  				sh.Nreloc = sh64.Nreloc
 452  				sh.Flags = sh64.Flags
 453  				if err := f.pushSection(sh, r); err != nil {
 454  					return nil, err
 455  				}
 456  			}
 457  		}
 458  		if s != nil {
 459  			if int64(s.Offset) < 0 {
 460  				return nil, &FormatError{offset, "invalid section offset", s.Offset}
 461  			}
 462  			if int64(s.Filesz) < 0 {
 463  				return nil, &FormatError{offset, "invalid section file size", s.Filesz}
 464  			}
 465  			s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
 466  			s.ReaderAt = s.sr
 467  		}
 468  	}
 469  	return f, nil
 470  }
 471  
 472  func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
 473  	bo := f.ByteOrder
 474  	c := saferio.SliceCap[Symbol](uint64(hdr.Nsyms))
 475  	if c < 0 {
 476  		return nil, &FormatError{offset, "too many symbols", nil}
 477  	}
 478  	symtab := []Symbol{:0:c}
 479  	b := bytes.NewReader(symdat)
 480  	for i := 0; i < int(hdr.Nsyms); i++ {
 481  		var n Nlist64
 482  		if f.Magic == Magic64 {
 483  			if err := binary.Read(b, bo, &n); err != nil {
 484  				return nil, err
 485  			}
 486  		} else {
 487  			var n32 Nlist32
 488  			if err := binary.Read(b, bo, &n32); err != nil {
 489  				return nil, err
 490  			}
 491  			n.Name = n32.Name
 492  			n.Type = n32.Type
 493  			n.Sect = n32.Sect
 494  			n.Desc = n32.Desc
 495  			n.Value = uint64(n32.Value)
 496  		}
 497  		if n.Name >= uint32(len(strtab)) {
 498  			return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
 499  		}
 500  		// We add "_" to Go symbols. Strip it here. See issue 33808.
 501  		name := cstring(strtab[n.Name:])
 502  		if bytes.Contains(name, ".") && name[0] == '_' {
 503  			name = name[1:]
 504  		}
 505  		symtab = append(symtab, Symbol{
 506  			Name:  name,
 507  			Type:  n.Type,
 508  			Sect:  n.Sect,
 509  			Desc:  n.Desc,
 510  			Value: n.Value,
 511  		})
 512  	}
 513  	st := &Symtab{}
 514  	st.LoadBytes = LoadBytes(cmddat)
 515  	st.Syms = symtab
 516  	return st, nil
 517  }
 518  
 519  type relocInfo struct {
 520  	Addr   uint32
 521  	Symnum uint32
 522  }
 523  
 524  func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
 525  	f.Sections = append(f.Sections, sh)
 526  	sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
 527  	sh.ReaderAt = sh.sr
 528  
 529  	if sh.Nreloc > 0 {
 530  		reldat, err := saferio.ReadDataAt(r, uint64(sh.Nreloc)*8, int64(sh.Reloff))
 531  		if err != nil {
 532  			return err
 533  		}
 534  		b := bytes.NewReader(reldat)
 535  
 536  		bo := f.ByteOrder
 537  
 538  		sh.Relocs = []Reloc{:sh.Nreloc}
 539  		for i := range sh.Relocs {
 540  			rel := &sh.Relocs[i]
 541  
 542  			var ri relocInfo
 543  			if err := binary.Read(b, bo, &ri); err != nil {
 544  				return err
 545  			}
 546  
 547  			if ri.Addr&(1<<31) != 0 { // scattered
 548  				rel.Addr = ri.Addr & (1<<24 - 1)
 549  				rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
 550  				rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
 551  				rel.Pcrel = ri.Addr&(1<<30) != 0
 552  				rel.Value = ri.Symnum
 553  				rel.Scattered = true
 554  			} else {
 555  				switch bo {
 556  				case binary.LittleEndian:
 557  					rel.Addr = ri.Addr
 558  					rel.Value = ri.Symnum & (1<<24 - 1)
 559  					rel.Pcrel = ri.Symnum&(1<<24) != 0
 560  					rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
 561  					rel.Extern = ri.Symnum&(1<<27) != 0
 562  					rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
 563  				case binary.BigEndian:
 564  					rel.Addr = ri.Addr
 565  					rel.Value = ri.Symnum >> 8
 566  					rel.Pcrel = ri.Symnum&(1<<7) != 0
 567  					rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
 568  					rel.Extern = ri.Symnum&(1<<4) != 0
 569  					rel.Type = uint8(ri.Symnum & (1<<4 - 1))
 570  				default:
 571  					panic("unreachable")
 572  				}
 573  			}
 574  		}
 575  	}
 576  
 577  	return nil
 578  }
 579  
 580  func cstring(b []byte) string {
 581  	i := bytes.IndexByte(b, 0)
 582  	if i == -1 {
 583  		i = len(b)
 584  	}
 585  	return string(b[0:i])
 586  }
 587  
 588  // Segment returns the first Segment with the given name, or nil if no such segment exists.
 589  func (f *File) Segment(name string) *Segment {
 590  	for _, l := range f.Loads {
 591  		if s, ok := l.(*Segment); ok && s.Name == name {
 592  			return s
 593  		}
 594  	}
 595  	return nil
 596  }
 597  
 598  // Section returns the first section with the given name, or nil if no such
 599  // section exists.
 600  func (f *File) Section(name string) *Section {
 601  	for _, s := range f.Sections {
 602  		if s.Name == name {
 603  			return s
 604  		}
 605  	}
 606  	return nil
 607  }
 608  
 609  // DWARF returns the DWARF debug information for the Mach-O file.
 610  func (f *File) DWARF() (*dwarf.Data, error) {
 611  	dwarfSuffix := func(s *Section) string {
 612  		sectname := s.Name
 613  		var pfx int
 614  		switch {
 615  		case bytes.HasPrefix(sectname, "__debug_"):
 616  			pfx = 8
 617  		case bytes.HasPrefix(sectname, "__zdebug_"):
 618  			pfx = 9
 619  		default:
 620  			return ""
 621  		}
 622  		// Mach-O executables truncate section names to 16 characters, mangling some DWARF sections.
 623  		// As of DWARFv5 these are the only problematic section names (see DWARFv5 Appendix G).
 624  		for _, longname := range [][]byte{
 625  			"__debug_str_offsets",
 626  			"__zdebug_line_str",
 627  			"__zdebug_loclists",
 628  			"__zdebug_pubnames",
 629  			"__zdebug_pubtypes",
 630  			"__zdebug_rnglists",
 631  			"__zdebug_str_offsets",
 632  		} {
 633  			if sectname == longname[:16] {
 634  				sectname = longname
 635  				break
 636  			}
 637  		}
 638  		return sectname[pfx:]
 639  	}
 640  	sectionData := func(s *Section) ([]byte, error) {
 641  		b, err := s.Data()
 642  		if err != nil && uint64(len(b)) < s.Size {
 643  			return nil, err
 644  		}
 645  
 646  		if len(b) >= 12 && string(b[:4]) == "ZLIB" {
 647  			dlen := binary.BigEndian.Uint64(b[4:12])
 648  			dbuf := []byte{:dlen}
 649  			r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
 650  			if err != nil {
 651  				return nil, err
 652  			}
 653  			if _, err := io.ReadFull(r, dbuf); err != nil {
 654  				return nil, err
 655  			}
 656  			if err := r.Close(); err != nil {
 657  				return nil, err
 658  			}
 659  			b = dbuf
 660  		}
 661  		return b, nil
 662  	}
 663  
 664  	// There are many other DWARF sections, but these
 665  	// are the ones the debug/dwarf package uses.
 666  	// Don't bother loading others.
 667  	var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
 668  	for _, s := range f.Sections {
 669  		suffix := dwarfSuffix(s)
 670  		if suffix == "" {
 671  			continue
 672  		}
 673  		if _, ok := dat[suffix]; !ok {
 674  			continue
 675  		}
 676  		b, err := sectionData(s)
 677  		if err != nil {
 678  			return nil, err
 679  		}
 680  		dat[suffix] = b
 681  	}
 682  
 683  	d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
 684  	if err != nil {
 685  		return nil, err
 686  	}
 687  
 688  	// Look for DWARF4 .debug_types sections and DWARF5 sections.
 689  	for i, s := range f.Sections {
 690  		suffix := dwarfSuffix(s)
 691  		if suffix == "" {
 692  			continue
 693  		}
 694  		if _, ok := dat[suffix]; ok {
 695  			// Already handled.
 696  			continue
 697  		}
 698  
 699  		b, err := sectionData(s)
 700  		if err != nil {
 701  			return nil, err
 702  		}
 703  
 704  		if suffix == "types" {
 705  			err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
 706  		} else {
 707  			err = d.AddSection(".debug_"+suffix, b)
 708  		}
 709  		if err != nil {
 710  			return nil, err
 711  		}
 712  	}
 713  
 714  	return d, nil
 715  }
 716  
 717  // ImportedSymbols returns the names of all symbols
 718  // referred to by the binary f that are expected to be
 719  // satisfied by other libraries at dynamic load time.
 720  func (f *File) ImportedSymbols() ([][]byte, error) {
 721  	if f.Dysymtab == nil || f.Symtab == nil {
 722  		return nil, &FormatError{0, "missing symbol table", nil}
 723  	}
 724  
 725  	st := f.Symtab
 726  	dt := f.Dysymtab
 727  	var all [][]byte
 728  	for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
 729  		all = append(all, s.Name)
 730  	}
 731  	return all, nil
 732  }
 733  
 734  // ImportedLibraries returns the paths of all libraries
 735  // referred to by the binary f that are expected to be
 736  // linked with the binary at dynamic link time.
 737  func (f *File) ImportedLibraries() ([][]byte, error) {
 738  	var all [][]byte
 739  	for _, l := range f.Loads {
 740  		if lib, ok := l.(*Dylib); ok {
 741  			all = append(all, lib.Name)
 742  		}
 743  	}
 744  	return all, nil
 745  }
 746