diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index 74262595b05d93ac9f082543e1445c402b56a9d8..52b1ed351d64d9428c5156fb74a9dc657bb75211 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -231,6 +231,11 @@ func compile(fn *Node) {
 		return
 	}
 
+	// Set up the function's LSym early to avoid data races with the assemblers.
+	// Do this before walk, as walk needs the LSym to set attributes/relocations
+	// (e.g. in markTypeUsedInInterface).
+	fn.Func.initLSym(true)
+
 	walk(fn)
 	if nerrors != 0 {
 		return
@@ -250,9 +255,6 @@ func compile(fn *Node) {
 		return
 	}
 
-	// Set up the function's LSym early to avoid data races with the assemblers.
-	fn.Func.initLSym(true)
-
 	// Make sure type syms are declared for all types that might
 	// be types of stack objects. We need to do this here
 	// because symbols must be allocated before the parallel
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
index 71ed5584612f9263fe1b351752d78466131094f4..af19a96bbc4b43beb91edaa9d0faef136b1116d9 100644
--- a/src/cmd/compile/internal/gc/sinit.go
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -278,7 +278,7 @@ func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
 			return Isconst(val, CTNIL)
 		}
 
-		markTypeUsedInInterface(val.Type)
+		markTypeUsedInInterface(val.Type, l.Sym.Linksym())
 
 		var itab *Node
 		if l.Type.IsEmptyInterface() {
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 933f16d9a01e381764ea96b713dc943baeb33260..d238cc2f45fb27acb35d365125ed513d784b894f 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -805,8 +805,8 @@ opswitch:
 		fromType := n.Left.Type
 		toType := n.Type
 
-		if !fromType.IsInterface() {
-			markTypeUsedInInterface(fromType)
+		if !fromType.IsInterface() && !Curfn.Func.Nname.isBlank() { // skip unnamed functions (func _())
+			markTypeUsedInInterface(fromType, Curfn.Func.lsym)
 		}
 
 		// typeword generates the type word of the interface value.
@@ -1621,8 +1621,13 @@ opswitch:
 
 // markTypeUsedInInterface marks that type t is converted to an interface.
 // This information is used in the linker in dead method elimination.
-func markTypeUsedInInterface(t *types.Type) {
-	typenamesym(t).Linksym().Set(obj.AttrUsedInIface, true)
+func markTypeUsedInInterface(t *types.Type, from *obj.LSym) {
+	tsym := typenamesym(t).Linksym()
+	// Emit a marker relocation. The linker will know the type is converted
+	// to an interface if "from" is reachable.
+	r := obj.Addrel(from)
+	r.Sym = tsym
+	r.Type = objabi.R_USEIFACE
 }
 
 // rtconvfn returns the parameter and result types that will be used by a
@@ -3687,6 +3692,8 @@ func usemethod(n *Node) {
 	// Also need to check for reflect package itself (see Issue #38515).
 	if s := res0.Type.Sym; s != nil && s.Name == "Method" && isReflectPkg(s.Pkg) {
 		Curfn.Func.SetReflectMethod(true)
+		// The LSym is initialized at this point. We need to set the attribute on the LSym.
+		Curfn.Func.lsym.Set(obj.AttrReflectMethod, true)
 	}
 }
 
diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go
index 68f01f1c5d8634f3ece428f6e166e749c28a82ee..cb3a2c31965604fe7b9617a02bcb5d96c7a54541 100644
--- a/src/cmd/internal/obj/s390x/asmz.go
+++ b/src/cmd/internal/obj/s390x/asmz.go
@@ -461,6 +461,7 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
 	buffer := make([]byte, 0)
 	changed := true
 	loop := 0
+	nrelocs0 := len(c.cursym.R)
 	for changed {
 		if loop > 100 {
 			c.ctxt.Diag("stuck in spanz loop")
@@ -468,7 +469,10 @@ func spanz(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
 		}
 		changed = false
 		buffer = buffer[:0]
-		c.cursym.R = make([]obj.Reloc, 0)
+		for i := range c.cursym.R[nrelocs0:] {
+			c.cursym.R[nrelocs0+i] = obj.Reloc{}
+		}
+		c.cursym.R = c.cursym.R[:nrelocs0] // preserve marker relocations generated by the compiler
 		for p := c.cursym.Func.Text; p != nil; p = p.Link {
 			pc := int64(len(buffer))
 			if pc != p.Pc {
diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go
index 70e8e51e6552ed4fa806cf42c100f8a9c1afa0ad..a9e093a8add4417bf955e929e513d072ee95a68c 100644
--- a/src/cmd/internal/obj/wasm/wasmobj.go
+++ b/src/cmd/internal/obj/wasm/wasmobj.go
@@ -1007,6 +1007,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
 					panic("bad name for Call")
 				}
 				r := obj.Addrel(s)
+				r.Siz = 1 // actually variable sized
 				r.Off = int32(w.Len())
 				r.Type = objabi.R_CALL
 				if p.Mark&WasmImport != 0 {
@@ -1033,6 +1034,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
 		case AI32Const, AI64Const:
 			if p.From.Name == obj.NAME_EXTERN {
 				r := obj.Addrel(s)
+				r.Siz = 1 // actually variable sized
 				r.Off = int32(w.Len())
 				r.Type = objabi.R_ADDR
 				r.Sym = p.From.Sym
diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go
index fb99c620ad69afb5fec2cae2b876f2f5488f1ce4..4940c79eaabef57514ba17d5eea1fea3e8150945 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -2100,14 +2100,15 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
 	var c int32
 	errors := ctxt.Errors
 	var nops []nopPad // Padding for a particular assembly (reuse slice storage if multiple assemblies)
+	nrelocs0 := len(s.R)
 	for {
 		// This loop continues while there are reasons to re-assemble
 		// whole block, like the presence of long forward jumps.
 		reAssemble := false
-		for i := range s.R {
-			s.R[i] = obj.Reloc{}
+		for i := range s.R[nrelocs0:] {
+			s.R[nrelocs0+i] = obj.Reloc{}
 		}
-		s.R = s.R[:0]
+		s.R = s.R[:nrelocs0] // preserve marker relocations generated by the compiler
 		s.P = s.P[:0]
 		c = 0
 		var pPrev *obj.Prog
diff --git a/src/cmd/internal/objabi/reloctype.go b/src/cmd/internal/objabi/reloctype.go
index f029a3c396dfb33dbb0fdbe981d8a20be5eaed68..1e328d659f60360c1120e50068f358cda0002857 100644
--- a/src/cmd/internal/objabi/reloctype.go
+++ b/src/cmd/internal/objabi/reloctype.go
@@ -89,6 +89,11 @@ const (
 	// should be linked into the final binary, even if there are no other
 	// direct references. (This is used for types reachable by reflection.)
 	R_USETYPE
+	// R_USEIFACE marks a type is converted to an interface in the function this
+	// relocation is applied to. The target is a type descriptor.
+	// This is a marker relocation (0-sized), for the linker's reachabililty
+	// analysis.
+	R_USEIFACE
 	// R_METHODOFF resolves to a 32-bit offset from the beginning of the section
 	// holding the data being relocated to the referenced symbol.
 	// It is a variant of R_ADDROFF used when linking from the uncommonType of a
diff --git a/src/cmd/internal/objabi/reloctype_string.go b/src/cmd/internal/objabi/reloctype_string.go
index 83dfe71e07173c98c0eaf232b6688cedd204c6df..caf24eea58dec157636ef5199517b4048d6a8c16 100644
--- a/src/cmd/internal/objabi/reloctype_string.go
+++ b/src/cmd/internal/objabi/reloctype_string.go
@@ -4,9 +4,71 @@ package objabi
 
 import "strconv"
 
-const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF"
+func _() {
+	// An "invalid array index" compiler error signifies that the constant values have changed.
+	// Re-run the stringer command to generate them again.
+	var x [1]struct{}
+	_ = x[R_ADDR-1]
+	_ = x[R_ADDRPOWER-2]
+	_ = x[R_ADDRARM64-3]
+	_ = x[R_ADDRMIPS-4]
+	_ = x[R_ADDROFF-5]
+	_ = x[R_WEAKADDROFF-6]
+	_ = x[R_SIZE-7]
+	_ = x[R_CALL-8]
+	_ = x[R_CALLARM-9]
+	_ = x[R_CALLARM64-10]
+	_ = x[R_CALLIND-11]
+	_ = x[R_CALLPOWER-12]
+	_ = x[R_CALLMIPS-13]
+	_ = x[R_CALLRISCV-14]
+	_ = x[R_CONST-15]
+	_ = x[R_PCREL-16]
+	_ = x[R_TLS_LE-17]
+	_ = x[R_TLS_IE-18]
+	_ = x[R_GOTOFF-19]
+	_ = x[R_PLT0-20]
+	_ = x[R_PLT1-21]
+	_ = x[R_PLT2-22]
+	_ = x[R_USEFIELD-23]
+	_ = x[R_USETYPE-24]
+	_ = x[R_USEIFACE-25]
+	_ = x[R_METHODOFF-26]
+	_ = x[R_POWER_TOC-27]
+	_ = x[R_GOTPCREL-28]
+	_ = x[R_JMPMIPS-29]
+	_ = x[R_DWARFSECREF-30]
+	_ = x[R_DWARFFILEREF-31]
+	_ = x[R_ARM64_TLS_LE-32]
+	_ = x[R_ARM64_TLS_IE-33]
+	_ = x[R_ARM64_GOTPCREL-34]
+	_ = x[R_ARM64_GOT-35]
+	_ = x[R_ARM64_PCREL-36]
+	_ = x[R_ARM64_LDST8-37]
+	_ = x[R_ARM64_LDST32-38]
+	_ = x[R_ARM64_LDST64-39]
+	_ = x[R_ARM64_LDST128-40]
+	_ = x[R_POWER_TLS_LE-41]
+	_ = x[R_POWER_TLS_IE-42]
+	_ = x[R_POWER_TLS-43]
+	_ = x[R_ADDRPOWER_DS-44]
+	_ = x[R_ADDRPOWER_GOT-45]
+	_ = x[R_ADDRPOWER_PCREL-46]
+	_ = x[R_ADDRPOWER_TOCREL-47]
+	_ = x[R_ADDRPOWER_TOCREL_DS-48]
+	_ = x[R_RISCV_PCREL_ITYPE-49]
+	_ = x[R_RISCV_PCREL_STYPE-50]
+	_ = x[R_PCRELDBL-51]
+	_ = x[R_ADDRMIPSU-52]
+	_ = x[R_ADDRMIPSTLS-53]
+	_ = x[R_ADDRCUOFF-54]
+	_ = x[R_WASMIMPORT-55]
+	_ = x[R_XCOFFREF-56]
+}
+
+const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CALLRISCVR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF"
 
-var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 133, 140, 147, 155, 163, 171, 177, 183, 189, 199, 208, 219, 230, 240, 249, 262, 276, 290, 304, 320, 331, 344, 357, 371, 385, 400, 414, 428, 439, 453, 468, 485, 503, 524, 543, 562, 572, 583, 596, 607, 619, 629}
+var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 133, 140, 147, 155, 163, 171, 177, 183, 189, 199, 208, 218, 229, 240, 250, 259, 272, 286, 300, 314, 330, 341, 354, 367, 381, 395, 410, 424, 438, 449, 463, 478, 495, 513, 534, 553, 572, 582, 593, 606, 617, 629, 639}
 
 func (i RelocType) String() string {
 	i -= 1
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index a730125cf2c780854940fce5b65130c13136b1ae..0a3418bfc9bf207be047dce91a373c0a02e4badc 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -698,6 +698,9 @@ func windynrelocsym(ctxt *Link, rel *loader.SymbolBuilder, s loader.Sym) {
 	relocs := ctxt.loader.Relocs(s)
 	for ri := 0; ri < relocs.Count(); ri++ {
 		r := relocs.At(ri)
+		if r.IsMarker() {
+			continue // skip marker relocations
+		}
 		targ := r.Sym()
 		if targ == 0 {
 			continue
@@ -775,6 +778,9 @@ func dynrelocsym(ctxt *Link, s loader.Sym) {
 	relocs := ldr.Relocs(s)
 	for ri := 0; ri < relocs.Count(); ri++ {
 		r := relocs.At(ri)
+		if r.IsMarker() {
+			continue // skip marker relocations
+		}
 		if ctxt.BuildMode == BuildModePIE && ctxt.LinkMode == LinkInternal {
 			// It's expected that some relocations will be done
 			// later by relocsym (R_TLS_LE, R_ADDROFF), so
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 7f14aa3d2786dd3864e96725458c0af4a564693f..816a23b9a713097bfb92fbaf06e8606f1e8a69c4 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -153,6 +153,20 @@ func (d *deadcodePass) flood() {
 				// do nothing for now as we still load all type symbols.
 				continue
 			}
+			if t == objabi.R_USEIFACE {
+				// R_USEIFACE is a marker relocation that tells the linker the type is
+				// converted to an interface, i.e. should have UsedInIface set. See the
+				// comment below for why we need to unset the Reachable bit and re-mark it.
+				rs := r.Sym()
+				if !d.ldr.AttrUsedInIface(rs) {
+					d.ldr.SetAttrUsedInIface(rs, true)
+					if d.ldr.AttrReachable(rs) {
+						d.ldr.SetAttrReachable(rs, false)
+						d.mark(rs, symIdx)
+					}
+				}
+				continue
+			}
 			rs := r.Sym()
 			if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) {
 				// If a type is converted to an interface, it is possible to obtain an
diff --git a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
index b62f18c342e106b2f2ee9e3b1384c823950502a8..32a24cf6f00c9823f3c3dbb21c065c46f68a61ca 100644
--- a/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
+++ b/src/cmd/link/internal/ld/testdata/deadcode/ifacemethod.go
@@ -18,6 +18,13 @@ var p *T
 var e interface{}
 
 func main() {
-	p = new(T) // used T, but never converted to interface
+	p = new(T) // used T, but never converted to interface in any reachable code
 	e.(I).M()  // used I and I.M
 }
+
+func Unused() { // convert T to interface, but this function is not reachable
+	var i I = T(0)
+	i.M()
+}
+
+var Unused2 interface{} = T(1) // convert T to interface, in an unreachable global initializer
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 43a0352e0bb4502bc2f245e31705aa77b59c4528..ea99233f67ca1e970c82c78396547cb001b50369 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -63,6 +63,7 @@ type Reloc struct {
 func (rel Reloc) Type() objabi.RelocType { return objabi.RelocType(rel.Reloc.Type()) + rel.typ }
 func (rel Reloc) Sym() Sym               { return rel.l.resolve(rel.r, rel.Reloc.Sym()) }
 func (rel Reloc) SetSym(s Sym)           { rel.Reloc.SetSym(goobj.SymRef{PkgIdx: 0, SymIdx: uint32(s)}) }
+func (rel Reloc) IsMarker() bool         { return rel.Siz() == 0 }
 
 func (rel Reloc) SetType(t objabi.RelocType) {
 	if t != objabi.RelocType(uint8(t)) {
diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go
index 3bd56a6e3adba59ff404580560b8a44ed27f134b..31851fbb56775259c5a882911621f806c0070e3e 100644
--- a/src/cmd/link/internal/wasm/asm.go
+++ b/src/cmd/link/internal/wasm/asm.go
@@ -167,6 +167,9 @@ func asmb2(ctxt *ld.Link, ldr *loader.Loader) {
 			off := int32(0)
 			for ri := 0; ri < relocs.Count(); ri++ {
 				r := relocs.At(ri)
+				if r.Siz() == 0 {
+					continue // skip marker relocations
+				}
 				wfn.Write(P[off:r.Off()])
 				off = r.Off()
 				rs := ldr.ResolveABIAlias(r.Sym())