Golang中出现栈上无效指针的致命错误如何解决
Golang中出现栈上无效指针的致命错误如何解决 我刚刚遇到了迄今为止见过的最奇怪的错误。
我正在开发 cimgui-go 库(代码在此:GitHub - gucio321/cimgui-go at callbacks)
我遇到了这个 panic:
runtime: bad pointer in frame runtime.assertE2I2 at 0xc000145408: 0x1
fatal error: invalid pointer found on stack
runtime stack:
runtime.throw({0x859d38?, 0x9f1c80?})
/usr/local/go/src/runtime/panic.go:1077 +0x5c fp=0x7ffffa0eabe0 sp=0x7ffffa0eabb0 pc=0x43845c
runtime.adjustpointers(0x7ffffa0eae58?, 0x7ffffa0eaca0, 0x459e45?, {0x7ffffa0eae58?, 0x0?})
/usr/local/go/src/runtime/stack.go:627 +0x1ad fp=0x7ffffa0eac40 sp=0x7ffffa0eabe0 pc=0x44feed
runtime.adjustframe(0x7ffffa0eae58, 0x7ffffa0ead38)
/usr/local/go/src/runtime/stack.go:684 +0xdb fp=0x7ffffa0eacd0 sp=0x7ffffa0eac40 pc=0x45001b
runtime.copystack(0xc0000061a0, 0x800000002?)
/usr/local/go/src/runtime/stack.go:935 +0x2c5 fp=0x7ffffa0eafc8 sp=0x7ffffa0eacd0 pc=0x4507c5
runtime.newstack()
/usr/local/go/src/runtime/stack.go:1116 +0x47f fp=0x7ffffa0eb178 sp=0x7ffffa0eafc8 pc=0x450d7f
traceback: unexpected SPWRITE function runtime.morestack
runtime.morestack()
/usr/local/go/src/runtime/asm_amd64.s:593 +0x8f fp=0x7ffffa0eb180 sp=0x7ffffa0eb178 pc=0x4636ef
这是由以下这行代码引起的:
v := texture.ID()
fmt.Printf("%v\n", v) // <- 这行代码会 panic
ID 是一个 getter 函数,它返回 Texture 结构体的一个 unsafe.Pointer 类型字段。这个 unsafe.Pointer 是由 C 代码生成的。
最奇怪的部分是,如果我以下面的方式修改这段代码:
fmt.Println(texture)
v := texture.ID()
fmt.Printf("%v\n", v)
一切又都按预期工作了。
如果能得到任何帮助,我将不胜感激。这是 Go 的一个 bug 吗? 提前感谢!
更多关于Golang中出现栈上无效指针的致命错误如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好 @gucio321,我不完全确定,但这里的问题在于 %v 使用了针对特定对象(一个指向 texture 的指针)的 Stringer 实现中的 String() 方法,而在这个例子中,该实现是在 C 语言中完成的,依赖于 cgo 接口。因此,在此之前对 texture 调用 Println 会让 Go 语言在稍后再次使用它之前先获取到相关信息。我认为这是一个默认表示格式的问题 🙂。
更多关于Golang中出现栈上无效指针的致命错误如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这是一个典型的栈上无效指针错误,通常发生在使用 unsafe.Pointer 与C代码交互时。从错误信息看,问题出现在 runtime.assertE2I2 函数中,这表明Go运行时在栈复制过程中发现了无效指针。
主要原因是C代码返回的指针被Go运行时误认为是Go堆上的指针,但实际上它指向C内存。当Go进行栈扩容时,会尝试调整栈上的指针,遇到C指针就会报错。
解决方案是确保C指针不被Go运行时误扫描。以下是几种解决方法:
方法1:使用uintptr作为中间类型
// 修改Texture结构体
type Texture struct {
// 将unsafe.Pointer改为uintptr
id uintptr
}
// ID方法返回uintptr而不是unsafe.Pointer
func (t *Texture) ID() uintptr {
return t.id
}
// 如果需要unsafe.Pointer,可以单独提供转换方法
func (t *Texture) IDAsPointer() unsafe.Pointer {
return unsafe.Pointer(t.id)
}
方法2:使用runtime.Pinner固定指针(Go 1.21+)
import "runtime"
func processTexture(texture *Texture) {
// 固定指针防止被移动
var pinner runtime.Pinner
pinner.Pin(texture)
defer pinner.Unpin()
v := texture.ID()
fmt.Printf("%v\n", v)
}
方法3:使用cgo直接处理指针
// 在C代码层面处理指针传递
/*
#include <stdint.h>
extern uintptr_t get_texture_id(void* texture);
*/
import "C"
func (t *Texture) ID() uintptr {
return uintptr(C.get_texture_id(unsafe.Pointer(t)))
}
方法4:禁用栈复制(不推荐,仅用于调试)
// 在main函数开始时设置
func init() {
debug.SetGCPercent(-1) // 临时禁用GC
}
从你的代码看,最可能的原因是Texture结构体包含的unsafe.Pointer字段被Go运行时误认为是有效Go指针。当调用fmt.Println(texture)时,可能改变了内存布局或逃逸分析的结果。
这不是Go的bug,而是unsafe使用中的常见问题。建议优先使用方法1,将unsafe.Pointer存储为uintptr,只在需要时转换为指针。

