runtime, cmd/compile: rename memclr -> memclrNoHeapPointers

Since barrier-less memclr is only safe in very narrow circumstances,
this commit renames memclr to avoid accidentally calling memclr on
typed memory. This can cause subtle, non-deterministic bugs, so it's
worth some effort to prevent. In the near term, this will also prevent
bugs creeping in from any concurrent CLs that add calls to memclr; if
this happens, whichever patch hits master second will fail to compile.

This also adds the other new memclr variants to the compiler's
builtin.go to minimize the churn on that binary blob. We'll use these
in future commits.

Updates #17503.

Change-Id: I00eead049f5bd35ca107ea525966831f3d1ed9ca
Reviewed-on: https://go-review.googlesource.com/31369
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
diff --git a/src/runtime/alg.go b/src/runtime/alg.go
index 80f205c..5c378c6 100644
--- a/src/runtime/alg.go
+++ b/src/runtime/alg.go
@@ -275,12 +275,6 @@
 	return algarray[alg_INTER].hash(noescape(unsafe.Pointer(&i)), seed)
 }
 
-// Testing adapter for memclr
-func memclrBytes(b []byte) {
-	s := (*slice)(unsafe.Pointer(&b))
-	memclr(s.array, uintptr(s.len))
-}
-
 const hashRandomBytes = sys.PtrSize / 4 * 64
 
 // used in asm_{386,amd64}.s to seed the hash function
diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s
index 60613b1..ab73508 100644
--- a/src/runtime/asm_amd64p32.s
+++ b/src/runtime/asm_amd64p32.s
@@ -484,7 +484,7 @@
 	MOVL	0, AX
 	RET
 
-TEXT runtime·memclr(SB),NOSPLIT,$0-8
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-8
 	MOVL	ptr+0(FP), DI
 	MOVL	n+4(FP), CX
 	MOVQ	CX, BX
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index 5510a27..d83b3b0 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -155,7 +155,11 @@
 var Int64Hash = int64Hash
 var EfaceHash = efaceHash
 var IfaceHash = ifaceHash
-var MemclrBytes = memclrBytes
+
+func MemclrBytes(b []byte) {
+	s := (*slice)(unsafe.Pointer(&b))
+	memclrNoHeapPointers(s.array, uintptr(s.len))
+}
 
 var HashLoad = &hashLoad
 
diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go
index 86d3b37..086d374 100644
--- a/src/runtime/hashmap.go
+++ b/src/runtime/hashmap.go
@@ -1090,7 +1090,7 @@
 			if t.bucket.kind&kindNoPointers == 0 {
 				memclrHasPointers(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
 			} else {
-				memclr(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
+				memclrNoHeapPointers(add(unsafe.Pointer(b), dataOffset), uintptr(t.bucketsize)-dataOffset)
 			}
 		}
 	}
diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go
index 8cdccb8..6039417 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -631,7 +631,7 @@
 			s.ensureSwept()
 		}
 	}
-	memclr(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
+	memclrNoHeapPointers(unsafe.Pointer(&typecache), unsafe.Sizeof(typecache))
 	dwrite(unsafe.Pointer(&dumphdr[0]), uintptr(len(dumphdr)))
 	dumpparams()
 	dumpitabs()
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index c5f6fac..7cdca03 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -682,7 +682,7 @@
 			}
 			x = unsafe.Pointer(v)
 			if needzero && span.needzero != 0 {
-				memclr(unsafe.Pointer(v), size)
+				memclrNoHeapPointers(unsafe.Pointer(v), size)
 			}
 		}
 	} else {
diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index 1a7bef4..a8766c7 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -333,14 +333,17 @@
 }
 
 // typedmemclr clears the typed memory at ptr with type typ. The
-// memory at ptr must already be type-safe.
+// memory at ptr must already be initialized (and hence in type-safe
+// state). If the memory is being initialized for the first time, see
+// memclrNoHeapPointers.
 //
 // If the caller knows that typ has pointers, it can alternatively
 // call memclrHasPointers.
 //
 //go:nosplit
 func typedmemclr(typ *_type, ptr unsafe.Pointer) {
-	memclr(ptr, typ.size)
+	// TODO(austin): Call the hybrid barrier.
+	memclrNoHeapPointers(ptr, typ.size)
 }
 
 // memclrHasPointers clears n bytes of typed memory starting at ptr.
