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