package goobj2 // TODO: replace the goobj package?
 
 import (
+       "bytes"
        "cmd/internal/bio"
        "encoding/binary"
        "errors"
        "fmt"
        "io"
+       "unsafe"
 )
 
 // New object file format.
 }
 
 type Reader struct {
+       b        []byte // mmapped bytes, if not nil
+       readonly bool   // whether b is backed with read-only memory
+
        rd    io.ReaderAt
        start uint32
        h     Header // keep block offsets
        return r
 }
 
+func NewReaderFromBytes(b []byte, readonly bool) *Reader {
+       r := &Reader{b: b, readonly: readonly, rd: bytes.NewReader(b), start: 0}
+       err := r.h.Read(r)
+       if err != nil {
+               return nil
+       }
+       return r
+}
+
 func (r *Reader) BytesAt(off uint32, len int) []byte {
-       // TODO: read from mapped memory
+       if len == 0 {
+               return nil
+       }
+       if r.b != nil {
+               end := int(off) + len
+               return r.b[int(off):end:end]
+       }
        b := make([]byte, len)
-       _, err := r.rd.ReadAt(b[:], int64(r.start+off))
+       _, err := r.rd.ReadAt(b, int64(r.start+off))
        if err != nil {
                panic("corrupted input")
        }
 }
 
 func (r *Reader) uint64At(off uint32) uint64 {
-       var b [8]byte
-       n, err := r.rd.ReadAt(b[:], int64(r.start+off))
-       if n != 8 || err != nil {
-               panic("corrupted input")
-       }
-       return binary.LittleEndian.Uint64(b[:])
+       b := r.BytesAt(off, 8)
+       return binary.LittleEndian.Uint64(b)
 }
 
 func (r *Reader) int64At(off uint32) int64 {
 }
 
 func (r *Reader) uint32At(off uint32) uint32 {
-       var b [4]byte
-       n, err := r.rd.ReadAt(b[:], int64(r.start+off))
-       if n != 4 || err != nil {
-               panic("corrupted input")
-       }
-       return binary.LittleEndian.Uint32(b[:])
+       b := r.BytesAt(off, 4)
+       return binary.LittleEndian.Uint32(b)
 }
 
 func (r *Reader) int32At(off uint32) int32 {
 }
 
 func (r *Reader) uint16At(off uint32) uint16 {
-       var b [2]byte
-       n, err := r.rd.ReadAt(b[:], int64(r.start+off))
-       if n != 2 || err != nil {
-               panic("corrupted input")
-       }
-       return binary.LittleEndian.Uint16(b[:])
+       b := r.BytesAt(off, 2)
+       return binary.LittleEndian.Uint16(b)
 }
 
 func (r *Reader) uint8At(off uint32) uint8 {
-       var b [1]byte
-       n, err := r.rd.ReadAt(b[:], int64(r.start+off))
-       if n != 1 || err != nil {
-               panic("corrupted input")
-       }
+       b := r.BytesAt(off, 1)
        return b[0]
 }
 
 func (r *Reader) StringAt(off uint32) string {
-       // TODO: have some way to construct a string without copy
        l := r.uint32At(off)
+       if r.b != nil {
+               b := r.b[off+4 : off+4+l]
+               if r.readonly {
+                       return toString(b) // backed by RO memory, ok to make unsafe string
+               }
+               return string(b)
+       }
        b := make([]byte, l)
        n, err := r.rd.ReadAt(b, int64(r.start+off+4))
        if n != int(l) || err != nil {
        return string(b)
 }
 
+func toString(b []byte) string {
+       type stringHeader struct {
+               str unsafe.Pointer
+               len int
+       }
+
+       if len(b) == 0 {
+               return ""
+       }
+       ss := stringHeader{str: unsafe.Pointer(&b[0]), len: len(b)}
+       s := *(*string)(unsafe.Pointer(&ss))
+       return s
+}
+
 func (r *Reader) StringRef(off uint32) string {
        return r.StringAt(r.uint32At(off))
 }
 func (r *Reader) PcdataBase() uint32 {
        return r.h.Offsets[BlkPcdata]
 }
+
+// ReadOnly returns whether r.BytesAt returns read-only bytes.
+func (r *Reader) ReadOnly() bool {
+       return r.readonly
+}
 
 // Preload a package: add autolibs, add symbols to the symbol table.
 // Does not read symbol data yet.
 func LoadNew(l *Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
-       start := f.Offset()
-       r := goobj2.NewReader(f.File(), uint32(start))
+       roObject, readonly, err := f.Slice(uint64(length))
+       if err != nil {
+               log.Fatal("cannot read object file:", err)
+       }
+       r := goobj2.NewReaderFromBytes(roObject, readonly)
        if r == nil {
                panic("cannot read object file")
        }
                // XXX deadcode needs symbol data for type symbols. Read it now.
                if strings.HasPrefix(name, "type.") {
                        s.P = r.BytesAt(r.DataOff(i), r.DataSize(i))
+                       s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
                        s.Size = int64(osym.Siz)
                }
 
 
                // Symbol data
                s.P = r.BytesAt(r.DataOff(i), datasize)
+               s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
 
                // Aux symbol info
                isym := -1