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