@@ -350,5 +353,6 @@
 //
 //go:nosplit
 func memclrHasPointers(ptr unsafe.Pointer, n uintptr) {
-	memclr(ptr, n)
+	// TODO(austin): Call the hybrid barrier.
+	memclrNoHeapPointers(ptr, n)
 }
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index f1f9158..be52bfa 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -742,7 +742,7 @@
 		}
 		return
 	}
-	memclr(unsafe.Pointer(subtractb(h.bitp, nbyte-1)), nbyte)
+	memclrNoHeapPointers(unsafe.Pointer(subtractb(h.bitp, nbyte-1)), nbyte)
 }
 
 // initCheckmarkSpan initializes a span for being checkmarked.
@@ -1433,7 +1433,7 @@
 	}
 	endProg := unsafe.Pointer(subtractb(h.bitp, (totalBits+3)/4))
 	endAlloc := unsafe.Pointer(subtractb(h.bitp, allocSize/heapBitmapScale))
-	memclr(add(endAlloc, 1), uintptr(endProg)-uintptr(endAlloc))
+	memclrNoHeapPointers(add(endAlloc, 1), uintptr(endProg)-uintptr(endAlloc))
 }
 
 // progToPointerMask returns the 1-bit pointer mask output by the GC program prog.
diff --git a/src/runtime/mem_plan9.go b/src/runtime/mem_plan9.go
index 3d82a98..98bfc7f 100644
--- a/src/runtime/mem_plan9.go
+++ b/src/runtime/mem_plan9.go
@@ -38,7 +38,7 @@
 				p.size -= n
 				p = (*memHdr)(add(unsafe.Pointer(p), p.size))
 			}
-			memclr(unsafe.Pointer(p), unsafe.Sizeof(memHdr{}))
+			*p = memHdr{}
 			return unsafe.Pointer(p)
 		}
 		prevp = p
