大家好,我是煎鱼。
Go1.23 的新版本特性中,还有一些声势浩大的变更,引发了社区的一些争议和讨论。
今天主要给大家分享 //go:linkname
变更的前因后果和新特性。
背景
在今年 5 月份,rsc 不知道是看到了什么项目在乱搞什么骚操作,触发了他的 "逆鳞"。直接反手光速敲出了以下提案:
他发现的问题是:目前存在过度使用 //go:linkname
来深入 Go 标准库内部(尤其用在 runtime 内部)的场景。
这意味着当 Go 官方团队以正常的设计迭代去更改 Go 标准库内部时,最终有可能会关联地破坏 Go 生态系统中大量依赖的软件包。因为 Go "家里" 很多东西都被外部社区依赖了。
例如像是在该项 CL 修改:internal/reflectlite: remove redundent ifaceIndir 破坏了 github.com/goccy/go-json
库的引用:
最终事实证明该库复制了 runtime 的大部分内部类型 API。现在倒逼的 Go 团队无法更改该 CL 中的任何内容。(煎鱼:许多包都使用 goccy,包括 Kubernetes。意思是很重要,有 KA 大客户在用)
自我解释
很多同学看到这个提案要封杀 //go:linkname
的用法后,纷纷吐槽:"Go 团队自己也会用,Go 源码里一大堆这么用的。这不是典中典吗?"
为此 rsc 对此也给出了对应的解释:这种使用内部类型的情况是不可持续的!
内部是内部的,这么用是有原因的。当用户的 Go 程序对我们保留在内部的细节产生显式依赖时,我们无法让它们继续运行。
但我们也非常关心兼容性:我们也不想破坏 Go 程序。显而易见的结论是:必须首先阻止 Go 程序能够对内部细节产生这些依赖。
Go1.23 //go:linkname 的改变
改进计划
理想的目标状态是:所有 //go:linkname
用法都必须采用握手(Handshake)的方式/机制,也就是:双方必须同意使用给定符号的 linkname 才能成功。(煎鱼:但也知道已经无法如此圆满了)
Go1.23 新版本起的新机制(计划)如下:
- 加新的握手标识 checklinkname :向
cmd/link
引入新的-checklinkname=1
标志,该标志要求 Go 标准库中的内部符号采用新的握手的机制。 - 查找和标记正在使用的开源库 :调查所有现有的开源 Go 软件包,以查找使用正在背后使用
//go:linkname
的标准库符号。向标准库添加必要的//go:linkname
注释以保持这些注释正常工作,并记录每个注释存在的原因。 - 新版本将 checklinkname 设置为默认 :将
-checklinkname=1
设为 Go 1.23 的默认设置。如果这导致任何中断,用户可以使用-ldflags=-checklinkname=0
来解决问题,我们希望他们能提交报告,让我们知道我们遗漏了什么。
可以达到的目的
虽然 rsc 做了各种措施,依然无法达到 rsc 心目中的理想、美好 Go 世界。但是至少可以达到以下两个核心目的:
- 不会破坏任何原有东西,保持对现有已经引用的 linkname 的支持。(煎鱼:这个后面狠狠打脸了)
- 可以将阻止新的损害累积,也就是不会再引入对 runtime 内部的新 linkname 引用。未来也不会有任何由外部新的引用。
戏剧一幕
rsc 直接冷不丁搞大招。他直接把国内外好几个知名库给直接钉上了 Go 源码库的耻辱柱上了。
如下图所示:
详情可以具体查看《国内外多个库被 rsc 钉上 Go 耻辱柱。。。》,我就不再赘述了。
争议
这方面争议是不小的,摘抄其中一句比较有冲击的。
社区有位网友 @szmcdull 直接怒评:"我们是黑客,我们不需要一些对儿童安全的玩具。"
总结
这次 rsc 可能是在回顾 go issues 或处理相关 //go:linkname
的兼容性逻辑处理时,发现了这个一直会给内外部带来破坏性更新的地方。
这显然和 Go1 的兼容性保障相违背的,虽然这是直接拿了合法钥匙去 Go 家的后院把东西给接上了。
为此这次也输出了 -checklinkname=1
的握手模式把这个后院给堵住了。rsc 的执行力确实是很猛的!
- 本文作者:煎鱼
- 公众号:脑子进煎鱼了
- 联系方式(微信号):cJY0728(加我拉你进技术交流群)
文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo... 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。