// A Reloc describes a relocation applied to a memory image to refer
 // to an address within a particular symbol.
 type Reloc struct {
-       // The bytes at [Offset, Offset+Size) within the memory image
+       // The bytes at [Offset, Offset+Size) within the containing Sym
        // should be updated to refer to the address Add bytes after the start
        // of the symbol Sym.
        Offset int
        // The Type records the form of address expected in the bytes
        // described by the previous fields: absolute, PC-relative, and so on.
        // TODO(rsc): The interpretation of Type is not exposed by this package.
-       Type int
+       Type obj.RelocType
 }
 
 // A Var describes a variable in a function stack frame: a declared
        SymRefs    []SymID  // list of symbol names and versions referred to by this pack
        Syms       []*Sym   // symbols defined by this package
        MaxVersion int      // maximum Version in any SymID in Syms
+       Arch       string   // architecture
 }
 
 var (
 // The format of that part is defined in a comment at the top
 // of src/liblink/objfile.c.
 func (r *objReader) parseObject(prefix []byte) error {
-       // TODO(rsc): Maybe use prefix and the initial input to
-       // record the header line from the file, which would
-       // give the architecture and other version information.
-
        r.p.MaxVersion++
+       h := make([]byte, 0, 256)
+       h = append(h, prefix...)
        var c1, c2, c3 byte
        for {
                c1, c2, c3 = c2, c3, r.readByte()
+               h = append(h, c3)
                // The new export format can contain 0 bytes.
                // Don't consider them errors, only look for r.err != nil.
                if r.err != nil {
                }
        }
 
+       hs := strings.Fields(string(h))
+       if len(hs) >= 4 {
+               r.p.Arch = hs[3]
+       }
+       // TODO: extract OS + build ID if/when we need it
+
        r.readFull(r.tmp[:8])
        if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go17ld")) {
                return r.error(errCorruptObject)
                        rel := &s.Reloc[i]
                        rel.Offset = r.readInt()
                        rel.Size = r.readInt()
-                       rel.Type = r.readInt()
+                       rel.Type = obj.RelocType(r.readInt())
                        rel.Add = r.readInt()
                        rel.Sym = r.readSymID()
                }
 
        return nil
 }
+
+func (r *Reloc) String(insnOffset uint64) string {
+       delta := r.Offset - int(insnOffset)
+       s := fmt.Sprintf("[%d:%d]%s", delta, delta+r.Size, r.Type)
+       if r.Sym.Name != "" {
+               if r.Add != 0 {
+                       return fmt.Sprintf("%s:%s+%d", s, r.Sym.Name, r.Add)
+               }
+               return fmt.Sprintf("%s:%s", s, r.Sym.Name)
+       }
+       if r.Add != 0 {
+               return fmt.Sprintf("%s:%d", s, r.Add)
+       }
+       return s
+}
 
 type Reloc struct {
        Off  int32
        Siz  uint8
-       Type int32
+       Type RelocType
        Add  int64
        Sym  *LSym
 }
 
