[dev.link] all: merge branch 'master' into dev.link

Change-Id: Ic66b5138f3ecd9e9a48d7ab05782297c06e4a5b5
diff --git a/src/cmd/internal/goobj/funcinfo.go b/src/cmd/internal/goobj/funcinfo.go
index e0e6068..2cca8f6 100644
--- a/src/cmd/internal/goobj/funcinfo.go
+++ b/src/cmd/internal/goobj/funcinfo.go
@@ -23,12 +23,11 @@
 	Locals uint32
 	FuncID objabi.FuncID
 
-	Pcsp        uint32
-	Pcfile      uint32
-	Pcline      uint32
-	Pcinline    uint32
-	Pcdata      []uint32
-	PcdataEnd   uint32
+	Pcsp        SymRef
+	Pcfile      SymRef
+	Pcline      SymRef
+	Pcinline    SymRef
+	Pcdata      []SymRef
 	Funcdataoff []uint32
 	File        []CUFileIndex
 
@@ -41,20 +40,24 @@
 		binary.LittleEndian.PutUint32(b[:], x)
 		w.Write(b[:])
 	}
+	writeSymRef := func(s SymRef) {
+		writeUint32(s.PkgIdx)
+		writeUint32(s.SymIdx)
+	}
 
 	writeUint32(a.Args)
 	writeUint32(a.Locals)
 	writeUint32(uint32(a.FuncID))
 
-	writeUint32(a.Pcsp)
-	writeUint32(a.Pcfile)
-	writeUint32(a.Pcline)
-	writeUint32(a.Pcinline)
+	writeSymRef(a.Pcsp)
+	writeSymRef(a.Pcfile)
+	writeSymRef(a.Pcline)
+	writeSymRef(a.Pcinline)
 	writeUint32(uint32(len(a.Pcdata)))
-	for _, x := range a.Pcdata {
-		writeUint32(x)
+	for _, sym := range a.Pcdata {
+		writeSymRef(sym)
 	}
-	writeUint32(a.PcdataEnd)
+
 	writeUint32(uint32(len(a.Funcdataoff)))
 	for _, x := range a.Funcdataoff {
 		writeUint32(x)
@@ -75,21 +78,23 @@
 		b = b[4:]
 		return x
 	}
+	readSymIdx := func() SymRef {
+		return SymRef{readUint32(), readUint32()}
+	}
 
 	a.Args = readUint32()
 	a.Locals = readUint32()
 	a.FuncID = objabi.FuncID(readUint32())
 
-	a.Pcsp = readUint32()
-	a.Pcfile = readUint32()
-	a.Pcline = readUint32()
-	a.Pcinline = readUint32()
-	pcdatalen := readUint32()
-	a.Pcdata = make([]uint32, pcdatalen)
+	a.Pcsp = readSymIdx()
+	a.Pcfile = readSymIdx()
+	a.Pcline = readSymIdx()
+	a.Pcinline = readSymIdx()
+	a.Pcdata = make([]SymRef, readUint32())
 	for i := range a.Pcdata {
-		a.Pcdata[i] = readUint32()
+		a.Pcdata[i] = readSymIdx()
 	}
-	a.PcdataEnd = readUint32()
+
 	funcdataofflen := readUint32()
 	a.Funcdataoff = make([]uint32, funcdataofflen)
 	for i := range a.Funcdataoff {
@@ -127,11 +132,13 @@
 func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths {
 	var result FuncInfoLengths
 
-	const numpcdataOff = 28
+	// Offset to the number of pcdata values. This value is determined by counting
+	// the number of bytes until we write pcdata to the file.
+	const numpcdataOff = 44
 	result.NumPcdata = binary.LittleEndian.Uint32(b[numpcdataOff:])
 	result.PcdataOff = numpcdataOff + 4
 
-	numfuncdataoffOff := result.PcdataOff + 4*(result.NumPcdata+1)
+	numfuncdataoffOff := result.PcdataOff + 8*result.NumPcdata
 	result.NumFuncdataoff = binary.LittleEndian.Uint32(b[numfuncdataoffOff:])
 	result.FuncdataoffOff = numfuncdataoffOff + 4
 
@@ -154,29 +161,28 @@
 
 func (*FuncInfo) ReadFuncID(b []byte) uint32 { return binary.LittleEndian.Uint32(b[8:]) }
 
-// return start and end offsets.
-func (*FuncInfo) ReadPcsp(b []byte) (uint32, uint32) {
-	return binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])
+func (*FuncInfo) ReadPcsp(b []byte) SymRef {
+	return SymRef{binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])}
 }
 
-// return start and end offsets.
-func (*FuncInfo) ReadPcfile(b []byte) (uint32, uint32) {
-	return binary.LittleEndian.Uint32(b[16:]), binary.LittleEndian.Uint32(b[20:])
+func (*FuncInfo) ReadPcfile(b []byte) SymRef {
+	return SymRef{binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:])}
 }
 
-// return start and end offsets.
-func (*FuncInfo) ReadPcline(b []byte) (uint32, uint32) {
-	return binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:])
+func (*FuncInfo) ReadPcline(b []byte) SymRef {
+	return SymRef{binary.LittleEndian.Uint32(b[28:]), binary.LittleEndian.Uint32(b[32:])}
 }
 
-// return start and end offsets.
-func (*FuncInfo) ReadPcinline(b []byte, pcdataoffset uint32) (uint32, uint32) {
-	return binary.LittleEndian.Uint32(b[24:]), binary.LittleEndian.Uint32(b[pcdataoffset:])
+func (*FuncInfo) ReadPcinline(b []byte) SymRef {
+	return SymRef{binary.LittleEndian.Uint32(b[36:]), binary.LittleEndian.Uint32(b[40:])}
 }
 
-// return start and end offsets.
-func (*FuncInfo) ReadPcdata(b []byte, pcdataoffset uint32, k uint32) (uint32, uint32) {
-	return binary.LittleEndian.Uint32(b[pcdataoffset+4*k:]), binary.LittleEndian.Uint32(b[pcdataoffset+4+4*k:])
+func (*FuncInfo) ReadPcdata(b []byte) []SymRef {
+	syms := make([]SymRef, binary.LittleEndian.Uint32(b[44:]))
+	for i := range syms {
+		syms[i] = SymRef{binary.LittleEndian.Uint32(b[48+i*8:]), binary.LittleEndian.Uint32(b[52+i*8:])}
+	}
+	return syms
 }
 
 func (*FuncInfo) ReadFuncdataoff(b []byte, funcdataofffoff uint32, k uint32) int64 {
diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go
index 5d4a253..9a64f96 100644
--- a/src/cmd/internal/goobj/objfile.go
+++ b/src/cmd/internal/goobj/objfile.go
@@ -421,8 +421,11 @@
 	AuxDwarfLoc
 	AuxDwarfRanges
 	AuxDwarfLines
-
-	// TODO: more. Pcdata?
+	AuxPcsp
+	AuxPcfile
+	AuxPcline
+	AuxPcinline
+	AuxPcdata
 )
 
 func (a *Aux) Type() uint8 { return a[0] }
@@ -827,11 +830,6 @@
 	return r.BytesAt(base+off, int(end-off))
 }
 
-// AuxDataBase returns the base offset of the aux data block.
-func (r *Reader) PcdataBase() uint32 {
-	return r.h.Offsets[BlkPcdata]
-}
-
 // NRefName returns the number of referenced symbol names.
 func (r *Reader) NRefName() int {
 	return int(r.h.Offsets[BlkRefName+1]-r.h.Offsets[BlkRefName]) / RefNameSize
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 311e5ae..2660a56 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -621,11 +621,12 @@
 }
 
 type Pcln struct {
-	Pcsp        Pcdata
-	Pcfile      Pcdata
-	Pcline      Pcdata
-	Pcinline    Pcdata
-	Pcdata      []Pcdata
+	// Aux symbols for pcln
+	Pcsp        *LSym
+	Pcfile      *LSym
+	Pcline      *LSym
+	Pcinline    *LSym
+	Pcdata      []*LSym
 	Funcdata    []*LSym
 	Funcdataoff []int64
 	UsedFiles   map[goobj.CUFileIndex]struct{} // file indices used while generating pcfile
@@ -647,10 +648,6 @@
 	Gotype  *LSym
 }
 
-type Pcdata struct {
-	P []byte
-}
-
 // Link holds the context for writing object code from a compiler
 // to be linker input or for reading that input into the linker.
 type Link struct {
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index 7bc4f49..a2bbdff 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -185,7 +185,11 @@
 	// Pcdata
 	h.Offsets[goobj.BlkPcdata] = w.Offset()
 	for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms
-		if s.Func != nil {
+		// Because of the phase order, it's possible that we try to write an invalid
+		// object file, and the Pcln variables haven't been filled in. As such, we
+		// need to check that Pcsp exists, and assume the other pcln variables exist
+		// as well. Tests like test/fixedbugs/issue22200.go demonstrate this issue.
+		if s.Func != nil && s.Func.Pcln.Pcsp != nil {
 			pc := &s.Func.Pcln
 			w.Bytes(pc.Pcsp.P)
 			w.Bytes(pc.Pcfile.P)
@@ -372,10 +376,22 @@
 // hashed symbols.
 func (w *writer) contentHash(s *LSym) goobj.HashType {
 	h := sha1.New()
+	var tmp [14]byte
+
+	// Include the size of the symbol in the hash.
+	// This preserves the length of symbols, preventing the following two symbols
+	// from hashing the same:
+	//
+	//    [2]int{1,2} ≠ [10]int{1,2,0,0,0...}
+	//
+	// In this case, if the smaller symbol is alive, the larger is not kept unless
+	// needed.
+	binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size))
+	h.Write(tmp[:8])
+
 	// The compiler trims trailing zeros _sometimes_. We just do
 	// it always.
 	h.Write(bytes.TrimRight(s.P, "\x00"))
-	var tmp [14]byte
 	for i := range s.R {
 		r := &s.R[i]
 		binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off))
@@ -466,6 +482,22 @@
 		if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
 			w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym)
 		}
