github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/pkg/debug/plan9obj/file.go (about)

     1  // Copyright 2014 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 plan9obj implements access to Plan 9 a.out object files.
     6  package plan9obj
     7  
     8  import (
     9  	"encoding/binary"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  )
    15  
    16  // A FileHeader represents a Plan 9 a.out file header.
    17  type FileHeader struct {
    18  	Magic   uint32
    19  	Bss     uint32
    20  	Entry   uint64
    21  	PtrSize int
    22  }
    23  
    24  // A File represents an open Plan 9 a.out file.
    25  type File struct {
    26  	FileHeader
    27  	Sections []*Section
    28  	closer   io.Closer
    29  }
    30  
    31  // A SectionHeader represents a single Plan 9 a.out section header.
    32  // This structure doesn't exist on-disk, but eases navigation
    33  // through the object file.
    34  type SectionHeader struct {
    35  	Name   string
    36  	Size   uint32
    37  	Offset uint32
    38  }
    39  
    40  // A Section represents a single section in a Plan 9 a.out file.
    41  type Section struct {
    42  	SectionHeader
    43  
    44  	// Embed ReaderAt for ReadAt method.
    45  	// Do not embed SectionReader directly
    46  	// to avoid having Read and Seek.
    47  	// If a client wants Read and Seek it must use
    48  	// Open() to avoid fighting over the seek offset
    49  	// with other clients.
    50  	io.ReaderAt
    51  	sr *io.SectionReader
    52  }
    53  
    54  // Data reads and returns the contents of the Plan 9 a.out section.
    55  func (s *Section) Data() ([]byte, error) {
    56  	dat := make([]byte, s.sr.Size())
    57  	n, err := s.sr.ReadAt(dat, 0)
    58  	if n == len(dat) {
    59  		err = nil
    60  	}
    61  	return dat[0:n], err
    62  }
    63  
    64  // Open returns a new ReadSeeker reading the Plan 9 a.out section.
    65  func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
    66  
    67  // A Symbol represents an entry in a Plan 9 a.out symbol table section.
    68  type Sym struct {
    69  	Value uint64
    70  	Type  rune
    71  	Name  string
    72  }
    73  
    74  /*
    75   * Plan 9 a.out reader
    76   */
    77  
    78  // formatError is returned by some operations if the data does
    79  // not have the correct format for an object file.
    80  type formatError struct {
    81  	off int
    82  	msg string
    83  	val interface{}
    84  }
    85  
    86  func (e *formatError) Error() string {
    87  	msg := e.msg
    88  	if e.val != nil {
    89  		msg += fmt.Sprintf(" '%v'", e.val)
    90  	}
    91  	msg += fmt.Sprintf(" in record at byte %#x", e.off)
    92  	return msg
    93  }
    94  
    95  // Open opens the named file using os.Open and prepares it for use as a Plan 9 a.out binary.
    96  func Open(name string) (*File, error) {
    97  	f, err := os.Open(name)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	ff, err := NewFile(f)
   102  	if err != nil {
   103  		f.Close()
   104  		return nil, err
   105  	}
   106  	ff.closer = f
   107  	return ff, nil
   108  }
   109  
   110  // Close closes the File.
   111  // If the File was created using NewFile directly instead of Open,
   112  // Close has no effect.
   113  func (f *File) Close() error {
   114  	var err error
   115  	if f.closer != nil {
   116  		err = f.closer.Close()
   117  		f.closer = nil
   118  	}
   119  	return err
   120  }
   121  
   122  func parseMagic(magic []byte) (uint32, error) {
   123  	m := binary.BigEndian.Uint32(magic)
   124  	switch m {
   125  	case Magic386, MagicAMD64, MagicARM:
   126  		return m, nil
   127  	}
   128  	return 0, &formatError{0, "bad magic number", magic}
   129  }
   130  
   131  // NewFile creates a new File for accessing a Plan 9 binary in an underlying reader.
   132  // The Plan 9 binary is expected to start at position 0 in the ReaderAt.
   133  func NewFile(r io.ReaderAt) (*File, error) {
   134  	sr := io.NewSectionReader(r, 0, 1<<63-1)
   135  	// Read and decode Plan 9 magic
   136  	var magic [4]byte
   137  	if _, err := r.ReadAt(magic[:], 0); err != nil {
   138  		return nil, err
   139  	}
   140  	_, err := parseMagic(magic[:])
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	ph := new(prog)
   146  	if err := binary.Read(sr, binary.BigEndian, ph); err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	f := &File{FileHeader: FileHeader{
   151  		Magic:   ph.Magic,
   152  		Bss:     ph.Bss,
   153  		Entry:   uint64(ph.Entry),
   154  		PtrSize: 4,
   155  	}}
   156  
   157  	hdrSize := 4 * 8
   158  
   159  	if ph.Magic&Magic64 != 0 {
   160  		if err := binary.Read(sr, binary.BigEndian, &f.Entry); err != nil {
   161  			return nil, err
   162  		}
   163  		f.PtrSize = 8
   164  		hdrSize += 8
   165  	}
   166  
   167  	var sects = []struct {
   168  		name string
   169  		size uint32
   170  	}{
   171  		{"text", ph.Text},
   172  		{"data", ph.Data},
   173  		{"syms", ph.Syms},
   174  		{"spsz", ph.Spsz},
   175  		{"pcsz", ph.Pcsz},
   176  	}
   177  
   178  	f.Sections = make([]*Section, 5)
   179  
   180  	off := uint32(hdrSize)
   181  
   182  	for i, sect := range sects {
   183  		s := new(Section)
   184  		s.SectionHeader = SectionHeader{
   185  			Name:   sect.name,
   186  			Size:   sect.size,
   187  			Offset: off,
   188  		}
   189  		off += sect.size
   190  		s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size))
   191  		s.ReaderAt = s.sr
   192  		f.Sections[i] = s
   193  	}
   194  
   195  	return f, nil
   196  }
   197  
   198  func walksymtab(data []byte, ptrsz int, fn func(sym) error) error {
   199  	var order binary.ByteOrder = binary.BigEndian
   200  	var s sym
   201  	p := data
   202  	for len(p) >= 4 {
   203  		// Symbol type, value.
   204  		if len(p) < ptrsz {
   205  			return &formatError{len(data), "unexpected EOF", nil}
   206  		}
   207  		// fixed-width value
   208  		if ptrsz == 8 {
   209  			s.value = order.Uint64(p[0:8])
   210  			p = p[8:]
   211  		} else {
   212  			s.value = uint64(order.Uint32(p[0:4]))
   213  			p = p[4:]
   214  		}
   215  
   216  		var typ byte
   217  		typ = p[0] & 0x7F
   218  		s.typ = typ
   219  		p = p[1:]
   220  
   221  		// Name.
   222  		var i int
   223  		var nnul int
   224  		for i = 0; i < len(p); i++ {
   225  			if p[i] == 0 {
   226  				nnul = 1
   227  				break
   228  			}
   229  		}
   230  		switch typ {
   231  		case 'z', 'Z':
   232  			p = p[i+nnul:]
   233  			for i = 0; i+2 <= len(p); i += 2 {
   234  				if p[i] == 0 && p[i+1] == 0 {
   235  					nnul = 2
   236  					break
   237  				}
   238  			}
   239  		}
   240  		if len(p) < i+nnul {
   241  			return &formatError{len(data), "unexpected EOF", nil}
   242  		}
   243  		s.name = p[0:i]
   244  		i += nnul
   245  		p = p[i:]
   246  
   247  		fn(s)
   248  	}
   249  	return nil
   250  }
   251  
   252  // NewTable decodes the Go symbol table in data,
   253  // returning an in-memory representation.
   254  func newTable(symtab []byte, ptrsz int) ([]Sym, error) {
   255  	var n int
   256  	err := walksymtab(symtab, ptrsz, func(s sym) error {
   257  		n++
   258  		return nil
   259  	})
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  
   264  	fname := make(map[uint16]string)
   265  	syms := make([]Sym, 0, n)
   266  	err = walksymtab(symtab, ptrsz, func(s sym) error {
   267  		n := len(syms)
   268  		syms = syms[0 : n+1]
   269  		ts := &syms[n]
   270  		ts.Type = rune(s.typ)
   271  		ts.Value = s.value
   272  		switch s.typ {
   273  		default:
   274  			ts.Name = string(s.name[:])
   275  		case 'z', 'Z':
   276  			for i := 0; i < len(s.name); i += 2 {
   277  				eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
   278  				elt, ok := fname[eltIdx]
   279  				if !ok {
   280  					return &formatError{-1, "bad filename code", eltIdx}
   281  				}
   282  				if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
   283  					ts.Name += "/"
   284  				}
   285  				ts.Name += elt
   286  			}
   287  		}
   288  		switch s.typ {
   289  		case 'f':
   290  			fname[uint16(s.value)] = ts.Name
   291  		}
   292  		return nil
   293  	})
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  
   298  	return syms, nil
   299  }
   300  
   301  // Symbols returns the symbol table for f.
   302  func (f *File) Symbols() ([]Sym, error) {
   303  	symtabSection := f.Section("syms")
   304  	if symtabSection == nil {
   305  		return nil, errors.New("no symbol section")
   306  	}
   307  
   308  	symtab, err := symtabSection.Data()
   309  	if err != nil {
   310  		return nil, errors.New("cannot load symbol section")
   311  	}
   312  
   313  	return newTable(symtab, f.PtrSize)
   314  }
   315  
   316  // Section returns a section with the given name, or nil if no such
   317  // section exists.
   318  func (f *File) Section(name string) *Section {
   319  	for _, s := range f.Sections {
   320  		if s.Name == name {
   321  			return s
   322  		}
   323  	}
   324  	return nil
   325  }