mirror of
https://github.com/Binject/debug
synced 2026-06-08 10:28:33 +00:00
ae710b60a7
This is a subset of https://golang.org/cl/20022 with only the copyright header lines, so the next CL will be smaller and more reviewable. Go policy has been single space after periods in comments for some time. The copyright header template at: https://golang.org/doc/contribute.html#copyright also uses a single space. Make them all consistent. Change-Id: Icc26c6b8495c3820da6b171ca96a74701b4a01b0 Reviewed-on: https://go-review.googlesource.com/20111 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
329 lines
6.7 KiB
Go
329 lines
6.7 KiB
Go
// 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 interface{}
|
|
}
|
|
|
|
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:]
|
|
}
|
|
|
|
var typ byte
|
|
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
|
|
}
|
|
|
|
// Symbols returns the symbol table for f.
|
|
func (f *File) Symbols() ([]Sym, error) {
|
|
symtabSection := f.Section("syms")
|
|
if symtabSection == nil {
|
|
return nil, errors.New("no symbol section")
|
|
}
|
|
|
|
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
|
|
}
|