+		if s.Func.Pcln.Pcsp != nil && s.Func.Pcln.Pcsp.Size != 0 {
+			w.aux1(goobj.AuxPcsp, s.Func.Pcln.Pcsp)
+		}
+		if s.Func.Pcln.Pcfile != nil && s.Func.Pcln.Pcfile.Size != 0 {
+			w.aux1(goobj.AuxPcfile, s.Func.Pcln.Pcfile)
+		}
+		if s.Func.Pcln.Pcline != nil && s.Func.Pcln.Pcline.Size != 0 {
+			w.aux1(goobj.AuxPcline, s.Func.Pcln.Pcline)
+		}
+		if s.Func.Pcln.Pcinline != nil && s.Func.Pcln.Pcinline.Size != 0 {
+			w.aux1(goobj.AuxPcinline, s.Func.Pcln.Pcinline)
+		}
+		for _, pcSym := range s.Func.Pcln.Pcdata {
+			w.aux1(goobj.AuxPcdata, pcSym)
+		}
+
 	}
 }
 
@@ -547,6 +579,19 @@
 		if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
 			n++
 		}
+		if s.Func.Pcln.Pcsp != nil && s.Func.Pcln.Pcsp.Size != 0 {
+			n++
+		}
+		if s.Func.Pcln.Pcfile != nil && s.Func.Pcln.Pcfile.Size != 0 {
+			n++
+		}
+		if s.Func.Pcln.Pcline != nil && s.Func.Pcln.Pcline.Size != 0 {
+			n++
+		}
+		if s.Func.Pcln.Pcinline != nil && s.Func.Pcln.Pcinline.Size != 0 {
+			n++
+		}
+		n += len(s.Func.Pcln.Pcdata)
 	}
 	return n
 }
@@ -554,7 +599,17 @@
 // generate symbols for FuncInfo.
 func genFuncInfoSyms(ctxt *Link) {
 	infosyms := make([]*LSym, 0, len(ctxt.Text))
-	var pcdataoff uint32
+	hashedsyms := make([]*LSym, 0, 4*len(ctxt.Text))
+	preparePcSym := func(s *LSym) *LSym {
+		if s == nil {
+			return s
+		}
+		s.PkgIdx = goobj.PkgIdxHashed
+		s.SymIdx = int32(len(hashedsyms) + len(ctxt.hasheddefs))
+		s.Set(AttrIndexed, true)
+		hashedsyms = append(hashedsyms, s)
+		return s
+	}
 	var b bytes.Buffer
 	symidx := int32(len(ctxt.defs))
 	for _, s := range ctxt.Text {
@@ -567,20 +622,14 @@
 			FuncID: objabi.FuncID(s.Func.FuncID),
 		}
 		pc := &s.Func.Pcln
-		o.Pcsp = pcdataoff
-		pcdataoff += uint32(len(pc.Pcsp.P))
-		o.Pcfile = pcdataoff
-		pcdataoff += uint32(len(pc.Pcfile.P))
-		o.Pcline = pcdataoff
-		pcdataoff += uint32(len(pc.Pcline.P))
-		o.Pcinline = pcdataoff
-		pcdataoff += uint32(len(pc.Pcinline.P))
-		o.Pcdata = make([]uint32, len(pc.Pcdata))
-		for i, pcd := range pc.Pcdata {
-			o.Pcdata[i] = pcdataoff
-			pcdataoff += uint32(len(pcd.P))
+		o.Pcsp = makeSymRef(preparePcSym(pc.Pcsp))
+		o.Pcfile = makeSymRef(preparePcSym(pc.Pcfile))
+		o.Pcline = makeSymRef(preparePcSym(pc.Pcline))
+		o.Pcinline = makeSymRef(preparePcSym(pc.Pcinline))
+		o.Pcdata = make([]goobj.SymRef, len(pc.Pcdata))
+		for i, pcSym := range pc.Pcdata {
+			o.Pcdata[i] = makeSymRef(preparePcSym(pcSym))
 		}
-		o.PcdataEnd = pcdataoff
 		o.Funcdataoff = make([]uint32, len(pc.Funcdataoff))
 		for i, x := range pc.Funcdataoff {
 			o.Funcdataoff[i] = uint32(x)
@@ -630,9 +679,9 @@
 		}
 	}
 	ctxt.defs = append(ctxt.defs, infosyms...)
+	ctxt.hasheddefs = append(ctxt.hasheddefs, hashedsyms...)
 }
 
-// debugDumpAux is a dumper for selected aux symbols.
 func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) {
 	// Most aux symbols (ex: funcdata) are not interesting--
 	// pick out just the DWARF ones for now.
diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go
index bffeda0..ce0d371 100644
--- a/src/cmd/internal/obj/pcln.go
+++ b/src/cmd/internal/obj/pcln.go
@@ -6,6 +6,7 @@
 
 import (
 	"cmd/internal/goobj"
+	"cmd/internal/objabi"
 	"encoding/binary"
 	"log"
 )
@@ -14,16 +15,19 @@
 // returned by valfunc parameterized by arg. The invocation of valfunc to update the
 // current value is, for each p,
 //
-//	val = valfunc(func, val, p, 0, arg);
-//	record val as value at p->pc;
-//	val = valfunc(func, val, p, 1, arg);
+//	sym = valfunc(func, p, 0, arg);
+//	record sym.P as value at p->pc;
+//	sym = valfunc(func, p, 1, arg);
 //
 // where func is the function, val is the current value, p is the instruction being
 // considered, and arg can be used to further parameterize valfunc.
-func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) {
+func funcpctab(ctxt *Link, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) *LSym {
 	dbg := desc == ctxt.Debugpcln
-
-	dst.P = dst.P[:0]
+	dst := []byte{}
+	sym := &LSym{
+		Type:      objabi.SRODATA,
+		Attribute: AttrContentAddressable,
+	}
 
 	if dbg {
 		ctxt.Logf("funcpctab %s [valfunc=%s]\n", func_.Name, desc)
@@ -32,7 +36,8 @@
 	val := int32(-1)
 	oldval := val
 	if func_.Func.Text == nil {
-		return
+		// Return the emtpy symbol we've built so far.
+		return sym
 	}
 
 	pc := func_.Func.Text.Pc
@@ -88,13 +93,13 @@
 		if started {
 			pcdelta := (p.Pc - pc) / int64(ctxt.Arch.MinLC)
 			n := binary.PutUvarint(buf, uint64(pcdelta))
-			dst.P = append(dst.P, buf[:n]...)
+			dst = append(dst, buf[:n]...)
 			pc = p.Pc
 		}
 
 		delta := val - oldval
 		n := binary.PutVarint(buf, int64(delta))
-		dst.P = append(dst.P, buf[:n]...)
+		dst = append(dst, buf[:n]...)
 		oldval = val
 		started = true
 		val = valfunc(ctxt, func_, val, p, 1, arg)
@@ -109,18 +114,22 @@
 			ctxt.Diag("negative pc offset: %v", v)
 		}
 		n := binary.PutUvarint(buf, uint64(v))
-		dst.P = append(dst.P, buf[:n]...)
+		dst = append(dst, buf[:n]...)
 		// add terminating varint-encoded 0, which is just 0
-		dst.P = append(dst.P, 0)
+		dst = append(dst, 0)
 	}
 
 	if dbg {
-		ctxt.Logf("wrote %d bytes to %p\n", len(dst.P), dst)
-		for _, p := range dst.P {
+		ctxt.Logf("wrote %d bytes to %p\n", len(dst), dst)
+		for _, p := range dst {
 			ctxt.Logf(" %02x", p)
 		}
 		ctxt.Logf("\n")
 	}
+
+	sym.Size = int64(len(dst))
+	sym.P = dst
+	return sym
 }
 
 // pctofileline computes either the file number (arg == 0)
@@ -268,15 +277,14 @@
 		}
 	}
 