@@ -48,7 +48,7 @@
 
 func memFree(ap unsafe.Pointer, n uintptr) {
 	n = memRound(n)
-	memclr(ap, n)
+	memclrNoHeapPointers(ap, n)
 	bp := (*memHdr)(ap)
 	bp.size = n
 	bpn := uintptr(ap)
@@ -63,7 +63,7 @@
 		if bpn+bp.size == uintptr(unsafe.Pointer(p)) {
 			bp.size += p.size
 			bp.next = p.next
-			memclr(unsafe.Pointer(p), unsafe.Sizeof(memHdr{}))
+			*p = memHdr{}
 		} else {
 			bp.next.set(p)
 		}
@@ -77,14 +77,14 @@
 	if bpn+bp.size == uintptr(unsafe.Pointer(p.next)) {
 		bp.size += p.next.ptr().size
 		bp.next = p.next.ptr().next
-		memclr(unsafe.Pointer(p.next), unsafe.Sizeof(memHdr{}))
+		*p.next.ptr() = memHdr{}
 	} else {
 		bp.next = p.next
 	}
 	if uintptr(unsafe.Pointer(p))+p.size == bpn {
 		p.size += bp.size
 		p.next = bp.next
-		memclr(unsafe.Pointer(bp), unsafe.Sizeof(memHdr{}))
+		*bp = memHdr{}
 	} else {
 		p.next.set(bp)
 	}
diff --git a/src/runtime/memclr_386.s b/src/runtime/memclr_386.s
index ce962f3..ef6e602 100644
--- a/src/runtime/memclr_386.s
+++ b/src/runtime/memclr_386.s
@@ -8,8 +8,8 @@
 
 // NOTE: Windows externalthreadhandler expects memclr to preserve DX.
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT, $0-8
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-8
 	MOVL	ptr+0(FP), DI
 	MOVL	n+4(FP), BX
 	XORL	AX, AX
diff --git a/src/runtime/memclr_amd64.s b/src/runtime/memclr_amd64.s
index 6f30eca..244f5b4 100644
--- a/src/runtime/memclr_amd64.s
+++ b/src/runtime/memclr_amd64.s
@@ -8,8 +8,8 @@
 
 // NOTE: Windows externalthreadhandler expects memclr to preserve DX.
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT, $0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-16
 	MOVQ	ptr+0(FP), DI
 	MOVQ	n+8(FP), BX
 	XORQ	AX, AX
diff --git a/src/runtime/memclr_arm.s b/src/runtime/memclr_arm.s
index 6ad70fb..eb37674 100644
--- a/src/runtime/memclr_arm.s
+++ b/src/runtime/memclr_arm.s
@@ -30,7 +30,7 @@
 #define N	R12
 #define TMP	R12				/* N and TMP don't overlap */
 
-TEXT runtime·memclr(SB),NOSPLIT,$0-8
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-8
 	MOVW	ptr+0(FP), TO
 	MOVW	n+4(FP), N
 	MOVW	$0, R0
diff --git a/src/runtime/memclr_arm64.s b/src/runtime/memclr_arm64.s
index 47c6b73..9d756bc 100644
--- a/src/runtime/memclr_arm64.s
+++ b/src/runtime/memclr_arm64.s
@@ -4,8 +4,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
 	MOVD	ptr+0(FP), R3
 	MOVD	n+8(FP), R4
 	// TODO(mwhudson): this is written this way to avoid tickling
diff --git a/src/runtime/memclr_mips64x.s b/src/runtime/memclr_mips64x.s
index 30a4af3..5018d43 100644
--- a/src/runtime/memclr_mips64x.s
+++ b/src/runtime/memclr_mips64x.s
@@ -6,8 +6,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
 	MOVV	ptr+0(FP), R1
 	MOVV	n+8(FP), R2
 	ADDV	R1, R2, R4
diff --git a/src/runtime/memclr_plan9_386.s b/src/runtime/memclr_plan9_386.s
index 4707ab2..c3d92a9 100644
--- a/src/runtime/memclr_plan9_386.s
+++ b/src/runtime/memclr_plan9_386.s
@@ -4,8 +4,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT, $0-8
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT, $0-8
 	MOVL	ptr+0(FP), DI
 	MOVL	n+4(FP), BX
 	XORL	AX, AX
diff --git a/src/runtime/memclr_plan9_amd64.s b/src/runtime/memclr_plan9_amd64.s
index 37e61df..d4d1a3a 100644
--- a/src/runtime/memclr_plan9_amd64.s
+++ b/src/runtime/memclr_plan9_amd64.s
@@ -4,8 +4,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
 	MOVQ	ptr+0(FP), DI
 	MOVQ	n+8(FP), CX
 	MOVQ	CX, BX
diff --git a/src/runtime/memclr_ppc64x.s b/src/runtime/memclr_ppc64x.s
index f7375db..e3a4673 100644
--- a/src/runtime/memclr_ppc64x.s
+++ b/src/runtime/memclr_ppc64x.s
@@ -6,8 +6,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB), NOSPLIT|NOFRAME, $0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB), NOSPLIT|NOFRAME, $0-16
 	MOVD ptr+0(FP), R3
 	MOVD n+8(FP), R4
 
diff --git a/src/runtime/memclr_s390x.s b/src/runtime/memclr_s390x.s
index 846131e..43da10d 100644
--- a/src/runtime/memclr_s390x.s
+++ b/src/runtime/memclr_s390x.s
@@ -4,8 +4,8 @@
 
 #include "textflag.h"
 
-// void runtime·memclr(void*, uintptr)
-TEXT runtime·memclr(SB),NOSPLIT|NOFRAME,$0-16
+// void runtime·memclrNoHeapPointers(void*, uintptr)
+TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT|NOFRAME,$0-16
 	MOVD	ptr+0(FP), R4
 	MOVD	n+8(FP), R5
 
diff --git a/src/runtime/mfixalloc.go b/src/runtime/mfixalloc.go
index 0d3d895..fe4b0fc 100644
--- a/src/runtime/mfixalloc.go
+++ b/src/runtime/mfixalloc.go
@@ -72,7 +72,7 @@
 		f.list = f.list.next
 		f.inuse += f.size
 		if f.zero {
-			memclr(v, f.size)
+			memclrNoHeapPointers(v, f.size)
 		}
 		return v
 	}
diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go
index e81e410..a0f5599 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -629,7 +629,7 @@
 
 	if s != nil {
 		if needzero && s.needzero != 0 {
-			memclr(unsafe.Pointer(s.base()), s.npages<<_PageShift)
+			memclrNoHeapPointers(unsafe.Pointer(s.base()), s.npages<<_PageShift)
 		}
 		s.needzero = 0
 	}
