Source file src/debug/plan9obj/file.go

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

View as plain text