-	pcln.Pcdata = make([]Pcdata, npcdata)
-	pcln.Pcdata = pcln.Pcdata[:npcdata]
+	pcln.Pcdata = make([]*LSym, npcdata)
 	pcln.Funcdata = make([]*LSym, nfuncdata)
 	pcln.Funcdataoff = make([]int64, nfuncdata)
 	pcln.Funcdataoff = pcln.Funcdataoff[:nfuncdata]
 
-	funcpctab(ctxt, &pcln.Pcsp, cursym, "pctospadj", pctospadj, nil)
-	funcpctab(ctxt, &pcln.Pcfile, cursym, "pctofile", pctofileline, pcln)
-	funcpctab(ctxt, &pcln.Pcline, cursym, "pctoline", pctofileline, nil)
+	pcln.Pcsp = funcpctab(ctxt, cursym, "pctospadj", pctospadj, nil)
+	pcln.Pcfile = funcpctab(ctxt, cursym, "pctofile", pctofileline, pcln)
+	pcln.Pcline = funcpctab(ctxt, cursym, "pctoline", pctofileline, nil)
 
 	// Check that all the Progs used as inline markers are still reachable.
 	// See issue #40473.
@@ -294,7 +302,7 @@
 	}
 
 	pcinlineState := new(pcinlineState)
-	funcpctab(ctxt, &pcln.Pcinline, cursym, "pctoinline", pcinlineState.pctoinline, nil)
+	pcln.Pcinline = funcpctab(ctxt, cursym, "pctoinline", pcinlineState.pctoinline, nil)
 	for _, inlMark := range cursym.Func.InlMarks {
 		pcinlineState.setParentPC(ctxt, int(inlMark.id), int32(inlMark.p.Pc))
 	}
@@ -324,9 +332,14 @@
 	// pcdata.
 	for i := 0; i < npcdata; i++ {
 		if (havepc[i/32]>>uint(i%32))&1 == 0 {
-			continue
+			// use an empty symbol.
+			pcln.Pcdata[i] = &LSym{
+				Type:      objabi.SRODATA,
+				Attribute: AttrContentAddressable,
+			}
+		} else {
+			pcln.Pcdata[i] = funcpctab(ctxt, cursym, "pctopcdata", pctopcdata, interface{}(uint32(i)))
 		}
-		funcpctab(ctxt, &pcln.Pcdata[i], cursym, "pctopcdata", pctopcdata, interface{}(uint32(i)))
 	}
 
 	// funcdata
diff --git a/src/cmd/internal/objfile/goobj.go b/src/cmd/internal/objfile/goobj.go
index af9ada3..7f74a82 100644
--- a/src/cmd/internal/objfile/goobj.go
+++ b/src/cmd/internal/objfile/goobj.go
@@ -234,7 +234,15 @@
 	if f.arch == nil {
 		return "", 0, nil
 	}
-	pcdataBase := r.PcdataBase()
+	getSymData := func(s goobj.SymRef) []byte {
+		if s.PkgIdx != goobj.PkgIdxHashed {
+			// We don't need the data for non-hashed symbols, yet.
+			panic("not supported")
+		}
+		i := uint32(s.SymIdx + uint32(r.NSym()+r.NHashed64def()))
+		return r.BytesAt(r.DataOff(i), r.DataSize(i))
+	}
+
 	ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef())
 	for i := uint32(0); i < ndef; i++ {
 		osym := r.Sym(i)
@@ -260,11 +268,9 @@
 		b := r.BytesAt(r.DataOff(isym), r.DataSize(isym))
 		var info *goobj.FuncInfo
 		lengths := info.ReadFuncInfoLengths(b)
-		off, end := info.ReadPcline(b)
-		pcline := r.BytesAt(pcdataBase+off, int(end-off))
+		pcline := getSymData(info.ReadPcline(b))
 		line := int(pcValue(pcline, pc-addr, f.arch))
-		off, end = info.ReadPcfile(b)
-		pcfile := r.BytesAt(pcdataBase+off, int(end-off))
+		pcfile := getSymData(info.ReadPcfile(b))
 		fileID := pcValue(pcfile, pc-addr, f.arch)
 		globalFileID := info.ReadFile(b, lengths.FileOff, uint32(fileID))
 		fileName := r.File(int(globalFileID))
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index dc7096e..2aecbfb 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -1923,6 +1923,9 @@
 	ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect)
 	ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pcheader", 0), sect)
 	ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.funcnametab", 0), sect)
+	ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.cutab", 0), sect)
+	ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect)
+	ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect)
 	ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab_old", 0), sect)
 	ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
 	if ctxt.HeadType == objabi.Haix {
@@ -2507,6 +2510,9 @@
 	ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr))
 	ctxt.defineInternal("runtime.pcheader", sym.SRODATA)
 	ctxt.defineInternal("runtime.funcnametab", sym.SRODATA)
+	ctxt.defineInternal("runtime.cutab", sym.SRODATA)
+	ctxt.defineInternal("runtime.filetab", sym.SRODATA)
+	ctxt.defineInternal("runtime.pctab", sym.SRODATA)
 	ctxt.defineInternal("runtime.pclntab_old", sym.SRODATA)
 	ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
 	ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr))
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index d1f2ac5..2b95ad5 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1421,7 +1421,7 @@
 			deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr))
 		}
 
