|  | // Copyright 2014 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | // Package plan9obj implements access to Plan 9 a.out object files. | 
|  | package plan9obj | 
|  |  | 
|  | import ( | 
|  | "encoding/binary" | 
|  | "errors" | 
|  | "fmt" | 
|  | "io" | 
|  | "os" | 
|  | ) | 
|  |  | 
|  | // A FileHeader represents a Plan 9 a.out file header. | 
|  | type FileHeader struct { | 
|  | Magic       uint32 | 
|  | Bss         uint32 | 
|  | Entry       uint64 | 
|  | PtrSize     int | 
|  | LoadAddress uint64 | 
|  | HdrSize     uint64 | 
|  | } | 
|  |  | 
|  | // A File represents an open Plan 9 a.out file. | 
|  | type File struct { | 
|  | FileHeader | 
|  | Sections []*Section | 
|  | closer   io.Closer | 
|  | } | 
|  |  | 
|  | // A SectionHeader represents a single Plan 9 a.out section header. | 
|  | // This structure doesn't exist on-disk, but eases navigation | 
|  | // through the object file. | 
|  | type SectionHeader struct { | 
|  | Name   string | 
|  | Size   uint32 | 
|  | Offset uint32 | 
|  | } | 
|  |  | 
|  | // A Section represents a single section in a Plan 9 a.out file. | 
|  | type Section struct { | 
|  | SectionHeader | 
|  |  | 
|  | // Embed ReaderAt for ReadAt method. | 
|  | // Do not embed SectionReader directly | 
|  | // to avoid having Read and Seek. | 
|  | // If a client wants Read and Seek it must use | 
|  | // Open() to avoid fighting over the seek offset | 
|  | // with other clients. | 
|  | io.ReaderAt | 
|  | sr *io.SectionReader | 
|  | } | 
|  |  | 
|  | // Data reads and returns the contents of the Plan 9 a.out section. | 
|  | func (s *Section) Data() ([]byte, error) { | 
|  | dat := make([]byte, s.sr.Size()) | 
|  | n, err := s.sr.ReadAt(dat, 0) | 
|  | if n == len(dat) { | 
|  | err = nil | 
|  | } | 
|  | return dat[0:n], err | 
|  | } | 
|  |  | 
|  | // Open returns a new ReadSeeker reading the Plan 9 a.out section. | 
|  | func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } | 
|  |  | 
|  | // A Symbol represents an entry in a Plan 9 a.out symbol table section. | 
|  | type Sym struct { | 
|  | Value uint64 | 
|  | Type  rune | 
|  | Name  string | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Plan 9 a.out reader | 
|  | */ | 
|  |  | 
|  | // formatError is returned by some operations if the data does | 
|  | // not have the correct format for an object file. | 
|  | type formatError struct { | 
|  | off int | 
|  | msg string | 
|  | val any | 
|  | } | 
|  |  | 
|  | func (e *formatError) Error() string { | 
|  | msg := e.msg | 
|  | if e.val != nil { | 
|  | msg += fmt.Sprintf(" '%v'", e.val) | 
|  | } | 
|  | msg += fmt.Sprintf(" in record at byte %#x", e.off) | 
|  | return msg | 
|  | } | 
|  |  | 
|  | // Open opens the named file using os.Open and prepares it for use as a Plan 9 a.out binary. | 
|  | func Open(name string) (*File, error) { | 
|  | f, err := os.Open(name) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | ff, err := NewFile(f) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, err | 
|  | } | 
|  | ff.closer = f | 
|  | return ff, nil | 
|  | } | 
|  |  | 
|  | // Close closes the File. | 
|  | // If the File was created using NewFile directly instead of Open, | 
|  | // Close has no effect. | 
|  | func (f *File) Close() error { | 
|  | var err error | 
|  | if f.closer != nil { | 
|  | err = f.closer.Close() | 
|  | f.closer = nil | 
|  | } | 
|  | return err | 
|  | } | 
|  |  | 
|  | func parseMagic(magic []byte) (uint32, error) { | 
|  | m := binary.BigEndian.Uint32(magic) | 
|  | switch m { | 
|  | case Magic386, MagicAMD64, MagicARM: | 
|  | return m, nil | 
|  | } | 
|  | return 0, &formatError{0, "bad magic number", magic} | 
|  | } | 
|  |  | 
|  | // NewFile creates a new File for accessing a Plan 9 binary in an underlying reader. | 
|  | // The Plan 9 binary is expected to start at position 0 in the ReaderAt. | 
|  | func NewFile(r io.ReaderAt) (*File, error) { | 
|  | sr := io.NewSectionReader(r, 0, 1<<63-1) | 
|  | // Read and decode Plan 9 magic | 
|  | var magic [4]byte | 
|  | if _, err := r.ReadAt(magic[:], 0); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | _, err := parseMagic(magic[:]) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | ph := new(prog) | 
|  | if err := binary.Read(sr, binary.BigEndian, ph); err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | f := &File{FileHeader: FileHeader{ | 
|  | Magic:       ph.Magic, | 
|  | Bss:         ph.Bss, | 
|  | Entry:       uint64(ph.Entry), | 
|  | PtrSize:     4, | 
|  | LoadAddress: 0x1000, | 
|  | HdrSize:     4 * 8, | 
|  | }} | 
|  |  | 
|  | if ph.Magic&Magic64 != 0 { | 
|  | if err := binary.Read(sr, binary.BigEndian, &f.Entry); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | f.PtrSize = 8 | 
|  | f.LoadAddress = 0x200000 | 
|  | f.HdrSize += 8 | 
|  | } | 
|  |  | 
|  | var sects = []struct { | 
|  | name string | 
|  | size uint32 | 
|  | }{ | 
|  | {"text", ph.Text}, | 
|  | {"data", ph.Data}, | 
|  | {"syms", ph.Syms}, | 
|  | {"spsz", ph.Spsz}, | 
|  | {"pcsz", ph.Pcsz}, | 
|  | } | 
|  |  | 
|  | f.Sections = make([]*Section, 5) | 
|  |  | 
|  | off := uint32(f.HdrSize) | 
|  |  | 
|  | for i, sect := range sects { | 
|  | s := new(Section) | 
|  | s.SectionHeader = SectionHeader{ | 
|  | Name:   sect.name, | 
|  | Size:   sect.size, | 
|  | Offset: off, | 
|  | } | 
|  | off += sect.size | 
|  | s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size)) | 
|  | s.ReaderAt = s.sr | 
|  | f.Sections[i] = s | 
|  | } | 
|  |  | 
|  | return f, nil | 
|  | } | 
|  |  | 
|  | func walksymtab(data []byte, ptrsz int, fn func(sym) error) error { | 
|  | var order binary.ByteOrder = binary.BigEndian | 
|  | var s sym | 
|  | p := data | 
|  | for len(p) >= 4 { | 
|  | // Symbol type, value. | 
|  | if len(p) < ptrsz { | 
|  | return &formatError{len(data), "unexpected EOF", nil} | 
|  | } | 
|  | // fixed-width value | 
|  | if ptrsz == 8 { | 
|  | s.value = order.Uint64(p[0:8]) | 
|  | p = p[8:] | 
|  | } else { | 
|  | s.value = uint64(order.Uint32(p[0:4])) | 
|  | p = p[4:] | 
|  | } | 
|  |  | 
|  | typ := p[0] & 0x7F | 
|  | s.typ = typ | 
|  | p = p[1:] | 
|  |  | 
|  | // Name. | 
|  | var i int | 
|  | var nnul int | 
|  | for i = 0; i < len(p); i++ { | 
|  | if p[i] == 0 { | 
|  | nnul = 1 | 
|  | break | 
|  | } | 
|  | } | 
|  | switch typ { | 
|  | case 'z', 'Z': | 
|  | p = p[i+nnul:] | 
|  | for i = 0; i+2 <= len(p); i += 2 { | 
|  | if p[i] == 0 && p[i+1] == 0 { | 
|  | nnul = 2 | 
|  | break | 
|  | } | 
|  | } | 
|  | } | 
|  | if len(p) < i+nnul { | 
|  | return &formatError{len(data), "unexpected EOF", nil} | 
|  | } | 
|  | s.name = p[0:i] | 
|  | i += nnul | 
|  | p = p[i:] | 
|  |  | 
|  | fn(s) | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // NewTable decodes the Go symbol table in data, | 
|  | // returning an in-memory representation. | 
|  | func newTable(symtab []byte, ptrsz int) ([]Sym, error) { | 
|  | var n int | 
|  | err := walksymtab(symtab, ptrsz, func(s sym) error { | 
|  | n++ | 
|  | return nil | 
|  | }) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | fname := make(map[uint16]string) | 
|  | syms := make([]Sym, 0, n) | 
|  | err = walksymtab(symtab, ptrsz, func(s sym) error { | 
|  | n := len(syms) | 
|  | syms = syms[0 : n+1] | 
|  | ts := &syms[n] | 
|  | ts.Type = rune(s.typ) | 
|  | ts.Value = s.value | 
|  | switch s.typ { | 
|  | default: | 
|  | ts.Name = string(s.name) | 
|  | case 'z', 'Z': | 
|  | for i := 0; i < len(s.name); i += 2 { | 
|  | eltIdx := binary.BigEndian.Uint16(s.name[i : i+2]) | 
|  | elt, ok := fname[eltIdx] | 
|  | if !ok { | 
|  | return &formatError{-1, "bad filename code", eltIdx} | 
|  | } | 
|  | if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' { | 
|  | ts.Name += "/" | 
|  | } | 
|  | ts.Name += elt | 
|  | } | 
|  | } | 
|  | switch s.typ { | 
|  | case 'f': | 
|  | fname[uint16(s.value)] = ts.Name | 
|  | } | 
|  | return nil | 
|  | }) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | return syms, nil | 
|  | } | 
|  |  | 
|  | // ErrNoSymbols is returned by File.Symbols if there is no such section | 
|  | // in the File. | 
|  | var ErrNoSymbols = errors.New("no symbol section") | 
|  |  | 
|  | // Symbols returns the symbol table for f. | 
|  | func (f *File) Symbols() ([]Sym, error) { | 
|  | symtabSection := f.Section("syms") | 
|  | if symtabSection == nil { | 
|  | return nil, ErrNoSymbols | 
|  | } | 
|  |  | 
|  | symtab, err := symtabSection.Data() | 
|  | if err != nil { | 
|  | return nil, errors.New("cannot load symbol section") | 
|  | } | 
|  |  | 
|  | return newTable(symtab, f.PtrSize) | 
|  | } | 
|  |  | 
|  | // Section returns a section with the given name, or nil if no such | 
|  | // section exists. | 
|  | func (f *File) Section(name string) *Section { | 
|  | for _, s := range f.Sections { | 
|  | if s.Name == name { | 
|  | return s | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } |