github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/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 "debug/dwarf" 13 "encoding/binary" 14 "fmt" 15 "io" 16 "os" 17 ) 18 19 // A File represents an open Mach-O file. 20 type File struct { 21 FileHeader 22 ByteOrder binary.ByteOrder 23 Loads []Load 24 Sections []*Section 25 26 Symtab *Symtab 27 Dysymtab *Dysymtab 28 29 closer io.Closer 30 } 31 32 // A Load represents any Mach-O load command. 33 type Load interface { 34 Raw() []byte 35 } 36 37 // A LoadBytes is the uninterpreted bytes of a Mach-O load command. 38 type LoadBytes []byte 39 40 func (b LoadBytes) Raw() []byte { return b } 41 42 // A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command. 43 type SegmentHeader struct { 44 Cmd LoadCmd 45 Len uint32 46 Name string 47 Addr uint64 48 Memsz uint64 49 Offset uint64 50 Filesz uint64 51 Maxprot uint32 52 Prot uint32 53 Nsect uint32 54 Flag uint32 55 } 56 57 // A Segment represents a Mach-O 32-bit or 64-bit load segment command. 58 type Segment struct { 59 LoadBytes 60 SegmentHeader 61 62 // Embed ReaderAt for ReadAt method. 63 // Do not embed SectionReader directly 64 // to avoid having Read and Seek. 65 // If a client wants Read and Seek it must use 66 // Open() to avoid fighting over the seek offset 67 // with other clients. 68 io.ReaderAt 69 sr *io.SectionReader 70 } 71 72 // Data reads and returns the contents of the segment. 73 func (s *Segment) Data() ([]byte, error) { 74 dat := make([]byte, s.sr.Size()) 75 n, err := s.sr.ReadAt(dat, 0) 76 if n == len(dat) { 77 err = nil 78 } 79 return dat[0:n], err 80 } 81 82 // Open returns a new ReadSeeker reading the segment. 83 func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } 84 85 type SectionHeader struct { 86 Name string 87 Seg string 88 Addr uint64 89 Size uint64 90 Offset uint32 91 Align uint32 92 Reloff uint32 93 Nreloc uint32 94 Flags uint32 95 } 96 97 // A Reloc represents a Mach-O relocation. 98 type Reloc struct { 99 Addr uint32 100 Value uint32 101 // when Scattered == false && Extern == true, Value is the symbol number. 102 // when Scattered == false && Extern == false, Value is the section number. 103 // when Scattered == true, Value is the value that this reloc refers to. 104 Type uint8 105 Len uint8 // 0=byte, 1=word, 2=long, 3=quad 106 Pcrel bool 107 Extern bool // valid if Scattered == false 108 Scattered bool 109 } 110 111 type Section struct { 112 SectionHeader 113 Relocs []Reloc 114 115 // Embed ReaderAt for ReadAt method. 116 // Do not embed SectionReader directly 117 // to avoid having Read and Seek. 118 // If a client wants Read and Seek it must use 119 // Open() to avoid fighting over the seek offset 120 // with other clients. 121 io.ReaderAt 122 sr *io.SectionReader 123 } 124 125 // Data reads and returns the contents of the Mach-O section. 126 func (s *Section) Data() ([]byte, error) { 127 dat := make([]byte, s.sr.Size()) 128 n, err := s.sr.ReadAt(dat, 0) 129 if n == len(dat) { 130 err = nil 131 } 132 return dat[0:n], err 133 } 134 135 // Open returns a new ReadSeeker reading the Mach-O section. 136 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } 137 138 // A Dylib represents a Mach-O load dynamic library command. 139 type Dylib struct { 140 LoadBytes 141 Name string 142 Time uint32 143 CurrentVersion uint32 144 CompatVersion uint32 145 } 146 147 // A Symtab represents a Mach-O symbol table command. 148 type Symtab struct { 149 LoadBytes 150 SymtabCmd 151 Syms []Symbol 152 } 153 154 // A Dysymtab represents a Mach-O dynamic symbol table command. 155 type Dysymtab struct { 156 LoadBytes 157 DysymtabCmd 158 IndirectSyms []uint32 // indices into Symtab.Syms 159 } 160 161 // A Rpath represents a Mach-O rpath command. 162 type Rpath struct { 163 LoadBytes 164 Path string 165 } 166 167 // A Symbol is a Mach-O 32-bit or 64-bit symbol table entry. 168 type Symbol struct { 169 Name string 170 Type uint8 171 Sect uint8 172 Desc uint16 173 Value uint64 174 } 175 176 /* 177 * Mach-O reader 178 */ 179 180 // FormatError is returned by some operations if the data does 181 // not have the correct format for an object file. 182 type FormatError struct { 183 off int64 184 msg string 185 val interface{} 186 } 187 188 func (e *FormatError) Error() string { 189 msg := e.msg 190 if e.val != nil { 191 msg += fmt.Sprintf(" '%v'", e.val) 192 } 193 msg += fmt.Sprintf(" in record at byte %#x", e.off) 194 return msg 195 } 196 197 // Open opens the named file using os.Open and prepares it for use as a Mach-O binary. 198 func Open(name string) (*File, error) { 199 f, err := os.Open(name) 200 if err != nil { 201 return nil, err 202 } 203 ff, err := NewFile(f) 204 if err != nil { 205 f.Close() 206 return nil, err 207 } 208 ff.closer = f 209 return ff, nil 210 } 211 212 // Close closes the File. 213 // If the File was created using NewFile directly instead of Open, 214 // Close has no effect. 215 func (f *File) Close() error { 216 var err error 217 if f.closer != nil { 218 err = f.closer.Close() 219 f.closer = nil 220 } 221 return err 222 } 223 224 // NewFile creates a new File for accessing a Mach-O binary in an underlying reader. 225 // The Mach-O binary is expected to start at position 0 in the ReaderAt. 226 func NewFile(r io.ReaderAt) (*File, error) { 227 f := new(File) 228 sr := io.NewSectionReader(r, 0, 1<<63-1) 229 230 // Read and decode Mach magic to determine byte order, size. 231 // Magic32 and Magic64 differ only in the bottom bit. 232 var ident [4]byte 233 if _, err := r.ReadAt(ident[0:], 0); err != nil { 234 return nil, err 235 } 236 be := binary.BigEndian.Uint32(ident[0:]) 237 le := binary.LittleEndian.Uint32(ident[0:]) 238 switch Magic32 &^ 1 { 239 case be &^ 1: 240 f.ByteOrder = binary.BigEndian 241 f.Magic = be 242 case le &^ 1: 243 f.ByteOrder = binary.LittleEndian 244 f.Magic = le 245 default: 246 return nil, &FormatError{0, "invalid magic number", nil} 247 } 248 249 // Read entire file header. 250 if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil { 251 return nil, err 252 } 253 254 // Then load commands. 255 offset := int64(fileHeaderSize32) 256 if f.Magic == Magic64 { 257 offset = fileHeaderSize64 258 } 259 dat := make([]byte, f.Cmdsz) 260 if _, err := r.ReadAt(dat, offset); err != nil { 261 return nil, err 262 } 263 f.Loads = make([]Load, f.Ncmd) 264 bo := f.ByteOrder 265 for i := range f.Loads { 266 // Each load command begins with uint32 command and length. 267 if len(dat) < 8 { 268 return nil, &FormatError{offset, "command block too small", nil} 269 } 270 cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8]) 271 if siz < 8 || siz > uint32(len(dat)) { 272 return nil, &FormatError{offset, "invalid command block size", nil} 273 } 274 var cmddat []byte 275 cmddat, dat = dat[0:siz], dat[siz:] 276 offset += int64(siz) 277 var s *Segment 278 switch cmd { 279 default: 280 f.Loads[i] = LoadBytes(cmddat) 281 282 case LoadCmdRpath: 283 var hdr RpathCmd 284 b := bytes.NewReader(cmddat) 285 if err := binary.Read(b, bo, &hdr); err != nil { 286 return nil, err 287 } 288 l := new(Rpath) 289 if hdr.Path >= uint32(len(cmddat)) { 290 return nil, &FormatError{offset, "invalid path in rpath command", hdr.Path} 291 } 292 l.Path = cstring(cmddat[hdr.Path:]) 293 l.LoadBytes = LoadBytes(cmddat) 294 f.Loads[i] = l 295 296 case LoadCmdDylib: 297 var hdr DylibCmd 298 b := bytes.NewReader(cmddat) 299 if err := binary.Read(b, bo, &hdr); err != nil { 300 return nil, err 301 } 302 l := new(Dylib) 303 if hdr.Name >= uint32(len(cmddat)) { 304 return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name} 305 } 306 l.Name = cstring(cmddat[hdr.Name:]) 307 l.Time = hdr.Time 308 l.CurrentVersion = hdr.CurrentVersion 309 l.CompatVersion = hdr.CompatVersion 310 l.LoadBytes = LoadBytes(cmddat) 311 f.Loads[i] = l 312 313 case LoadCmdSymtab: 314 var hdr SymtabCmd 315 b := bytes.NewReader(cmddat) 316 if err := binary.Read(b, bo, &hdr); err != nil { 317 return nil, err 318 } 319 strtab := make([]byte, hdr.Strsize) 320 if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil { 321 return nil, err 322 } 323 var symsz int 324 if f.Magic == Magic64 { 325 symsz = 16 326 } else { 327 symsz = 12 328 } 329 symdat := make([]byte, int(hdr.Nsyms)*symsz) 330 if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil { 331 return nil, err 332 } 333 st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset) 334 if err != nil { 335 return nil, err 336 } 337 f.Loads[i] = st 338 f.Symtab = st 339 340 case LoadCmdDysymtab: 341 var hdr DysymtabCmd 342 b := bytes.NewReader(cmddat) 343 if err := binary.Read(b, bo, &hdr); err != nil { 344 return nil, err 345 } 346 dat := make([]byte, hdr.Nindirectsyms*4) 347 if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil { 348 return nil, err 349 } 350 x := make([]uint32, hdr.Nindirectsyms) 351 if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil { 352 return nil, err 353 } 354 st := new(Dysymtab) 355 st.LoadBytes = LoadBytes(cmddat) 356 st.DysymtabCmd = hdr 357 st.IndirectSyms = x 358 f.Loads[i] = st 359 f.Dysymtab = st 360 361 case LoadCmdSegment: 362 var seg32 Segment32 363 b := bytes.NewReader(cmddat) 364 if err := binary.Read(b, bo, &seg32); err != nil { 365 return nil, err 366 } 367 s = new(Segment) 368 s.LoadBytes = cmddat 369 s.Cmd = cmd 370 s.Len = siz 371 s.Name = cstring(seg32.Name[0:]) 372 s.Addr = uint64(seg32.Addr) 373 s.Memsz = uint64(seg32.Memsz) 374 s.Offset = uint64(seg32.Offset) 375 s.Filesz = uint64(seg32.Filesz) 376 s.Maxprot = seg32.Maxprot 377 s.Prot = seg32.Prot 378 s.Nsect = seg32.Nsect 379 s.Flag = seg32.Flag 380 f.Loads[i] = s 381 for i := 0; i < int(s.Nsect); i++ { 382 var sh32 Section32 383 if err := binary.Read(b, bo, &sh32); err != nil { 384 return nil, err 385 } 386 sh := new(Section) 387 sh.Name = cstring(sh32.Name[0:]) 388 sh.Seg = cstring(sh32.Seg[0:]) 389 sh.Addr = uint64(sh32.Addr) 390 sh.Size = uint64(sh32.Size) 391 sh.Offset = sh32.Offset 392 sh.Align = sh32.Align 393 sh.Reloff = sh32.Reloff 394 sh.Nreloc = sh32.Nreloc 395 sh.Flags = sh32.Flags 396 if err := f.pushSection(sh, r); err != nil { 397 return nil, err 398 } 399 } 400 401 case LoadCmdSegment64: 402 var seg64 Segment64 403 b := bytes.NewReader(cmddat) 404 if err := binary.Read(b, bo, &seg64); err != nil { 405 return nil, err 406 } 407 s = new(Segment) 408 s.LoadBytes = cmddat 409 s.Cmd = cmd 410 s.Len = siz 411 s.Name = cstring(seg64.Name[0:]) 412 s.Addr = seg64.Addr 413 s.Memsz = seg64.Memsz 414 s.Offset = seg64.Offset 415 s.Filesz = seg64.Filesz 416 s.Maxprot = seg64.Maxprot 417 s.Prot = seg64.Prot 418 s.Nsect = seg64.Nsect 419 s.Flag = seg64.Flag 420 f.Loads[i] = s 421 for i := 0; i < int(s.Nsect); i++ { 422 var sh64 Section64 423 if err := binary.Read(b, bo, &sh64); err != nil { 424 return nil, err 425 } 426 sh := new(Section) 427 sh.Name = cstring(sh64.Name[0:]) 428 sh.Seg = cstring(sh64.Seg[0:]) 429 sh.Addr = sh64.Addr 430 sh.Size = sh64.Size 431 sh.Offset = sh64.Offset 432 sh.Align = sh64.Align 433 sh.Reloff = sh64.Reloff 434 sh.Nreloc = sh64.Nreloc 435 sh.Flags = sh64.Flags 436 if err := f.pushSection(sh, r); err != nil { 437 return nil, err 438 } 439 } 440 } 441 if s != nil { 442 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz)) 443 s.ReaderAt = s.sr 444 } 445 } 446 return f, nil 447 } 448 449 func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) { 450 bo := f.ByteOrder 451 symtab := make([]Symbol, hdr.Nsyms) 452 b := bytes.NewReader(symdat) 453 for i := range symtab { 454 var n Nlist64 455 if f.Magic == Magic64 { 456 if err := binary.Read(b, bo, &n); err != nil { 457 return nil, err 458 } 459 } else { 460 var n32 Nlist32 461 if err := binary.Read(b, bo, &n32); err != nil { 462 return nil, err 463 } 464 n.Name = n32.Name 465 n.Type = n32.Type 466 n.Sect = n32.Sect 467 n.Desc = n32.Desc 468 n.Value = uint64(n32.Value) 469 } 470 sym := &symtab[i] 471 if n.Name >= uint32(len(strtab)) { 472 return nil, &FormatError{offset, "invalid name in symbol table", n.Name} 473 } 474 sym.Name = cstring(strtab[n.Name:]) 475 sym.Type = n.Type 476 sym.Sect = n.Sect 477 sym.Desc = n.Desc 478 sym.Value = n.Value 479 } 480 st := new(Symtab) 481 st.LoadBytes = LoadBytes(cmddat) 482 st.Syms = symtab 483 return st, nil 484 } 485 486 type relocInfo struct { 487 Addr uint32 488 Symnum uint32 489 } 490 491 func (f *File) pushSection(sh *Section, r io.ReaderAt) error { 492 f.Sections = append(f.Sections, sh) 493 sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size)) 494 sh.ReaderAt = sh.sr 495 496 if sh.Nreloc > 0 { 497 reldat := make([]byte, int(sh.Nreloc)*8) 498 if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil { 499 return err 500 } 501 b := bytes.NewReader(reldat) 502 503 bo := f.ByteOrder 504 505 sh.Relocs = make([]Reloc, sh.Nreloc) 506 for i := range sh.Relocs { 507 rel := &sh.Relocs[i] 508 509 var ri relocInfo 510 if err := binary.Read(b, bo, &ri); err != nil { 511 return err 512 } 513 514 if ri.Addr&(1<<31) != 0 { // scattered 515 rel.Addr = ri.Addr & (1<<24 - 1) 516 rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1)) 517 rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1)) 518 rel.Pcrel = ri.Addr&(1<<30) != 0 519 rel.Value = ri.Symnum 520 rel.Scattered = true 521 } else { 522 switch bo { 523 case binary.LittleEndian: 524 rel.Addr = ri.Addr 525 rel.Value = ri.Symnum & (1<<24 - 1) 526 rel.Pcrel = ri.Symnum&(1<<24) != 0 527 rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1)) 528 rel.Extern = ri.Symnum&(1<<27) != 0 529 rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1)) 530 case binary.BigEndian: 531 rel.Addr = ri.Addr 532 rel.Value = ri.Symnum >> 8 533 rel.Pcrel = ri.Symnum&(1<<7) != 0 534 rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1)) 535 rel.Extern = ri.Symnum&(1<<4) != 0 536 rel.Type = uint8(ri.Symnum & (1<<4 - 1)) 537 default: 538 panic("unreachable") 539 } 540 } 541 } 542 } 543 544 return nil 545 } 546 547 func cstring(b []byte) string { 548 var i int 549 for i = 0; i < len(b) && b[i] != 0; i++ { 550 } 551 return string(b[0:i]) 552 } 553 554 // Segment returns the first Segment with the given name, or nil if no such segment exists. 555 func (f *File) Segment(name string) *Segment { 556 for _, l := range f.Loads { 557 if s, ok := l.(*Segment); ok && s.Name == name { 558 return s 559 } 560 } 561 return nil 562 } 563 564 // Section returns the first section with the given name, or nil if no such 565 // section exists. 566 func (f *File) Section(name string) *Section { 567 for _, s := range f.Sections { 568 if s.Name == name { 569 return s 570 } 571 } 572 return nil 573 } 574 575 // DWARF returns the DWARF debug information for the Mach-O file. 576 func (f *File) DWARF() (*dwarf.Data, error) { 577 // There are many other DWARF sections, but these 578 // are the ones the debug/dwarf package uses. 579 // Don't bother loading others. 580 var names = [...]string{"abbrev", "info", "line", "ranges", "str"} 581 var dat [len(names)][]byte 582 for i, name := range names { 583 name = "__debug_" + name 584 s := f.Section(name) 585 if s == nil { 586 continue 587 } 588 b, err := s.Data() 589 if err != nil && uint64(len(b)) < s.Size { 590 return nil, err 591 } 592 dat[i] = b 593 } 594 595 abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] 596 return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) 597 } 598 599 // ImportedSymbols returns the names of all symbols 600 // referred to by the binary f that are expected to be 601 // satisfied by other libraries at dynamic load time. 602 func (f *File) ImportedSymbols() ([]string, error) { 603 if f.Dysymtab == nil || f.Symtab == nil { 604 return nil, &FormatError{0, "missing symbol table", nil} 605 } 606 607 st := f.Symtab 608 dt := f.Dysymtab 609 var all []string 610 for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] { 611 all = append(all, s.Name) 612 } 613 return all, nil 614 } 615 616 // ImportedLibraries returns the paths of all libraries 617 // referred to by the binary f that are expected to be 618 // linked with the binary at dynamic link time. 619 func (f *File) ImportedLibraries() ([]string, error) { 620 var all []string 621 for _, l := range f.Loads { 622 if lib, ok := l.(*Dylib); ok { 623 all = append(all, lib.Name) 624 } 625 } 626 return all, nil 627 }