-		for pcsp.Init(fpcsp); !pcsp.Done; pcsp.Next() {
+		for pcsp.Init(d.linkctxt.loader.Data(fpcsp)); !pcsp.Done; pcsp.Next() {
 			nextpc := pcsp.NextPC
 
 			// pciterinit goes up to the end of the function,
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 09c7bbf..caa4566 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -2252,7 +2252,7 @@
 	var ch1 chain
 	pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
 	ri := 0
-	for pcsp.Init(info.Pcsp()); !pcsp.Done; pcsp.Next() {
+	for pcsp.Init(ldr.Data(info.Pcsp())); !pcsp.Done; pcsp.Next() {
 		// pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
 
 		// Check stack size in effect for this span.
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index a2c8552..f26d051 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -71,7 +71,6 @@
 	LibraryByPkg map[string]*sym.Library
 	Shlibs       []Shlib
 	Textp        []loader.Sym
-	NumFilesyms  int
 	Moduledata   loader.Sym
 
 	PackageFile  map[string]string
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 30e0bdc..33476ec 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -6,45 +6,21 @@
 
 import (
 	"cmd/internal/goobj"
-	"cmd/internal/obj"
 	"cmd/internal/objabi"
-	"cmd/internal/src"
 	"cmd/internal/sys"
 	"cmd/link/internal/loader"
 	"cmd/link/internal/sym"
-	"encoding/binary"
 	"fmt"
-	"log"
-	"math"
 	"os"
 	"path/filepath"
 	"strings"
 )
 
-// oldPclnState holds state information used during pclntab generation.  Here
-// 'ldr' is just a pointer to the context's loader, 'deferReturnSym' is the
-// index for the symbol "runtime.deferreturn", 'nameToOffset' is a helper
-// function for capturing function names, 'numberedFiles' records the file
-// number assigned to a given file symbol, 'filepaths' is a slice of expanded
-// paths (indexed by file number).
-//
-// NB: This is deprecated, and will be eliminated when pclntab_old is
-// eliminated.
-type oldPclnState struct {
-	ldr            *loader.Loader
-	deferReturnSym loader.Sym
-	numberedFiles  map[string]int64
-	filepaths      []string
-}
-
 // pclntab holds the state needed for pclntab generation.
 type pclntab struct {
 	// The first and last functions found.
 	firstFunc, lastFunc loader.Sym
 
-	// The offset to the filetab.
-	filetabOffset int32
-
 	// Running total size of pclntab.
 	size int64
 
@@ -54,6 +30,9 @@
 	pcheader    loader.Sym
 	funcnametab loader.Sym
 	findfunctab loader.Sym
+	cutab       loader.Sym
+	filetab     loader.Sym
+	pctab       loader.Sym
 
 	// The number of functions + number of TEXT sections - 1. This is such an
 	// unexpected value because platforms that have more than one TEXT section
@@ -64,11 +43,8 @@
 	// On most platforms this is the number of reachable functions.
 	nfunc int32
 
-	// maps the function symbol to offset in runtime.funcnametab
-	// This doesn't need to reside in the state once pclntab_old's been
-	// deleted -- it can live in generateFuncnametab.
-	// TODO(jfaller): Delete me!
-	funcNameOffset map[loader.Sym]int32
+	// The number of filenames in runtime.filetab.
+	nfiles uint32
 }
 
 // addGeneratedSym adds a generator symbol to pclntab, returning the new Sym.
@@ -83,40 +59,26 @@
 	return s
 }
 
-func makeOldPclnState(ctxt *Link) *oldPclnState {
-	ldr := ctxt.loader
-	drs := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
-	state := &oldPclnState{
-		ldr:            ldr,
-		deferReturnSym: drs,
-		numberedFiles:  make(map[string]int64),
-		// NB: initial entry in filepaths below is to reserve the zero value,
-		// so that when we do a map lookup in numberedFiles fails, it will not
-		// return a value slot in filepaths.
-		filepaths: []string{""},
-	}
-
-	return state
-}
-
 // makePclntab makes a pclntab object, and assembles all the compilation units
-// we'll need to write pclntab.
-func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit) {
+// we'll need to write pclntab. Returns the pclntab structure, a slice of the
+// CompilationUnits we need, and a slice of the function symbols we need to
+// generate pclntab.
+func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) {
 	ldr := ctxt.loader
 
-	state := &pclntab{
-		funcNameOffset: make(map[loader.Sym]int32),
-	}
+	state := &pclntab{}
 
 	// Gather some basic stats and info.
 	seenCUs := make(map[*sym.CompilationUnit]struct{})
 	prevSect := ldr.SymSect(ctxt.Textp[0])
 	compUnits := []*sym.CompilationUnit{}
+	funcs := []loader.Sym{}
 
 	for _, s := range ctxt.Textp {
 		if !emitPcln(ctxt, s, container) {
 			continue
 		}
+		funcs = append(funcs, s)
 		state.nfunc++
 		if state.firstFunc == 0 {
 			state.firstFunc = s
@@ -142,87 +104,7 @@
 			compUnits = append(compUnits, cu)
 		}
 	}
-	return state, compUnits
-}
-
-func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 {
-	start := len(ftab.Data())
-	ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL
-	ftd := ftab.Data()
-	copy(ftd[start:], s)
-	return int32(start)
-}
-
-// numberfile assigns a file number to the file if it hasn't been assigned already.
-// This funciton looks at a CU's file at index [i], and if it's a new filename,
-// stores that filename in the global file table, and adds it to the map lookup
-// for renumbering pcfile.
-func (state *oldPclnState) numberfile(cu *sym.CompilationUnit, i goobj.CUFileIndex) int64 {
-	file := cu.FileTable[i]
-	if val, ok := state.numberedFiles[file]; ok {
-		return val
-	}
-	path := file
-	if strings.HasPrefix(path, src.FileSymPrefix) {
-		path = file[len(src.FileSymPrefix):]
-	}
-	val := int64(len(state.filepaths))
-	state.numberedFiles[file] = val
-	state.filepaths = append(state.filepaths, expandGoroot(path))
-	return val
-}
-
-func (state *oldPclnState) fileVal(cu *sym.CompilationUnit, i int32) int64 {
-	file := cu.FileTable[i]
-	if val, ok := state.numberedFiles[file]; ok {
-		return val
-	}
-	panic("should have been numbered first")
-}
-
-func (state *oldPclnState) renumberfiles(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, d *sym.Pcdata) {
-	// Give files numbers.
-	nf := fi.NumFile()
-	for i := uint32(0); i < nf; i++ {
-		state.numberfile(cu, fi.File(int(i)))
-	}
-
-	buf := make([]byte, binary.MaxVarintLen32)
-	newval := int32(-1)
-	var out sym.Pcdata
-	it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
-	for it.Init(d.P); !it.Done; it.Next() {
-		// value delta
-		oldval := it.Value
-
-		var val int32
-		if oldval == -1 {
-			val = -1
-		} else {
-			if oldval < 0 || oldval >= int32(len(cu.FileTable)) {
-				log.Fatalf("bad pcdata %d", oldval)
-			}
-			val = int32(state.fileVal(cu, oldval))
-		}
-
-		dv := val - newval
-		newval = val
-
-		// value
-		n := binary.PutVarint(buf, int64(dv))
-		out.P = append(out.P, buf[:n]...)
-
-		// pc delta
-		pc := (it.NextPC - it.PC) / it.PCScale
-		n = binary.PutUvarint(buf, uint64(pc))
-		out.P = append(out.P, buf[:n]...)
-	}
-
-	// terminating value delta
-	// we want to write varint-encoded 0, which is just 0
-	out.P = append(out.P, 0)
-
-	*d = out
+	return state, compUnits, funcs
 }
 
 // onlycsymbol looks at a symbol's name to report whether this is a
@@ -247,11 +129,13 @@
 	return !container.Has(s)
 }
 
-func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 {
+func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 {
+	ldr := ctxt.loader
+	target := ctxt.Target
 	deferreturn := uint32(0)
 	lastWasmAddr := uint32(0)
 
-	relocs := state.ldr.Relocs(s)
+	relocs := ldr.Relocs(s)
 	for ri := 0; ri < relocs.Count(); ri++ {
 		r := relocs.At(ri)
 		if target.IsWasm() && r.Type() == objabi.R_ADDR {
@@ -262,7 +146,7 @@
 			// set the resumption point to PC_B.
 			lastWasmAddr = uint32(r.Add())
 		}
-		if r.Type().IsDirectCall() && (r.Sym() == state.deferReturnSym || state.ldr.IsDeferReturnTramp(r.Sym())) {
+		if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) {
 			if target.IsWasm() {
 				deferreturn = lastWasmAddr - 1
 			} else {
@@ -295,8 +179,8 @@
 
 // genInlTreeSym generates the InlTree sym for a function with the
 // specified FuncInfo.
-func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, newState *pclntab) loader.Sym {
-	ldr := state.ldr
+func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym {
+	ldr := ctxt.loader
 	its := ldr.CreateExtSym("", 0)
 	inlTreeSym := ldr.MakeSymbolUpdater(its)
 	// Note: the generated symbol is given a type of sym.SGOFUNC, as a
@@ -308,13 +192,8 @@
 	ninl := fi.NumInlTree()
 	for i := 0; i < int(ninl); i++ {
 		call := fi.InlTree(i)
-		// Usually, call.File is already numbered since the file
-		// shows up in the Pcfile table. However, two inlined calls
-		// might overlap exactly so that only the innermost file
-		// appears in the Pcfile table. In that case, this assigns
-		// the outer file a number.
-		val := state.numberfile(cu, call.File)
-		nameoff, ok := newState.funcNameOffset[call.Func]
+		val := call.File
+		nameoff, ok := nameOffsets[call.Func]
 		if !ok {
 			panic("couldn't find function name offset")
 		}
@@ -359,24 +238,24 @@
 		header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
 		header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
 		off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc))
+		off = header.SetUint(ctxt.Arch, off, uint64(state.nfiles))
 		off = writeSymOffset(off, state.funcnametab)
+		off = writeSymOffset(off, state.cutab)
+		off = writeSymOffset(off, state.filetab)
+		off = writeSymOffset(off, state.pctab)
 		off = writeSymOffset(off, state.pclntab)
 	}
 
-	size := int64(8 + 3*ctxt.Arch.PtrSize)
+	size := int64(8 + 7*ctxt.Arch.PtrSize)
 	state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
 }
 
-// walkFuncs iterates over the Textp, calling a function for each unique
+// walkFuncs iterates over the funcs, calling a function for each unique
 // function and inlined function.
-func (state *pclntab) walkFuncs(ctxt *Link, container loader.Bitmap, f func(loader.Sym)) {
+func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) {
 	ldr := ctxt.loader
 	seen := make(map[loader.Sym]struct{})
-	for _, ls := range ctxt.Textp {
-		s := loader.Sym(ls)
-		if !emitPcln(ctxt, s, container) {
-			continue
-		}
+	for _, s := range funcs {
 		if _, ok := seen[s]; !ok {
 			f(s)
 			seen[s] = struct{}{}
@@ -397,24 +276,216 @@
 	}
 }
 
-// generateFuncnametab creates the function name table.
-func (state *pclntab) generateFuncnametab(ctxt *Link, container loader.Bitmap) {
+// generateFuncnametab creates the function name table. Returns a map of
+// func symbol to the name offset in runtime.funcnamtab.
+func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 {
+	nameOffsets := make(map[loader.Sym]uint32, state.nfunc)
+
 	// Write the null terminated strings.
 	writeFuncNameTab := func(ctxt *Link, s loader.Sym) {
 		symtab := ctxt.loader.MakeSymbolUpdater(s)
-		for s, off := range state.funcNameOffset {
+		for s, off := range nameOffsets {
 			symtab.AddStringAt(int64(off), ctxt.loader.SymName(s))
 		}
 	}
 
 	// Loop through the CUs, and calculate the size needed.
 	var size int64
-	state.walkFuncs(ctxt, container, func(s loader.Sym) {
-		state.funcNameOffset[s] = int32(size)
+	walkFuncs(ctxt, funcs, func(s loader.Sym) {
+		nameOffsets[s] = uint32(size)
 		size += int64(ctxt.loader.SymNameLen(s)) + 1 // NULL terminate
 	})
 
 	state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
+	return nameOffsets
+}
+
+// walkFilenames walks funcs, calling a function for each filename used in each
+// function's line table.
+func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) {
+	ldr := ctxt.loader
+
+	// Loop through all functions, finding the filenames we need.
+	for _, s := range funcs {
+		fi := ldr.FuncInfo(s)
+		if !fi.Valid() {
+			continue
+		}
+		fi.Preload()
+
+		cu := ldr.SymUnit(s)
+		for i, nf := 0, int(fi.NumFile()); i < nf; i++ {
+			f(cu, fi.File(i))
+		}
+		for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ {
+			call := fi.InlTree(i)
+			f(cu, call.File)
+		}
+	}
+}
+
+// generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice
+// of the index at which each CU begins in runtime.cutab.
+//
+// Function objects keep track of the files they reference to print the stack.
+// This function creates a per-CU list of filenames if CU[M] references
+// files[1-N], the following is generated:
+//
+//  runtime.cutab:
+//    CU[M]
+//     offsetToFilename[0]
+//     offsetToFilename[1]
+//     ..
+//
+//  runtime.filetab
+//     filename[0]
+//     filename[1]
+//
+// Looking up a filename then becomes:
+//  0) Given a func, and filename index [K]
+//  1) Get Func.CUIndex:       M := func.cuOffset
+//  2) Find filename offset:   fileOffset := runtime.cutab[M+K]
+//  3) Get the filename:       getcstring(runtime.filetab[fileOffset])
+func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 {
+	// On a per-CU basis, keep track of all the filenames we need.
+	//
+	// Note, that we store the filenames in a separate section in the object
+	// files, and deduplicate based on the actual value. It would be better to
+	// store the filenames as symbols, using content addressable symbols (and
+	// then not loading extra filenames), and just use the hash value of the
+	// symbol name to do this cataloging.
+	//
+	// TOOD: Store filenames as symbols. (Note this would be easiest if you
+	// also move strings to ALWAYS using the larger content addressable hash
+	// function, and use that hash value for uniqueness testing.)
+	cuEntries := make([]goobj.CUFileIndex, len(compUnits))
+	fileOffsets := make(map[string]uint32)
+
+	// Walk the filenames.
+	// We store the total filename string length we need to load, and the max
+	// file index we've seen per CU so we can calculate how large the
+	// CU->global table needs to be.
+	var fileSize int64
+	walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) {
+		// Note we use the raw filename for lookup, but use the expanded filename
+		// when we save the size.
+		filename := cu.FileTable[i]
+		if _, ok := fileOffsets[filename]; !ok {
+			fileOffsets[filename] = uint32(fileSize)
+			fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate
+		}
+
+		// Find the maximum file index we've seen.
+		if cuEntries[cu.PclnIndex] < i+1 {
+			cuEntries[cu.PclnIndex] = i + 1 // Store max + 1
+		}
+	})
+
+	// Calculate the size of the runtime.cutab variable.
+	var totalEntries uint32
+	cuOffsets := make([]uint32, len(cuEntries))
+	for i, entries := range cuEntries {
+		// Note, cutab is a slice of uint32, so an offset to a cu's entry is just the
+		// running total of all cu indices we've needed to store so far, not the
+		// number of bytes we've stored so far.
+		cuOffsets[i] = totalEntries
+		totalEntries += uint32(entries)
+	}
+
+	// Write cutab.
+	writeCutab := func(ctxt *Link, s loader.Sym) {
+		sb := ctxt.loader.MakeSymbolUpdater(s)
+
+		var off int64
+		for i, max := range cuEntries {
+			// Write the per CU LUT.
+			cu := compUnits[i]
+			for j := goobj.CUFileIndex(0); j < max; j++ {
+				fileOffset, ok := fileOffsets[cu.FileTable[j]]
+				if !ok {
+					// We're looping through all possible file indices. It's possible a file's
+					// been deadcode eliminated, and although it's a valid file in the CU, it's
+					// not needed in this binary. When that happens, use an invalid offset.
+					fileOffset = ^uint32(0)
+				}
+				off = sb.SetUint32(ctxt.Arch, off, fileOffset)
+			}
+		}
+	}
+	state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab)
+
+	// Write filetab.
+	writeFiletab := func(ctxt *Link, s loader.Sym) {
+		sb := ctxt.loader.MakeSymbolUpdater(s)
+
+		// Write the strings.
+		for filename, loc := range fileOffsets {
+			sb.AddStringAt(int64(loc), expandFile(filename))
+		}
+	}
+	state.nfiles = uint32(len(fileOffsets))
+	state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab)
+
+	return cuOffsets
+}
+
+// generatePctab creates the runtime.pctab variable, holding all the
+// deduplicated pcdata.
+func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
+	ldr := ctxt.loader
+
+	// Pctab offsets of 0 are considered invalid in the runtime. We respect
+	// that by just padding a single byte at the beginning of runtime.pctab,
+	// that way no real offsets can be zero.
+	size := int64(1)
+
+	// Walk the functions, finding offset to store each pcdata.
+	seen := make(map[loader.Sym]struct{})
+	saveOffset := func(pcSym loader.Sym) {
+		if _, ok := seen[pcSym]; !ok {
+			datSize := ldr.SymSize(pcSym)
+			if datSize != 0 {
+				ldr.SetSymValue(pcSym, size)
+			} else {
+				// Invalid PC data, record as zero.
+				ldr.SetSymValue(pcSym, 0)
+			}
+			size += datSize
+			seen[pcSym] = struct{}{}
+		}
+	}
+	for _, s := range funcs {
+		fi := ldr.FuncInfo(s)
+		if !fi.Valid() {
+			continue
+		}
+		fi.Preload()
+
+		pcSyms := []loader.Sym{fi.Pcsp(), fi.Pcfile(), fi.Pcline()}
+		for _, pcSym := range pcSyms {
+			saveOffset(pcSym)
+		}
+		for _, pcSym := range fi.Pcdata() {
+			saveOffset(pcSym)
+		}
+		if fi.NumInlTree() > 0 {
+			saveOffset(fi.Pcinline())
+		}
+	}
+
+	// TODO: There is no reason we need a generator for this variable, and it
+	// could be moved to a carrier symbol. However, carrier symbols containing
+	// carrier symbols don't work yet (as of Aug 2020). Once this is fixed,
+	// runtime.pctab could just be a carrier sym.
+	writePctab := func(ctxt *Link, s loader.Sym) {
+		ldr := ctxt.loader
+		sb := ldr.MakeSymbolUpdater(s)
+		for sym := range seen {
+			sb.SetBytesAt(ldr.SymValue(sym), ldr.Data(sym))
+		}
+	}
+
+	state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
 }
 
 // pclntab initializes the pclntab symbol with
@@ -425,7 +496,7 @@
 	// Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the
 	// layout and data has changed since that time.
 	//
-	// As of July 2020, here's the layout of pclntab:
+	// As of August 2020, here's the layout of pclntab:
 	//
 	//  .gopclntab/__gopclntab [elf/macho section]
 	//    runtime.pclntab
@@ -438,17 +509,25 @@
 	//        offset to runtime.pclntab_old from beginning of runtime.pcheader
 	//
 	//      runtime.funcnametab
-	//         []list of null terminated function names
+	//        []list of null terminated function names
+	//
+	//      runtime.cutab
+	//        for i=0..#CUs
+	//          for j=0..#max used file index in CU[i]
+	//            uint32 offset into runtime.filetab for the filename[j]
+	//
+	//      runtime.filetab
+	//        []null terminated filename strings
+	//
+	//      runtime.pctab
+	//        []byte of deduplicated pc data.
 	//
 	//      runtime.pclntab_old
 	//        function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
 	//        end PC [thearch.ptrsize bytes]
-	//        offset to file table [4 bytes]
 	//        func structures, pcdata tables.
-	//        filetable
 
-	oldState := makeOldPclnState(ctxt)
-	state, _ := makePclntab(ctxt, container)
+	state, compUnits, funcs := makePclntab(ctxt, container)
 
 	ldr := ctxt.loader
 	state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
@@ -460,7 +539,12 @@
 	// rational form.
 	state.pclntab = ldr.LookupOrCreateSym("runtime.pclntab_old", 0)
 	state.generatePCHeader(ctxt)
-	state.generateFuncnametab(ctxt, container)
+	nameOffsets := state.generateFuncnametab(ctxt, funcs)
+	cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs)
+	state.generatePctab(ctxt, funcs)
+
+	// Used to when computing defer return.
+	deferReturnSym := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
 
 	funcdataBytes := int64(0)
 	ldr.SetCarrierSym(state.pclntab, state.carrier)
@@ -472,21 +556,6 @@
 
 	ftab.Grow(int64(state.nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
 
-	szHint := len(ctxt.Textp) * 2
-	pctaboff := make(map[string]uint32, szHint)
-	writepctab := func(off int32, p []byte) int32 {
-		start, ok := pctaboff[string(p)]
-		if !ok {
-			if len(p) > 0 {
-				start = uint32(len(ftab.Data()))
-				ftab.AddBytes(p)
-			}
-			pctaboff[string(p)] = start
-		}
-		newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start))
-		return newoff
-	}
-
 	setAddr := (*loader.SymbolBuilder).SetAddrPlus
 	if ctxt.IsExe() && ctxt.IsInternal() {
 		// Internal linking static executable. At this point the function
@@ -502,20 +571,12 @@
 		}
 	}
 
-	pcsp := sym.Pcdata{}
-	pcfile := sym.Pcdata{}
-	pcline := sym.Pcdata{}
-	pcdata := []sym.Pcdata{}
 	funcdata := []loader.Sym{}
 	funcdataoff := []int64{}
 
 	var nfunc int32
 	prevFunc := ctxt.Textp[0]
-	for _, s := range ctxt.Textp {
-		if !emitPcln(ctxt, s, container) {
-			continue
-		}
-
+	for _, s := range funcs {
 		thisSect := ldr.SymSect(s)
 		prevSect := ldr.SymSect(prevFunc)
 		if thisSect != prevSect {
@@ -530,19 +591,13 @@
 		}
 		prevFunc = s
 
-		pcsp.P = pcsp.P[:0]
-		pcline.P = pcline.P[:0]
-		pcfile.P = pcfile.P[:0]
-		pcdata = pcdata[:0]
+		var numPCData int32
 		funcdataoff = funcdataoff[:0]
 		funcdata = funcdata[:0]
 		fi := ldr.FuncInfo(s)
 		if fi.Valid() {
 			fi.Preload()
-			npc := fi.NumPcdata()
-			for i := uint32(0); i < npc; i++ {
-				pcdata = append(pcdata, sym.Pcdata{P: fi.Pcdata(int(i))})
-			}
+			numPCData = int32(len(fi.Pcdata()))
 			nfd := fi.NumFuncdataoff()
 			for i := uint32(0); i < nfd; i++ {
 				funcdataoff = append(funcdataoff, fi.Funcdataoff(int(i)))
@@ -550,15 +605,12 @@
 			funcdata = fi.Funcdata(funcdata)
 		}
 
+		writeInlPCData := false
 		if fi.Valid() && fi.NumInlTree() > 0 {
-
-			if len(pcdata) <= objabi.PCDATA_InlTreeIndex {
-				// Create inlining pcdata table.
-				newpcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
-				copy(newpcdata, pcdata)
-				pcdata = newpcdata
+			writeInlPCData = true
+			if numPCData <= objabi.PCDATA_InlTreeIndex {
+				numPCData = objabi.PCDATA_InlTreeIndex + 1
 			}
-
 			if len(funcdataoff) <= objabi.FUNCDATA_InlTree {
 				// Create inline tree funcdata.
 				newfuncdata := make([]loader.Sym, objabi.FUNCDATA_InlTree+1)
@@ -583,7 +635,7 @@
 		// fixed size of struct, checked below
 		off := funcstart
 
-		end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcdata))*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize)
+		end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 6*4 + numPCData*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize)
 		if len(funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
 			end += 4
 		}
@@ -593,7 +645,7 @@
 		off = int32(setAddr(ftab, ctxt.Arch, int64(off), s, 0))
 
 		// name int32
-		nameoff, ok := state.funcNameOffset[s]
+		nameoff, ok := nameOffsets[s]
 		if !ok {
 			panic("couldn't find function name offset")
 		}
@@ -608,48 +660,32 @@
 		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
 
 		// deferreturn
-		deferreturn := oldState.computeDeferReturn(&ctxt.Target, s)
+		deferreturn := computeDeferReturn(ctxt, deferReturnSym, s)
 		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
 
 		cu := ldr.SymUnit(s)
-		if fi.Valid() {
-			pcsp = sym.Pcdata{P: fi.Pcsp()}
-			pcfile = sym.Pcdata{P: fi.Pcfile()}
-			pcline = sym.Pcdata{P: fi.Pcline()}
-			oldState.renumberfiles(ctxt, cu, fi, &pcfile)
-			if false {
-				// Sanity check the new numbering
-				it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
-				for it.Init(pcfile.P); !it.Done; it.Next() {
-					if it.Value < 1 || it.Value > int32(len(oldState.numberedFiles)) {
-						ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(oldState.numberedFiles))
-						errorexit()
-					}
-				}
-			}
-		}
 
 		if fi.Valid() && fi.NumInlTree() > 0 {
-			its := oldState.genInlTreeSym(cu, fi, ctxt.Arch, state)
+			its := genInlTreeSym(ctxt, cu, fi, ctxt.Arch, nameOffsets)
 			funcdata[objabi.FUNCDATA_InlTree] = its
-			pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()}
 		}
 
 		// pcdata
-		off = writepctab(off, pcsp.P)
-		off = writepctab(off, pcfile.P)
-		off = writepctab(off, pcline.P)
-		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcdata))))
-
-		// Store the compilation unit index.
-		cuIdx := ^uint16(0)
-		if cu := ldr.SymUnit(s); cu != nil {
-			if cu.PclnIndex > math.MaxUint16 {
-				panic("cu limit reached.")
-			}
-			cuIdx = uint16(cu.PclnIndex)
+		if fi.Valid() {
+			off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcsp()))))
+			off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcfile()))))
+			off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcline()))))
+		} else {
+			off += 12
 		}
