@@ -194,3 +194,105 @@ TEXT runtime·gogo(SB), NOSPLIT, $16-8
194194```
195195
196196这样所有流程就都打通了。
197+
198+ ---
199+
200+ 同样需要注意的是, 如多次` panic(defer) ` 后再利用` recover ` 恢复后获取到的` panic ` 信息仅仅为最近一次的信息, 是获取不了` _panic ` 整个链表中记录的所有信息的, 如果坚持想这样做, 那只能从 TLS 中获取当前的上下文, 通过基址加变址寻址的方式从当前的` g ` 中来获取` _panic ` 信息
201+
202+ go 1.14.3 为例:
203+ ``` assembly
204+ #include "textflag.h"
205+ // main.s
206+ // func GetPanicPtr() uintptr
207+ TEXT ·GetPanicPtr(SB), NOSPLIT, $0-8
208+ MOVQ TLS, CX
209+ MOVQ 0(CX)(TLS*1), AX
210+ MOVQ $32, BX // 32 根据不同版本 type g struct 中 _panic 字段计算偏移量即可
211+ LEAQ 0(AX)(BX*1), DX
212+ MOVQ (DX), AX
213+ MOVQ AX, ret+0(FP)
214+ RET
215+ ```
216+ ``` go
217+ // main.go
218+ package main
219+
220+ import (
221+ " fmt"
222+ " unsafe"
223+ )
224+
225+ func GetPanicPtr () uintptr
226+
227+ type gobuf struct {
228+ // The offsets of sp, pc, and g are known to (hard-coded in) libmach.
229+ //
230+ // ctxt is unusual with respect to GC: it may be a
231+ // heap-allocated funcval, so GC needs to track it, but it
232+ // needs to be set and cleared from assembly, where it's
233+ // difficult to have write barriers. However, ctxt is really a
234+ // saved, live register, and we only ever exchange it between
235+ // the real register and the gobuf. Hence, we treat it as a
236+ // root during stack scanning, which means assembly that saves
237+ // and restores it doesn't need write barriers. It's still
238+ // typed as a pointer so that any other writes from Go get
239+ // write barriers.
240+ sp uintptr
241+ pc uintptr
242+ g uintptr
243+ ctxt unsafe.Pointer
244+ ret uint64
245+ lr uintptr
246+ bp uintptr // for GOEXPERIMENT=framepointer
247+ }
248+ type stack struct {
249+ lo uintptr
250+ hi uintptr
251+ }
252+ type g struct {
253+ // Stack parameters.
254+ // stack describes the actual stack memory: [stack.lo, stack.hi).
255+ // stackguard0 is the stack pointer compared in the Go stack growth prologue.
256+ // It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption.
257+ // stackguard1 is the stack pointer compared in the C stack growth prologue.
258+ // It is stack.lo+StackGuard on g0 and gsignal stacks.
259+ // It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash).
260+ stack stack // offset known to runtime/cgo
261+ stackguard0 uintptr // offset known to liblink
262+ stackguard1 uintptr // offset known to liblink
263+
264+ _panic uintptr // innermost panic - offset known to liblink
265+ _defer uintptr // innermost defer
266+ m uintptr // current m; offset known to arm liblink
267+ sched gobuf
268+ syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc
269+ syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc
270+ stktopsp uintptr // expected sp at top of stack, to check in traceback
271+ param unsafe.Pointer // passed parameter on wakeup
272+ atomicstatus uint32
273+ stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
274+ goid int64
275+ }
276+ type _panic struct {
277+ argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
278+ arg interface {} // argument to panic
279+ link *_panic // link to earlier panic
280+ pc uintptr // where to return to in runtime if this panic is bypassed
281+ sp unsafe.Pointer // where to return to in runtime if this panic is bypassed
282+ recovered bool // whether this panic is over
283+ aborted bool // the panic was aborted
284+ goexit bool
285+ }
286+
287+ func main () {
288+ defer func () {
289+ var __panic *_panic
290+ var _panic_ptr uintptr = GetPanicPtr ()
291+ __panic = (*_panic)(unsafe.Pointer (_panic_ptr))
292+ fmt.Println (__panic)
293+ }()
294+ defer panic (3 )
295+ defer panic (2 )
296+ panic (1 )
297+ }
298+ ```
0 commit comments