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