-		off = int32(ftab.SetUint16(ctxt.Arch, int64(off), cuIdx))
+		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(numPCData)))
+
+		// Store the offset to compilation unit's file table.
+		cuIdx := ^uint32(0)
+		if cu := ldr.SymUnit(s); cu != nil {
+			cuIdx = cuOffsets[cu.PclnIndex]
+		}
+		off = int32(ftab.SetUint32(ctxt.Arch, int64(off), cuIdx))
 
 		// funcID uint8
 		var funcID objabi.FuncID
@@ -658,11 +694,21 @@
 		}
 		off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
 
+		off += 2 // pad
+
 		// nfuncdata must be the final entry.
 		off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
-		for i := range pcdata {
-			off = writepctab(off, pcdata[i].P)
+
+		// Output the pcdata.
+		if fi.Valid() {
+			for i, pcSym := range fi.Pcdata() {
+				ftab.SetUint32(ctxt.Arch, int64(off+int32(i*4)), uint32(ldr.SymValue(pcSym)))
+			}
+			if writeInlPCData {
+				ftab.SetUint32(ctxt.Arch, int64(off+objabi.PCDATA_InlTreeIndex*4), uint32(ldr.SymValue(fi.Pcinline())))
+			}
 		}
+		off += numPCData * 4
 
 		// funcdata, must be pointer-aligned and we're only int32-aligned.
 		// Missing funcdata will be 0 (nil pointer).
@@ -684,7 +730,7 @@
 		}
 
 		if off != end {
-			ctxt.Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcdata), len(funcdata), ctxt.Arch.PtrSize)
+			ctxt.Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, numPCData, len(funcdata), ctxt.Arch.PtrSize)
 			errorexit()
 		}
 
