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