ogle/probe: start of package
Implement functions to test validity of addresses for read and write.
Needs some very hacky assembly support; provide this for amd64
only, darwin and linux only.
LGTM=nigeltao
R=nigeltao
https://golang.org/cl/60070045
diff --git a/probe/addr_test.go b/probe/addr_test.go
new file mode 100644
index 0000000..65b1cc9
--- /dev/null
+++ b/probe/addr_test.go
@@ -0,0 +1,123 @@
+// Copyright 2014 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.
+
+package main
+
+import (
+ "reflect"
+ "testing"
+)
+
+var dataItem = 3
+var bssItem [100]int
+
+// addr turns an arbitrary address into a uintptr.
+func addr(p interface{}) uintptr {
+ v := reflect.ValueOf(p)
+ switch v.Kind() {
+ case reflect.Ptr:
+ return v.Elem().UnsafeAddr()
+ default:
+ // TODO: how do we find the address of a text symbol?
+ panic("unknown type " + v.Type().String())
+ }
+}
+
+func TestGoodReadAddresses(t *testing.T) {
+ addrs := []uintptr{
+ base(),
+ addr(&t), // On the stack.
+ addr(&dataItem), // In data.
+ addr(&bssItem), // In bss.
+ heapUsed() - 1,
+ }
+ for _, a := range addrs {
+ if !validRead(a, 1) {
+ t.Errorf("%#x is invalid; should be valid", a)
+ }
+ }
+}
+
+func TestBadReadAddresses(t *testing.T) {
+ addrs := []uintptr{
+ 0,
+ base() - 1,
+ heapUsed() + 1,
+ ^uintptr(0),
+ }
+ for _, a := range addrs {
+ if validRead(a, 1) {
+ t.Errorf("%#x is valid; should be invalid", a)
+ }
+ }
+}
+
+func TestGoodWriteAddresses(t *testing.T) {
+ addrs := []uintptr{
+ addr(&t), // On the stack.
+ addr(&dataItem), // In data.
+ addr(&bssItem), // In bss.
+ heapUsed() - 1,
+ }
+ for _, a := range addrs {
+ if !validWrite(a, 1) {
+ t.Errorf("%#x is invalid; should be valid", a)
+ }
+ }
+}
+
+func TestBadWriteAddresses(t *testing.T) {
+ addrs := []uintptr{
+ 0,
+ base(), // In the text segment.
+ base() - 1,
+ heapUsed(),
+ ^uintptr(0),
+ }
+ for _, a := range addrs {
+ if validWrite(a, 1) {
+ t.Errorf("%#x is valid; should be invalid", a)
+ }
+ }
+}
+
+type span struct {
+ p uintptr
+ size int
+ ok bool
+}
+
+func TestReadAddressSpan(t *testing.T) {
+ spans := []span{
+ {base(), 1, true},
+ {base(), 4096, true},
+ {base(), int(heapStart() - base()), false},
+ {base(), 1e9, false},
+ {heapStart(), 1, true},
+ {heapStart(), 4096, true},
+ {heapStart(), 1e9, false},
+ }
+ for _, s := range spans {
+ if validRead(s.p, s.size) != s.ok {
+ t.Errorf("(%#x,%d) should be %t; is %t", s.p, s.size, s.ok, !s.ok)
+ }
+ }
+}
+
+func TestWriteAddressSpan(t *testing.T) {
+ spans := []span{
+ {etext(), 1, true},
+ {etext(), 4096, true},
+ {etext(), int(heapStart() - base()), false},
+ {etext(), 1e9, false},
+ {heapStart(), 1, true},
+ {heapStart(), 4096, true},
+ {heapStart(), 1e9, false},
+ }
+ for _, s := range spans {
+ if validWrite(s.p, s.size) != s.ok {
+ t.Errorf("(%#x,%d) should be %t; is %t", s.p, s.size, s.ok, !s.ok)
+ }
+ }
+}
diff --git a/probe/probe.go b/probe/probe.go
new file mode 100644
index 0000000..002f671
--- /dev/null
+++ b/probe/probe.go
@@ -0,0 +1,52 @@
+// Copyright 2014 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.
+
+// Package probe is imported by programs to provide (possibly remote)
+// access to a separate debugger program.
+package main
+
+func base() uintptr
+func etext() uintptr
+func edata() uintptr
+func end() uintptr
+
+func heapStart() uintptr
+func heapUsed() uintptr
+func heapEnd() uintptr
+
+// validRead reports whether a read of the specified size can be done at address p.
+func validRead(p uintptr, size int) bool {
+ if size <= 0 {
+ return false
+ }
+ // The read must be in a single contiguous valid region.
+ switch {
+ case base() <= p && p < end():
+ // Assumes text is before data, but ld's binaries always satisfy that constraint.
+ p += uintptr(size)
+ return base() <= p && p <= end()
+ case heapStart() <= p && p < heapUsed(): // Don't allow reads past the used part of the heap.
+ p += uintptr(size)
+ return heapStart() <= p && p <= heapUsed()
+ }
+ return false
+}
+
+// validWrite reports whether a write of the specified size can be done at address p.
+func validWrite(p uintptr, size int) bool {
+ if size <= 0 {
+ return false
+ }
+ // The write must be in a single contiguous valid region.
+ switch {
+ case etext() <= p && p < end():
+ // Assumes text is before data, but ld's binaries always satisfy that constraint.
+ p += uintptr(size)
+ return etext() <= p && p <= end()
+ case heapStart() <= p && p < heapUsed(): // Don't allow writes past the used part of the heap.
+ p += uintptr(size)
+ return heapStart() <= p && p <= heapUsed()
+ }
+ return false
+}
diff --git a/probe/probe_darwin_amd64.s b/probe/probe_darwin_amd64.s
new file mode 100644
index 0000000..a29a792
--- /dev/null
+++ b/probe/probe_darwin_amd64.s
@@ -0,0 +1,62 @@
+// Copyright 2014 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.
+
+// TODO #include "../../cmd/ld/textflag.h"
+
+#define NOSPLIT 4
+
+// Functions that return details about our address space.
+// They use the C-defined symbols like edata and also know
+// a little about the heap and memory layout.
+
+// From the linker. Well-known but might change.
+// TODO: Is there a better way to know this?
+#define INITTEXT 0x2000
+
+// base of the address space.
+TEXT ·base(SB), NOSPLIT, $0
+ MOVQ $INITTEXT, ret+0(FP)
+ RET
+
+// end of the text segment.
+TEXT ·etext(SB), NOSPLIT, $0
+ LEAQ etext+0(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// end of the data segment.
+TEXT ·edata(SB), NOSPLIT, $0
+ LEAQ edata+0(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// end of the pre-defined address space.
+TEXT ·end(SB), NOSPLIT, $0
+ LEAQ end+0(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// These are offsets of critical fields of runtime.mheap.
+// TODO: Very susceptible to change! They (or something equivalent) need to be published by runtime.
+#define arena_start_offset 14504
+#define arena_used_offset 14512
+#define arena_end_offset 14520
+
+// start of heap.
+TEXT ·heapStart(SB), NOSPLIT, $0
+ MOVQ runtime·mheap+arena_start_offset(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// end of active region of heap.
+TEXT ·heapUsed(SB), NOSPLIT, $0
+ MOVQ runtime·mheap+arena_used_offset(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// end of all of heap.
+TEXT ·heapEnd(SB), NOSPLIT, $0
+ MOVQ runtime·mheap+arena_end_offset(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
diff --git a/probe/probe_linux_amd64.s b/probe/probe_linux_amd64.s
new file mode 100644
index 0000000..f373f8c
--- /dev/null
+++ b/probe/probe_linux_amd64.s
@@ -0,0 +1,63 @@
+// Copyright 2014 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.
+
+// TODO #include "../../cmd/ld/textflag.h"
+
+#define NOSPLIT 4
+
+// Functions that return details about our address space.
+// They use the C-defined symbols like edata and also know
+// a little about the heap and memory layout.
+
+// From the linker. Well-known but might change.
+// TODO: Is there a better way to know this?
+#define ELFRESERVE 3072
+#define INITTEXT ((1<<22)+ELFRESERVE)
+
+// base of the address space.
+TEXT ·base(SB), NOSPLIT, $0
+ MOVQ $INITTEXT, ret+0(FP)
+ RET
+
+// end of the text segment.
+TEXT ·etext(SB), NOSPLIT, $0
+ LEAQ etext+0(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// end of the data segment.
+TEXT ·edata(SB), NOSPLIT, $0
+ LEAQ edata+0(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// end of the pre-defined address space.
+TEXT ·end(SB), NOSPLIT, $0
+ LEAQ end+0(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// These are offsets of critical fields of runtime.mheap.
+// TODO: Very susceptible to change! They (or something equivalent) need to be published by runtime.
+#define arena_start_offset 28840
+#define arena_used_offset 28848
+#define arena_end_offset 28856
+
+// start of heap.
+TEXT ·heapStart(SB), NOSPLIT, $0
+ MOVQ runtime·mheap+arena_start_offset(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// end of active region of heap.
+TEXT ·heapUsed(SB), NOSPLIT, $0
+ MOVQ runtime·mheap+arena_used_offset(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET
+
+// end of all of heap.
+TEXT ·heapEnd(SB), NOSPLIT, $0
+ MOVQ runtime·mheap+arena_end_offset(SB), BX
+ MOVQ BX, ret+0(FP)
+ RET