-
-
Notifications
You must be signed in to change notification settings - Fork 504
/
cgocall.go
731 lines (658 loc) · 25.5 KB
/
cgocall.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Cgo call and callback support.
//
// To call into the C function f from Go, the cgo-generated code calls
// runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a
// gcc-compiled function written by cgo.
//
// runtime.cgocall (below) calls entersyscall so as not to block
// other goroutines or the garbage collector, and then calls
// runtime.asmcgocall(_cgo_Cfunc_f, frame).
//
// runtime.asmcgocall (in asm_$GOARCH.s) switches to the m->g0 stack
// (assumed to be an operating system-allocated stack, so safe to run
// gcc-compiled code on) and calls _cgo_Cfunc_f(frame).
//
// _cgo_Cfunc_f invokes the actual C function f with arguments
// taken from the frame structure, records the results in the frame,
// and returns to runtime.asmcgocall.
//
// After it regains control, runtime.asmcgocall switches back to the
// original g (m->curg)'s stack and returns to runtime.cgocall.
//
// After it regains control, runtime.cgocall calls exitsyscall, which blocks
// until this m can run Go code without violating the $GOMAXPROCS limit,
// and then unlocks g from m.
//
// The above description skipped over the possibility of the gcc-compiled
// function f calling back into Go. If that happens, we continue down
// the rabbit hole during the execution of f.
//
// To make it possible for gcc-compiled C code to call a Go function p.GoF,
// cgo writes a gcc-compiled function named GoF (not p.GoF, since gcc doesn't
// know about packages). The gcc-compiled C function f calls GoF.
//
// GoF initializes "frame", a structure containing all of its
// arguments and slots for p.GoF's results. It calls
// crosscall2(_cgoexp_GoF, frame, framesize, ctxt) using the gcc ABI.
//
// crosscall2 (in cgo/asm_$GOARCH.s) is a four-argument adapter from
// the gcc function call ABI to the gc function call ABI. At this
// point we're in the Go runtime, but we're still running on m.g0's
// stack and outside the $GOMAXPROCS limit. crosscall2 calls
// runtime.cgocallback(_cgoexp_GoF, frame, ctxt) using the gc ABI.
// (crosscall2's framesize argument is no longer used, but there's one
// case where SWIG calls crosscall2 directly and expects to pass this
// argument. See _cgo_panic.)
//
// runtime.cgocallback (in asm_$GOARCH.s) switches from m.g0's stack
// to the original g (m.curg)'s stack, on which it calls
// runtime.cgocallbackg(_cgoexp_GoF, frame, ctxt). As part of the
// stack switch, runtime.cgocallback saves the current SP as
// m.g0.sched.sp, so that any use of m.g0's stack during the execution
// of the callback will be done below the existing stack frames.
// Before overwriting m.g0.sched.sp, it pushes the old value on the
// m.g0 stack, so that it can be restored later.
//
// runtime.cgocallbackg (below) is now running on a real goroutine
// stack (not an m.g0 stack). First it calls runtime.exitsyscall, which will
// block until the $GOMAXPROCS limit allows running this goroutine.
// Once exitsyscall has returned, it is safe to do things like call the memory
// allocator or invoke the Go callback function. runtime.cgocallbackg
// first defers a function to unwind m.g0.sched.sp, so that if p.GoF
// panics, m.g0.sched.sp will be restored to its old value: the m.g0 stack
// and the m.curg stack will be unwound in lock step.
// Then it calls _cgoexp_GoF(frame).
//
// _cgoexp_GoF, which was generated by cmd/cgo, unpacks the arguments
// from frame, calls p.GoF, writes the results back to frame, and
// returns. Now we start unwinding this whole process.
//
// runtime.cgocallbackg pops but does not execute the deferred
// function to unwind m.g0.sched.sp, calls runtime.entersyscall, and
// returns to runtime.cgocallback.
//
// After it regains control, runtime.cgocallback switches back to
// m.g0's stack (the pointer is still in m.g0.sched.sp), restores the old
// m.g0.sched.sp value from the stack, and returns to crosscall2.
//
// crosscall2 restores the callee-save registers for gcc and returns
// to GoF, which unpacks any result values and returns to f.
// Cgo 调用与回调支持
//
// 为从 Go 调用 C 函数 f,cgo 生成的代码调用 runtime.cgocall(_cgo_Cfunc_f, frame),
// 其中 _cgo_Cfunc_f 为由 cgo 编写的并由 gcc 编译的函数。
//
// runtime.cgocall (见下) 调用 entersyscall,从而不会阻塞其他 goroutine 或垃圾回收器
// 而后调用 runtime.asmcgocall(_cgo_Cfunc_f, frame)。
//
// runtime.asmcgocall (见 asm_$GOARCH.s) 会切换到 m->g0 栈
// (假设为操作系统分配的栈,因此能安全的在运行 gcc 编译的代码) 并调用 _cgo_Cfunc_f(frame)。
//
// _cgo_Cfunc_f 获取了帧结构中的参数,调用了实际的 C 函数 f,在帧中记录其结果,
// 并返回到 runtime.asmcgocall。
//
// 在重新获得控制权后,runtime.asmcgocall 会切换回原来的 g (m->curg) 的执行栈
// 并返回 runtime.cgocall。
//
// 在重新获得控制权后,runtime.cgocall 会调用 exitsyscall,并阻塞,直到该 m 运行能够在不与
// $GOMAXPROCS 限制冲突的情况下运行 Go 代码。
//
// 上面的描述跳过了当 gcc 编译的函数 f 调用回 Go 的情况。如果此类情况发生,则下面描述了 f 执行期间的调用过程。
//
// 为了 gcc 编译的 C 代码调用 Go 函数 p.GoF 成为可能,cgo 编写了以 GoF 命名的 gcc 编译的函数
// (不是 p.GoF,因为 gcc 没有包的概念)。然后 gcc 编译的 C 函数 f 调用 GoF。
//
// GoF 调用了 crosscall2(_cgoexp_GoF, frame, framesize)。
// Crosscall2(见 cgo/gcc_$GOARCH.S,gcc 编译的汇编文件)为一个具有两个参数的从 gcc 函数调用 ABI
// 到 6c 函数调用 API 的适配器.
// gcc 通过调用它来调用 6c 函数。这种情况下,它会调用 _cgoexp_GoF(frame, framesize),
// 仍然会在 m->g0 栈上运行,且不受 $GOMAXPROCS 的限制。因此该代码不能直接调用任意的 Go 代码,
// 并且必须非常小心的分配内存以及小心的使用 m->g0 栈。
//
// _cgoexp_GoF 调用了 runtime.cgocallback(p.GoF, frame, framesize, ctxt).
// (使用 _cgoexp_GoF 而不是编写 crosscall3 直接进行此调用的原因是 _cgoexp_GoF
// 是用 6c 而不是 gcc 编译的,可以引用像 runs.cgocallback 和 p.GoF 这样的带点的名称。)
//
// runtime.cgocallback(在 asm_$GOARCH.s中)从 m->g0 的堆切换到原始 g(m->curg)的栈,
// 并在在栈上调用 runtime.cgocallbackg(p.GoF,frame,framesize)。
// 作为栈切换的一部分,runtime.cgocallback 将当前 SP 保存为 m->g0->sched.sp,
// 因此在执行回调期间任何使用 m->g0 的栈都将在现有栈帧之下完成。
// 在覆盖 m->g0->sched.sp 之前,它会在 m->g0 栈上将旧值压栈,以便以后可以恢复。
//
// runtime.cgocallbackg(见下)现在在一个真正的 goroutine 栈上运行(不是 m->g0 栈)。
// 首先它调用 runtime.exitsyscall,它将阻塞到不与 $GOMAXPROCS 限制冲突的情况下运行此 goroutine。
// 一旦 exitsyscall 返回,就可以安全地执行调用内存分配器或调用 Go 的 p.GoF 回调函数等操作。
// runtime.cgocallbackg 首先推迟一个函数来 unwind m->g0.sched.sp,这样如果 p.GoF 发生 panic
// m->g0.sched.sp 将恢复到其旧值:m->g0 栈和 m->curg 栈将在 unwind 步骤中展开。
// 接下来它调用 p.GoF。最后它弹出但不执行 defer 函数,而是调用 runtime.entersyscall,
// 并返回到 runtime.cgocallback。
//
// 在重新获得控制权后,runtime.cgocallback 切换回 m->g0 栈(指针仍然为 m->g0.sched.sp),
// 从栈中恢复原来的 m->g0.sched.sp 的值,并返回到 _cgoexp_GoF。
//
// _cgoexp_GoF 直接返回 crosscall2,从而为 gcc 恢复调用方寄存器,并返回到 GoF,从而返回到 f 中。
package runtime
import (
"runtime/internal/atomic"
"runtime/internal/sys"
"unsafe"
)
// Addresses collected in a cgo backtrace when crashing.
// Length must match arg.Max in x_cgo_callers in runtime/cgo/gcc_traceback.c.
// cgo 崩溃回溯时搜集的地址
// 长度必须等于 x_cgo_callers 中 的 arg.Max,位于 runtime/cgo/gcc_traceback.c
type cgoCallers [32]uintptr
// argset matches runtime/cgo/linux_syscall.c:argset_t
type argset struct {
args unsafe.Pointer
retval uintptr
}
// wrapper for syscall package to call cgocall for libc (cgo) calls.
//go:linkname syscall_cgocaller syscall.cgocaller
//go:nosplit
//go:uintptrescapes
func syscall_cgocaller(fn unsafe.Pointer, args ...uintptr) uintptr {
as := argset{args: unsafe.Pointer(&args[0])}
cgocall(fn, unsafe.Pointer(&as))
return as.retval
}
// Call from Go to C.
// 从 Go 调用 C
//
// This must be nosplit because it's used for syscalls on some
// platforms. Syscalls may have untyped arguments on the stack, so
// it's not safe to grow or scan the stack.
//
//go:nosplit
func cgocall(fn, arg unsafe.Pointer) int32 {
if !iscgo && GOOS != "solaris" && GOOS != "illumos" && GOOS != "windows" {
throw("cgocall unavailable")
}
// cgo 调用不允许为空
if fn == nil {
throw("cgocall nil")
}
if raceenabled {
racereleasemerge(unsafe.Pointer(&racecgosync))
}
// 运行时会记录 cgo 调用的次数
mp := getg().m
mp.ncgocall++
mp.ncgo++
// Reset traceback.
// 重置回溯信息
mp.cgoCallers[0] = 0
// Announce we are entering a system call
// so that the scheduler knows to create another
// M to run goroutines while we are in the
// foreign code.
//
// The call to asmcgocall is guaranteed not to
// grow the stack and does not allocate memory,
// so it is safe to call while "in a system call", outside
// the $GOMAXPROCS accounting.
//
// fn may call back into Go code, in which case we'll exit the
// "system call", run the Go code (which may grow the stack),
// and then re-enter the "system call" reusing the PC and SP
// saved by entersyscall here.
// 宣布正在进入系统调用,从而调度器会创建另一个 M 来运行 goroutine
//
// 对 asmcgocall 的调用保证了不会增加栈并且不分配内存,
// 因此在 $GOMAXPROCS 计数之外的 "系统调用内" 的调用是安全的。
//
// fn 可能会回调 Go 代码,这种情况下我们将退出系统调用来运行 Go 代码
//(可能增长栈),然后再重新进入系统调用来复用 entersyscall 保存的
// PC 和 SP 寄存器
entersyscall()
// Tell asynchronous preemption that we're entering external
// code. We do this after entersyscall because this may block
// and cause an async preemption to fail, but at this point a
// sync preemption will succeed (though this is not a matter
// of correctness).
osPreemptExtEnter(mp)
// 将 m 标记为正在 cgo
mp.incgo = true
// 进入调用
errno := asmcgocall(fn, arg)
// Update accounting before exitsyscall because exitsyscall may
// reschedule us on to a different M.
// 在 exitsyscall 之前进行计数,因为 exitsyscall 可能会把
// 我们重新调度到不同的 M 中
//
// 取消运行 cgo 的标记
mp.incgo = false
// 正在进行的 cgo 数量减少
mp.ncgo--
osPreemptExtExit(mp)
// 宣告退出系统调用
exitsyscall()
// Note that raceacquire must be called only after exitsyscall has
// wired this M to a P.
// race 相关,raceacquire 必须在 exitsyscall 连接了 M 和 P 之后调用
if raceenabled {
raceacquire(unsafe.Pointer(&racecgosync))
}
// From the garbage collector's perspective, time can move
// backwards in the sequence above. If there's a callback into
// Go code, GC will see this function at the call to
// asmcgocall. When the Go call later returns to C, the
// syscall PC/SP is rolled back and the GC sees this function
// back at the call to entersyscall. Normally, fn and arg
// would be live at entersyscall and dead at asmcgocall, so if
// time moved backwards, GC would see these arguments as dead
// and then live. Prevent these undead arguments from crashing
// GC by forcing them to stay live across this time warp.
// 从垃圾收集器的角度来看,时间可以按照上面的顺序向后移动。
// 如果对 Go 代码进行回调,GC 将在调用 asmcgocall 时能看到此函数。
// 当 Go 调用稍后返回到 C 时,系统调用 PC/SP 将被回滚并且 GC 在调用
// enteryscall 时看到此函数。通常情况下,fn 和 arg 将在 enteryscall 上运行
// 并在 asmcgocall 处死亡,因此如果时间向后移动,GC 会将这些参数视为已死,
// 然后生效。通过强制它们在这个时间中保持活跃来防止这些未死亡的参数崩溃。
KeepAlive(fn)
KeepAlive(arg)
KeepAlive(mp)
return errno
}
// Call from C back to Go.
// 从 C 调用 Go
//go:nosplit
func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
gp := getg()
if gp != gp.m.curg {
println("runtime: bad g in cgocallback")
exit(2)
}
// The call from C is on gp.m's g0 stack, so we must ensure
// that we stay on that M. We have to do this before calling
// exitsyscall, since it would otherwise be free to move us to
// a different M. The call to unlockOSThread is in unwindm.
// 从 C 发起的调用运行在 gp.m 的 g0 栈上,因此我们必须确认我们停留在这个 M 上
// 在调用 exitsyscall 之前,必须调用这个操作,否则它会被释放且将我们移动到其他的 M 上
// 当前调用结束前,会调用 unlockOSThread
lockOSThread()
// Save current syscall parameters, so m.syscall can be
// used again if callback decide to make syscall.
// 保存当前系统调用的参数,因此 m.syscall 可以在进行系统调用时候进而回调使用
syscall := gp.m.syscall
// entersyscall saves the caller's SP to allow the GC to trace the Go
// stack. However, since we're returning to an earlier stack frame and
// need to pair with the entersyscall() call made by cgocall, we must
// save syscall* and let reentersyscall restore them.
// entersyscall 保存了调用方 SP 并允许 GC 能够跟踪 Go 栈。然而因为我们会返回到
// 前一个栈帧并需要与 cgocall 的 entersyscall() 匹配,我们必须保存 syscall*
// 并让 reentersyscall 恢复它们
savedsp := unsafe.Pointer(gp.syscallsp)
savedpc := gp.syscallpc
exitsyscall() // coming out of cgo call
gp.m.incgo = false
osPreemptExtExit(gp.m)
cgocallbackg1(fn, frame, ctxt)
// At this point unlockOSThread has been called.
// The following code must not change to a different m.
// This is enforced by checking incgo in the schedule function.
// 这时,unlockOSThread 已经被调用,下面的代码不能修改到其他的 m 上。
// incgo 检查位于 schedule 函数中,这是强制的
osPreemptExtEnter(gp.m)
gp.m.incgo = true
// going back to cgo call
// 返回到 cgo 调用going back to cgo call
reentersyscall(savedpc, uintptr(savedsp))
gp.m.syscall = syscall
}
func cgocallbackg1(fn, frame unsafe.Pointer, ctxt uintptr) {
gp := getg()
if gp.m.needextram || atomic.Load(&extraMWaiters) > 0 {
gp.m.needextram = false
systemstack(newextram)
}
if ctxt != 0 {
s := append(gp.cgoCtxt, ctxt)
// Now we need to set gp.cgoCtxt = s, but we could get
// a SIGPROF signal while manipulating the slice, and
// the SIGPROF handler could pick up gp.cgoCtxt while
// tracing up the stack. We need to ensure that the
// handler always sees a valid slice, so set the
// values in an order such that it always does.
// 现在我们需要设置 gp.cgoCtxt = s,但我们可以得到操纵切片时的 SIGPROF 信号,和
// SIGPROF 处理程序可以获取 gp.cgoCtxt
// 追踪堆栈。我们需要确保 handler 总是看到一个有效的切片,所以设置
// 按顺序排列的值,以便始终如此。
p := (*slice)(unsafe.Pointer(&gp.cgoCtxt))
atomicstorep(unsafe.Pointer(&p.array), unsafe.Pointer(&s[0]))
p.cap = cap(s)
p.len = len(s)
defer func(gp *g) {
// Decrease the length of the slice by one, safely.
// 安全地减少切片的长度。
p := (*slice)(unsafe.Pointer(&gp.cgoCtxt))
p.len--
}(gp)
}
if gp.m.ncgo == 0 {
// The C call to Go came from a thread not currently running
// any Go. In the case of -buildmode=c-archive or c-shared,
// this call may be coming in before package initialization
// is complete. Wait until it is.
// 对 Go 的 C 调用来自一个当前没有运行任何 Go 的线程。
// 在 -buildmode=c-archive 或 c-shared 的情况下,
// 此调用可能在包初始化完成之前进入,等待完成。
<-main_init_done
}
// Add entry to defer stack in case of panic.
restore := true
defer unwindm(&restore)
if raceenabled {
raceacquire(unsafe.Pointer(&racecgosync))
}
// Invoke callback. This function is generated by cmd/cgo and
// will unpack the argument frame and call the Go function.
var cb func(frame unsafe.Pointer)
cbFV := funcval{uintptr(fn)}
*(*unsafe.Pointer)(unsafe.Pointer(&cb)) = noescape(unsafe.Pointer(&cbFV))
cb(frame)
if raceenabled {
racereleasemerge(unsafe.Pointer(&racecgosync))
}
// Do not unwind m->g0->sched.sp.
// Our caller, cgocallback, will do that.
restore = false
}
func unwindm(restore *bool) {
if *restore {
// Restore sp saved by cgocallback during
// unwind of g's stack (see comment at top of file).
mp := acquirem()
sched := &mp.g0.sched
switch GOARCH {
default:
throw("unwindm not implemented")
case "386", "amd64", "arm", "ppc64", "ppc64le", "mips64", "mips64le", "s390x", "mips", "mipsle", "riscv64":
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + sys.MinFrameSize))
case "arm64":
sched.sp = *(*uintptr)(unsafe.Pointer(sched.sp + 16))
}
// Do the accounting that cgocall will not have a chance to do
// during an unwind.
//
// In the case where a Go call originates from C, ncgo is 0
// and there is no matching cgocall to end.
if mp.ncgo > 0 {
mp.incgo = false
mp.ncgo--
osPreemptExtExit(mp)
}
releasem(mp)
}
// Undo the call to lockOSThread in cgocallbackg.
// We must still stay on the same m.
unlockOSThread()
}
// called from assembly
func badcgocallback() {
throw("misaligned stack in cgocallback")
}
// called from (incomplete) assembly
func cgounimpl() {
throw("cgo not implemented")
}
var racecgosync uint64 // represents possible synchronization in C code
// Pointer checking for cgo code.
// We want to detect all cases where a program that does not use
// unsafe makes a cgo call passing a Go pointer to memory that
// contains a Go pointer. Here a Go pointer is defined as a pointer
// to memory allocated by the Go runtime. Programs that use unsafe
// can evade this restriction easily, so we don't try to catch them.
// The cgo program will rewrite all possibly bad pointer arguments to
// call cgoCheckPointer, where we can catch cases of a Go pointer
// pointing to a Go pointer.
// Complicating matters, taking the address of a slice or array
// element permits the C program to access all elements of the slice
// or array. In that case we will see a pointer to a single element,
// but we need to check the entire data structure.
// The cgoCheckPointer call takes additional arguments indicating that
// it was called on an address expression. An additional argument of
// true means that it only needs to check a single element. An
// additional argument of a slice or array means that it needs to
// check the entire slice/array, but nothing else. Otherwise, the
// pointer could be anything, and we check the entire heap object,
// which is conservative but safe.
// When and if we implement a moving garbage collector,
// cgoCheckPointer will pin the pointer for the duration of the cgo
// call. (This is necessary but not sufficient; the cgo program will
// also have to change to pin Go pointers that cannot point to Go
// pointers.)
// cgoCheckPointer checks if the argument contains a Go pointer that
// points to a Go pointer, and panics if it does.
func cgoCheckPointer(ptr interface{}, arg interface{}) {
if debug.cgocheck == 0 {
return
}
ep := efaceOf(&ptr)
t := ep._type
top := true
if arg != nil && (t.kind&kindMask == kindPtr || t.kind&kindMask == kindUnsafePointer) {
p := ep.data
if t.kind&kindDirectIface == 0 {
p = *(*unsafe.Pointer)(p)
}
if p == nil || !cgoIsGoPointer(p) {
return
}
aep := efaceOf(&arg)
switch aep._type.kind & kindMask {
case kindBool:
if t.kind&kindMask == kindUnsafePointer {
// We don't know the type of the element.
break
}
pt := (*ptrtype)(unsafe.Pointer(t))
cgoCheckArg(pt.elem, p, true, false, cgoCheckPointerFail)
return
case kindSlice:
// Check the slice rather than the pointer.
ep = aep
t = ep._type
case kindArray:
// Check the array rather than the pointer.
// Pass top as false since we have a pointer
// to the array.
ep = aep
t = ep._type
top = false
default:
throw("can't happen")
}
}
cgoCheckArg(t, ep.data, t.kind&kindDirectIface == 0, top, cgoCheckPointerFail)
}
const cgoCheckPointerFail = "cgo argument has Go pointer to Go pointer"
const cgoResultFail = "cgo result has Go pointer"
// cgoCheckArg is the real work of cgoCheckPointer. The argument p
// is either a pointer to the value (of type t), or the value itself,
// depending on indir. The top parameter is whether we are at the top
// level, where Go pointers are allowed.
func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) {
if t.ptrdata == 0 || p == nil {
// If the type has no pointers there is nothing to do.
return
}
switch t.kind & kindMask {
default:
throw("can't happen")
case kindArray:
at := (*arraytype)(unsafe.Pointer(t))
if !indir {
if at.len != 1 {
throw("can't happen")
}
cgoCheckArg(at.elem, p, at.elem.kind&kindDirectIface == 0, top, msg)
return
}
for i := uintptr(0); i < at.len; i++ {
cgoCheckArg(at.elem, p, true, top, msg)
p = add(p, at.elem.size)
}
case kindChan, kindMap:
// These types contain internal pointers that will
// always be allocated in the Go heap. It's never OK
// to pass them to C.
panic(errorString(msg))
case kindFunc:
if indir {
p = *(*unsafe.Pointer)(p)
}
if !cgoIsGoPointer(p) {
return
}
panic(errorString(msg))
case kindInterface:
it := *(**_type)(p)
if it == nil {
return
}
// A type known at compile time is OK since it's
// constant. A type not known at compile time will be
// in the heap and will not be OK.
if inheap(uintptr(unsafe.Pointer(it))) {
panic(errorString(msg))
}
p = *(*unsafe.Pointer)(add(p, sys.PtrSize))
if !cgoIsGoPointer(p) {
return
}
if !top {
panic(errorString(msg))
}
cgoCheckArg(it, p, it.kind&kindDirectIface == 0, false, msg)
case kindSlice:
st := (*slicetype)(unsafe.Pointer(t))
s := (*slice)(p)
p = s.array
if p == nil || !cgoIsGoPointer(p) {
return
}
if !top {
panic(errorString(msg))
}
if st.elem.ptrdata == 0 {
return
}
for i := 0; i < s.cap; i++ {
cgoCheckArg(st.elem, p, true, false, msg)
p = add(p, st.elem.size)
}
case kindString:
ss := (*stringStruct)(p)
if !cgoIsGoPointer(ss.str) {
return
}
if !top {
panic(errorString(msg))
}
case kindStruct:
st := (*structtype)(unsafe.Pointer(t))
if !indir {
if len(st.fields) != 1 {
throw("can't happen")
}
cgoCheckArg(st.fields[0].typ, p, st.fields[0].typ.kind&kindDirectIface == 0, top, msg)
return
}
for _, f := range st.fields {
if f.typ.ptrdata == 0 {
continue
}
cgoCheckArg(f.typ, add(p, f.offset()), true, top, msg)
}
case kindPtr, kindUnsafePointer:
if indir {
p = *(*unsafe.Pointer)(p)
if p == nil {
return
}
}
if !cgoIsGoPointer(p) {
return
}
if !top {
panic(errorString(msg))
}
cgoCheckUnknownPointer(p, msg)
}
}
// cgoCheckUnknownPointer is called for an arbitrary pointer into Go
// memory. It checks whether that Go memory contains any other
// pointer into Go memory. If it does, we panic.
// The return values are unused but useful to see in panic tracebacks.
func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) {
if inheap(uintptr(p)) {
b, span, _ := findObject(uintptr(p), 0, 0)
base = b
if base == 0 {
return
}
hbits := heapBitsForAddr(base)
n := span.elemsize
for i = uintptr(0); i < n; i += sys.PtrSize {
if !hbits.morePointers() {
// No more possible pointers.
break
}
if hbits.isPointer() && cgoIsGoPointer(*(*unsafe.Pointer)(unsafe.Pointer(base + i))) {
panic(errorString(msg))
}
hbits = hbits.next()
}
return
}
for _, datap := range activeModules() {
if cgoInRange(p, datap.data, datap.edata) || cgoInRange(p, datap.bss, datap.ebss) {
// We have no way to know the size of the object.
// We have to assume that it might contain a pointer.
panic(errorString(msg))
}
// In the text or noptr sections, we know that the
// pointer does not point to a Go pointer.
}
return
}
// cgoIsGoPointer reports whether the pointer is a Go pointer--a
// pointer to Go memory. We only care about Go memory that might
// contain pointers.
//go:nosplit
//go:nowritebarrierrec
func cgoIsGoPointer(p unsafe.Pointer) bool {
if p == nil {
return false
}
if inHeapOrStack(uintptr(p)) {
return true
}
for _, datap := range activeModules() {
if cgoInRange(p, datap.data, datap.edata) || cgoInRange(p, datap.bss, datap.ebss) {
return true
}
}
return false
}
// cgoInRange reports whether p is between start and end.
//go:nosplit
//go:nowritebarrierrec
func cgoInRange(p unsafe.Pointer, start, end uintptr) bool {
return start <= uintptr(p) && uintptr(p) < end
}
// cgoCheckResult is called to check the result parameter of an
// exported Go function. It panics if the result is or contains a Go
// pointer.
func cgoCheckResult(val interface{}) {
if debug.cgocheck == 0 {
return
}
ep := efaceOf(&val)
t := ep._type
cgoCheckArg(t, ep.data, t.kind&kindDirectIface == 0, false, cgoResultFail)
}