Home | History | Annotate | Download | only in 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