github.com/april1989/origin-go-tools@v0.0.32/cmd/splitdwarf/internal/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 "unsafe" 20 ) 21 22 // A File represents an open Mach-O file. 23 type File struct { 24 FileTOC 25 26 Symtab *Symtab 27 Dysymtab *Dysymtab 28 29 closer io.Closer 30 } 31 32 type FileTOC struct { 33 FileHeader 34 ByteOrder binary.ByteOrder 35 Loads []Load 36 Sections []*Section 37 } 38 39 func (t *FileTOC) AddLoad(l Load) { 40 t.Loads = append(t.Loads, l) 41 t.NCommands++ 42 t.SizeCommands += l.LoadSize(t) 43 } 44 45 // AddSegment adds segment s to the file table of contents, 46 // and also zeroes out the segment information with the expectation 47 // that this will be added next. 48 func (t *FileTOC) AddSegment(s *Segment) { 49 t.AddLoad(s) 50 s.Nsect = 0 51 s.Firstsect = 0 52 } 53 54 // Adds section to the most recently added Segment 55 func (t *FileTOC) AddSection(s *Section) { 56 g := t.Loads[len(t.Loads)-1].(*Segment) 57 if g.Nsect == 0 { 58 g.Firstsect = uint32(len(t.Sections)) 59 } 60 g.Nsect++ 61 t.Sections = append(t.Sections, s) 62 sectionsize := uint32(unsafe.Sizeof(Section32{})) 63 if g.Command() == LcSegment64 { 64 sectionsize = uint32(unsafe.Sizeof(Section64{})) 65 } 66 t.SizeCommands += sectionsize 67 g.Len += sectionsize 68 } 69 70 // A Load represents any Mach-O load command. 71 type Load interface { 72 String() string 73 Command() LoadCmd 74 LoadSize(*FileTOC) uint32 // Need the TOC for alignment, sigh. 75 Put([]byte, binary.ByteOrder) int 76 77 // command LC_DYLD_INFO_ONLY contains offsets into __LINKEDIT 78 // e.g., from "otool -l a.out" 79 // 80 // Load command 3 81 // cmd LC_SEGMENT_64 82 // cmdsize 72 83 // segname __LINKEDIT 84 // vmaddr 0x0000000100002000 85 // vmsize 0x0000000000001000 86 // fileoff 8192 87 // filesize 520 88 // maxprot 0x00000007 89 // initprot 0x00000001 90 // nsects 0 91 // flags 0x0 92 // Load command 4 93 // cmd LC_DYLD_INFO_ONLY 94 // cmdsize 48 95 // rebase_off 8192 96 // rebase_size 8 97 // bind_off 8200 98 // bind_size 24 99 // weak_bind_off 0 100 // weak_bind_size 0 101 // lazy_bind_off 8224 102 // lazy_bind_size 16 103 // export_off 8240 104 // export_size 48 105 } 106 107 // LoadBytes is the uninterpreted bytes of a Mach-O load command. 108 type LoadBytes []byte 109 110 // A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command. 111 type SegmentHeader struct { 112 LoadCmd 113 Len uint32 114 Name string // 16 characters or fewer 115 Addr uint64 // memory address 116 Memsz uint64 // memory size 117 Offset uint64 // file offset 118 Filesz uint64 // number of bytes starting at that file offset 119 Maxprot uint32 120 Prot uint32 121 Nsect uint32 122 Flag SegFlags 123 Firstsect uint32 124 } 125 126 // A Segment represents a Mach-O 32-bit or 64-bit load segment command. 127 type Segment struct { 128 SegmentHeader 129 130 // Embed ReaderAt for ReadAt method. 131 // Do not embed SectionReader directly 132 // to avoid having Read and Seek. 133 // If a client wants Read and Seek it must use 134 // Open() to avoid fighting over the seek offset 135 // with other clients. 136 io.ReaderAt 137 sr *io.SectionReader 138 } 139 140 func (s *Segment) Put32(b []byte, o binary.ByteOrder) int { 141 o.PutUint32(b[0*4:], uint32(s.LoadCmd)) 142 o.PutUint32(b[1*4:], s.Len) 143 putAtMost16Bytes(b[2*4:], s.Name) 144 o.PutUint32(b[6*4:], uint32(s.Addr)) 145 o.PutUint32(b[7*4:], uint32(s.Memsz)) 146 o.PutUint32(b[8*4:], uint32(s.Offset)) 147 o.PutUint32(b[9*4:], uint32(s.Filesz)) 148 o.PutUint32(b[10*4:], s.Maxprot) 149 o.PutUint32(b[11*4:], s.Prot) 150 o.PutUint32(b[12*4:], s.Nsect) 151 o.PutUint32(b[13*4:], uint32(s.Flag)) 152 return 14 * 4 153 } 154 155 func (s *Segment) Put64(b []byte, o binary.ByteOrder) int { 156 o.PutUint32(b[0*4:], uint32(s.LoadCmd)) 157 o.PutUint32(b[1*4:], s.Len) 158 putAtMost16Bytes(b[2*4:], s.Name) 159 o.PutUint64(b[6*4+0*8:], s.Addr) 160 o.PutUint64(b[6*4+1*8:], s.Memsz) 161 o.PutUint64(b[6*4+2*8:], s.Offset) 162 o.PutUint64(b[6*4+3*8:], s.Filesz) 163 o.PutUint32(b[6*4+4*8:], s.Maxprot) 164 o.PutUint32(b[7*4+4*8:], s.Prot) 165 o.PutUint32(b[8*4+4*8:], s.Nsect) 166 o.PutUint32(b[9*4+4*8:], uint32(s.Flag)) 167 return 10*4 + 4*8 168 } 169 170 // LoadCmdBytes is a command-tagged sequence of bytes. 171 // This is used for Load Commands that are not (yet) 172 // interesting to us, and to common up this behavior for 173 // all those that are. 174 type LoadCmdBytes struct { 175 LoadCmd 176 LoadBytes 177 } 178 179 type SectionHeader struct { 180 Name string 181 Seg string 182 Addr uint64 183 Size uint64 184 Offset uint32 185 Align uint32 186 Reloff uint32 187 Nreloc uint32 188 Flags SecFlags 189 Reserved1 uint32 190 Reserved2 uint32 191 Reserved3 uint32 // only present if original was 64-bit 192 } 193 194 // A Reloc represents a Mach-O relocation. 195 type Reloc struct { 196 Addr uint32 197 Value uint32 198 // when Scattered == false && Extern == true, Value is the symbol number. 199 // when Scattered == false && Extern == false, Value is the section number. 200 // when Scattered == true, Value is the value that this reloc refers to. 201 Type uint8 202 Len uint8 // 0=byte, 1=word, 2=long, 3=quad 203 Pcrel bool 204 Extern bool // valid if Scattered == false 205 Scattered bool 206 } 207 208 type Section struct { 209 SectionHeader 210 Relocs []Reloc 211 212 // Embed ReaderAt for ReadAt method. 213 // Do not embed SectionReader directly 214 // to avoid having Read and Seek. 215 // If a client wants Read and Seek it must use 216 // Open() to avoid fighting over the seek offset 217 // with other clients. 218 io.ReaderAt 219 sr *io.SectionReader 220 } 221 222 func (s *Section) Put32(b []byte, o binary.ByteOrder) int { 223 putAtMost16Bytes(b[0:], s.Name) 224 putAtMost16Bytes(b[16:], s.Seg) 225 o.PutUint32(b[8*4:], uint32(s.Addr)) 226 o.PutUint32(b[9*4:], uint32(s.Size)) 227 o.PutUint32(b[10*4:], s.Offset) 228 o.PutUint32(b[11*4:], s.Align) 229 o.PutUint32(b[12*4:], s.Reloff) 230 o.PutUint32(b[13*4:], s.Nreloc) 231 o.PutUint32(b[14*4:], uint32(s.Flags)) 232 o.PutUint32(b[15*4:], s.Reserved1) 233 o.PutUint32(b[16*4:], s.Reserved2) 234 a := 17 * 4 235 return a + s.PutRelocs(b[a:], o) 236 } 237 238 func (s *Section) Put64(b []byte, o binary.ByteOrder) int { 239 putAtMost16Bytes(b[0:], s.Name) 240 putAtMost16Bytes(b[16:], s.Seg) 241 o.PutUint64(b[8*4+0*8:], s.Addr) 242 o.PutUint64(b[8*4+1*8:], s.Size) 243 o.PutUint32(b[8*4+2*8:], s.Offset) 244 o.PutUint32(b[9*4+2*8:], s.Align) 245 o.PutUint32(b[10*4+2*8:], s.Reloff) 246 o.PutUint32(b[11*4+2*8:], s.Nreloc) 247 o.PutUint32(b[12*4+2*8:], uint32(s.Flags)) 248 o.PutUint32(b[13*4+2*8:], s.Reserved1) 249 o.PutUint32(b[14*4+2*8:], s.Reserved2) 250 o.PutUint32(b[15*4+2*8:], s.Reserved3) 251 a := 16*4 + 2*8 252 return a + s.PutRelocs(b[a:], o) 253 } 254 255 func (s *Section) PutRelocs(b []byte, o binary.ByteOrder) int { 256 a := 0 257 for _, r := range s.Relocs { 258 var ri relocInfo 259 typ := uint32(r.Type) & (1<<4 - 1) 260 len := uint32(r.Len) & (1<<2 - 1) 261 pcrel := uint32(0) 262 if r.Pcrel { 263 pcrel = 1 264 } 265 ext := uint32(0) 266 if r.Extern { 267 ext = 1 268 } 269 switch { 270 case r.Scattered: 271 ri.Addr = r.Addr&(1<<24-1) | typ<<24 | len<<28 | 1<<31 | pcrel<<30 272 ri.Symnum = r.Value 273 case o == binary.LittleEndian: 274 ri.Addr = r.Addr 275 ri.Symnum = r.Value&(1<<24-1) | pcrel<<24 | len<<25 | ext<<27 | typ<<28 276 case o == binary.BigEndian: 277 ri.Addr = r.Addr 278 ri.Symnum = r.Value<<8 | pcrel<<7 | len<<5 | ext<<4 | typ 279 } 280 o.PutUint32(b, ri.Addr) 281 o.PutUint32(b[4:], ri.Symnum) 282 a += 8 283 b = b[8:] 284 } 285 return a 286 } 287 288 func putAtMost16Bytes(b []byte, n string) { 289 for i := range n { // at most 16 bytes 290 if i == 16 { 291 break 292 } 293 b[i] = n[i] 294 } 295 } 296 297 // A Symbol is a Mach-O 32-bit or 64-bit symbol table entry. 298 type Symbol struct { 299 Name string 300 Type uint8 301 Sect uint8 302 Desc uint16 303 Value uint64 304 } 305 306 /* 307 * Mach-O reader 308 */ 309 310 // FormatError is returned by some operations if the data does 311 // not have the correct format for an object file. 312 type FormatError struct { 313 off int64 314 msg string 315 } 316 317 func formatError(off int64, format string, data ...interface{}) *FormatError { 318 return &FormatError{off, fmt.Sprintf(format, data...)} 319 } 320 321 func (e *FormatError) Error() string { 322 return e.msg + fmt.Sprintf(" in record at byte %#x", e.off) 323 } 324 325 func (e *FormatError) String() string { 326 return e.Error() 327 } 328 329 // DerivedCopy returns a modified copy of the TOC, with empty loads and sections, 330 // and with the specified header type and flags. 331 func (t *FileTOC) DerivedCopy(Type HdrType, Flags HdrFlags) *FileTOC { 332 h := t.FileHeader 333 h.NCommands, h.SizeCommands, h.Type, h.Flags = 0, 0, Type, Flags 334 335 return &FileTOC{FileHeader: h, ByteOrder: t.ByteOrder} 336 } 337 338 // TOCSize returns the size in bytes of the object file representation 339 // of the header and Load Commands (including Segments and Sections, but 340 // not their contents) at the beginning of a Mach-O file. This typically 341 // overlaps the text segment in the object file. 342 func (t *FileTOC) TOCSize() uint32 { 343 return t.HdrSize() + t.LoadSize() 344 } 345 346 // LoadAlign returns the required alignment of Load commands in a binary. 347 // This is used to add padding for necessary alignment. 348 func (t *FileTOC) LoadAlign() uint64 { 349 if t.Magic == Magic64 { 350 return 8 351 } 352 return 4 353 } 354 355 // SymbolSize returns the size in bytes of a Symbol (Nlist32 or Nlist64) 356 func (t *FileTOC) SymbolSize() uint32 { 357 if t.Magic == Magic64 { 358 return uint32(unsafe.Sizeof(Nlist64{})) 359 } 360 return uint32(unsafe.Sizeof(Nlist32{})) 361 } 362 363 // HdrSize returns the size in bytes of the Macho header for a given 364 // magic number (where the magic number has been appropriately byte-swapped). 365 func (t *FileTOC) HdrSize() uint32 { 366 switch t.Magic { 367 case Magic32: 368 return fileHeaderSize32 369 case Magic64: 370 return fileHeaderSize64 371 case MagicFat: 372 panic("MagicFat not handled yet") 373 default: 374 panic(fmt.Sprintf("Unexpected magic number 0x%x, expected Mach-O object file", t.Magic)) 375 } 376 } 377 378 // LoadSize returns the size of all the load commands in a file's table-of contents 379 // (but not their associated data, e.g., sections and symbol tables) 380 func (t *FileTOC) LoadSize() uint32 { 381 cmdsz := uint32(0) 382 for _, l := range t.Loads { 383 s := l.LoadSize(t) 384 cmdsz += s 385 } 386 return cmdsz 387 } 388 389 // FileSize returns the size in bytes of the header, load commands, and the 390 // in-file contents of all the segments and sections included in those 391 // load commands, accounting for their offsets within the file. 392 func (t *FileTOC) FileSize() uint64 { 393 sz := uint64(t.LoadSize()) // ought to be contained in text segment, but just in case. 394 for _, l := range t.Loads { 395 if s, ok := l.(*Segment); ok { 396 if m := s.Offset + s.Filesz; m > sz { 397 sz = m 398 } 399 } 400 } 401 return sz 402 } 403 404 // Put writes the header and all load commands to buffer, using 405 // the byte ordering specified in FileTOC t. For sections, this 406 // writes the headers that come in-line with the segment Load commands, 407 // but does not write the reference data for those sections. 408 func (t *FileTOC) Put(buffer []byte) int { 409 next := t.FileHeader.Put(buffer, t.ByteOrder) 410 for _, l := range t.Loads { 411 if s, ok := l.(*Segment); ok { 412 switch t.Magic { 413 case Magic64: 414 next += s.Put64(buffer[next:], t.ByteOrder) 415 for i := uint32(0); i < s.Nsect; i++ { 416 c := t.Sections[i+s.Firstsect] 417 next += c.Put64(buffer[next:], t.ByteOrder) 418 } 419 case Magic32: 420 next += s.Put32(buffer[next:], t.ByteOrder) 421 for i := uint32(0); i < s.Nsect; i++ { 422 c := t.Sections[i+s.Firstsect] 423 next += c.Put32(buffer[next:], t.ByteOrder) 424 } 425 default: 426 panic(fmt.Sprintf("Unexpected magic number 0x%x", t.Magic)) 427 } 428 429 } else { 430 next += l.Put(buffer[next:], t.ByteOrder) 431 } 432 } 433 return next 434 } 435 436 // UncompressedSize returns the size of the segment with its sections uncompressed, ignoring 437 // its offset within the file. The returned size is rounded up to the power of two in align. 438 func (s *Segment) UncompressedSize(t *FileTOC, align uint64) uint64 { 439 sz := uint64(0) 440 for j := uint32(0); j < s.Nsect; j++ { 441 c := t.Sections[j+s.Firstsect] 442 sz += c.UncompressedSize() 443 } 444 return (sz + align - 1) & uint64(-int64(align)) 445 } 446 447 func (s *Section) UncompressedSize() uint64 { 448 if !strings.HasPrefix(s.Name, "__z") { 449 return s.Size 450 } 451 b := make([]byte, 12) 452 n, err := s.sr.ReadAt(b, 0) 453 if err != nil { 454 panic("Malformed object file") 455 } 456 if n != len(b) { 457 return s.Size 458 } 459 if string(b[:4]) == "ZLIB" { 460 return binary.BigEndian.Uint64(b[4:12]) 461 } 462 return s.Size 463 } 464 465 func (s *Section) PutData(b []byte) { 466 bb := b[0:s.Size] 467 n, err := s.sr.ReadAt(bb, 0) 468 if err != nil || uint64(n) != s.Size { 469 panic("Malformed object file (ReadAt error)") 470 } 471 } 472 473 func (s *Section) PutUncompressedData(b []byte) { 474 if strings.HasPrefix(s.Name, "__z") { 475 bb := make([]byte, 12) 476 n, err := s.sr.ReadAt(bb, 0) 477 if err != nil { 478 panic("Malformed object file") 479 } 480 if n == len(bb) && string(bb[:4]) == "ZLIB" { 481 size := binary.BigEndian.Uint64(bb[4:12]) 482 // Decompress starting at b[12:] 483 r, err := zlib.NewReader(io.NewSectionReader(s, 12, int64(size)-12)) 484 if err != nil { 485 panic("Malformed object file (zlib.NewReader error)") 486 } 487 n, err := io.ReadFull(r, b[0:size]) 488 if err != nil { 489 panic("Malformed object file (ReadFull error)") 490 } 491 if uint64(n) != size { 492 panic(fmt.Sprintf("PutUncompressedData, expected to read %d bytes, instead read %d", size, n)) 493 } 494 if err := r.Close(); err != nil { 495 panic("Malformed object file (Close error)") 496 } 497 return 498 } 499 } 500 // Not compressed 501 s.PutData(b) 502 } 503 504 func (b LoadBytes) String() string { 505 s := "[" 506 for i, a := range b { 507 if i > 0 { 508 s += " " 509 if len(b) > 48 && i >= 16 { 510 s += fmt.Sprintf("... (%d bytes)", len(b)) 511 break 512 } 513 } 514 s += fmt.Sprintf("%x", a) 515 } 516 s += "]" 517 return s 518 } 519 520 func (b LoadBytes) Raw() []byte { return b } 521 func (b LoadBytes) Copy() LoadBytes { return LoadBytes(append([]byte{}, b...)) } 522 func (b LoadBytes) LoadSize(t *FileTOC) uint32 { return uint32(len(b)) } 523 524 func (lc LoadCmd) Put(b []byte, o binary.ByteOrder) int { 525 panic(fmt.Sprintf("Put not implemented for %s", lc.String())) 526 } 527 528 func (s LoadCmdBytes) String() string { 529 return s.LoadCmd.String() + ": " + s.LoadBytes.String() 530 } 531 func (s LoadCmdBytes) Copy() LoadCmdBytes { 532 return LoadCmdBytes{LoadCmd: s.LoadCmd, LoadBytes: s.LoadBytes.Copy()} 533 } 534 535 func (s *SegmentHeader) String() string { 536 return fmt.Sprintf( 537 "Seg %s, len=0x%x, addr=0x%x, memsz=0x%x, offset=0x%x, filesz=0x%x, maxprot=0x%x, prot=0x%x, nsect=%d, flag=0x%x, firstsect=%d", 538 s.Name, s.Len, s.Addr, s.Memsz, s.Offset, s.Filesz, s.Maxprot, s.Prot, s.Nsect, s.Flag, s.Firstsect) 539 } 540 541 func (s *Segment) String() string { 542 return fmt.Sprintf( 543 "Seg %s, len=0x%x, addr=0x%x, memsz=0x%x, offset=0x%x, filesz=0x%x, maxprot=0x%x, prot=0x%x, nsect=%d, flag=0x%x, firstsect=%d", 544 s.Name, s.Len, s.Addr, s.Memsz, s.Offset, s.Filesz, s.Maxprot, s.Prot, s.Nsect, s.Flag, s.Firstsect) 545 } 546 547 // Data reads and returns the contents of the segment. 548 func (s *Segment) Data() ([]byte, error) { 549 dat := make([]byte, s.sr.Size()) 550 n, err := s.sr.ReadAt(dat, 0) 551 if n == len(dat) { 552 err = nil 553 } 554 return dat[0:n], err 555 } 556 557 func (s *Segment) Copy() *Segment { 558 r := &Segment{SegmentHeader: s.SegmentHeader} 559 return r 560 } 561 func (s *Segment) CopyZeroed() *Segment { 562 r := s.Copy() 563 r.Filesz = 0 564 r.Offset = 0 565 r.Nsect = 0 566 r.Firstsect = 0 567 if s.Command() == LcSegment64 { 568 r.Len = uint32(unsafe.Sizeof(Segment64{})) 569 } else { 570 r.Len = uint32(unsafe.Sizeof(Segment32{})) 571 } 572 return r 573 } 574 575 func (s *Segment) LoadSize(t *FileTOC) uint32 { 576 if s.Command() == LcSegment64 { 577 return uint32(unsafe.Sizeof(Segment64{})) + uint32(s.Nsect)*uint32(unsafe.Sizeof(Section64{})) 578 } 579 return uint32(unsafe.Sizeof(Segment32{})) + uint32(s.Nsect)*uint32(unsafe.Sizeof(Section32{})) 580 } 581 582 // Open returns a new ReadSeeker reading the segment. 583 func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } 584 585 // Data reads and returns the contents of the Mach-O section. 586 func (s *Section) Data() ([]byte, error) { 587 dat := make([]byte, s.sr.Size()) 588 n, err := s.sr.ReadAt(dat, 0) 589 if n == len(dat) { 590 err = nil 591 } 592 return dat[0:n], err 593 } 594 595 func (s *Section) Copy() *Section { 596 return &Section{SectionHeader: s.SectionHeader} 597 } 598 599 // Open returns a new ReadSeeker reading the Mach-O section. 600 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } 601 602 // A Dylib represents a Mach-O load dynamic library command. 603 type Dylib struct { 604 DylibCmd 605 Name string 606 Time uint32 607 CurrentVersion uint32 608 CompatVersion uint32 609 } 610 611 func (s *Dylib) String() string { return "Dylib " + s.Name } 612 func (s *Dylib) Copy() *Dylib { 613 r := *s 614 return &r 615 } 616 func (s *Dylib) LoadSize(t *FileTOC) uint32 { 617 return uint32(RoundUp(uint64(unsafe.Sizeof(DylibCmd{}))+uint64(len(s.Name)), t.LoadAlign())) 618 } 619 620 type Dylinker struct { 621 DylinkerCmd // shared by 3 commands, need the LoadCmd 622 Name string 623 } 624 625 func (s *Dylinker) String() string { return s.DylinkerCmd.LoadCmd.String() + " " + s.Name } 626 func (s *Dylinker) Copy() *Dylinker { 627 return &Dylinker{DylinkerCmd: s.DylinkerCmd, Name: s.Name} 628 } 629 func (s *Dylinker) LoadSize(t *FileTOC) uint32 { 630 return uint32(RoundUp(uint64(unsafe.Sizeof(DylinkerCmd{}))+uint64(len(s.Name)), t.LoadAlign())) 631 } 632 633 // A Symtab represents a Mach-O symbol table command. 634 type Symtab struct { 635 SymtabCmd 636 Syms []Symbol 637 } 638 639 func (s *Symtab) Put(b []byte, o binary.ByteOrder) int { 640 o.PutUint32(b[0*4:], uint32(s.LoadCmd)) 641 o.PutUint32(b[1*4:], s.Len) 642 o.PutUint32(b[2*4:], s.Symoff) 643 o.PutUint32(b[3*4:], s.Nsyms) 644 o.PutUint32(b[4*4:], s.Stroff) 645 o.PutUint32(b[5*4:], s.Strsize) 646 return 6 * 4 647 } 648 649 func (s *Symtab) String() string { return fmt.Sprintf("Symtab %#v", s.SymtabCmd) } 650 func (s *Symtab) Copy() *Symtab { 651 return &Symtab{SymtabCmd: s.SymtabCmd, Syms: append([]Symbol{}, s.Syms...)} 652 } 653 func (s *Symtab) LoadSize(t *FileTOC) uint32 { 654 return uint32(unsafe.Sizeof(SymtabCmd{})) 655 } 656 657 type LinkEditData struct { 658 LinkEditDataCmd 659 } 660 661 func (s *LinkEditData) String() string { return "LinkEditData " + s.LoadCmd.String() } 662 func (s *LinkEditData) Copy() *LinkEditData { 663 return &LinkEditData{LinkEditDataCmd: s.LinkEditDataCmd} 664 } 665 func (s *LinkEditData) LoadSize(t *FileTOC) uint32 { 666 return uint32(unsafe.Sizeof(LinkEditDataCmd{})) 667 } 668 669 type Uuid struct { 670 UuidCmd 671 } 672 673 func (s *Uuid) String() string { 674 return fmt.Sprintf("Uuid %X-%X-%X-%X-%X", 675 s.Id[0:4], s.Id[4:6], s.Id[6:8], s.Id[8:10], s.Id[10:16]) 676 } // 8-4-4-4-12 677 func (s *Uuid) Copy() *Uuid { 678 return &Uuid{UuidCmd: s.UuidCmd} 679 } 680 func (s *Uuid) LoadSize(t *FileTOC) uint32 { 681 return uint32(unsafe.Sizeof(UuidCmd{})) 682 } 683 func (s *Uuid) Put(b []byte, o binary.ByteOrder) int { 684 o.PutUint32(b[0*4:], uint32(s.LoadCmd)) 685 o.PutUint32(b[1*4:], s.Len) 686 copy(b[2*4:], s.Id[0:]) 687 return int(s.Len) 688 } 689 690 type DyldInfo struct { 691 DyldInfoCmd 692 } 693 694 func (s *DyldInfo) String() string { return "DyldInfo " + s.LoadCmd.String() } 695 func (s *DyldInfo) Copy() *DyldInfo { 696 return &DyldInfo{DyldInfoCmd: s.DyldInfoCmd} 697 } 698 func (s *DyldInfo) LoadSize(t *FileTOC) uint32 { 699 return uint32(unsafe.Sizeof(DyldInfoCmd{})) 700 } 701 702 type EncryptionInfo struct { 703 EncryptionInfoCmd 704 } 705 706 func (s *EncryptionInfo) String() string { return "EncryptionInfo " + s.LoadCmd.String() } 707 func (s *EncryptionInfo) Copy() *EncryptionInfo { 708 return &EncryptionInfo{EncryptionInfoCmd: s.EncryptionInfoCmd} 709 } 710 func (s *EncryptionInfo) LoadSize(t *FileTOC) uint32 { 711 return uint32(unsafe.Sizeof(EncryptionInfoCmd{})) 712 } 713 714 // A Dysymtab represents a Mach-O dynamic symbol table command. 715 type Dysymtab struct { 716 DysymtabCmd 717 IndirectSyms []uint32 // indices into Symtab.Syms 718 } 719 720 func (s *Dysymtab) String() string { return fmt.Sprintf("Dysymtab %#v", s.DysymtabCmd) } 721 func (s *Dysymtab) Copy() *Dysymtab { 722 return &Dysymtab{DysymtabCmd: s.DysymtabCmd, IndirectSyms: append([]uint32{}, s.IndirectSyms...)} 723 } 724 func (s *Dysymtab) LoadSize(t *FileTOC) uint32 { 725 return uint32(unsafe.Sizeof(DysymtabCmd{})) 726 } 727 728 // A Rpath represents a Mach-O rpath command. 729 type Rpath struct { 730 LoadCmd 731 Path string 732 } 733 734 func (s *Rpath) String() string { return "Rpath " + s.Path } 735 func (s *Rpath) Command() LoadCmd { return LcRpath } 736 func (s *Rpath) Copy() *Rpath { 737 return &Rpath{Path: s.Path} 738 } 739 func (s *Rpath) LoadSize(t *FileTOC) uint32 { 740 return uint32(RoundUp(uint64(unsafe.Sizeof(RpathCmd{}))+uint64(len(s.Path)), t.LoadAlign())) 741 } 742 743 // Open opens the named file using os.Open and prepares it for use as a Mach-O binary. 744 func Open(name string) (*File, error) { 745 f, err := os.Open(name) 746 if err != nil { 747 return nil, err 748 } 749 ff, err := NewFile(f) 750 if err != nil { 751 f.Close() 752 return nil, err 753 } 754 ff.closer = f 755 return ff, nil 756 } 757 758 // Close closes the File. 759 // If the File was created using NewFile directly instead of Open, 760 // Close has no effect. 761 func (f *File) Close() error { 762 var err error 763 if f.closer != nil { 764 err = f.closer.Close() 765 f.closer = nil 766 } 767 return err 768 } 769 770 // NewFile creates a new File for accessing a Mach-O binary in an underlying reader. 771 // The Mach-O binary is expected to start at position 0 in the ReaderAt. 772 func NewFile(r io.ReaderAt) (*File, error) { 773 f := new(File) 774 sr := io.NewSectionReader(r, 0, 1<<63-1) 775 776 // Read and decode Mach magic to determine byte order, size. 777 // Magic32 and Magic64 differ only in the bottom bit. 778 var ident [4]byte 779 if _, err := r.ReadAt(ident[0:], 0); err != nil { 780 return nil, err 781 } 782 be := binary.BigEndian.Uint32(ident[0:]) 783 le := binary.LittleEndian.Uint32(ident[0:]) 784 switch Magic32 &^ 1 { 785 case be &^ 1: 786 f.ByteOrder = binary.BigEndian 787 f.Magic = be 788 case le &^ 1: 789 f.ByteOrder = binary.LittleEndian 790 f.Magic = le 791 default: 792 return nil, formatError(0, "invalid magic number be=0x%x, le=0x%x", be, le) 793 } 794 795 // Read entire file header. 796 if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil { 797 return nil, err 798 } 799 800 // Then load commands. 801 offset := int64(fileHeaderSize32) 802 if f.Magic == Magic64 { 803 offset = fileHeaderSize64 804 } 805 dat := make([]byte, f.SizeCommands) 806 if _, err := r.ReadAt(dat, offset); err != nil { 807 return nil, err 808 } 809 f.Loads = make([]Load, f.NCommands) 810 bo := f.ByteOrder 811 for i := range f.Loads { 812 // Each load command begins with uint32 command and length. 813 if len(dat) < 8 { 814 return nil, formatError(offset, "command block too small, len(dat) = %d", len(dat)) 815 } 816 cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8]) 817 if siz < 8 || siz > uint32(len(dat)) { 818 return nil, formatError(offset, "invalid command block size, len(dat)=%d, size=%d", len(dat), siz) 819 } 820 var cmddat []byte 821 cmddat, dat = dat[0:siz], dat[siz:] 822 offset += int64(siz) 823 var s *Segment 824 switch cmd { 825 default: 826 f.Loads[i] = LoadCmdBytes{LoadCmd(cmd), LoadBytes(cmddat)} 827 828 case LcUuid: 829 var hdr UuidCmd 830 b := bytes.NewReader(cmddat) 831 if err := binary.Read(b, bo, &hdr); err != nil { 832 return nil, err 833 } 834 l := &Uuid{UuidCmd: hdr} 835 836 f.Loads[i] = l 837 838 case LcRpath: 839 var hdr RpathCmd 840 b := bytes.NewReader(cmddat) 841 if err := binary.Read(b, bo, &hdr); err != nil { 842 return nil, err 843 } 844 l := &Rpath{LoadCmd: hdr.LoadCmd} 845 if hdr.Path >= uint32(len(cmddat)) { 846 return nil, formatError(offset, "invalid path in rpath command, len(cmddat)=%d, hdr.Path=%d", len(cmddat), hdr.Path) 847 } 848 l.Path = cstring(cmddat[hdr.Path:]) 849 f.Loads[i] = l 850 851 case LcLoadDylinker, LcIdDylinker, LcDyldEnvironment: 852 var hdr DylinkerCmd 853 b := bytes.NewReader(cmddat) 854 if err := binary.Read(b, bo, &hdr); err != nil { 855 return nil, err 856 } 857 l := new(Dylinker) 858 if hdr.Name >= uint32(len(cmddat)) { 859 return nil, formatError(offset, "invalid name in dynamic linker command, hdr.Name=%d, len(cmddat)=%d", hdr.Name, len(cmddat)) 860 } 861 l.Name = cstring(cmddat[hdr.Name:]) 862 l.DylinkerCmd = hdr 863 f.Loads[i] = l 864 865 case LcDylib: 866 var hdr DylibCmd 867 b := bytes.NewReader(cmddat) 868 if err := binary.Read(b, bo, &hdr); err != nil { 869 return nil, err 870 } 871 l := new(Dylib) 872 if hdr.Name >= uint32(len(cmddat)) { 873 return nil, formatError(offset, "invalid name in dynamic library command, hdr.Name=%d, len(cmddat)=%d", hdr.Name, len(cmddat)) 874 } 875 l.Name = cstring(cmddat[hdr.Name:]) 876 l.Time = hdr.Time 877 l.CurrentVersion = hdr.CurrentVersion 878 l.CompatVersion = hdr.CompatVersion 879 f.Loads[i] = l 880 881 case LcSymtab: 882 var hdr SymtabCmd 883 b := bytes.NewReader(cmddat) 884 if err := binary.Read(b, bo, &hdr); err != nil { 885 return nil, err 886 } 887 strtab := make([]byte, hdr.Strsize) 888 if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil { 889 return nil, err 890 } 891 var symsz int 892 if f.Magic == Magic64 { 893 symsz = 16 894 } else { 895 symsz = 12 896 } 897 symdat := make([]byte, int(hdr.Nsyms)*symsz) 898 if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil { 899 return nil, err 900 } 901 st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset) 902 st.SymtabCmd = hdr 903 if err != nil { 904 return nil, err 905 } 906 f.Loads[i] = st 907 f.Symtab = st 908 909 case LcDysymtab: 910 var hdr DysymtabCmd 911 b := bytes.NewReader(cmddat) 912 if err := binary.Read(b, bo, &hdr); err != nil { 913 return nil, err 914 } 915 dat := make([]byte, hdr.Nindirectsyms*4) 916 if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil { 917 return nil, err 918 } 919 x := make([]uint32, hdr.Nindirectsyms) 920 if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil { 921 return nil, err 922 } 923 st := new(Dysymtab) 924 st.DysymtabCmd = hdr 925 st.IndirectSyms = x 926 f.Loads[i] = st 927 f.Dysymtab = st 928 929 case LcSegment: 930 var seg32 Segment32 931 b := bytes.NewReader(cmddat) 932 if err := binary.Read(b, bo, &seg32); err != nil { 933 return nil, err 934 } 935 s = new(Segment) 936 s.LoadCmd = cmd 937 s.Len = siz 938 s.Name = cstring(seg32.Name[0:]) 939 s.Addr = uint64(seg32.Addr) 940 s.Memsz = uint64(seg32.Memsz) 941 s.Offset = uint64(seg32.Offset) 942 s.Filesz = uint64(seg32.Filesz) 943 s.Maxprot = seg32.Maxprot 944 s.Prot = seg32.Prot 945 s.Nsect = seg32.Nsect 946 s.Flag = seg32.Flag 947 s.Firstsect = uint32(len(f.Sections)) 948 f.Loads[i] = s 949 for i := 0; i < int(s.Nsect); i++ { 950 var sh32 Section32 951 if err := binary.Read(b, bo, &sh32); err != nil { 952 return nil, err 953 } 954 sh := new(Section) 955 sh.Name = cstring(sh32.Name[0:]) 956 sh.Seg = cstring(sh32.Seg[0:]) 957 sh.Addr = uint64(sh32.Addr) 958 sh.Size = uint64(sh32.Size) 959 sh.Offset = sh32.Offset 960 sh.Align = sh32.Align 961 sh.Reloff = sh32.Reloff 962 sh.Nreloc = sh32.Nreloc 963 sh.Flags = sh32.Flags 964 sh.Reserved1 = sh32.Reserve1 965 sh.Reserved2 = sh32.Reserve2 966 if err := f.pushSection(sh, r); err != nil { 967 return nil, err 968 } 969 } 970 971 case LcSegment64: 972 var seg64 Segment64 973 b := bytes.NewReader(cmddat) 974 if err := binary.Read(b, bo, &seg64); err != nil { 975 return nil, err 976 } 977 s = new(Segment) 978 s.LoadCmd = cmd 979 s.Len = siz 980 s.Name = cstring(seg64.Name[0:]) 981 s.Addr = seg64.Addr 982 s.Memsz = seg64.Memsz 983 s.Offset = seg64.Offset 984 s.Filesz = seg64.Filesz 985 s.Maxprot = seg64.Maxprot 986 s.Prot = seg64.Prot 987 s.Nsect = seg64.Nsect 988 s.Flag = seg64.Flag 989 s.Firstsect = uint32(len(f.Sections)) 990 f.Loads[i] = s 991 for i := 0; i < int(s.Nsect); i++ { 992 var sh64 Section64 993 if err := binary.Read(b, bo, &sh64); err != nil { 994 return nil, err 995 } 996 sh := new(Section) 997 sh.Name = cstring(sh64.Name[0:]) 998 sh.Seg = cstring(sh64.Seg[0:]) 999 sh.Addr = sh64.Addr 1000 sh.Size = sh64.Size 1001 sh.Offset = sh64.Offset 1002 sh.Align = sh64.Align 1003 sh.Reloff = sh64.Reloff 1004 sh.Nreloc = sh64.Nreloc 1005 sh.Flags = sh64.Flags 1006 sh.Reserved1 = sh64.Reserve1 1007 sh.Reserved2 = sh64.Reserve2 1008 sh.Reserved3 = sh64.Reserve3 1009 if err := f.pushSection(sh, r); err != nil { 1010 return nil, err 1011 } 1012 } 1013 1014 case LcCodeSignature, LcSegmentSplitInfo, LcFunctionStarts, 1015 LcDataInCode, LcDylibCodeSignDrs: 1016 var hdr LinkEditDataCmd 1017 b := bytes.NewReader(cmddat) 1018 1019 if err := binary.Read(b, bo, &hdr); err != nil { 1020 return nil, err 1021 } 1022 l := new(LinkEditData) 1023 1024 l.LinkEditDataCmd = hdr 1025 f.Loads[i] = l 1026 1027 case LcEncryptionInfo, LcEncryptionInfo64: 1028 var hdr EncryptionInfoCmd 1029 b := bytes.NewReader(cmddat) 1030 1031 if err := binary.Read(b, bo, &hdr); err != nil { 1032 return nil, err 1033 } 1034 l := new(EncryptionInfo) 1035 1036 l.EncryptionInfoCmd = hdr 1037 f.Loads[i] = l 1038 1039 case LcDyldInfo, LcDyldInfoOnly: 1040 var hdr DyldInfoCmd 1041 b := bytes.NewReader(cmddat) 1042 1043 if err := binary.Read(b, bo, &hdr); err != nil { 1044 return nil, err 1045 } 1046 l := new(DyldInfo) 1047 1048 l.DyldInfoCmd = hdr 1049 f.Loads[i] = l 1050 } 1051 if s != nil { 1052 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz)) 1053 s.ReaderAt = s.sr 1054 } 1055 if f.Loads[i].LoadSize(&f.FileTOC) != siz { 1056 fmt.Printf("Oops, actual size was %d, calculated was %d, load was %s\n", siz, f.Loads[i].LoadSize(&f.FileTOC), f.Loads[i].String()) 1057 panic("oops") 1058 } 1059 } 1060 return f, nil 1061 } 1062 1063 func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) { 1064 bo := f.ByteOrder 1065 symtab := make([]Symbol, hdr.Nsyms) 1066 b := bytes.NewReader(symdat) 1067 for i := range symtab { 1068 var n Nlist64 1069 if f.Magic == Magic64 { 1070 if err := binary.Read(b, bo, &n); err != nil { 1071 return nil, err 1072 } 1073 } else { 1074 var n32 Nlist32 1075 if err := binary.Read(b, bo, &n32); err != nil { 1076 return nil, err 1077 } 1078 n.Name = n32.Name 1079 n.Type = n32.Type 1080 n.Sect = n32.Sect 1081 n.Desc = n32.Desc 1082 n.Value = uint64(n32.Value) 1083 } 1084 sym := &symtab[i] 1085 if n.Name >= uint32(len(strtab)) { 1086 return nil, formatError(offset, "invalid name in symbol table, n.Name=%d, len(strtab)=%d", n.Name, len(strtab)) 1087 } 1088 sym.Name = cstring(strtab[n.Name:]) 1089 sym.Type = n.Type 1090 sym.Sect = n.Sect 1091 sym.Desc = n.Desc 1092 sym.Value = n.Value 1093 } 1094 st := new(Symtab) 1095 st.Syms = symtab 1096 return st, nil 1097 } 1098 1099 type relocInfo struct { 1100 Addr uint32 1101 Symnum uint32 1102 } 1103 1104 func (f *File) pushSection(sh *Section, r io.ReaderAt) error { 1105 f.Sections = append(f.Sections, sh) 1106 sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size)) 1107 sh.ReaderAt = sh.sr 1108 1109 if sh.Nreloc > 0 { 1110 reldat := make([]byte, int(sh.Nreloc)*8) 1111 if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil { 1112 return err 1113 } 1114 b := bytes.NewReader(reldat) 1115 1116 bo := f.ByteOrder 1117 1118 sh.Relocs = make([]Reloc, sh.Nreloc) 1119 for i := range sh.Relocs { 1120 rel := &sh.Relocs[i] 1121 1122 var ri relocInfo 1123 if err := binary.Read(b, bo, &ri); err != nil { 1124 return err 1125 } 1126 1127 if ri.Addr&(1<<31) != 0 { // scattered 1128 rel.Addr = ri.Addr & (1<<24 - 1) 1129 rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1)) 1130 rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1)) 1131 rel.Pcrel = ri.Addr&(1<<30) != 0 1132 rel.Value = ri.Symnum 1133 rel.Scattered = true 1134 } else { 1135 switch bo { 1136 case binary.LittleEndian: 1137 rel.Addr = ri.Addr 1138 rel.Value = ri.Symnum & (1<<24 - 1) 1139 rel.Pcrel = ri.Symnum&(1<<24) != 0 1140 rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1)) 1141 rel.Extern = ri.Symnum&(1<<27) != 0 1142 rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1)) 1143 case binary.BigEndian: 1144 rel.Addr = ri.Addr 1145 rel.Value = ri.Symnum >> 8 1146 rel.Pcrel = ri.Symnum&(1<<7) != 0 1147 rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1)) 1148 rel.Extern = ri.Symnum&(1<<4) != 0 1149 rel.Type = uint8(ri.Symnum & (1<<4 - 1)) 1150 default: 1151 panic("unreachable") 1152 } 1153 } 1154 } 1155 } 1156 1157 return nil 1158 } 1159 1160 func cstring(b []byte) string { 1161 i := bytes.IndexByte(b, 0) 1162 if i == -1 { 1163 i = len(b) 1164 } 1165 return string(b[0:i]) 1166 } 1167 1168 // Segment returns the first Segment with the given name, or nil if no such segment exists. 1169 func (f *File) Segment(name string) *Segment { 1170 for _, l := range f.Loads { 1171 if s, ok := l.(*Segment); ok && s.Name == name { 1172 return s 1173 } 1174 } 1175 return nil 1176 } 1177 1178 // Section returns the first section with the given name, or nil if no such 1179 // section exists. 1180 func (f *File) Section(name string) *Section { 1181 for _, s := range f.Sections { 1182 if s.Name == name { 1183 return s 1184 } 1185 } 1186 return nil 1187 } 1188 1189 // DWARF returns the DWARF debug information for the Mach-O file. 1190 func (f *File) DWARF() (*dwarf.Data, error) { 1191 dwarfSuffix := func(s *Section) string { 1192 switch { 1193 case strings.HasPrefix(s.Name, "__debug_"): 1194 return s.Name[8:] 1195 case strings.HasPrefix(s.Name, "__zdebug_"): 1196 return s.Name[9:] 1197 default: 1198 return "" 1199 } 1200 1201 } 1202 sectionData := func(s *Section) ([]byte, error) { 1203 b, err := s.Data() 1204 if err != nil && uint64(len(b)) < s.Size { 1205 return nil, err 1206 } 1207 1208 if len(b) >= 12 && string(b[:4]) == "ZLIB" { 1209 dlen := binary.BigEndian.Uint64(b[4:12]) 1210 dbuf := make([]byte, dlen) 1211 r, err := zlib.NewReader(bytes.NewBuffer(b[12:])) 1212 if err != nil { 1213 return nil, err 1214 } 1215 if _, err := io.ReadFull(r, dbuf); err != nil { 1216 return nil, err 1217 } 1218 if err := r.Close(); err != nil { 1219 return nil, err 1220 } 1221 b = dbuf 1222 } 1223 return b, nil 1224 } 1225 1226 // There are many other DWARF sections, but these 1227 // are the ones the debug/dwarf package uses. 1228 // Don't bother loading others. 1229 var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} 1230 for _, s := range f.Sections { 1231 suffix := dwarfSuffix(s) 1232 if suffix == "" { 1233 continue 1234 } 1235 if _, ok := dat[suffix]; !ok { 1236 continue 1237 } 1238 b, err := sectionData(s) 1239 if err != nil { 1240 return nil, err 1241 } 1242 dat[suffix] = b 1243 } 1244 1245 d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"]) 1246 if err != nil { 1247 return nil, err 1248 } 1249 1250 // Look for DWARF4 .debug_types sections. 1251 for i, s := range f.Sections { 1252 suffix := dwarfSuffix(s) 1253 if suffix != "types" { 1254 continue 1255 } 1256 1257 b, err := sectionData(s) 1258 if err != nil { 1259 return nil, err 1260 } 1261 1262 err = d.AddTypes(fmt.Sprintf("types-%d", i), b) 1263 if err != nil { 1264 return nil, err 1265 } 1266 } 1267 1268 return d, nil 1269 } 1270 1271 // ImportedSymbols returns the names of all symbols 1272 // referred to by the binary f that are expected to be 1273 // satisfied by other libraries at dynamic load time. 1274 func (f *File) ImportedSymbols() ([]string, error) { 1275 if f.Dysymtab == nil || f.Symtab == nil { 1276 return nil, formatError(0, "missing symbol table, f.Dsymtab=%v, f.Symtab=%v", f.Dysymtab, f.Symtab) 1277 } 1278 1279 st := f.Symtab 1280 dt := f.Dysymtab 1281 var all []string 1282 for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] { 1283 all = append(all, s.Name) 1284 } 1285 return all, nil 1286 } 1287 1288 // ImportedLibraries returns the paths of all libraries 1289 // referred to by the binary f that are expected to be 1290 // linked with the binary at dynamic link time. 1291 func (f *File) ImportedLibraries() ([]string, error) { 1292 var all []string 1293 for _, l := range f.Loads { 1294 if lib, ok := l.(*Dylib); ok { 1295 all = append(all, lib.Name) 1296 } 1297 } 1298 return all, nil 1299 } 1300 1301 func RoundUp(x, align uint64) uint64 { 1302 return uint64((x + align - 1) & -align) 1303 }