...

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