github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/debug/macho/file.go (about) 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 }