1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package runtime
6
7import (
8	"internal/abi"
9	"internal/goarch"
10	"internal/runtime/atomic"
11	"unsafe"
12)
13
14//go:cgo_export_dynamic runtime.end _end
15//go:cgo_export_dynamic runtime.etext _etext
16//go:cgo_export_dynamic runtime.edata _edata
17
18//go:cgo_import_dynamic libc____errno ___errno "libc.so"
19//go:cgo_import_dynamic libc_clock_gettime clock_gettime "libc.so"
20//go:cgo_import_dynamic libc_exit _exit "libc.so"
21//go:cgo_import_dynamic libc_getcontext getcontext "libc.so"
22//go:cgo_import_dynamic libc_kill kill "libc.so"
23//go:cgo_import_dynamic libc_madvise madvise "libc.so"
24//go:cgo_import_dynamic libc_malloc malloc "libc.so"
25//go:cgo_import_dynamic libc_mmap mmap "libc.so"
26//go:cgo_import_dynamic libc_munmap munmap "libc.so"
27//go:cgo_import_dynamic libc_open open "libc.so"
28//go:cgo_import_dynamic libc_pthread_attr_destroy pthread_attr_destroy "libc.so"
29//go:cgo_import_dynamic libc_pthread_attr_getstack pthread_attr_getstack "libc.so"
30//go:cgo_import_dynamic libc_pthread_attr_init pthread_attr_init "libc.so"
31//go:cgo_import_dynamic libc_pthread_attr_setdetachstate pthread_attr_setdetachstate "libc.so"
32//go:cgo_import_dynamic libc_pthread_attr_setstack pthread_attr_setstack "libc.so"
33//go:cgo_import_dynamic libc_pthread_create pthread_create "libc.so"
34//go:cgo_import_dynamic libc_pthread_self pthread_self "libc.so"
35//go:cgo_import_dynamic libc_pthread_kill pthread_kill "libc.so"
36//go:cgo_import_dynamic libc_raise raise "libc.so"
37//go:cgo_import_dynamic libc_read read "libc.so"
38//go:cgo_import_dynamic libc_select select "libc.so"
39//go:cgo_import_dynamic libc_sched_yield sched_yield "libc.so"
40//go:cgo_import_dynamic libc_sem_init sem_init "libc.so"
41//go:cgo_import_dynamic libc_sem_post sem_post "libc.so"
42//go:cgo_import_dynamic libc_sem_reltimedwait_np sem_reltimedwait_np "libc.so"
43//go:cgo_import_dynamic libc_sem_wait sem_wait "libc.so"
44//go:cgo_import_dynamic libc_setitimer setitimer "libc.so"
45//go:cgo_import_dynamic libc_sigaction sigaction "libc.so"
46//go:cgo_import_dynamic libc_sigaltstack sigaltstack "libc.so"
47//go:cgo_import_dynamic libc_sigprocmask sigprocmask "libc.so"
48//go:cgo_import_dynamic libc_sysconf sysconf "libc.so"
49//go:cgo_import_dynamic libc_usleep usleep "libc.so"
50//go:cgo_import_dynamic libc_write write "libc.so"
51//go:cgo_import_dynamic libc_pipe2 pipe2 "libc.so"
52
53//go:linkname libc____errno libc____errno
54//go:linkname libc_clock_gettime libc_clock_gettime
55//go:linkname libc_exit libc_exit
56//go:linkname libc_getcontext libc_getcontext
57//go:linkname libc_kill libc_kill
58//go:linkname libc_madvise libc_madvise
59//go:linkname libc_malloc libc_malloc
60//go:linkname libc_mmap libc_mmap
61//go:linkname libc_munmap libc_munmap
62//go:linkname libc_open libc_open
63//go:linkname libc_pthread_attr_destroy libc_pthread_attr_destroy
64//go:linkname libc_pthread_attr_getstack libc_pthread_attr_getstack
65//go:linkname libc_pthread_attr_init libc_pthread_attr_init
66//go:linkname libc_pthread_attr_setdetachstate libc_pthread_attr_setdetachstate
67//go:linkname libc_pthread_attr_setstack libc_pthread_attr_setstack
68//go:linkname libc_pthread_create libc_pthread_create
69//go:linkname libc_pthread_self libc_pthread_self
70//go:linkname libc_pthread_kill libc_pthread_kill
71//go:linkname libc_raise libc_raise
72//go:linkname libc_read libc_read
73//go:linkname libc_select libc_select
74//go:linkname libc_sched_yield libc_sched_yield
75//go:linkname libc_sem_init libc_sem_init
76//go:linkname libc_sem_post libc_sem_post
77//go:linkname libc_sem_reltimedwait_np libc_sem_reltimedwait_np
78//go:linkname libc_sem_wait libc_sem_wait
79//go:linkname libc_setitimer libc_setitimer
80//go:linkname libc_sigaction libc_sigaction
81//go:linkname libc_sigaltstack libc_sigaltstack
82//go:linkname libc_sigprocmask libc_sigprocmask
83//go:linkname libc_sysconf libc_sysconf
84//go:linkname libc_usleep libc_usleep
85//go:linkname libc_write libc_write
86//go:linkname libc_pipe2 libc_pipe2
87
88var (
89	libc____errno,
90	libc_clock_gettime,
91	libc_exit,
92	libc_getcontext,
93	libc_kill,
94	libc_madvise,
95	libc_malloc,
96	libc_mmap,
97	libc_munmap,
98	libc_open,
99	libc_pthread_attr_destroy,
100	libc_pthread_attr_getstack,
101	libc_pthread_attr_init,
102	libc_pthread_attr_setdetachstate,
103	libc_pthread_attr_setstack,
104	libc_pthread_create,
105	libc_pthread_self,
106	libc_pthread_kill,
107	libc_raise,
108	libc_read,
109	libc_sched_yield,
110	libc_select,
111	libc_sem_init,
112	libc_sem_post,
113	libc_sem_reltimedwait_np,
114	libc_sem_wait,
115	libc_setitimer,
116	libc_sigaction,
117	libc_sigaltstack,
118	libc_sigprocmask,
119	libc_sysconf,
120	libc_usleep,
121	libc_write,
122	libc_pipe2 libcFunc
123)
124
125var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
126
127func getPageSize() uintptr {
128	n := int32(sysconf(__SC_PAGESIZE))
129	if n <= 0 {
130		return 0
131	}
132	return uintptr(n)
133}
134
135func osinit() {
136	// Call miniterrno so that we can safely make system calls
137	// before calling minit on m0.
138	asmcgocall(unsafe.Pointer(abi.FuncPCABI0(miniterrno)), unsafe.Pointer(&libc____errno))
139
140	ncpu = getncpu()
141	if physPageSize == 0 {
142		physPageSize = getPageSize()
143	}
144}
145
146func tstart_sysvicall(newm *m) uint32
147
148// May run with m.p==nil, so write barriers are not allowed.
149//
150//go:nowritebarrier
151func newosproc(mp *m) {
152	var (
153		attr pthreadattr
154		oset sigset
155		tid  pthread
156		ret  int32
157		size uint64
158	)
159
160	if pthread_attr_init(&attr) != 0 {
161		throw("pthread_attr_init")
162	}
163	// Allocate a new 2MB stack.
164	if pthread_attr_setstack(&attr, 0, 0x200000) != 0 {
165		throw("pthread_attr_setstack")
166	}
167	// Read back the allocated stack.
168	if pthread_attr_getstack(&attr, unsafe.Pointer(&mp.g0.stack.hi), &size) != 0 {
169		throw("pthread_attr_getstack")
170	}
171	mp.g0.stack.lo = mp.g0.stack.hi - uintptr(size)
172	if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
173		throw("pthread_attr_setdetachstate")
174	}
175
176	// Disable signals during create, so that the new thread starts
177	// with signals disabled. It will enable them in minit.
178	sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
179	ret = retryOnEAGAIN(func() int32 {
180		return pthread_create(&tid, &attr, abi.FuncPCABI0(tstart_sysvicall), unsafe.Pointer(mp))
181	})
182	sigprocmask(_SIG_SETMASK, &oset, nil)
183	if ret != 0 {
184		print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n")
185		if ret == _EAGAIN {
186			println("runtime: may need to increase max user processes (ulimit -u)")
187		}
188		throw("newosproc")
189	}
190}
191
192func exitThread(wait *atomic.Uint32) {
193	// We should never reach exitThread on Solaris because we let
194	// libc clean up threads.
195	throw("exitThread")
196}
197
198var urandom_dev = []byte("/dev/urandom\x00")
199
200//go:nosplit
201func readRandom(r []byte) int {
202	fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
203	n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
204	closefd(fd)
205	return int(n)
206}
207
208func goenvs() {
209	goenvs_unix()
210}
211
212// Called to initialize a new m (including the bootstrap m).
213// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
214func mpreinit(mp *m) {
215	mp.gsignal = malg(32 * 1024)
216	mp.gsignal.m = mp
217}
218
219func miniterrno()
220
221// Called to initialize a new m (including the bootstrap m).
222// Called on the new thread, cannot allocate memory.
223func minit() {
224	asmcgocall(unsafe.Pointer(abi.FuncPCABI0(miniterrno)), unsafe.Pointer(&libc____errno))
225
226	minitSignals()
227
228	getg().m.procid = uint64(pthread_self())
229}
230
231// Called from dropm to undo the effect of an minit.
232func unminit() {
233	unminitSignals()
234	getg().m.procid = 0
235}
236
237// Called from exitm, but not from drop, to undo the effect of thread-owned
238// resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
239func mdestroy(mp *m) {
240}
241
242func sigtramp()
243
244//go:nosplit
245//go:nowritebarrierrec
246func setsig(i uint32, fn uintptr) {
247	var sa sigactiont
248
249	sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
250	sa.sa_mask = sigset_all
251	if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go
252		fn = abi.FuncPCABI0(sigtramp)
253	}
254	*((*uintptr)(unsafe.Pointer(&sa._funcptr))) = fn
255	sigaction(i, &sa, nil)
256}
257
258//go:nosplit
259//go:nowritebarrierrec
260func setsigstack(i uint32) {
261	var sa sigactiont
262	sigaction(i, nil, &sa)
263	if sa.sa_flags&_SA_ONSTACK != 0 {
264		return
265	}
266	sa.sa_flags |= _SA_ONSTACK
267	sigaction(i, &sa, nil)
268}
269
270//go:nosplit
271//go:nowritebarrierrec
272func getsig(i uint32) uintptr {
273	var sa sigactiont
274	sigaction(i, nil, &sa)
275	return *((*uintptr)(unsafe.Pointer(&sa._funcptr)))
276}
277
278// setSignalstackSP sets the ss_sp field of a stackt.
279//
280//go:nosplit
281func setSignalstackSP(s *stackt, sp uintptr) {
282	*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
283}
284
285//go:nosplit
286//go:nowritebarrierrec
287func sigaddset(mask *sigset, i int) {
288	mask.__sigbits[(i-1)/32] |= 1 << ((uint32(i) - 1) & 31)
289}
290
291func sigdelset(mask *sigset, i int) {
292	mask.__sigbits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
293}
294
295//go:nosplit
296func (c *sigctxt) fixsigcode(sig uint32) {
297}
298
299func setProcessCPUProfiler(hz int32) {
300	setProcessCPUProfilerTimer(hz)
301}
302
303func setThreadCPUProfiler(hz int32) {
304	setThreadCPUProfilerHz(hz)
305}
306
307//go:nosplit
308func validSIGPROF(mp *m, c *sigctxt) bool {
309	return true
310}
311
312//go:nosplit
313func semacreate(mp *m) {
314	if mp.waitsema != 0 {
315		return
316	}
317
318	var sem *semt
319
320	// Call libc's malloc rather than malloc. This will
321	// allocate space on the C heap. We can't call malloc
322	// here because it could cause a deadlock.
323	mp.libcall.fn = uintptr(unsafe.Pointer(&libc_malloc))
324	mp.libcall.n = 1
325	mp.scratch = mscratch{}
326	mp.scratch.v[0] = unsafe.Sizeof(*sem)
327	mp.libcall.args = uintptr(unsafe.Pointer(&mp.scratch))
328	asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&mp.libcall))
329	sem = (*semt)(unsafe.Pointer(mp.libcall.r1))
330	if sem_init(sem, 0, 0) != 0 {
331		throw("sem_init")
332	}
333	mp.waitsema = uintptr(unsafe.Pointer(sem))
334}
335
336//go:nosplit
337func semasleep(ns int64) int32 {
338	mp := getg().m
339	if ns >= 0 {
340		mp.ts.tv_sec = ns / 1000000000
341		mp.ts.tv_nsec = ns % 1000000000
342
343		mp.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_reltimedwait_np))
344		mp.libcall.n = 2
345		mp.scratch = mscratch{}
346		mp.scratch.v[0] = mp.waitsema
347		mp.scratch.v[1] = uintptr(unsafe.Pointer(&mp.ts))
348		mp.libcall.args = uintptr(unsafe.Pointer(&mp.scratch))
349		asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&mp.libcall))
350		if *mp.perrno != 0 {
351			if *mp.perrno == _ETIMEDOUT || *mp.perrno == _EAGAIN || *mp.perrno == _EINTR {
352				return -1
353			}
354			throw("sem_reltimedwait_np")
355		}
356		return 0
357	}
358	for {
359		mp.libcall.fn = uintptr(unsafe.Pointer(&libc_sem_wait))
360		mp.libcall.n = 1
361		mp.scratch = mscratch{}
362		mp.scratch.v[0] = mp.waitsema
363		mp.libcall.args = uintptr(unsafe.Pointer(&mp.scratch))
364		asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&mp.libcall))
365		if mp.libcall.r1 == 0 {
366			break
367		}
368		if *mp.perrno == _EINTR {
369			continue
370		}
371		throw("sem_wait")
372	}
373	return 0
374}
375
376//go:nosplit
377func semawakeup(mp *m) {
378	if sem_post((*semt)(unsafe.Pointer(mp.waitsema))) != 0 {
379		throw("sem_post")
380	}
381}
382
383//go:nosplit
384func closefd(fd int32) int32 {
385	return int32(sysvicall1(&libc_close, uintptr(fd)))
386}
387
388//go:nosplit
389func exit(r int32) {
390	sysvicall1(&libc_exit, uintptr(r))
391}
392
393//go:nosplit
394func getcontext(context *ucontext) /* int32 */ {
395	sysvicall1(&libc_getcontext, uintptr(unsafe.Pointer(context)))
396}
397
398//go:nosplit
399func madvise(addr unsafe.Pointer, n uintptr, flags int32) {
400	sysvicall3(&libc_madvise, uintptr(addr), uintptr(n), uintptr(flags))
401}
402
403//go:nosplit
404func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) {
405	p, err := doMmap(uintptr(addr), n, uintptr(prot), uintptr(flags), uintptr(fd), uintptr(off))
406	if p == ^uintptr(0) {
407		return nil, int(err)
408	}
409	return unsafe.Pointer(p), 0
410}
411
412//go:nosplit
413//go:cgo_unsafe_args
414func doMmap(addr, n, prot, flags, fd, off uintptr) (uintptr, uintptr) {
415	var libcall libcall
416	libcall.fn = uintptr(unsafe.Pointer(&libc_mmap))
417	libcall.n = 6
418	libcall.args = uintptr(noescape(unsafe.Pointer(&addr)))
419	asmcgocall(unsafe.Pointer(&asmsysvicall6x), unsafe.Pointer(&libcall))
420	return libcall.r1, libcall.err
421}
422
423//go:nosplit
424func munmap(addr unsafe.Pointer, n uintptr) {
425	sysvicall2(&libc_munmap, uintptr(addr), uintptr(n))
426}
427
428const (
429	_CLOCK_REALTIME  = 3
430	_CLOCK_MONOTONIC = 4
431)
432
433//go:nosplit
434func nanotime1() int64 {
435	var ts mts
436	sysvicall2(&libc_clock_gettime, _CLOCK_MONOTONIC, uintptr(unsafe.Pointer(&ts)))
437	return ts.tv_sec*1e9 + ts.tv_nsec
438}
439
440//go:nosplit
441func open(path *byte, mode, perm int32) int32 {
442	return int32(sysvicall3(&libc_open, uintptr(unsafe.Pointer(path)), uintptr(mode), uintptr(perm)))
443}
444
445func pthread_attr_destroy(attr *pthreadattr) int32 {
446	return int32(sysvicall1(&libc_pthread_attr_destroy, uintptr(unsafe.Pointer(attr))))
447}
448
449func pthread_attr_getstack(attr *pthreadattr, addr unsafe.Pointer, size *uint64) int32 {
450	return int32(sysvicall3(&libc_pthread_attr_getstack, uintptr(unsafe.Pointer(attr)), uintptr(addr), uintptr(unsafe.Pointer(size))))
451}
452
453func pthread_attr_init(attr *pthreadattr) int32 {
454	return int32(sysvicall1(&libc_pthread_attr_init, uintptr(unsafe.Pointer(attr))))
455}
456
457func pthread_attr_setdetachstate(attr *pthreadattr, state int32) int32 {
458	return int32(sysvicall2(&libc_pthread_attr_setdetachstate, uintptr(unsafe.Pointer(attr)), uintptr(state)))
459}
460
461func pthread_attr_setstack(attr *pthreadattr, addr uintptr, size uint64) int32 {
462	return int32(sysvicall3(&libc_pthread_attr_setstack, uintptr(unsafe.Pointer(attr)), uintptr(addr), uintptr(size)))
463}
464
465func pthread_create(thread *pthread, attr *pthreadattr, fn uintptr, arg unsafe.Pointer) int32 {
466	return int32(sysvicall4(&libc_pthread_create, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(fn), uintptr(arg)))
467}
468
469func pthread_self() pthread {
470	return pthread(sysvicall0(&libc_pthread_self))
471}
472
473func signalM(mp *m, sig int) {
474	sysvicall2(&libc_pthread_kill, uintptr(pthread(mp.procid)), uintptr(sig))
475}
476
477//go:nosplit
478//go:nowritebarrierrec
479func raise(sig uint32) /* int32 */ {
480	sysvicall1(&libc_raise, uintptr(sig))
481}
482
483func raiseproc(sig uint32) /* int32 */ {
484	pid := sysvicall0(&libc_getpid)
485	sysvicall2(&libc_kill, pid, uintptr(sig))
486}
487
488//go:nosplit
489func read(fd int32, buf unsafe.Pointer, nbyte int32) int32 {
490	r1, err := sysvicall3Err(&libc_read, uintptr(fd), uintptr(buf), uintptr(nbyte))
491	if c := int32(r1); c >= 0 {
492		return c
493	}
494	return -int32(err)
495}
496
497//go:nosplit
498func sem_init(sem *semt, pshared int32, value uint32) int32 {
499	return int32(sysvicall3(&libc_sem_init, uintptr(unsafe.Pointer(sem)), uintptr(pshared), uintptr(value)))
500}
501
502//go:nosplit
503func sem_post(sem *semt) int32 {
504	return int32(sysvicall1(&libc_sem_post, uintptr(unsafe.Pointer(sem))))
505}
506
507//go:nosplit
508func sem_reltimedwait_np(sem *semt, timeout *timespec) int32 {
509	return int32(sysvicall2(&libc_sem_reltimedwait_np, uintptr(unsafe.Pointer(sem)), uintptr(unsafe.Pointer(timeout))))
510}
511
512//go:nosplit
513func sem_wait(sem *semt) int32 {
514	return int32(sysvicall1(&libc_sem_wait, uintptr(unsafe.Pointer(sem))))
515}
516
517func setitimer(which int32, value *itimerval, ovalue *itimerval) /* int32 */ {
518	sysvicall3(&libc_setitimer, uintptr(which), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(ovalue)))
519}
520
521//go:nosplit
522//go:nowritebarrierrec
523func sigaction(sig uint32, act *sigactiont, oact *sigactiont) /* int32 */ {
524	sysvicall3(&libc_sigaction, uintptr(sig), uintptr(unsafe.Pointer(act)), uintptr(unsafe.Pointer(oact)))
525}
526
527//go:nosplit
528//go:nowritebarrierrec
529func sigaltstack(ss *stackt, oss *stackt) /* int32 */ {
530	sysvicall2(&libc_sigaltstack, uintptr(unsafe.Pointer(ss)), uintptr(unsafe.Pointer(oss)))
531}
532
533//go:nosplit
534//go:nowritebarrierrec
535func sigprocmask(how int32, set *sigset, oset *sigset) /* int32 */ {
536	sysvicall3(&libc_sigprocmask, uintptr(how), uintptr(unsafe.Pointer(set)), uintptr(unsafe.Pointer(oset)))
537}
538
539func sysconf(name int32) int64 {
540	return int64(sysvicall1(&libc_sysconf, uintptr(name)))
541}
542
543func usleep1(usec uint32)
544
545//go:nosplit
546func usleep_no_gs uint32) {
547	usleep1s)
548}
549
550//go:nosplit
551func usleeps uint32) {
552	usleep1s)
553}
554
555func walltime() (sec int64, nsec int32) {
556	var ts mts
557	sysvicall2(&libc_clock_gettime, _CLOCK_REALTIME, uintptr(unsafe.Pointer(&ts)))
558	return ts.tv_sec, int32(ts.tv_nsec)
559}
560
561//go:nosplit
562func write1(fd uintptr, buf unsafe.Pointer, nbyte int32) int32 {
563	r1, err := sysvicall3Err(&libc_write, fd, uintptr(buf), uintptr(nbyte))
564	if c := int32(r1); c >= 0 {
565		return c
566	}
567	return -int32(err)
568}
569
570//go:nosplit
571func pipe2(flags int32) (r, w int32, errno int32) {
572	var p [2]int32
573	_, e := sysvicall2Err(&libc_pipe2, uintptr(noescape(unsafe.Pointer(&p))), uintptr(flags))
574	return p[0], p[1], int32(e)
575}
576
577//go:nosplit
578func fcntl(fd, cmd, arg int32) (ret int32, errno int32) {
579	r1, err := sysvicall3Err(&libc_fcntl, uintptr(fd), uintptr(cmd), uintptr(arg))
580	return int32(r1), int32(err)
581}
582
583func osyield1()
584
585//go:nosplit
586func osyield_no_g() {
587	osyield1()
588}
589
590//go:nosplit
591func osyield() {
592	sysvicall0(&libc_sched_yield)
593}
594
595//go:linkname executablePath os.executablePath
596var executablePath string
597
598func sysargs(argc int32, argv **byte) {
599	n := argc + 1
600
601	// skip over argv, envp to get to auxv
602	for argv_index(argv, n) != nil {
603		n++
604	}
605
606	// skip NULL separator
607	n++
608
609	// now argv+n is auxv
610	auxvp := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*goarch.PtrSize))
611	pairs := sysauxv(auxvp[:])
612	auxv = auxvp[: pairs*2 : pairs*2]
613}
614
615const (
616	_AT_NULL         = 0    // Terminates the vector
617	_AT_PAGESZ       = 6    // Page size in bytes
618	_AT_SUN_EXECNAME = 2014 // exec() path name
619)
620
621func sysauxv(auxv []uintptr) (pairs int) {
622	var i int
623	for i = 0; auxv[i] != _AT_NULL; i += 2 {
624		tag, val := auxv[i], auxv[i+1]
625		switch tag {
626		case _AT_PAGESZ:
627			physPageSize = val
628		case _AT_SUN_EXECNAME:
629			executablePath = gostringnocopy((*byte)(unsafe.Pointer(val)))
630		}
631	}
632	return i / 2
633}
634
635// sigPerThreadSyscall is only used on linux, so we assign a bogus signal
636// number.
637const sigPerThreadSyscall = 1 << 31
638
639//go:nosplit
640func runPerThreadSyscall() {
641	throw("runPerThreadSyscall only valid on linux")
642}
643