"debug/pe",
"internal/goversion",
"internal/race",
+ "internal/unsafeheader",
"internal/xcoff",
"math/big",
"math/bits",
"encoding/binary"
"errors"
"fmt"
+ "internal/unsafeheader"
"io"
"unsafe"
)
}
func toString(b []byte) string {
- type stringHeader struct {
- str unsafe.Pointer
- len int
- }
-
if len(b) == 0 {
return ""
}
- ss := stringHeader{str: unsafe.Pointer(&b[0]), len: len(b)}
- s := *(*string)(unsafe.Pointer(&ss))
+
+ var s string
+ hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
+ hdr.Data = unsafe.Pointer(&b[0])
+ hdr.Len = len(b)
+
return s
}
"cmd/internal/sys"
"cmd/oldlink/internal/sym"
"fmt"
+ "internal/unsafeheader"
"io"
"log"
"os"
return p
}
-type stringHeader struct {
- str unsafe.Pointer
- len int
-}
-
func mkROString(rodata []byte) string {
if len(rodata) == 0 {
return ""
}
- ss := stringHeader{str: unsafe.Pointer(&rodata[0]), len: len(rodata)}
- s := *(*string)(unsafe.Pointer(&ss))
+
+ var s string
+ hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
+ hdr.Data = unsafe.Pointer(&rodata[0])
+ hdr.Len = len(rodata)
+
return s
}
"unsafe": {},
"internal/cpu": {},
"internal/bytealg": {"unsafe", "internal/cpu"},
- "internal/reflectlite": {"runtime", "unsafe"},
+ "internal/reflectlite": {"runtime", "unsafe", "internal/unsafeheader"},
+ "internal/unsafeheader": {"unsafe"},
"L0": {
"errors",
"image/color": {"L2"}, // interfaces
"image/color/palette": {"L2", "image/color"},
"internal/fmtsort": {"reflect", "sort"},
- "reflect": {"L2"},
+ "reflect": {"L2", "internal/unsafeheader"},
"sort": {"internal/reflectlite"},
"L3": {
// End of linear dependency definitions.
// Operating system access.
- "syscall": {"L0", "internal/oserror", "internal/race", "internal/syscall/windows/sysdll", "syscall/js", "unicode/utf16"},
+ "syscall": {"L0", "internal/oserror", "internal/race", "internal/syscall/windows/sysdll", "internal/unsafeheader", "syscall/js", "unicode/utf16"},
"syscall/js": {"L0"},
"internal/oserror": {"L0"},
"internal/syscall/unix": {"L0", "syscall"},
package reflectlite
-import "unsafe"
+import (
+ "internal/unsafeheader"
+ "unsafe"
+)
// Swapper returns a function that swaps the elements in the provided
// slice.
}
}
- s := (*sliceHeader)(v.ptr)
+ s := (*unsafeheader.Slice)(v.ptr)
tmp := unsafe_New(typ) // swap scratch space
return func(i, j int) {
package reflectlite
import (
+ "internal/unsafeheader"
"unsafe"
)
}
b := (*[4]byte)(unsafe.Pointer(n.bytes))
- hdr := (*stringHeader)(unsafe.Pointer(&s))
+ hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
hdr.Data = unsafe.Pointer(&b[3])
hdr.Len = int(b[1])<<8 | int(b[2])
return s
return ""
}
nl := n.nameLen()
- hdr := (*stringHeader)(unsafe.Pointer(&s))
+ hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
hdr.Data = unsafe.Pointer(n.data(3+nl+2, "non-empty string"))
hdr.Len = tl
return s
package reflectlite
import (
+ "internal/unsafeheader"
"runtime"
"unsafe"
)
return maplen(v.pointer())
case Slice:
// Slice is bigger than a word; assume flagIndir.
- return (*sliceHeader)(v.ptr).Len
+ return (*unsafeheader.Slice)(v.ptr).Len
case String:
// String is bigger than a word; assume flagIndir.
- return (*stringHeader)(v.ptr).Len
+ return (*unsafeheader.String)(v.ptr).Len
}
panic(&ValueError{"reflect.Value.Len", v.kind()})
}
return v.typ
}
-// stringHeader is a safe version of StringHeader used within this package.
-type stringHeader struct {
- Data unsafe.Pointer
- Len int
-}
-
-// sliceHeader is a safe version of SliceHeader used within this package.
-type sliceHeader struct {
- Data unsafe.Pointer
- Len int
- Cap int
-}
-
/*
* constructors
*/
--- /dev/null
+// Copyright 2020 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 unsafeheader contains header declarations for the Go runtime's slice
+// and string implementations.
+//
+// This package allows packages that cannot import "reflect" to use types that
+// are tested to be equivalent to reflect.SliceHeader and reflect.StringHeader.
+package unsafeheader
+
+import (
+ "unsafe"
+)
+
+// Slice is the runtime representation of a slice.
+// It cannot be used safely or portably and its representation may
+// change in a later release.
+//
+// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the
+// data it references will not be garbage collected.
+type Slice struct {
+ Data unsafe.Pointer
+ Len int
+ Cap int
+}
+
+// String is the runtime representation of a string.
+// It cannot be used safely or portably and its representation may
+// change in a later release.
+//
+// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the
+// data it references will not be garbage collected.
+type String struct {
+ Data unsafe.Pointer
+ Len int
+}
--- /dev/null
+// Copyright 2020 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 unsafeheader_test
+
+import (
+ "bytes"
+ "internal/unsafeheader"
+ "reflect"
+ "testing"
+ "unsafe"
+)
+
+// TestTypeMatchesReflectType ensures that the name and layout of the
+// unsafeheader types matches the corresponding Header types in the reflect
+// package.
+func TestTypeMatchesReflectType(t *testing.T) {
+ t.Run("Slice", func(t *testing.T) {
+ testHeaderMatchesReflect(t, unsafeheader.Slice{}, reflect.SliceHeader{})
+ })
+
+ t.Run("String", func(t *testing.T) {
+ testHeaderMatchesReflect(t, unsafeheader.String{}, reflect.StringHeader{})
+ })
+}
+
+func testHeaderMatchesReflect(t *testing.T, header, reflectHeader interface{}) {
+ h := reflect.TypeOf(header)
+ rh := reflect.TypeOf(reflectHeader)
+
+ for i := 0; i < h.NumField(); i++ {
+ f := h.Field(i)
+ rf, ok := rh.FieldByName(f.Name)
+ if !ok {
+ t.Errorf("Field %d of %v is named %s, but no such field exists in %v", i, h, f.Name, rh)
+ continue
+ }
+ if !typeCompatible(f.Type, rf.Type) {
+ t.Errorf("%v.%s has type %v, but %v.%s has type %v", h, f.Name, f.Type, rh, rf.Name, rf.Type)
+ }
+ if f.Offset != rf.Offset {
+ t.Errorf("%v.%s has offset %d, but %v.%s has offset %d", h, f.Name, f.Offset, rh, rf.Name, rf.Offset)
+ }
+ }
+
+ if h.NumField() != rh.NumField() {
+ t.Errorf("%v has %d fields, but %v has %d", h, h.NumField(), rh, rh.NumField())
+ }
+ if h.Align() != rh.Align() {
+ t.Errorf("%v has alignment %d, but %v has alignment %d", h, h.Align(), rh, rh.Align())
+ }
+}
+
+var (
+ unsafePointerType = reflect.TypeOf(unsafe.Pointer(nil))
+ uintptrType = reflect.TypeOf(uintptr(0))
+)
+
+func typeCompatible(t, rt reflect.Type) bool {
+ return t == rt || (t == unsafePointerType && rt == uintptrType)
+}
+
+// TestWriteThroughHeader ensures that the headers in the unsafeheader package
+// can successfully mutate variables of the corresponding built-in types.
+//
+// This test is expected to fail under -race (which implicitly enables
+// -d=checkptr) if the runtime views the header types as incompatible with the
+// underlying built-in types.
+func TestWriteThroughHeader(t *testing.T) {
+ t.Run("Slice", func(t *testing.T) {
+ s := []byte("Hello, checkptr!")[:5]
+
+ var alias []byte
+ hdr := (*unsafeheader.Slice)(unsafe.Pointer(&alias))
+ hdr.Data = unsafe.Pointer(&s[0])
+ hdr.Cap = cap(s)
+ hdr.Len = len(s)
+
+ if !bytes.Equal(alias, s) {
+ t.Errorf("alias of %T(%q) constructed via Slice = %T(%q)", s, s, alias, alias)
+ }
+ if cap(alias) != cap(s) {
+ t.Errorf("alias of %T with cap %d has cap %d", s, cap(s), cap(alias))
+ }
+ })
+
+ t.Run("String", func(t *testing.T) {
+ s := "Hello, checkptr!"
+
+ var alias string
+ hdr := (*unsafeheader.String)(unsafe.Pointer(&alias))
+ hdr.Data = (*unsafeheader.String)(unsafe.Pointer(&s)).Data
+ hdr.Len = len(s)
+
+ if alias != s {
+ t.Errorf("alias of %q constructed via String = %q", s, alias)
+ }
+ })
+}
package reflect
-import "unsafe"
+import (
+ "internal/unsafeheader"
+ "unsafe"
+)
// Swapper returns a function that swaps the elements in the provided
// slice.
}
}
- s := (*sliceHeader)(v.ptr)
+ s := (*unsafeheader.Slice)(v.ptr)
tmp := unsafe_New(typ) // swap scratch space
return func(i, j int) {
package reflect
import (
+ "internal/unsafeheader"
"strconv"
"sync"
"unicode"
}
b := (*[4]byte)(unsafe.Pointer(n.bytes))
- hdr := (*stringHeader)(unsafe.Pointer(&s))
+ hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
hdr.Data = unsafe.Pointer(&b[3])
hdr.Len = int(b[1])<<8 | int(b[2])
return s
return ""
}
nl := n.nameLen()
- hdr := (*stringHeader)(unsafe.Pointer(&s))
+ hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
hdr.Data = unsafe.Pointer(n.data(3+nl+2, "non-empty string"))
hdr.Len = tl
return s
package reflect
import (
+ "internal/unsafeheader"
"math"
"runtime"
"unsafe"
return chancap(v.pointer())
case Slice:
// Slice is always bigger than a word; assume flagIndir.
- return (*sliceHeader)(v.ptr).Cap
+ return (*unsafeheader.Slice)(v.ptr).Cap
}
panic(&ValueError{"reflect.Value.Cap", v.kind()})
}
case Slice:
// Element flag same as Elem of Ptr.
// Addressable, indirect, possibly read-only.
- s := (*sliceHeader)(v.ptr)
+ s := (*unsafeheader.Slice)(v.ptr)
if uint(i) >= uint(s.Len) {
panic("reflect: slice index out of range")
}
return Value{typ, val, fl}
case String:
- s := (*stringHeader)(v.ptr)
+ s := (*unsafeheader.String)(v.ptr)
if uint(i) >= uint(s.Len) {
panic("reflect: string index out of range")
}
return maplen(v.pointer())
case Slice:
// Slice is bigger than a word; assume flagIndir.
- return (*sliceHeader)(v.ptr).Len
+ return (*unsafeheader.Slice)(v.ptr).Len
case String:
// String is bigger than a word; assume flagIndir.
- return (*stringHeader)(v.ptr).Len
+ return (*unsafeheader.String)(v.ptr).Len
}
panic(&ValueError{"reflect.Value.Len", v.kind()})
}
func (v Value) SetLen(n int) {
v.mustBeAssignable()
v.mustBe(Slice)
- s := (*sliceHeader)(v.ptr)
+ s := (*unsafeheader.Slice)(v.ptr)
if uint(n) > uint(s.Cap) {
panic("reflect: slice length out of range in SetLen")
}
func (v Value) SetCap(n int) {
v.mustBeAssignable()
v.mustBe(Slice)
- s := (*sliceHeader)(v.ptr)
+ s := (*unsafeheader.Slice)(v.ptr)
if n < s.Len || n > s.Cap {
panic("reflect: slice capacity out of range in SetCap")
}
case Slice:
typ = (*sliceType)(unsafe.Pointer(v.typ))
- s := (*sliceHeader)(v.ptr)
+ s := (*unsafeheader.Slice)(v.ptr)
base = s.Data
cap = s.Cap
case String:
- s := (*stringHeader)(v.ptr)
+ s := (*unsafeheader.String)(v.ptr)
if i < 0 || j < i || j > s.Len {
panic("reflect.Value.Slice: string slice index out of bounds")
}
- var t stringHeader
+ var t unsafeheader.String
if i < s.Len {
- t = stringHeader{arrayAt(s.Data, i, 1, "i < s.Len"), j - i}
+ t = unsafeheader.String{Data: arrayAt(s.Data, i, 1, "i < s.Len"), Len: j - i}
}
return Value{v.typ, unsafe.Pointer(&t), v.flag}
}
// Declare slice so that gc can see the base pointer in it.
var x []unsafe.Pointer
- // Reinterpret as *sliceHeader to edit.
- s := (*sliceHeader)(unsafe.Pointer(&x))
+ // Reinterpret as *unsafeheader.Slice to edit.
+ s := (*unsafeheader.Slice)(unsafe.Pointer(&x))
s.Len = j - i
s.Cap = cap - i
if cap-i > 0 {
case Slice:
typ = (*sliceType)(unsafe.Pointer(v.typ))
- s := (*sliceHeader)(v.ptr)
+ s := (*unsafeheader.Slice)(v.ptr)
base = s.Data
cap = s.Cap
}
// can see the base pointer in it.
var x []unsafe.Pointer
- // Reinterpret as *sliceHeader to edit.
- s := (*sliceHeader)(unsafe.Pointer(&x))
+ // Reinterpret as *unsafeheader.Slice to edit.
+ s := (*unsafeheader.Slice)(unsafe.Pointer(&x))
s.Len = j - i
s.Cap = k - i
if k-i > 0 {
Len int
}
-// stringHeader is a safe version of StringHeader used within this package.
-type stringHeader struct {
- Data unsafe.Pointer
- Len int
-}
-
// SliceHeader is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
Cap int
}
-// sliceHeader is a safe version of SliceHeader used within this package.
-type sliceHeader struct {
- Data unsafe.Pointer
- Len int
- Cap int
-}
-
func typesMustMatch(what string, t1, t2 Type) {
if t1 != t2 {
panic(what + ": " + t1.String() + " != " + t2.String())
typesMustMatch("reflect.Copy", de, se)
}
- var ds, ss sliceHeader
+ var ds, ss unsafeheader.Slice
if dk == Array {
ds.Data = dst.ptr
ds.Len = dst.Len()
ds.Cap = ds.Len
} else {
- ds = *(*sliceHeader)(dst.ptr)
+ ds = *(*unsafeheader.Slice)(dst.ptr)
}
if sk == Array {
ss.Data = src.ptr
ss.Len = src.Len()
ss.Cap = ss.Len
} else if sk == Slice {
- ss = *(*sliceHeader)(src.ptr)
+ ss = *(*unsafeheader.Slice)(src.ptr)
} else {
- sh := *(*stringHeader)(src.ptr)
+ sh := *(*unsafeheader.String)(src.ptr)
ss.Data = sh.Data
ss.Len = sh.Len
ss.Cap = sh.Len
panic("reflect.MakeSlice: len > cap")
}
- s := sliceHeader{unsafe_NewArray(typ.Elem().(*rtype), cap), len, cap}
+ s := unsafeheader.Slice{Data: unsafe_NewArray(typ.Elem().(*rtype), cap), Len: len, Cap: cap}
return Value{typ.(*rtype), unsafe.Pointer(&s), flagIndir | flag(Slice)}
}
// typedslicecopy copies a slice of elemType values from src to dst,
// returning the number of elements copied.
//go:noescape
-func typedslicecopy(elemType *rtype, dst, src sliceHeader) int
+func typedslicecopy(elemType *rtype, dst, src unsafeheader.Slice) int
//go:noescape
func typehash(t *rtype, p unsafe.Pointer, h uintptr) uintptr
import (
"internal/oserror"
"internal/race"
+ "internal/unsafeheader"
"runtime"
"sync"
"unsafe"
return nil, errno
}
- // Slice memory layout
- var sl = struct {
- addr uintptr
- len int
- cap int
- }{addr, length, length}
-
- // Use unsafe to turn sl into a []byte.
- b := *(*[]byte)(unsafe.Pointer(&sl))
+ // Use unsafe to turn addr into a []byte.
+ var b []byte
+ hdr := (*unsafeheader.Slice)(unsafe.Pointer(&b))
+ hdr.Data = unsafe.Pointer(addr)
+ hdr.Cap = length
+ hdr.Len = length
// Register mapping in m and return it.
p := &b[cap(b)-1]