...

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 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