@@ -694,26 +740,8 @@
 	// Final entry of table is just end pc.
 	setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), state.lastFunc, ldr.SymSize(state.lastFunc))
 
-	// Start file table.
-	dSize := len(ftab.Data())
-	start := int32(dSize)
-	start += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1)
-	state.filetabOffset = start
-	ftab.SetUint32(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
-
-	nf := len(oldState.numberedFiles)
-	ftab.Grow(int64(start) + int64((nf+1)*4))
-	ftab.SetUint32(ctxt.Arch, int64(start), uint32(nf+1))
-	for i := nf; i > 0; i-- {
-		path := oldState.filepaths[i]
-		val := int64(i)
-		ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(ftabaddstring(ftab, path)))
-	}
-
 	ftab.SetSize(int64(len(ftab.Data())))
 
-	ctxt.NumFilesyms = len(oldState.numberedFiles)
-
 	if ctxt.Debugvlog != 0 {
 		ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes)
 	}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index bc88095..520aaa4 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -619,6 +619,18 @@
 	moduledata.AddAddr(ctxt.Arch, pcln.funcnametab)
 	moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab)))
 	moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab)))
+	// The cutab slice
+	moduledata.AddAddr(ctxt.Arch, pcln.cutab)
+	moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab)))
+	moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab)))
+	// The filetab slice
+	moduledata.AddAddr(ctxt.Arch, pcln.filetab)
+	moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
+	moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab)))
+	// The pctab slice
+	moduledata.AddAddr(ctxt.Arch, pcln.pctab)
+	moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab)))
+	moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab)))
 	// The pclntab slice
 	moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
 	moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab)))
@@ -627,10 +639,6 @@
 	moduledata.AddAddr(ctxt.Arch, pcln.pclntab)
 	moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
 	moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1))
-	// The filetab slice
-	moduledata.AddAddrPlus(ctxt.Arch, pcln.pclntab, int64(pcln.filetabOffset))
-	moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
-	moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
 	// findfunctab
 	moduledata.AddAddr(ctxt.Arch, pcln.findfunctab)
 	// minpc, maxpc
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 8fd10b0..f149e3c 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -1878,19 +1878,24 @@
 	return objabi.FuncID((*goobj.FuncInfo)(nil).ReadFuncID(fi.data))
 }
 
-func (fi *FuncInfo) Pcsp() []byte {
-	pcsp, end := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data)
-	return fi.r.BytesAt(fi.r.PcdataBase()+pcsp, int(end-pcsp))
+func (fi *FuncInfo) Pcsp() Sym {
+	sym := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data)
+	return fi.l.resolve(fi.r, sym)
 }
 
-func (fi *FuncInfo) Pcfile() []byte {
-	pcf, end := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data)
-	return fi.r.BytesAt(fi.r.PcdataBase()+pcf, int(end-pcf))
+func (fi *FuncInfo) Pcfile() Sym {
+	sym := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data)
+	return fi.l.resolve(fi.r, sym)
 }
 
-func (fi *FuncInfo) Pcline() []byte {
-	pcln, end := (*goobj.FuncInfo)(nil).ReadPcline(fi.data)
-	return fi.r.BytesAt(fi.r.PcdataBase()+pcln, int(end-pcln))
+func (fi *FuncInfo) Pcline() Sym {
+	sym := (*goobj.FuncInfo)(nil).ReadPcline(fi.data)
+	return fi.l.resolve(fi.r, sym)
+}
+
+func (fi *FuncInfo) Pcinline() Sym {
+	sym := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data)
+	return fi.l.resolve(fi.r, sym)
 }
 
 // Preload has to be called prior to invoking the various methods
@@ -1899,27 +1904,16 @@
 	fi.lengths = (*goobj.FuncInfo)(nil).ReadFuncInfoLengths(fi.data)
 }
 
-func (fi *FuncInfo) Pcinline() []byte {
+func (fi *FuncInfo) Pcdata() []Sym {
 	if !fi.lengths.Initialized {
 		panic("need to call Preload first")
 	}
-	pcinl, end := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data, fi.lengths.PcdataOff)
-	return fi.r.BytesAt(fi.r.PcdataBase()+pcinl, int(end-pcinl))
-}
-
-func (fi *FuncInfo) NumPcdata() uint32 {
-	if !fi.lengths.Initialized {
-		panic("need to call Preload first")
+	syms := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data)
+	ret := make([]Sym, len(syms))
+	for i := range ret {
+		ret[i] = fi.l.resolve(fi.r, syms[i])
 	}
-	return fi.lengths.NumPcdata
-}
-
-func (fi *FuncInfo) Pcdata(k int) []byte {
-	if !fi.lengths.Initialized {
-		panic("need to call Preload first")
-	}
-	pcdat, end := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data, fi.lengths.PcdataOff, uint32(k))
-	return fi.r.BytesAt(fi.r.PcdataBase()+pcdat, int(end-pcdat))
+	return ret
 }
 
 func (fi *FuncInfo) NumFuncdataoff() uint32 {
diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go
index e14d89a..c0c723d 100644
--- a/src/cmd/link/internal/loader/symbolbuilder.go
+++ b/src/cmd/link/internal/loader/symbolbuilder.go
@@ -336,6 +336,15 @@
 	return r
 }
 
+func (sb *SymbolBuilder) SetBytesAt(off int64, b []byte) int64 {
+	datLen := int64(len(b))
+	if off+datLen > int64(len(sb.data)) {
+		panic("attempt to write past end of buffer")
+	}
+	copy(sb.data[off:off+datLen], b)
+	return off + datLen
+}
+
 func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 {
 	if sb.kind == 0 {
 		sb.kind = sym.SDATA
diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go
index 1a4165e..70cf36a 100644
--- a/src/cmd/link/internal/sym/symbol.go
+++ b/src/cmd/link/internal/sym/symbol.go
@@ -33,7 +33,3 @@
 	}
 	return ^obj.ABI(0), false
 }
-
-type Pcdata struct {
-	P []byte
-}
diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go
index e5c5052..a72f984 100644
--- a/src/debug/gosym/pclntab.go
+++ b/src/debug/gosym/pclntab.go
@@ -53,14 +53,19 @@
 	quantum     uint32
 	ptrsize     uint32
 	funcnametab []byte
+	cutab       []byte
 	funcdata    []byte
 	functab     []byte
 	nfunctab    uint32
 	filetab     []byte
+	pctab       []byte // points to the pctables.
 	nfiletab    uint32
-	fileMap     map[string]uint32
 	funcNames   map[uint32]string // cache the function names
 	strings     map[uint32]string // interned substrings of Data, keyed by offset
+	// fileMap varies depending on the version of the object file.
+	// For ver12, it maps the name to the index in the file table.
+	// For ver116, it maps the name to the offset in filetab.
+	fileMap map[string]uint32
 }
 
 // NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4,
@@ -223,22 +228,26 @@
 	switch possibleVersion {
 	case ver116:
 		t.nfunctab = uint32(t.uintptr(t.Data[8:]))
-		offset := t.uintptr(t.Data[8+t.ptrsize:])
+		t.nfiletab = uint32(t.uintptr(t.Data[8+t.ptrsize:]))
+		offset := t.uintptr(t.Data[8+2*t.ptrsize:])
 		t.funcnametab = t.Data[offset:]
-		offset = t.uintptr(t.Data[8+2*t.ptrsize:])
+		offset = t.uintptr(t.Data[8+3*t.ptrsize:])
+		t.cutab = t.Data[offset:]
+		offset = t.uintptr(t.Data[8+4*t.ptrsize:])
+		t.filetab = t.Data[offset:]
+		offset = t.uintptr(t.Data[8+5*t.ptrsize:])
+		t.pctab = t.Data[offset:]
+		offset = t.uintptr(t.Data[8+6*t.ptrsize:])
 		t.funcdata = t.Data[offset:]
 		t.functab = t.Data[offset:]
 		functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
-		fileoff := t.binary.Uint32(t.functab[functabsize:])
-		t.filetab = t.functab[fileoff:]
 		t.functab = t.functab[:functabsize]
-		t.nfiletab = t.binary.Uint32(t.filetab)
-		t.filetab = t.filetab[:t.nfiletab*4]
 	case ver12:
 		t.nfunctab = uint32(t.uintptr(t.Data[8:]))
 		t.funcdata = t.Data
 		t.funcnametab = t.Data
 		t.functab = t.Data[8+t.ptrsize:]
+		t.pctab = t.Data
 		functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
 		fileoff := t.binary.Uint32(t.functab[functabsize:])
 		t.functab = t.functab[:functabsize]
@@ -330,17 +339,22 @@
 	return s
 }
 
