directory search
archive archive/tar archive/zip bufio bufio(缓存) builtin builtin(内置包) bytes bytes(包字节) compress compress/bzip2(压缩/bzip2) compress/flate(压缩/flate) compress/gzip(压缩/gzip) compress/lzw(压缩/lzw) compress/zlib(压缩/zlib) container container/heap(容器数据结构heap) container/list(容器数据结构list) container/ring(容器数据结构ring) context context(上下文) crypto crypto(加密) crypto/aes(加密/aes) crypto/cipher(加密/cipher) crypto/des(加密/des) crypto/dsa(加密/dsa) crypto/ecdsa(加密/ecdsa) crypto/elliptic(加密/elliptic) crypto/hmac(加密/hmac) crypto/md5(加密/md5) crypto/rand(加密/rand) crypto/rc4(加密/rc4) crypto/rsa(加密/rsa) crypto/sha1(加密/sha1) crypto/sha256(加密/sha256) crypto/sha512(加密/sha512) crypto/subtle(加密/subtle) crypto/tls(加密/tls) crypto/x509(加密/x509) crypto/x509/pkix(加密/x509/pkix) database database/sql(数据库/sql) database/sql/driver(数据库/sql/driver) debug debug/dwarf(调试/dwarf) debug/elf(调试/elf) debug/gosym(调试/gosym) debug/macho(调试/macho) debug/pe(调试/pe) debug/plan9obj(调试/plan9obj) encoding encoding(编码) encoding/ascii85(编码/ascii85) encoding/asn1(编码/asn1) encoding/base32(编码/base32) encoding/base64(编码/base64) encoding/binary(编码/binary) encoding/csv(编码/csv) encoding/gob(编码/gob) encoding/hex(编码/hex) encoding/json(编码/json) encoding/pem(编码/pem) encoding/xml(编码/xml) errors errors(错误) expvar expvar flag flag(命令行参数解析flag包) fmt fmt go go/ast(抽象语法树) go/build go/constant(常量) go/doc(文档) go/format(格式) go/importer go/parser go/printer go/scanner(扫描仪) go/token(令牌) go/types(类型) hash hash(散列) hash/adler32 hash/crc32 hash/crc64 hash/fnv html html html/template(模板) image image(图像) image/color(颜色) image/color/palette(调色板) image/draw(绘图) image/gif image/jpeg image/png index index/suffixarray io io io/ioutil log log log/syslog(日志系统) math math math/big math/big math/bits math/bits math/cmplx math/cmplx math/rand math/rand mime mime mime/multipart(多部分) mime/quotedprintable net net net/http net/http net/http/cgi net/http/cookiejar net/http/fcgi net/http/httptest net/http/httptrace net/http/httputil net/http/internal net/http/pprof net/mail net/mail net/rpc net/rpc net/rpc/jsonrpc net/smtp net/smtp net/textproto net/textproto net/url net/url os os os/exec os/signal os/user path path path/filepath(文件路径) plugin plugin(插件) reflect reflect(反射) regexp regexp(正则表达式) regexp/syntax runtime runtime(运行时) runtime/debug(调试) runtime/internal/sys runtime/pprof runtime/race(竞争) runtime/trace(执行追踪器) sort sort(排序算法) strconv strconv(转换) strings strings(字符串) sync sync(同步) sync/atomic(原子操作) syscall syscall(系统调用) testing testing(测试) testing/iotest testing/quick text text/scanner(扫描文本) text/tabwriter text/template(定义模板) text/template/parse time time(时间戳) unicode unicode unicode/utf16 unicode/utf8 unsafe unsafe
characters

  • import "unsafe"

  • 概观

  • 索引

概观

软件打包不安全包含围绕 Go 程序类型安全的操作。

导入不安全的程序包可能不可移植,并且不受 Go 1 兼容性准则的保护。

索引

  • func Alignof(x ArbitraryType) uintptr

  • func Offsetof(x ArbitraryType) uintptr

  • func Sizeof(x ArbitraryType) uintptr

  • type ArbitraryType

  • type Pointer

打包文件

unsafe.go

func Alignof

func Alignof(x ArbitraryType) uintptr

Alignof 接受任何类型的表达式 x 并返回假设变量  v所需的对齐,就像 v 通过 var v = x 声明一样。它是最大的值 m,使得 v 的地址总是为零mod m。它与 reflect.TypeOf(x).Align() 返回的值相同。作为一种特殊情况,如果变量s是结构类型,并且 f 是该结构中的字段,那么 Alignof(s.f) 将返回结构中该类型字段所需的对齐。这种情况与 reflect.TypeOf(s.f).FieldAlign() 返回的值相同。

func Offsetof

func Offsetof(x ArbitraryType) uintptr

Offsetof 返回由 x 表示的字段结构中的偏移量,它必须是 structValue.field 的形式。换句话说,它返回结构开始和字段开始之间的字节数。

func Sizeof

func Sizeof(x ArbitraryType) uintptr

Sizeof 采用任何类型的表达式x并返回假设变量 v 的字节大小,就像 v 通过 var v = x 声明一样。该大小不包括可能由 x 引用的任何内存。例如,如果 x 是切片,则 Sizeof 返回切片描述符的大小,而不是切片引用的内存大小。

type ArbitraryType

ArbitraryType 仅用于文档目的,实际上并不是不安全软件包的一部分。它表示任意 Go 表达式的类型。

type ArbitraryType int

type Pointer