@@ -1418,7 +1418,7 @@
 	} else {
 		result = gcBitsArenas.free
 		gcBitsArenas.free = gcBitsArenas.free.next
-		memclr(unsafe.Pointer(result), gcBitsChunkBytes)
+		memclrNoHeapPointers(unsafe.Pointer(result), gcBitsChunkBytes)
 	}
 	result.next = nil
 	// If result.bits is not 8 byte aligned adjust index so
diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go
index d6def7b..067fb3b 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -317,7 +317,7 @@
 	// here because it could cause a deadlock.
 	_g_.m.libcall.fn = uintptr(unsafe.Pointer(&libc_malloc))
 	_g_.m.libcall.n = 1
-	memclr(unsafe.Pointer(&_g_.m.scratch), uintptr(len(_g_.m.scratch.v)))
+	_g_.m.scratch = mscratch{}
 	_g_.m.scratch.v[0] = unsafe.Sizeof(*sem)
 	_g_.m.libcall.args = uintptr(unsafe.Pointer(&_g_.m.scratch))
 	asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&_g_.m.libcall))
@@ -337,7 +337,7 @@
 
 		_m_.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_reltimedwait_np))
 		_m_.libcall.n = 2
-		memclr(unsafe.Pointer(&_m_.scratch), uintptr(len(_m_.scratch.v)))
+		_m_.scratch = mscratch{}
 		_m_.scratch.v[0] = _m_.waitsema
 		_m_.scratch.v[1] = uintptr(unsafe.Pointer(&_m_.ts))
 		_m_.libcall.args = uintptr(unsafe.Pointer(&_m_.scratch))
@@ -353,7 +353,7 @@
 	for {
 		_m_.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_wait))
 		_m_.libcall.n = 1
-		memclr(unsafe.Pointer(&_m_.scratch), uintptr(len(_m_.scratch.v)))
+		_m_.scratch = mscratch{}
 		_m_.scratch.v[0] = _m_.waitsema
 		_m_.libcall.args = uintptr(unsafe.Pointer(&_m_.scratch))
 		asmcgocall(unsafe.Pointer(&asmsysvicall6), unsafe.Pointer(&_m_.libcall))
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 8b57514..af11101 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -2812,7 +2812,7 @@
 	}
 	memmove(unsafe.Pointer(spArg), unsafe.Pointer(argp), uintptr(narg))
 
-	memclr(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
+	memclrNoHeapPointers(unsafe.Pointer(&newg.sched), unsafe.Sizeof(newg.sched))
 	newg.sched.sp = sp
 	newg.stktopsp = sp
 	newg.sched.pc = funcPC(goexit) + sys.PCQuantum // +PCQuantum so that previous instruction is in same function
diff --git a/src/runtime/slice.go b/src/runtime/slice.go
index 7f4de45..0f49df1 100644
--- a/src/runtime/slice.go
+++ b/src/runtime/slice.go
@@ -141,7 +141,7 @@
 		memmove(p, old.array, lenmem)
 		// The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length).
 		// Only clear the part that will not be overwritten.
-		memclr(add(p, newlenmem), capmem-newlenmem)
+		memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
 	} else {
 		// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
 		p = mallocgc(capmem, et, true)
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index dfc71b4..ea9a69a 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -436,7 +436,7 @@
 	}
 	if stackDebug >= 1 {
 		println("stackfree", v, n)
-		memclr(v, n) // for testing, clobber stack data
+		memclrNoHeapPointers(v, n) // for testing, clobber stack data
 	}
 	if debug.efence != 0 || stackFromSystem != 0 {
 		if debug.efence != 0 || stackFaultOnFree != 0 {
diff --git a/src/runtime/string.go b/src/runtime/string.go
index 4cf165b..0752823 100644
--- a/src/runtime/string.go
+++ b/src/runtime/string.go
@@ -249,7 +249,7 @@
 	cap := roundupsize(uintptr(size))
 	p := mallocgc(cap, nil, false)
 	if cap != uintptr(size) {
-		memclr(add(p, uintptr(size)), cap-uintptr(size))
+		memclrNoHeapPointers(add(p, uintptr(size)), cap-uintptr(size))
 	}
 
 	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)}
@@ -264,7 +264,7 @@
 	mem := roundupsize(uintptr(size) * 4)
 	p := mallocgc(mem, nil, false)
 	if mem != uintptr(size)*4 {
-		memclr(add(p, uintptr(size)*4), mem-uintptr(size)*4)
+		memclrNoHeapPointers(add(p, uintptr(size)*4), mem-uintptr(size)*4)
 	}
 
 	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(mem / 4)}
diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go
index 693a344..7384c78 100644
--- a/src/runtime/stubs.go
+++ b/src/runtime/stubs.go
@@ -60,20 +60,24 @@
 	throw("systemstack called from unexpected goroutine")
 }
 
-// memclr clears n bytes starting at ptr.
+// memclrNoHeapPointers clears n bytes starting at ptr.
 //
-// Usually you should use typedmemclr. memclr should be used only when
-// the caller knows that *ptr contains no heap pointers or to
-// initialize memory to a type-safe state when allocation reuses dead
-// memory.
+// Usually you should use typedmemclr. memclrNoHeapPointers should be
+// used only when the caller knows that *ptr contains no heap pointers
+// because either:
+//
+// 1. *ptr is initialized memory and its type is pointer-free.
+//
+// 2. *ptr is uninitialized memory (e.g., memory that's being reused
+//    for a new allocation) and hence contains only "junk".
 //
 // in memclr_*.s
 //go:noescape
-func memclr(ptr unsafe.Pointer, n uintptr)
+func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
 
-//go:linkname reflect_memclr reflect.memclr
-func reflect_memclr(ptr unsafe.Pointer, n uintptr) {
-	memclr(ptr, n)
+//go:linkname reflect_memclrNoHeapPointers reflect.memclrNoHeapPointers
+func reflect_memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) {
+	memclrNoHeapPointers(ptr, n)
 }
 
 // memmove copies n bytes from "from" to "to".
diff --git a/src/runtime/sys_windows_386.s b/src/runtime/sys_windows_386.s
index 60302e0..bd5de33 100644
--- a/src/runtime/sys_windows_386.s
+++ b/src/runtime/sys_windows_386.s
@@ -192,7 +192,7 @@
 	SUBL	$m__size, SP		// space for M
 	MOVL	SP, 0(SP)
 	MOVL	$m__size, 4(SP)
-	CALL	runtime·memclr(SB)	// smashes AX,BX,CX
+	CALL	runtime·memclrNoHeapPointers(SB)	// smashes AX,BX,CX
 
 	LEAL	m_tls(SP), CX
 	MOVL	CX, 0x14(FS)
@@ -203,7 +203,7 @@
 
 	MOVL	SP, 0(SP)
 	MOVL	$g__size, 4(SP)
-	CALL	runtime·memclr(SB)	// smashes AX,BX,CX
+	CALL	runtime·memclrNoHeapPointers(SB)	// smashes AX,BX,CX
 	LEAL	g__size(SP), BX
 	MOVL	BX, g_m(SP)
 
diff --git a/src/runtime/sys_windows_amd64.s b/src/runtime/sys_windows_amd64.s
index 9679099..c61b79d 100644
--- a/src/runtime/sys_windows_amd64.s
+++ b/src/runtime/sys_windows_amd64.s
@@ -236,7 +236,7 @@
 	SUBQ	$m__size, SP		// space for M
 	MOVQ	SP, 0(SP)
 	MOVQ	$m__size, 8(SP)
-	CALL	runtime·memclr(SB)	// smashes AX,BX,CX, maybe BP
+	CALL	runtime·memclrNoHeapPointers(SB)	// smashes AX,BX,CX, maybe BP
 
 	LEAQ	m_tls(SP), CX
 	MOVQ	CX, 0x28(GS)
@@ -247,7 +247,7 @@
 
 	MOVQ	SP, 0(SP)
 	MOVQ	$g__size, 8(SP)
-	CALL	runtime·memclr(SB)	// smashes AX,BX,CX, maybe BP
+	CALL	runtime·memclrNoHeapPointers(SB)	// smashes AX,BX,CX, maybe BP
 	LEAQ	g__size(SP), BX
 	MOVQ	BX, g_m(SP)
 
diff --git a/src/runtime/write_err_android.go b/src/runtime/write_err_android.go
index 4411a14..748dec6 100644
--- a/src/runtime/write_err_android.go
+++ b/src/runtime/write_err_android.go
@@ -75,7 +75,9 @@
 		if v == '\n' || writePos == len(dst)-1 {
 			dst[writePos] = 0
 			write(writeFD, unsafe.Pointer(&writeBuf[0]), int32(hlen+writePos))
-			memclrBytes(dst)
+			for i := range dst {
+				dst[i] = 0
+			}
 			writePos = 0
 		}
 	}