internal/gocore: fix IsPtr method

It should look up by word index, not byte index, and it
needs a &1 mask also.

Change-Id: Id4b25b120b8b043184e53d70450a5f0b6b197c37
Reviewed-on: https://go-review.googlesource.com/c/debug/+/689375
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
diff --git a/internal/gocore/gocore_test.go b/internal/gocore/gocore_test.go
index 9a5981b..268fbd7 100644
--- a/internal/gocore/gocore_test.go
+++ b/internal/gocore/gocore_test.go
@@ -370,6 +370,38 @@
 	})
 }
 
+func TestGlobals(t *testing.T) {
+	t.Run("goroot", func(t *testing.T) {
+		for _, test := range variations {
+			t.Run(test.String(), func(t *testing.T) {
+				t.Run("globals.go", func(t *testing.T) {
+					p := createAndLoadCore(t, "testdata/testprogs/globals.go", test.buildFlags, test.env)
+					for _, g := range p.Globals() {
+						var want []bool
+						switch g.Name {
+						default:
+							continue
+						case "main.string_":
+							want = []bool{true, false}
+						case "main.slice":
+							want = []bool{true, false, false}
+						case "main.struct_":
+							want = []bool{false, false, false, true, false, true, false, false}
+						}
+						a := g.Addr()
+						for i, wantPtr := range want {
+							gotPtr := p.IsPtr(a.Add(int64(i) * p.Process().PtrSize()))
+							if gotPtr != wantPtr {
+								t.Errorf("IsPtr(%s+%d)=%v, want %v", g.Name, int64(i)*p.Process().PtrSize(), gotPtr, wantPtr)
+							}
+						}
+					}
+				})
+			})
+		}
+	})
+}
+
 // typeName returns a string representing the type of this object.
 func typeName(c *Process, x Object) string {
 	size := c.Size(x)
diff --git a/internal/gocore/object.go b/internal/gocore/object.go
index 8218d7a..fa85a85 100644
--- a/internal/gocore/object.go
+++ b/internal/gocore/object.go
@@ -129,8 +129,8 @@
 				continue
 			}
 			gc := m.r.Field("gc" + s + "mask").Field("bytedata").Address()
-			i := a.Sub(min)
-			return p.proc.ReadUint8(gc.Add(i/8))>>uint(i%8) != 0
+			i := a.Sub(min) / p.proc.PtrSize()
+			return p.proc.ReadUint8(gc.Add(i/8))>>uint(i%8)&1 != 0
 		}
 	}
 	// Everywhere else can't be a pointer. At least, not a pointer into the Go heap.
diff --git a/internal/gocore/testdata/testprogs/globals.go b/internal/gocore/testdata/testprogs/globals.go
new file mode 100644
index 0000000..d163326
--- /dev/null
+++ b/internal/gocore/testdata/testprogs/globals.go
@@ -0,0 +1,36 @@
+// Copyright 2025 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+// Tests to make sure we can tell which globals have pointers.
+
+package main
+
+import (
+	"os"
+	"unsafe"
+
+	"golang.org/x/debug/internal/testenv"
+)
+
+var string_ string = "foo" // string type, in data section
+var slice []byte           // slice type, in bss section
+var struct_ struct {       // a more complicated layout
+	a, b, c uintptr
+	d       *byte
+	e       uintptr
+	f       unsafe.Pointer
+	g, h    uintptr
+}
+
+func main() {
+	testenv.RunThenCrash(os.Getenv("GO_DEBUG_TEST_COREDUMP_FILTER"), func() any {
+		// Reference globals so they don't get deadcoded.
+		string_ = "bar"
+		slice = []byte{1, 2, 3}
+		struct_.a = 3
+		return nil
+	})
+}