指针表示指向任意类型的指针。类型指针有四种可用于其他类型的特殊操作:

- A pointer value of any type can be converted to a Pointer.- A Pointer can be converted to a pointer value of any type.- A uintptr can be converted to a Pointer.- A Pointer can be converted to a uintptr.

指针因此允许程序打败类型系统并读写任意内存。应该非常小心地使用它。

涉及指针的以下模式是有效的。不使用这些模式的代码今天可能无效,或在将来变得无效。即使下面的有效模式也有重要的注意事项。

运行“go vet”可以帮助找到不符合这些模式的指针的使用,但是从“go vet”沉默并不能保证代码有效。

(1)将 * T1 转换为 * T2 的指针。

假设 T2 不大于 T1 并且两者共享相同的存储器布局,则该转换允许将一种类型的数据重新解释为另一种类型的数据。一个例子是 math.Float64bits 的实现:

func Float64bits(f float64) uint64 {return *(*uint64)(unsafe.Pointer(&f))}

(2)将指针转换为 uintptr(但不返回到指针)。

将指针转换为 uintptr 会以整数形式生成指向的内存地址。这种 uintptr 通常用于打印它。

将 uintptr 转换回指针通常无效。

uintptr 是一个整数,而不是引用。将指针转换为 uintptr 将创建不带指针语义的整数值。即使 uintptr 持有某个对象的地址,如果对象移动,垃圾收集器也不会更新该 uintptr 的值,uintptr 也不会使该对象不被回收。

其余模式枚举从 uintptr 到指针的唯一有效转换。

(3)用算术将指针转换为 uintptr 并返回。

如果 p 指向已分配的对象,则可以通过转换为 uintptr,添加偏移量以及转换回指针来将对象前进。

p = unsafe.Pointer(uintptr(p) + offset)

这种模式最常见的用途是访问结构中的字段或数组中的元素:

// equivalent to f := unsafe.Pointer(&s.f)f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))// equivalent to e := unsafe.Pointer(&x[i])e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))

以这种方式添加和减去指针上的偏移量是有效的。使用 &^ 来循环指针也是有效的,通常用于对齐。在所有情况下,结果必须继续指向原始分配的对象。

与 C 中不同的是,仅仅在它的原始分配结束时超前一个指针是无效的:

// INVALID: end points outside allocated space.var s thing
end = unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s))// INVALID: end points outside allocated space.b := make([]byte, n)end = unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(n))

请注意,这两个转换必须出现在相同的表达式中,只有它们之间的中间算术:

// INVALID: uintptr cannot be stored in variable// before conversion back to Pointer.u := uintptr(p)p = unsafe.Pointer(u + offset)

(4)在调用 syscall.Syscall 时将指针转换为 uintptr。

系统调用包中的 Syscall 函数直接将它们的 uintptr 参数传递给操作系统,然后根据调用的细节,将其中的一些重新解释为指针。也就是说,系统调用实现隐式地将某些参数从 uintptr 转换回指针。

如果必须将指针参数转换为 uintptr 以用作参数,则该转换必须出现在调用表达式本身中:

syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))

编译器处理一个转换为 uintptr 的指针,转换为在汇编中实现的函数的调用的参数列表中,通过安排被引用的已分配对象(如果有的话)被保留,直到调用完成时才移动,即使仅从类型在通话过程中将显示该对象不再需要。

为了让编译器识别这种模式,转换必须出现在参数列表中:

// INVALID: uintptr cannot be stored in variable// before implicit conversion back to Pointer during system call.u := uintptr(unsafe.Pointer(p))syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n))

(5)将 reflect.Value.Pointer 或 reflect.Value.UnsafeAddr 的结果从 uintptr 转换为指针。

Package 反射的 Value 方法名为 Pointer 和 UnsafeAddr,返回类型为 uintptr 而不是 unsafe.Pointer,以防止调用者将结果更改为任意类型,而不首先导入“unsafe”。但是,这意味着结果很脆弱,必须在调用后立即转换为指针,并使用相同的表达式:

p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))

与上述情况一样,在转换之前存储结果无效:

// INVALID: uintptr cannot be stored in variable// before conversion back to Pointer.u := reflect.ValueOf(new(int)).Pointer()p := (*int)(unsafe.Pointer(u))

(6)将 Reflection.SliceHeader 或 reflect.StringHeader 数据字段转换为指针或从指针转换而来。

与前面的情况一样,反射数据结构 SliceHeader 和 StringHeader 将字段 Data 声明为 uintptr,以防止调用者在不首先导入“不安全”的情况下将结果更改为任意类型。但是,这意味着 SliceHeader 和 StringHeader 仅在解释实际切片或字符串值的内容时有效。

var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1hdr.Data = uintptr(unsafe.Pointer(p))              // case 6 (this case)hdr.Len = n

在这种用法中,hdr.Data 实际上是一种替代方法来引用片头中的基础指针,而不是 uintptr 变量本身。

一般来说,reflect.SliceHeader 和 reflect.StringHeader 只能用作 * reflect.SliceHeader 和 * reflect.StringHeader 指向实际的切片或字符串,而不能用作普通结构。程序不应该声明或分配这些结构类型的变量。

// INVALID: a directly-declared header will not hold Data as a reference.var hdr reflect.StringHeader
hdr.Data = uintptr(unsafe.Pointer(p))hdr.Len = n
s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost
type Pointer *ArbitraryType
Previous article: Next article: