Black Lives Matter. Support the Equal Justice Initiative.

Source file src/debug/plan9obj/file.go

Documentation: debug/plan9obj

     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 interface{}
    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  		var typ byte
   220  		typ = p[0] & 0x7F
   221  		s.typ = typ
   222  		p = p[1:]
   223  
   224  		// Name.
   225  		var i int
   226  		var nnul int
   227  		for i = 0; i < len(p); i++ {
   228  			if p[i] == 0 {
   229  				nnul = 1
   230  				break
   231  			}
   232  		}
   233  		switch typ {
   234  		case 'z', 'Z':
   235  			p = p[i+nnul:]
   236  			for i = 0; i+2 <= len(p); i += 2 {
   237  				if p[i] == 0 && p[i+1] == 0 {
   238  					nnul = 2
   239  					break
   240  				}
   241  			}
   242  		}
   243  		if len(p) < i+nnul {
   244  			return &formatError{len(data), "unexpected EOF", nil}
   245  		}
   246  		s.name = p[0:i]
   247  		i += nnul
   248  		p = p[i:]
   249  
   250  		fn(s)
   251  	}
   252  	return nil
   253  }
   254  
   255  // NewTable decodes the Go symbol table in data,
   256  // returning an in-memory representation.
   257  func newTable(symtab []byte, ptrsz int) ([]Sym, error) {
   258  	var n int
   259  	err := walksymtab(symtab, ptrsz, func(s sym) error {
   260  		n++
   261  		return nil
   262  	})
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  
   267  	fname := make(map[uint16]string)
   268  	syms := make([]Sym, 0, n)
   269  	err = walksymtab(symtab, ptrsz, func(s sym) error {
   270  		n := len(syms)
   271  		syms = syms[0 : n+1]
   272  		ts := &syms[n]
   273  		ts.Type = rune(s.typ)
   274  		ts.Value = s.value
   275  		switch s.typ {
   276  		default:
   277  			ts.Name = string(s.name)
   278  		case 'z', 'Z':
   279  			for i := 0; i < len(s.name); i += 2 {
   280  				eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
   281  				elt, ok := fname[eltIdx]
   282  				if !ok {
   283  					return &formatError{-1, "bad filename code", eltIdx}
   284  				}
   285  				if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
   286  					ts.Name += "/"
   287  				}
   288  				ts.Name += elt
   289  			}
   290  		}
   291  		switch s.typ {
   292  		case 'f':
   293  			fname[uint16(s.value)] = ts.Name
   294  		}
   295  		return nil
   296  	})
   297  	if err != nil {
   298  		return nil, err
   299  	}
   300  
   301  	return syms, nil
   302  }
   303  
   304  // Symbols returns the symbol table for f.
   305  func (f *File) Symbols() ([]Sym, error) {
   306  	symtabSection := f.Section("syms")
   307  	if symtabSection == nil {
   308  		return nil, errors.New("no symbol section")
   309  	}
   310  
   311  	symtab, err := symtabSection.Data()
   312  	if err != nil {
   313  		return nil, errors.New("cannot load symbol section")
   314  	}
   315  
   316  	return newTable(symtab, f.PtrSize)
   317  }
   318  
   319  // Section returns a section with the given name, or nil if no such
   320  // section exists.
   321  func (f *File) Section(name string) *Section {
   322  	for _, s := range f.Sections {
   323  		if s.Name == name {
   324  			return s
   325  		}
   326  	}
   327  	return nil
   328  }
   329  

View as plain text