目录搜索
archivearchive/tararchive/zipbufiobufio(缓存)builtinbuiltin(内置包)bytesbytes(包字节)compresscompress/bzip2(压缩/bzip2)compress/flate(压缩/flate)compress/gzip(压缩/gzip)compress/lzw(压缩/lzw)compress/zlib(压缩/zlib)containercontainer/heap(容器数据结构heap)container/list(容器数据结构list)container/ring(容器数据结构ring)contextcontext(上下文)cryptocrypto(加密)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)databasedatabase/sql(数据库/sql)database/sql/driver(数据库/sql/driver)debugdebug/dwarf(调试/dwarf)debug/elf(调试/elf)debug/gosym(调试/gosym)debug/macho(调试/macho)debug/pe(调试/pe)debug/plan9obj(调试/plan9obj)encodingencoding(编码)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)errorserrors(错误)expvarexpvarflagflag(命令行参数解析flag包)fmtfmtgogo/ast(抽象语法树)go/buildgo/constant(常量)go/doc(文档)go/format(格式)go/importergo/parsergo/printergo/scanner(扫描仪)go/token(令牌)go/types(类型)hashhash(散列)hash/adler32hash/crc32hash/crc64hash/fnvhtmlhtmlhtml/template(模板)imageimage(图像)image/color(颜色)image/color/palette(调色板)image/draw(绘图)image/gifimage/jpegimage/pngindexindex/suffixarrayioioio/ioutillogloglog/syslog(日志系统)mathmathmath/bigmath/bigmath/bitsmath/bitsmath/cmplxmath/cmplxmath/randmath/randmimemimemime/multipart(多部分)mime/quotedprintablenetnetnet/httpnet/httpnet/http/cginet/http/cookiejarnet/http/fcginet/http/httptestnet/http/httptracenet/http/httputilnet/http/internalnet/http/pprofnet/mailnet/mailnet/rpcnet/rpcnet/rpc/jsonrpcnet/smtpnet/smtpnet/textprotonet/textprotonet/urlnet/urlososos/execos/signalos/userpathpathpath/filepath(文件路径)pluginplugin(插件)reflectreflect(反射)regexpregexp(正则表达式)regexp/syntaxruntimeruntime(运行时)runtime/debug(调试)runtime/internal/sysruntime/pprofruntime/race(竞争)runtime/trace(执行追踪器)sortsort(排序算法)strconvstrconv(转换)stringsstrings(字符串)syncsync(同步)sync/atomic(原子操作)syscallsyscall(系统调用)testingtesting(测试)testing/iotesttesting/quicktexttext/scanner(扫描文本)text/tabwritertext/template(定义模板)text/template/parsetimetime(时间戳)unicodeunicodeunicode/utf16unicode/utf8unsafeunsafe
文字

  • 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
上一篇:下一篇: