OSDN Git Service

Update Go library to r60.
[pf3gnuchains/gcc-fork.git] / libgo / go / 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, as defined by
6 // http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html.
7 package macho
8
9 // High level access to low level data structures.
10
11 import (
12         "bytes"
13         "debug/dwarf"
14         "encoding/binary"
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, os.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, os.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) String() 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, os.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() os.Error {
179         var err os.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, os.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, os.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, os.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, os.NewError("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, os.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, os.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 }