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