-// string returns a Go string found at off.
-func (t *LineTable) string(off uint32) string {
+// stringFrom returns a Go string found at off from a position.
+func (t *LineTable) stringFrom(arr []byte, off uint32) string {
 	if s, ok := t.strings[off]; ok {
 		return s
 	}
-	i := bytes.IndexByte(t.funcdata[off:], 0)
-	s := string(t.funcdata[off : off+uint32(i)])
+	i := bytes.IndexByte(arr[off:], 0)
+	s := string(arr[off : off+uint32(i)])
 	t.strings[off] = s
 	return s
 }
 
+// string returns a Go string found at off.
+func (t *LineTable) string(off uint32) string {
+	return t.stringFrom(t.funcdata, off)
+}
+
 // step advances to the next pc, value pair in the encoded table.
 func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
 	uvdelta := t.readvarint(p)
@@ -363,7 +377,7 @@
 // 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 {
-	p := t.funcdata[off:]
+	p := t.pctab[off:]
 
 	val := int32(-1)
 	pc := entry
@@ -381,21 +395,25 @@
 // to file number. Since most functions come from a single file, these
 // are usually short and quick to scan. If a file match is found, then the
 // code goes to the expense of looking for a simultaneous line number match.
-func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 {
+func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32, cutab []byte) uint64 {
 	if filetab == 0 || linetab == 0 {
 		return 0
 	}
 
-	fp := t.funcdata[filetab:]
-	fl := t.funcdata[linetab:]
+	fp := t.pctab[filetab:]
+	fl := t.pctab[linetab:]
 	fileVal := int32(-1)
 	filePC := entry
 	lineVal := int32(-1)
 	linePC := entry
 	fileStartPC := filePC
 	for t.step(&fp, &filePC, &fileVal, filePC == entry) {
-		if fileVal == filenum && fileStartPC < filePC {
-			// fileVal is in effect starting at fileStartPC up to
+		fileIndex := fileVal
+		if t.version == ver116 {
+			fileIndex = int32(t.binary.Uint32(cutab[fileVal*4:]))
+		}
+		if fileIndex == filenum && fileStartPC < filePC {
+			// fileIndex is in effect starting at fileStartPC up to
 			// but not including filePC, and it's the file we want.
 			// Run the PC table looking for a matching line number
 			// or until we reach filePC.
@@ -450,13 +468,24 @@
 	entry := t.uintptr(f)
 	filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
 	fno := t.pcvalue(filetab, entry, pc)
-	if fno <= 0 {
+	if t.version == ver12 {
+		if fno <= 0 {
+			return ""
+		}
+		return t.string(t.binary.Uint32(t.filetab[4*fno:]))
+	}
+	// Go ≥ 1.16
+	if fno < 0 { // 0 is valid for ≥ 1.16
 		return ""
 	}
-	return t.string(t.binary.Uint32(t.filetab[4*fno:]))
+	cuoff := t.binary.Uint32(f[t.ptrsize+7*4:])
+	if fnoff := t.binary.Uint32(t.cutab[(cuoff+uint32(fno))*4:]); fnoff != ^uint32(0) {
+		return t.stringFrom(t.filetab, fnoff)
+	}
+	return ""
 }
 
-// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table.
+// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2/1.16 pcln table.
 func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
 	defer func() {
 		if recover() != nil {
@@ -465,20 +494,25 @@
 	}()
 
 	t.initFileMap()
-	filenum := t.fileMap[file]
-	if filenum == 0 {
+	filenum, ok := t.fileMap[file]
+	if !ok {
 		return 0
 	}
 
 	// Scan all functions.
 	// If this turns out to be a bottleneck, we could build a map[int32][]int32
 	// mapping file number to a list of functions with code from that file.
+	var cutab []byte
 	for i := uint32(0); i < t.nfunctab; i++ {
 		f := t.funcdata[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
 		entry := t.uintptr(f)
 		filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
 		linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
-		pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line))
+		if t.version == ver116 {
+			cuoff := t.binary.Uint32(f[t.ptrsize+7*4:]) * 4
+			cutab = t.cutab[cuoff:]
+		}
+		pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line), cutab)
 		if pc != 0 {
 			return pc
 		}
@@ -496,9 +530,18 @@
 	}
 	m := make(map[string]uint32)
 
-	for i := uint32(1); i < t.nfiletab; i++ {
-		s := t.string(t.binary.Uint32(t.filetab[4*i:]))
-		m[s] = i
+	if t.version == ver12 {
+		for i := uint32(1); i < t.nfiletab; i++ {
+			s := t.string(t.binary.Uint32(t.filetab[4*i:]))
+			m[s] = i
+		}
+	} else {
+		var pos uint32
+		for i := uint32(0); i < t.nfiletab; i++ {
+			s := t.stringFrom(t.filetab, pos)
+			m[s] = pos
+			pos += uint32(len(s) + 1)
+		}
 	}
 	t.fileMap = m
 }
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index b7d0739..eba68da 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -806,13 +806,14 @@
 	args        int32  // in/out args size
 	deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.
 
-	pcsp      int32
-	pcfile    int32
-	pcln      int32
-	npcdata   int32
-	cuIndex   uint16 // TODO(jfaller): 16 bits is never enough, make this larger.
-	funcID    funcID // set for certain special runtime functions
-	nfuncdata uint8  // must be last
+	pcsp      uint32
+	pcfile    uint32
+	pcln      uint32
+	npcdata   uint32
+	cuOffset  uint32  // runtime.cutab offset of this function's CU
+	funcID    funcID  // set for certain special runtime functions
+	_         [2]byte // pad
+	nfuncdata uint8   // must be last
 }
 
 // Pseudo-Func that is returned for PCs that occur in inlined code.
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index ddb5ea8..0610f75 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -334,14 +334,18 @@
 	funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
 )
 
-// PCHeader holds data used by the pclntab lookups.
+// pcHeader holds data used by the pclntab lookups.
 type pcHeader struct {
 	magic          uint32  // 0xFFFFFFFA
 	pad1, pad2     uint8   // 0,0
 	minLC          uint8   // min instruction size
 	ptrSize        uint8   // size of a ptr in bytes
 	nfunc          int     // number of functions in the module
+	nfiles         uint    // number of entries in the file tab.
 	funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
+	cuOffset       uintptr // offset to the cutab variable from pcHeader
+	filetabOffset  uintptr // offset to the filetab variable from pcHeader
+	pctabOffset    uintptr // offset to the pctab varible from pcHeader
 	pclnOffset     uintptr // offset to the pclntab variable from pcHeader
 }
 
@@ -353,9 +357,11 @@
 type moduledata struct {
 	pcHeader     *pcHeader
 	funcnametab  []byte
+	cutab        []uint32
+	filetab      []byte
+	pctab        []byte
 	pclntable    []byte
 	ftab         []functab
-	filetab      []uint32
 	findfunctab  uintptr
 	minpc, maxpc uintptr
 
@@ -717,7 +723,7 @@
 type pcvalueCacheEnt struct {
 	// targetpc and off together are the key of this cache entry.
 	targetpc uintptr
-	off      int32
+	off      uint32
 	// val is the value of this cached pcvalue entry.
 	val int32
 }
@@ -732,7 +738,7 @@
 
 // Returns the PCData value, and the PC where this value starts.
 // TODO: the start PC is returned only when cache is nil.
-func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, strict bool) (int32, uintptr) {
+func pcvalue(f funcInfo, off uint32, targetpc uintptr, cache *pcvalueCache, strict bool) (int32, uintptr) {
 	if off == 0 {
 		return -1, 0
 	}
@@ -766,7 +772,7 @@
 		return -1, 0
 	}
 	datap := f.datap
-	p := datap.pclntable[off:]
+	p := datap.pctab[off:]
 	pc := f.entry
 	prevpc := pc
 	val := int32(-1)
@@ -808,7 +814,7 @@
 
 	print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n")
 
-	p = datap.pclntable[off:]
+	p = datap.pctab[off:]
 	pc = f.entry
 	val = -1
 	for {
@@ -851,7 +857,12 @@
 	if !f.valid() {
 		return "?"
 	}
-	return gostringnocopy(&datap.pclntable[datap.filetab[fileno]])
+	// Make sure the cu index and file offset are valid
+	if fileoff := datap.cutab[f.cuOffset+uint32(fileno)]; fileoff != ^uint32(0) {
+		return gostringnocopy(&datap.filetab[fileoff])
+	}
+	// pcln section is corrupt.
+	return "?"
 }
 
 func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int32) {
@@ -865,7 +876,7 @@
 		// print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n")
 		return "?", 0
 	}
-	file = gostringnocopy(&datap.pclntable[datap.filetab[fileno]])
+	file = funcfile(f, fileno)
 	return
 }
 
@@ -884,7 +895,7 @@
 // funcMaxSPDelta returns the maximum spdelta at any point in f.
 func funcMaxSPDelta(f funcInfo) int32 {
 	datap := f.datap
-	p := datap.pclntable[f.pcsp:]
+	p := datap.pctab[f.pcsp:]
 	pc := f.entry
 	val := int32(-1)
 	max := int32(0)
@@ -900,20 +911,20 @@
 	}
 }
 
-func pcdatastart(f funcInfo, table int32) int32 {
-	return *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
+func pcdatastart(f funcInfo, table uint32) uint32 {
+	return *(*uint32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4))
 }
 
-func pcdatavalue(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache) int32 {
-	if table < 0 || table >= f.npcdata {
+func pcdatavalue(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache) int32 {
+	if table >= f.npcdata {
 		return -1
 	}
 	r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, true)
 	return r
 }
 
-func pcdatavalue1(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
-	if table < 0 || table >= f.npcdata {
+func pcdatavalue1(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 {
+	if table >= f.npcdata {
 		return -1
 	}
 	r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, strict)
@@ -922,8 +933,8 @@
 
 // Like pcdatavalue, but also return the start PC of this PCData value.
 // It doesn't take a cache.
-func pcdatavalue2(f funcInfo, table int32, targetpc uintptr) (int32, uintptr) {
-	if table < 0 || table >= f.npcdata {
+func pcdatavalue2(f funcInfo, table uint32, targetpc uintptr) (int32, uintptr) {
+	if table >= f.npcdata {
 		return -1, 0
 	}
 	return pcvalue(f, pcdatastart(f, table), targetpc, nil, true)
@@ -1005,7 +1016,7 @@
 	parent   int16  // index of parent in the inltree, or < 0
 	funcID   funcID // type of the called function
 	_        byte
-	file     int32 // fileno index into filetab
+	file     int32 // perCU file index for inlined call. See cmd/link:pcln.go
 	line     int32 // line number of the call site
 	func_    int32 // offset into pclntab for name of called function
 	parentPc int32 // position of an instruction whose source position is the call site (offset from entry)