-// Reloc.type
+type RelocType int32
+
+//go:generate stringer -type=RelocType
 const (
-       R_ADDR = 1 + iota
+       R_ADDR RelocType = 1 + iota
        // R_ADDRPOWER relocates a pair of "D-form" instructions (instructions with 16-bit
        // immediates in the low half of the instruction word), usually addis followed by
        // another add or a load, inserting the "high adjusted" 16 bits of the address of
 
--- /dev/null
+// Code generated by "stringer -type=RelocType"; DO NOT EDIT
+
+package obj
+
+import "fmt"
+
+const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLS"
+
+var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 195, 206, 216, 225, 235, 249, 263, 279, 293, 307, 318, 332, 347, 364, 382, 403, 413, 424, 437}
+
+func (i RelocType) String() string {
+       i -= 1
+       if i < 0 || i >= RelocType(len(_RelocType_index)-1) {
+               return fmt.Sprintf("RelocType(%d)", i+1)
+       }
+       return _RelocType_name[_RelocType_index[i]:_RelocType_index[i+1]]
+}
 
 // Disasm is a disassembler for a given File.
 type Disasm struct {
        syms      []Sym            //symbols in file, sorted by address
-       pcln      *gosym.Table     // pcln table
+       pcln      Liner            // pcln table
        text      []byte           // bytes of text segment (actual instructions)
        textStart uint64           // start PC of text
        textEnd   uint64           // end PC of text
        for _, sym := range d.syms {
                symStart := sym.Addr
                symEnd := sym.Addr + uint64(sym.Size)
+               relocs := sym.Relocs
                if sym.Code != 'T' && sym.Code != 't' ||
                        symStart < d.textStart ||
                        symEnd <= start || end <= symStart ||
                        symEnd = end
                }
                code := d.text[:end-d.textStart]
-               d.Decode(symStart, symEnd, func(pc, size uint64, file string, line int, text string) {
+               d.Decode(symStart, symEnd, relocs, func(pc, size uint64, file string, line int, text string) {
                        i := pc - d.textStart
                        fmt.Fprintf(tw, "\t%s:%d\t%#x\t", base(file), line, pc)
                        if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" {
 }
 
 // Decode disassembles the text segment range [start, end), calling f for each instruction.
-func (d *Disasm) Decode(start, end uint64, f func(pc, size uint64, file string, line int, text string)) {
+func (d *Disasm) Decode(start, end uint64, relocs []Reloc, f func(pc, size uint64, file string, line int, text string)) {
        if start < d.textStart {
                start = d.textStart
        }
                i := pc - d.textStart
                text, size := d.disasm(code[i:], pc, lookup)
                file, line, _ := d.pcln.PCToLine(pc)
+               text += "\t"
+               first := true
+               for len(relocs) > 0 && relocs[0].Addr < i+uint64(size) {
+                       if first {
+                               first = false
+                       } else {
+                               text += " "
+                       }
+                       text += relocs[0].Stringer.String(pc - start)
+                       relocs = relocs[1:]
+               }
                f(pc, uint64(size), file, line, text)
                pc += uint64(size)
        }
        "ppc64le": binary.LittleEndian,
        "s390x":   binary.BigEndian,
 }
+
+type Liner interface {
+       // Given a pc, returns the corresponding file, line, and function data.
+       // If unknown, returns "",0,nil.
+       PCToLine(uint64) (string, int, *gosym.Func)
+}
 
 
 import (
        "cmd/internal/goobj"
+       "cmd/internal/sys"
        "debug/dwarf"
+       "debug/gosym"
        "errors"
        "fmt"
        "os"
 
 type goobjFile struct {
        goobj *goobj.Package
+       f     *os.File // the underlying .o or .a file
 }
 
 func openGoobj(r *os.File) (rawFile, error) {
        if err != nil {
                return nil, err
        }
-       return &goobjFile{f}, nil
+       return &goobjFile{goobj: f, f: r}, nil
 }
 
 func goobjName(id goobj.SymID) string {
                if s.Version != 0 {
                        sym.Code += 'a' - 'A'
                }
+               for i, r := range s.Reloc {
+                       sym.Relocs = append(sym.Relocs, Reloc{Addr: uint64(s.Data.Offset) + uint64(r.Offset), Size: uint64(r.Size), Stringer: &s.Reloc[i]})
+               }
                syms = append(syms, sym)
        }
 
        return syms, nil
 }
 
-// pcln does not make sense for Go object files, because each
-// symbol has its own individual pcln table, so there is no global
-// space of addresses to map.
 func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+       // Should never be called.  We implement Liner below, callers
+       // should use that instead.
        return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
 }
 
-// text does not make sense for Go object files, because
-// each function has a separate section.
+// Find returns the file name, line, and function data for the given pc.
+// Returns "",0,nil if unknown.
+// This function implements the Liner interface in preference to pcln() above.
+func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
+       // TODO: this is really inefficient.  Binary search?  Memoize last result?
+       var arch *sys.Arch
+       for _, a := range sys.Archs {
+               if a.Name == f.goobj.Arch {
+                       arch = a
+                       break
+               }
+       }
+       if arch == nil {
+               return "", 0, nil
+       }
+       for _, s := range f.goobj.Syms {
+               if pc < uint64(s.Data.Offset) || pc >= uint64(s.Data.Offset+s.Data.Size) {
+                       continue
+               }
+               if s.Func == nil {
+                       return "", 0, nil
+               }
+               pcfile := make([]byte, s.Func.PCFile.Size)
+               _, err := f.f.ReadAt(pcfile, s.Func.PCFile.Offset)
+               if err != nil {
+                       return "", 0, nil
+               }
+               fileID := gosym.PCValue(pcfile, pc-uint64(s.Data.Offset), arch.MinLC)
+               fileName := s.Func.File[fileID]
+               pcline := make([]byte, s.Func.PCLine.Size)
+               _, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset)
+               if err != nil {
+                       return "", 0, nil
+               }
+               line := gosym.PCValue(pcline, pc-uint64(s.Data.Offset), arch.MinLC)
+               // Note: we provide only the name in the Func structure.
+               // We could provide more if needed.
+               return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: s.Name}}
+       }
+       return "", 0, nil
+}
+
+// We treat the whole object file as the text section.
 func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
-       return 0, nil, fmt.Errorf("text not available in go object file")
+       var info os.FileInfo
+       info, err = f.f.Stat()
+       if err != nil {
+               return
+       }
+       text = make([]byte, info.Size())
+       _, err = f.f.ReadAt(text, 0)
+       return
 }
 
