What is a context register in golang?

huangapple go评论80阅读模式
英文:

What is a context register in golang?

问题

上下文寄存器是什么,它如何改变Go代码的编译方式?

*背景:*我在一些GOROOT的部分(例如reflect)中看到了对存根函数的使用,但我不太确定它们是如何工作的。

英文:

What is a context register and how does it change the way Go code is compiled?

Context: I've seen uses of stub functions in some parts of GOROOT, i.e. reflect, but am not quite sure how these work.

答案1

得分: 6

“上下文寄存器”一词首次出现在提交 b1b67a3(2013年2月,Go 1.1rc2)中,用于实现Go 1.1函数调用的第三步。

更改reflect.MakeFunc的实现,以避免运行时代码生成。

它在2014年2月的提交 4a000b9中被采用,用于原生客户端 x86-64 的汇编和系统调用,其中将sigreturn管理为

NaCl已放弃其传统的操作系统责任,并拒绝实现"sigreturn"。
相反,返回到我们程序的执行的唯一方法是自己恢复寄存器。

不幸的是,这在严格的准确性下是不可能的,因为没有办法在不进行以下操作的情况下完成结束序列的最后一次更新:

  • (1) 跳转到一个寄存器,这样寄存器最终将保存 PC 值而不是其预期值,或者
  • (2) 将 PC 存储在堆栈上并使用 RET,这要求 SP 是有效的,并且可以破坏其下面的字。

第二种方法通常是两种方法中较小的邪恶,但在 NaCl 上,链接器必须将 RET 重写为"POP reg; AND $~31, reg; JMP reg",因此无论哪种方式,我们都将因为传入的信号而丢失一个寄存器。

类似地,没有办法恢复 EFLAGS;通常的方法是使用 POPFL,但 NaCl 拒绝该指令。
我们可以检查位并执行一系列旨在重新创建这些标志设置的指令,但这是很多工作。

幸运的是,Go 的信号处理程序从不尝试直接返回到执行代码,因此所有寄存器和 EFLAGS 都是无效的,可以被破坏。
唯一重要的寄存器是为信号处理程序创建的模拟调用设置的寄存器。
今天,这些寄存器只是 PCSP,但是以后可能还会有其他寄存器相关(例如 DX 是 Go 函数的“上下文寄存器”),我们尽可能恢复尽可能多的寄存器。

最近(2016年第四季度),为了 Go 1.8,我们有了提交 d5bd797提交 bf9c71c,用于消除堆栈重新扫描

morestack将上下文指针写入gobuf.ctxt,但由于morestack是用汇编编写的(必须非常小心地处理状态),因此对于此写入,调用所需的写入屏障。相反,我们稍后在newstack中修复这个问题,在那里我们为ctxt调用一个显式的写入屏障。

这已经需要一些微妙的推理,并且在混合屏障中会变得更加复杂。

通过简化整个机制来修复这个问题。
不要在morestack中写入gobuf.ctxt只需将上下文寄存器的值传递给newstack,并让它将其写入gobuf.ctxt。这是一个普通的 Go 指针写入,因此它会得到正常的 Go 写入屏障。不需要微妙的推理。

英文:

The expression "context register" first appeared in commit b1b67a3 (Feb. 2013, Go 1.1rc2) for implementing step 3 of the Go 1.1 Function Calls

> Change the reflect.MakeFunc implementation to avoid run-time code generation as well

It was picked up in commit 4a000b9 in Feb. 2014, Go 1.3beta1, for assembly and system calls for Native Client x86-64, where sigreturn is managed as:

> NaCl has abidcated its traditional operating system responsibility and declined to implement 'sigreturn'.
Instead the only way to return to the execution of our program is to restore the registers ourselves.

> Unfortunately, that is impossible to do with strict fidelity, because there is no way to do the final update of PC that ends the sequence without either

> - (1) jumping to a register, in which case the register ends holding the PC value instead of its intended value, or

  • (2) storing the PC on the stack and using RET, which imposes the requirement that SP is valid and that is okay to smash the word below it.

> The second would normally be the lesser of the two evils, except that on NaCl, the linker must rewrite RET into "POP reg; AND $~31, reg; JMP reg", so either way we are going to lose a register as a result of the incoming signal.

> Similarly, there is no way to restore EFLAGS; the usual way is to use POPFL, but NaCl rejects that instruction.
We could inspect the bits and execute a sequence of instructions designed to recreate those flag settings, but that's a lot of work.

> Thankfully, Go's signal handlers never try to return directly to the executing code, so all the registers and EFLAGS are dead and can be smashed.
The only registers that matter are the ones that are setting up for the simulated call that the signal handler has created.
Today those registers are just PC and SP, but in case additional registers are relevant in the future (for example DX is the Go func context register) we restore as many registers as possible.

Much more recently (Q4 2016), for Go 1.8, we have commit d5bd797 and commit bf9c71c, for eliminating stack rescanning:

> morestack writes the context pointer to gobuf.ctxt, but since
morestack is written in assembly (and has to be very careful with
state), it does not invoke the requisite write barrier for this
write. Instead, we patch this up later, in newstack, where we invoke
an explicit write barrier for ctxt.

> This already requires some subtle reasoning, and it's going to get a
lot hairier with the hybrid barrier.

> Fix this by simplifying the whole mechanism.
Instead of writing gobuf.ctxt in morestack, just pass the value of the context register to newstack and let it write it to gobuf.ctxt. This is a normal Go pointer write, so it gets the normal Go write barrier. No subtle reasoning required.

huangapple
  • 本文由 发表于 2016年12月10日 02:55:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/41067095.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定