-// goarch makes sense but is not exposed in debug/goobj's API,
-// and we don't need it yet for any users of internal/objfile.
 func (f *goobjFile) goarch() string {
-       return "GOARCH unimplemented for debug/goobj files"
+       return f.goobj.Arch
 }
 
 func (f *goobjFile) loadAddress() (uint64, error) {
 
 
 // A Sym is a symbol defined in an executable file.
 type Sym struct {
-       Name string // symbol name
-       Addr uint64 // virtual address of symbol
-       Size int64  // size in bytes
-       Code rune   // nm code (T for text, D for data, and so on)
-       Type string // XXX?
+       Name   string  // symbol name
+       Addr   uint64  // virtual address of symbol
+       Size   int64   // size in bytes
+       Code   rune    // nm code (T for text, D for data, and so on)
+       Type   string  // XXX?
+       Relocs []Reloc // in increasing Addr order
+}
+
+type Reloc struct {
+       Addr     uint64 // Address of first byte that reloc applies to.
+       Size     uint64 // Number of bytes
+       Stringer RelocStringer
+}
+
+type RelocStringer interface {
+       // insnOffset is the offset of the instruction containing the relocation
+       // from the start of the symbol containing the relocation.
+       String(insnOffset uint64) string
 }
 
 var openers = []func(*os.File) (rawFile, error){
 func (x byAddr) Len() int           { return len(x) }
 func (x byAddr) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
 
-func (f *File) PCLineTable() (*gosym.Table, error) {
+func (f *File) PCLineTable() (Liner, error) {
+       // If the raw file implements Liner directly, use that.
+       // Currently, only Go intermediate objects and archives (goobj) use this path.
+       if pcln, ok := f.raw.(Liner); ok {
+               return pcln, nil
+       }
+       // Otherwise, read the pcln tables and build a Liner out of that.
        textStart, symtab, pclntab, err := f.raw.pcln()
        if err != nil {
                return nil, err
 
        RegSize:   8,
        MinLC:     2,
 }
+
+var Archs = [...]*Arch{
+       Arch386,
+       ArchAMD64,
+       ArchAMD64P32,
+       ArchARM,
+       ArchARM64,
+       ArchMIPS64,
+       ArchMIPS64LE,
+       ArchPPC64,
+       ArchPPC64LE,
+       ArchS390X,
+}
 
        return t
 }
 
-func addpltreloc(ctxt *ld.Link, plt *ld.Symbol, got *ld.Symbol, sym *ld.Symbol, typ int) *ld.Reloc {
+func addpltreloc(ctxt *ld.Link, plt *ld.Symbol, got *ld.Symbol, sym *ld.Symbol, typ obj.RelocType) *ld.Reloc {
        r := ld.Addrel(plt)
        r.Sym = got
        r.Off = int32(plt.Size)
        r.Siz = 4
-       r.Type = int32(typ)
+       r.Type = typ
        r.Add = int64(sym.Got) - 8
 
        plt.Attr |= ld.AttrReachable
 
                                rp.Sym = sym.sym
                        }
 
-                       rp.Type = 256 + int32(info)
+                       rp.Type = 256 + obj.RelocType(info)
                        rp.Siz = relSize(ctxt, pn, uint32(info))
                        if rela != 0 {
                                rp.Add = int64(add)
 
                        }
 
                        rp.Siz = rel.length
-                       rp.Type = 512 + (int32(rel.type_) << 1) + int32(rel.pcrel)
+                       rp.Type = 512 + (obj.RelocType(rel.type_) << 1) + obj.RelocType(rel.pcrel)
                        rp.Off = int32(rel.addr)
 
                        // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0).
 
 
 import (
        "bufio"
+       "cmd/internal/obj"
        "cmd/internal/sys"
        "debug/elf"
        "fmt"
        Off     int32
        Siz     uint8
        Done    uint8
-       Type    int32
+       Type    obj.RelocType
        Variant int32
        Add     int64
        Xadd    int64
 
                        s.R[i] = Reloc{
                                Off:  r.readInt32(),
                                Siz:  r.readUint8(),
-                               Type: r.readInt32(),
+                               Type: obj.RelocType(r.readInt32()),
                                Add:  r.readInt64(),
                                Sym:  r.readSymIndex(),
                        }
 
 
 import (
        "debug/dwarf"
-       "debug/gosym"
        "flag"
        "fmt"
        "net/url"
                return nil, err
        }
        var asm []plugin.Inst
-       d.Decode(start, end, func(pc, size uint64, file string, line int, text string) {
+       d.Decode(start, end, nil, func(pc, size uint64, file string, line int, text string) {
                asm = append(asm, plugin.Inst{Addr: pc, File: file, Line: line, Text: text})
        })
        return asm, nil
        offset uint64
        sym    []objfile.Sym
        file   *objfile.File
-       pcln   *gosym.Table
+       pcln   objfile.Liner
 
        triedDwarf bool
        dwarf      *dwarf.Data
 
        return true
 }
 
+// PCValue looks up the given PC in a pc value table. target is the
+// offset of the pc from the entry point.
+func PCValue(tab []byte, target uint64, quantum int) int {
+       t := LineTable{Data: tab, quantum: uint32(quantum)}
+       return int(t.pcvalue(0, 0, target))
+}
+
 // pcvalue reports the value associated with the target pc.
 // off is the offset to the beginning of the pc-value table,
 // and entry is the start PC for the corresponding function.
 func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
-       if off == 0 {
-               return -1
-       }
        p := t.Data[off:]
 
        val := int32(-1)