mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 07:22:24 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			1033 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1033 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2023 The Libc Authors. All rights reserved.
 | ||
| // Use of this source code is governed by a BSD-style
 | ||
| // license that can be found in the LICENSE file.
 | ||
| 
 | ||
| //go:build linux && (amd64 || loong64)
 | ||
| 
 | ||
| //go:generate go run generator.go
 | ||
| 
 | ||
| // Package libc is the runtime for programs generated by ccgo/v4 or later.
 | ||
| //
 | ||
| // # Version compatibility
 | ||
| //
 | ||
| // The API of this package, in particular the bits that directly support the
 | ||
| // ccgo compiler, may change in a way that is not backward compatible. If you
 | ||
| // have generated some Go code from C you should stick to the version of this
 | ||
| // package that you used at that time and was tested with your payload. The
 | ||
| // correct way to upgrade to a newer version of this package is to first
 | ||
| // recompile (C to Go) your code with a newwer version if ccgo that depends on
 | ||
| // the new libc version.
 | ||
| //
 | ||
| // If you use C to Go translated code provided by others, stick to the version
 | ||
| // of libc that translated code shows in its go.mod file and do not upgrade the
 | ||
| // dependency just because a newer libc is tagged.Vgq
 | ||
| //
 | ||
| // This is if course unfortunate. However, it's somewhat similar to C code
 | ||
| // linked with a specific version of, say GNU libc. When such code asking for
 | ||
| // glibc5 is run on a system with glibc6, or vice versa, it will fail.
 | ||
| //
 | ||
| // As a particular example, if your project imports modernc.org/sqlite you
 | ||
| // should use the same libc version as seen in the go.mod file of the sqlite
 | ||
| // package.
 | ||
| //
 | ||
| // tl;dr: It is not always possible to fix ccgo bugs and/or improve performance
 | ||
| // of the ccgo transpiled code without occasionally making incompatible changes
 | ||
| // to this package.
 | ||
| //
 | ||
| // # Thread Local Storage
 | ||
| //
 | ||
| // A TLS instance represents a main thread or a thread created by
 | ||
| // Xpthread_create. A TLS instance is not safe for concurrent use by multiple
 | ||
| // goroutines.
 | ||
| //
 | ||
| // If a program starts the C main function, a TLS instance is created
 | ||
| // automatically and the goroutine entering main() is locked to the OS thread.
 | ||
| // The translated C code then may create other pthreads by calling
 | ||
| // Xpthread_create.
 | ||
| //
 | ||
| // If the translated C code is part of a library package, new TLS instances
 | ||
| // must be created manually in user/client code. The first TLS instance created
 | ||
| // will be the "main" libc thread, but it will be not locked to OS thread
 | ||
| // automatically. Any subsequently manually created TLS instances will call
 | ||
| // Xpthread_create, but without spawning a new goroutine.
 | ||
| //
 | ||
| // A manual call to Xpthread_create will create a new TLS instance automatically
 | ||
| // and spawn a new goroutine executing the thread function.
 | ||
| 
 | ||
| // Package libc provides run time support for programs generated by the
 | ||
| // [ccgo] C to Go transpiler, version 4 or later.
 | ||
| //
 | ||
| // # Concurrency
 | ||
| //
 | ||
| // Many C libc functions are not thread safe. Such functions are not safe
 | ||
| // for concurrent use by multiple goroutines in the Go translation as well.
 | ||
| //
 | ||
| // # Thread Local Storage
 | ||
| //
 | ||
| // C threads are modeled as Go goroutines.  Every such C thread, ie. a Go
 | ||
| // goroutine, must use its own Thread Local Storage instance implemented by the
 | ||
| // [TLS] type.
 | ||
| //
 | ||
| // # Signals
 | ||
| //
 | ||
| // Signal handling in translated C code is not coordinated with the Go runtime.
 | ||
| // This is probably the same as when running C code via CGo.
 | ||
| //
 | ||
| // # Environmental variables
 | ||
| //
 | ||
| // This package synchronizes its environ with the current Go environ lazily and
 | ||
| // only once.
 | ||
| //
 | ||
| // # libc API documentation copyright
 | ||
| //
 | ||
| // From [Linux man-pages Copyleft]
 | ||
| //
 | ||
| //	Permission is granted to make and distribute verbatim copies of this
 | ||
| //	manual provided the copyright notice and this permission notice are
 | ||
| //	preserved on all copies.
 | ||
| //
 | ||
| //	Permission is granted to copy and distribute modified versions of this
 | ||
| //	manual under the conditions for verbatim copying, provided that the
 | ||
| //	entire resulting derived work is distributed under the terms of a
 | ||
| //	permission notice identical to this one.
 | ||
| //
 | ||
| //	Since the Linux kernel and libraries are constantly changing, this
 | ||
| //	manual page may be incorrect or out-of-date. The author(s) assume no
 | ||
| //	responsibility for errors or omissions, or for damages resulting from
 | ||
| //	the use of the information contained herein. The author(s) may not have
 | ||
| //	taken the same level of care in the production of this manual, which is
 | ||
| //	licensed free of charge, as they might when working professionally.
 | ||
| //
 | ||
| //	Formatted or processed versions of this manual, if unaccompanied by the
 | ||
| //	source, must acknowledge the copyright and authors of this work.
 | ||
| //
 | ||
| // [Linux man-pages Copyleft]: https://spdx.org/licenses/Linux-man-pages-copyleft.html
 | ||
| // [ccgo]: http://modernc.org/ccgo/v4
 | ||
| package libc // import "modernc.org/libc"
 | ||
| 
 | ||
| import (
 | ||
| 	"fmt"
 | ||
| 	"io"
 | ||
| 	"math"
 | ||
| 	"math/rand"
 | ||
| 	"os"
 | ||
| 	"os/exec"
 | ||
| 	gosignal "os/signal"
 | ||
| 	"path/filepath"
 | ||
| 	"runtime"
 | ||
| 	"sort"
 | ||
| 	"strings"
 | ||
| 	"sync"
 | ||
| 	"sync/atomic"
 | ||
| 	"syscall"
 | ||
| 	"unsafe"
 | ||
| 
 | ||
| 	"golang.org/x/sys/unix"
 | ||
| 	"modernc.org/memory"
 | ||
| )
 | ||
| 
 | ||
| const (
 | ||
| 	heapAlign = 16
 | ||
| 	heapGuard = 16
 | ||
| )
 | ||
| 
 | ||
| var (
 | ||
| 	_ error = (*MemAuditError)(nil)
 | ||
| 
 | ||
| 	allocator   memory.Allocator
 | ||
| 	allocatorMu sync.Mutex
 | ||
| 
 | ||
| 	atExitMu sync.Mutex
 | ||
| 	atExit   []func()
 | ||
| 
 | ||
| 	tid atomic.Int32 // TLS Go ID
 | ||
| 
 | ||
| 	Covered  = map[uintptr]struct{}{}
 | ||
| 	CoveredC = map[string]struct{}{}
 | ||
| 	coverPCs [1]uintptr //TODO not concurrent safe
 | ||
| )
 | ||
| 
 | ||
| func init() {
 | ||
| 	nm, err := os.Executable()
 | ||
| 	if err != nil {
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	Xprogram_invocation_name = mustCString(nm)
 | ||
| 	Xprogram_invocation_short_name = mustCString(filepath.Base(nm))
 | ||
| }
 | ||
| 
 | ||
| // RawMem64 represents the biggest uint64 array the runtime can handle.
 | ||
| type RawMem64 [unsafe.Sizeof(RawMem{}) / unsafe.Sizeof(uint64(0))]uint64
 | ||
| 
 | ||
| type MemAuditError struct {
 | ||
| 	Caller  string
 | ||
| 	Message string
 | ||
| }
 | ||
| 
 | ||
| func (e *MemAuditError) Error() string {
 | ||
| 	return fmt.Sprintf("%s: %s", e.Caller, e.Message)
 | ||
| }
 | ||
| 
 | ||
| // Start executes C's main.
 | ||
| func Start(main func(*TLS, int32, uintptr) int32) {
 | ||
| 	runtime.LockOSThread()
 | ||
| 	if isMemBrk {
 | ||
| 		defer func() {
 | ||
| 			trc("==== PANIC")
 | ||
| 			for _, v := range MemAudit() {
 | ||
| 				trc("", v.Error())
 | ||
| 			}
 | ||
| 		}()
 | ||
| 	}
 | ||
| 
 | ||
| 	tls := NewTLS()
 | ||
| 	Xexit(tls, main(tls, int32(len(os.Args)), mustAllocStrings(os.Args)))
 | ||
| }
 | ||
| 
 | ||
| func mustAllocStrings(a []string) (r uintptr) {
 | ||
| 	nPtrs := len(a) + 1
 | ||
| 	pPtrs := mustCalloc(Tsize_t(uintptr(nPtrs) * unsafe.Sizeof(uintptr(0))))
 | ||
| 	ptrs := unsafe.Slice((*uintptr)(unsafe.Pointer(pPtrs)), nPtrs)
 | ||
| 	nBytes := 0
 | ||
| 	for _, v := range a {
 | ||
| 		nBytes += len(v) + 1
 | ||
| 	}
 | ||
| 	pBytes := mustCalloc(Tsize_t(nBytes))
 | ||
| 	b := unsafe.Slice((*byte)(unsafe.Pointer(pBytes)), nBytes)
 | ||
| 	for i, v := range a {
 | ||
| 		copy(b, v)
 | ||
| 		b = b[len(v)+1:]
 | ||
| 		ptrs[i] = pBytes
 | ||
| 		pBytes += uintptr(len(v)) + 1
 | ||
| 	}
 | ||
| 	return pPtrs
 | ||
| }
 | ||
| 
 | ||
| func mustCString(s string) (r uintptr) {
 | ||
| 	n := len(s)
 | ||
| 	r = mustMalloc(Tsize_t(n + 1))
 | ||
| 	copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), s)
 | ||
| 	*(*byte)(unsafe.Pointer(r + uintptr(n))) = 0
 | ||
| 	return r
 | ||
| }
 | ||
| 
 | ||
| // CString returns a pointer to a zero-terminated version of s. The caller is
 | ||
| // responsible for freeing the allocated memory using Xfree.
 | ||
| func CString(s string) (uintptr, error) {
 | ||
| 	n := len(s)
 | ||
| 	p := Xmalloc(nil, Tsize_t(n)+1)
 | ||
| 	if p == 0 {
 | ||
| 		return 0, fmt.Errorf("CString: cannot allocate %d bytes", n+1)
 | ||
| 	}
 | ||
| 
 | ||
| 	copy(unsafe.Slice((*byte)(unsafe.Pointer(p)), n), s)
 | ||
| 	*(*byte)(unsafe.Pointer(p + uintptr(n))) = 0
 | ||
| 	return p, nil
 | ||
| }
 | ||
| 
 | ||
| // GoBytes returns a byte slice from a C char* having length len bytes.
 | ||
| func GoBytes(s uintptr, len int) []byte {
 | ||
| 	return unsafe.Slice((*byte)(unsafe.Pointer(s)), len)
 | ||
| }
 | ||
| 
 | ||
| // GoString returns the value of a C string at s.
 | ||
| func GoString(s uintptr) string {
 | ||
| 	if s == 0 {
 | ||
| 		return ""
 | ||
| 	}
 | ||
| 
 | ||
| 	var buf []byte
 | ||
| 	for {
 | ||
| 		b := *(*byte)(unsafe.Pointer(s))
 | ||
| 		if b == 0 {
 | ||
| 			return string(buf)
 | ||
| 		}
 | ||
| 
 | ||
| 		buf = append(buf, b)
 | ||
| 		s++
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func mustMalloc(sz Tsize_t) (r uintptr) {
 | ||
| 	if r = Xmalloc(nil, sz); r != 0 || sz == 0 {
 | ||
| 		return r
 | ||
| 	}
 | ||
| 
 | ||
| 	panic(todo("OOM"))
 | ||
| }
 | ||
| 
 | ||
| func mustCalloc(sz Tsize_t) (r uintptr) {
 | ||
| 	if r := Xcalloc(nil, 1, sz); r != 0 || sz == 0 {
 | ||
| 		return r
 | ||
| 	}
 | ||
| 
 | ||
| 	panic(todo("OOM"))
 | ||
| }
 | ||
| 
 | ||
| type tlsStackSlot struct {
 | ||
| 	p  uintptr
 | ||
| 	sz Tsize_t
 | ||
| }
 | ||
| 
 | ||
| // TLS emulates thread local storage. TLS is not safe for concurrent use by
 | ||
| // multiple goroutines.
 | ||
| type TLS struct {
 | ||
| 	allocaStack         []int
 | ||
| 	allocas             []uintptr
 | ||
| 	jumpBuffers         []uintptr
 | ||
| 	pthread             uintptr // *t__pthread
 | ||
| 	pthreadCleanupItems []pthreadCleanupItem
 | ||
| 	pthreadKeyValues    map[Tpthread_key_t]uintptr
 | ||
| 	sp                  int
 | ||
| 	stack               []tlsStackSlot
 | ||
| 
 | ||
| 	ID int32
 | ||
| 
 | ||
| 	ownsPthread bool
 | ||
| }
 | ||
| 
 | ||
| var __ccgo_environOnce sync.Once
 | ||
| 
 | ||
| // NewTLS returns a newly created TLS that must be eventually closed to prevent
 | ||
| // resource leaks.
 | ||
| func NewTLS() (r *TLS) {
 | ||
| 	id := tid.Add(1)
 | ||
| 	if id == 0 {
 | ||
| 		id = tid.Add(1)
 | ||
| 	}
 | ||
| 	__ccgo_environOnce.Do(func() {
 | ||
| 		Xenviron = mustAllocStrings(os.Environ())
 | ||
| 	})
 | ||
| 	pthread := mustMalloc(Tsize_t(unsafe.Sizeof(t__pthread{})))
 | ||
| 	*(*t__pthread)(unsafe.Pointer(pthread)) = t__pthread{
 | ||
| 		Flocale: uintptr(unsafe.Pointer(&X__libc.Fglobal_locale)),
 | ||
| 		Fself:   pthread,
 | ||
| 		Ftid:    id,
 | ||
| 	}
 | ||
| 	return &TLS{
 | ||
| 		ID:          id,
 | ||
| 		ownsPthread: true,
 | ||
| 		pthread:     pthread,
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // int *__errno_location(void)
 | ||
| func X__errno_location(tls *TLS) (r uintptr) {
 | ||
| 	return tls.pthread + unsafe.Offsetof(t__pthread{}.Ferrno_val)
 | ||
| }
 | ||
| 
 | ||
| // int *__errno_location(void)
 | ||
| func X___errno_location(tls *TLS) (r uintptr) {
 | ||
| 	return X__errno_location(tls)
 | ||
| }
 | ||
| 
 | ||
| func (tls *TLS) setErrno(n int32) {
 | ||
| 	if tls == nil {
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	*(*int32)(unsafe.Pointer(X__errno_location(tls))) = n
 | ||
| }
 | ||
| 
 | ||
| func (tls *TLS) String() string {
 | ||
| 	return fmt.Sprintf("TLS#%v pthread=%x", tls.ID, tls.pthread)
 | ||
| }
 | ||
| 
 | ||
| // Alloc allocates n bytes in tls's local storage. Calls to Alloc() must be
 | ||
| // strictly paired with calls to TLS.Free on function exit. That also means any
 | ||
| // memory from Alloc must not be used after a function returns.
 | ||
| //
 | ||
| // The order matters. This is ok:
 | ||
| //
 | ||
| //	p := tls.Alloc(11)
 | ||
| //		q := tls.Alloc(22)
 | ||
| //		tls.Free(22)
 | ||
| //		// q is no more usable here.
 | ||
| //	tls.Free(11)
 | ||
| //	// p is no more usable here.
 | ||
| //
 | ||
| // This is not correct:
 | ||
| //
 | ||
| //	tls.Alloc(11)
 | ||
| //		tls.Alloc(22)
 | ||
| //		tls.Free(11)
 | ||
| //	tls.Free(22)
 | ||
| func (tls *TLS) Alloc(n0 int) (r uintptr) {
 | ||
| 	// shrink	stats									speedtest1
 | ||
| 	// -----------------------------------------------------------------------------------------------
 | ||
| 	//    0		total  2,544, nallocs 107,553,070, nmallocs 25, nreallocs 107,553,045	10.984s
 | ||
| 	//    1		total  2,544, nallocs 107,553,070, nmallocs 25, nreallocs  38,905,980	 9.597s
 | ||
| 	//    2		total  2,616, nallocs 107,553,070, nmallocs 25, nreallocs  18,201,284	 9.206s
 | ||
| 	//    3		total  2,624, nallocs 107,553,070, nmallocs 25, nreallocs  16,716,302	 9.155s
 | ||
| 	//    4		total  2,624, nallocs 107,553,070, nmallocs 25, nreallocs  16,156,102	 9.398s
 | ||
| 	//    8		total  3,408, nallocs 107,553,070, nmallocs 25, nreallocs  14,364,274	 9.198s
 | ||
| 	//   16		total  3,976, nallocs 107,553,070, nmallocs 25, nreallocs   6,219,602	 8.910s
 | ||
| 	// ---------------------------------------------------------------------------------------------
 | ||
| 	//   32		total  5,120, nallocs 107,553,070, nmallocs 25, nreallocs   1,089,037	 8.836s
 | ||
| 	// ---------------------------------------------------------------------------------------------
 | ||
| 	//   64		total  6,520, nallocs 107,553,070, nmallocs 25, nreallocs       1,788	 8.420s
 | ||
| 	//  128		total  8,848, nallocs 107,553,070, nmallocs 25, nreallocs       1,098	 8.833s
 | ||
| 	//  256		total  8,848, nallocs 107,553,070, nmallocs 25, nreallocs       1,049	 9.508s
 | ||
| 	//  512		total 33,336, nallocs 107,553,070, nmallocs 25, nreallocs          88	 8.667s
 | ||
| 	// none		total 33,336, nallocs 107,553,070, nmallocs 25, nreallocs          88	 8.408s
 | ||
| 	const shrinkSegment = 32
 | ||
| 	n := Tsize_t(n0)
 | ||
| 	if tls.sp < len(tls.stack) {
 | ||
| 		p := tls.stack[tls.sp].p
 | ||
| 		sz := tls.stack[tls.sp].sz
 | ||
| 		if sz >= n /* && sz <= shrinkSegment*n */ {
 | ||
| 			// Segment shrinking is nice to have but Tcl does some dirty hacks in coroutine
 | ||
| 			// handling that require stability of stack addresses, out of the C execution
 | ||
| 			// model. Disabled.
 | ||
| 			tls.sp++
 | ||
| 			return p
 | ||
| 		}
 | ||
| 
 | ||
| 		Xfree(tls, p)
 | ||
| 		r = mustMalloc(n)
 | ||
| 		tls.stack[tls.sp] = tlsStackSlot{p: r, sz: Xmalloc_usable_size(tls, r)}
 | ||
| 		tls.sp++
 | ||
| 		return r
 | ||
| 
 | ||
| 	}
 | ||
| 
 | ||
| 	r = mustMalloc(n)
 | ||
| 	tls.stack = append(tls.stack, tlsStackSlot{p: r, sz: Xmalloc_usable_size(tls, r)})
 | ||
| 	tls.sp++
 | ||
| 	return r
 | ||
| }
 | ||
| 
 | ||
| // Free manages memory of the preceding TLS.Alloc()
 | ||
| func (tls *TLS) Free(n int) {
 | ||
| 	//TODO shrink stacks if possible. Tcl is currently against.
 | ||
| 	tls.sp--
 | ||
| }
 | ||
| 
 | ||
| func (tls *TLS) alloca(n Tsize_t) (r uintptr) {
 | ||
| 	r = mustMalloc(n)
 | ||
| 	tls.allocas = append(tls.allocas, r)
 | ||
| 	return r
 | ||
| }
 | ||
| 
 | ||
| // AllocaEntry must be called early on function entry when the function calls
 | ||
| // or may call alloca(3).
 | ||
| func (tls *TLS) AllocaEntry() {
 | ||
| 	tls.allocaStack = append(tls.allocaStack, len(tls.allocas))
 | ||
| }
 | ||
| 
 | ||
| // AllocaExit must be defer-called on function exit when the function calls or
 | ||
| // may call alloca(3).
 | ||
| func (tls *TLS) AllocaExit() {
 | ||
| 	n := len(tls.allocaStack)
 | ||
| 	x := tls.allocaStack[n-1]
 | ||
| 	tls.allocaStack = tls.allocaStack[:n-1]
 | ||
| 	for _, v := range tls.allocas[x:] {
 | ||
| 		Xfree(tls, v)
 | ||
| 	}
 | ||
| 	tls.allocas = tls.allocas[:x]
 | ||
| }
 | ||
| 
 | ||
| func (tls *TLS) Close() {
 | ||
| 	defer func() { *tls = TLS{} }()
 | ||
| 
 | ||
| 	for _, v := range tls.allocas {
 | ||
| 		Xfree(tls, v)
 | ||
| 	}
 | ||
| 	for _, v := range tls.stack /* shrink diabled[:tls.sp] */ {
 | ||
| 		Xfree(tls, v.p)
 | ||
| 	}
 | ||
| 	if tls.ownsPthread {
 | ||
| 		Xfree(tls, tls.pthread)
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| func (tls *TLS) PushJumpBuffer(jb uintptr) {
 | ||
| 	tls.jumpBuffers = append(tls.jumpBuffers, jb)
 | ||
| }
 | ||
| 
 | ||
| type LongjmpRetval int32
 | ||
| 
 | ||
| func (tls *TLS) PopJumpBuffer(jb uintptr) {
 | ||
| 	n := len(tls.jumpBuffers)
 | ||
| 	if n == 0 || tls.jumpBuffers[n-1] != jb {
 | ||
| 		panic(todo("unsupported setjmp/longjmp usage"))
 | ||
| 	}
 | ||
| 
 | ||
| 	tls.jumpBuffers = tls.jumpBuffers[:n-1]
 | ||
| }
 | ||
| 
 | ||
| func (tls *TLS) Longjmp(jb uintptr, val int32) {
 | ||
| 	tls.PopJumpBuffer(jb)
 | ||
| 	if val == 0 {
 | ||
| 		val = 1
 | ||
| 	}
 | ||
| 	panic(LongjmpRetval(val))
 | ||
| }
 | ||
| 
 | ||
| // ============================================================================
 | ||
| 
 | ||
| func Xexit(tls *TLS, code int32) {
 | ||
| 	//TODO atexit finalizers
 | ||
| 	X__stdio_exit(tls)
 | ||
| 	for _, v := range atExit {
 | ||
| 		v()
 | ||
| 	}
 | ||
| 	os.Exit(int(code))
 | ||
| }
 | ||
| 
 | ||
| func _exit(tls *TLS, code int32) {
 | ||
| 	Xexit(tls, code)
 | ||
| }
 | ||
| 
 | ||
| var abort Tsigaction
 | ||
| 
 | ||
| func Xabort(tls *TLS) {
 | ||
| 	X__libc_sigaction(tls, SIGABRT, uintptr(unsafe.Pointer(&abort)), 0)
 | ||
| 	unix.Kill(unix.Getpid(), syscall.Signal(SIGABRT))
 | ||
| 	panic(todo("unrechable"))
 | ||
| }
 | ||
| 
 | ||
| type lock struct {
 | ||
| 	sync.Mutex
 | ||
| 	waiters int
 | ||
| }
 | ||
| 
 | ||
| var (
 | ||
| 	locksMu sync.Mutex
 | ||
| 	locks   = map[uintptr]*lock{}
 | ||
| )
 | ||
| 
 | ||
| /*
 | ||
| 
 | ||
| 	T1		T2
 | ||
| 
 | ||
| 	lock(&foo)			// foo: 0 -> 1
 | ||
| 
 | ||
| 			lock(&foo)	// foo: 1 -> 2
 | ||
| 
 | ||
| 	unlock(&foo)			// foo: 2 -> 1, non zero means waiter(s) active
 | ||
| 
 | ||
| 			unlock(&foo)	// foo: 1 -> 0
 | ||
| 
 | ||
| */
 | ||
| 
 | ||
| func ___lock(tls *TLS, p uintptr) {
 | ||
| 	if atomic.AddInt32((*int32)(unsafe.Pointer(p)), 1) == 1 {
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	// foo was already acquired by some other C thread.
 | ||
| 	locksMu.Lock()
 | ||
| 	l := locks[p]
 | ||
| 	if l == nil {
 | ||
| 		l = &lock{}
 | ||
| 		locks[p] = l
 | ||
| 		l.Lock()
 | ||
| 	}
 | ||
| 	l.waiters++
 | ||
| 	locksMu.Unlock()
 | ||
| 	l.Lock() // Wait for T1 to release foo. (X below)
 | ||
| }
 | ||
| 
 | ||
| func ___unlock(tls *TLS, p uintptr) {
 | ||
| 	if atomic.AddInt32((*int32)(unsafe.Pointer(p)), -1) == 0 {
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	// Some other C thread is waiting for foo.
 | ||
| 	locksMu.Lock()
 | ||
| 	l := locks[p]
 | ||
| 	if l == nil {
 | ||
| 		// We are T1 and we got the locksMu locked before T2.
 | ||
| 		l = &lock{waiters: 1}
 | ||
| 		l.Lock()
 | ||
| 	}
 | ||
| 	l.Unlock() // Release foo, T2 may now lock it. (X above)
 | ||
| 	l.waiters--
 | ||
| 	if l.waiters == 0 { // we are T2
 | ||
| 		delete(locks, p)
 | ||
| 	}
 | ||
| 	locksMu.Unlock()
 | ||
| }
 | ||
| 
 | ||
| type lockedFile struct {
 | ||
| 	ch      chan struct{}
 | ||
| 	waiters int
 | ||
| }
 | ||
| 
 | ||
| var (
 | ||
| 	lockedFilesMu sync.Mutex
 | ||
| 	lockedFiles   = map[uintptr]*lockedFile{}
 | ||
| )
 | ||
| 
 | ||
| func X__lockfile(tls *TLS, file uintptr) int32 {
 | ||
| 	return ___lockfile(tls, file)
 | ||
| }
 | ||
| 
 | ||
| // int __lockfile(FILE *f)
 | ||
| func ___lockfile(tls *TLS, file uintptr) int32 {
 | ||
| 	panic(todo(""))
 | ||
| 	// lockedFilesMu.Lock()
 | ||
| 
 | ||
| 	// defer lockedFilesMu.Unlock()
 | ||
| 
 | ||
| 	// l := lockedFiles[file]
 | ||
| 	// if l == nil {
 | ||
| 	// 	l = &lockedFile{ch: make(chan struct{}, 1)}
 | ||
| 	// 	lockedFiles[file] = l
 | ||
| 	// }
 | ||
| 
 | ||
| 	// l.waiters++
 | ||
| 	// l.ch <- struct{}{}
 | ||
| }
 | ||
| 
 | ||
| func X__unlockfile(tls *TLS, file uintptr) {
 | ||
| 	___unlockfile(tls, file)
 | ||
| }
 | ||
| 
 | ||
| // void __unlockfile(FILE *f)
 | ||
| func ___unlockfile(tls *TLS, file uintptr) {
 | ||
| 	panic(todo(""))
 | ||
| 	lockedFilesMu.Lock()
 | ||
| 
 | ||
| 	defer lockedFilesMu.Unlock()
 | ||
| 
 | ||
| 	l := lockedFiles[file]
 | ||
| 	l.waiters--
 | ||
| 	if l.waiters == 0 {
 | ||
| 		delete(lockedFiles, file)
 | ||
| 	}
 | ||
| 	<-l.ch
 | ||
| }
 | ||
| 
 | ||
| // void __synccall(void (*func)(void *), void *ctx)
 | ||
| func ___synccall(tls *TLS, fn, ctx uintptr) {
 | ||
| 	(*(*func(*TLS, uintptr))(unsafe.Pointer(&struct{ uintptr }{fn})))(tls, ctx)
 | ||
| }
 | ||
| 
 | ||
| func ___randname(tls *TLS, template uintptr) (r1 uintptr) {
 | ||
| 	bp := tls.Alloc(16)
 | ||
| 	defer tls.Free(16)
 | ||
| 	var i int32
 | ||
| 	var r uint64
 | ||
| 	var _ /* ts at bp+0 */ Ttimespec
 | ||
| 	X__clock_gettime(tls, CLOCK_REALTIME, bp)
 | ||
| 	goto _2
 | ||
| _2:
 | ||
| 	r = uint64((*(*Ttimespec)(unsafe.Pointer(bp))).Ftv_sec+(*(*Ttimespec)(unsafe.Pointer(bp))).Ftv_nsec) + uint64(tls.ID)*uint64(65537)
 | ||
| 	i = 0
 | ||
| 	for {
 | ||
| 		if !(i < int32(6)) {
 | ||
| 			break
 | ||
| 		}
 | ||
| 		*(*int8)(unsafe.Pointer(template + uintptr(i))) = int8(uint64('A') + r&uint64(15) + r&uint64(16)*uint64(2))
 | ||
| 		goto _3
 | ||
| 	_3:
 | ||
| 		i++
 | ||
| 		r >>= uint64(5)
 | ||
| 	}
 | ||
| 	return template
 | ||
| }
 | ||
| 
 | ||
| func ___get_tp(tls *TLS) uintptr {
 | ||
| 	return tls.pthread
 | ||
| }
 | ||
| 
 | ||
| func Xfork(t *TLS) int32 {
 | ||
| 	if __ccgo_strace {
 | ||
| 		trc("t=%v, (%v:)", t, origin(2))
 | ||
| 	}
 | ||
| 	t.setErrno(ENOSYS)
 | ||
| 	return -1
 | ||
| }
 | ||
| 
 | ||
| const SIG_DFL = 0
 | ||
| const SIG_IGN = 1
 | ||
| 
 | ||
| var sigHandlers = map[int32]uintptr{}
 | ||
| 
 | ||
| func Xsignal(tls *TLS, signum int32, handler uintptr) (r uintptr) {
 | ||
| 	r, sigHandlers[signum] = sigHandlers[signum], handler
 | ||
| 	sigHandlers[signum] = handler
 | ||
| 	switch handler {
 | ||
| 	case SIG_DFL:
 | ||
| 		gosignal.Reset(syscall.Signal(signum))
 | ||
| 	case SIG_IGN:
 | ||
| 		gosignal.Ignore(syscall.Signal(signum))
 | ||
| 	default:
 | ||
| 		panic(todo(""))
 | ||
| 	}
 | ||
| 	return r
 | ||
| }
 | ||
| 
 | ||
| func Xatexit(tls *TLS, func_ uintptr) (r int32) {
 | ||
| 	return -1
 | ||
| }
 | ||
| 
 | ||
| var __sync_synchronize_dummy int32
 | ||
| 
 | ||
| // __sync_synchronize();
 | ||
| func X__sync_synchronize(t *TLS) {
 | ||
| 	if __ccgo_strace {
 | ||
| 		trc("t=%v, (%v:)", t, origin(2))
 | ||
| 	}
 | ||
| 	// Attempt to implement a full memory barrier without assembler.
 | ||
| 	atomic.StoreInt32(&__sync_synchronize_dummy, atomic.LoadInt32(&__sync_synchronize_dummy)+1)
 | ||
| }
 | ||
| 
 | ||
| func Xdlopen(t *TLS, filename uintptr, flags int32) uintptr {
 | ||
| 	if __ccgo_strace {
 | ||
| 		trc("t=%v filename=%v flags=%v, (%v:)", t, filename, flags, origin(2))
 | ||
| 	}
 | ||
| 	return 0
 | ||
| }
 | ||
| 
 | ||
| func Xdlsym(t *TLS, handle, symbol uintptr) uintptr {
 | ||
| 	if __ccgo_strace {
 | ||
| 		trc("t=%v symbol=%v, (%v:)", t, symbol, origin(2))
 | ||
| 	}
 | ||
| 	return 0
 | ||
| }
 | ||
| 
 | ||
| var dlErrorMsg = []byte("not supported\x00")
 | ||
| 
 | ||
| func Xdlerror(t *TLS) uintptr {
 | ||
| 	if __ccgo_strace {
 | ||
| 		trc("t=%v, (%v:)", t, origin(2))
 | ||
| 	}
 | ||
| 	return uintptr(unsafe.Pointer(&dlErrorMsg[0]))
 | ||
| }
 | ||
| 
 | ||
| func Xdlclose(t *TLS, handle uintptr) int32 {
 | ||
| 	if __ccgo_strace {
 | ||
| 		trc("t=%v handle=%v, (%v:)", t, handle, origin(2))
 | ||
| 	}
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| func Xsystem(t *TLS, command uintptr) int32 {
 | ||
| 	if __ccgo_strace {
 | ||
| 		trc("t=%v command=%v, (%v:)", t, command, origin(2))
 | ||
| 	}
 | ||
| 	s := GoString(command)
 | ||
| 	if command == 0 {
 | ||
| 		panic(todo(""))
 | ||
| 	}
 | ||
| 
 | ||
| 	cmd := exec.Command("sh", "-c", s)
 | ||
| 	cmd.Stdout = os.Stdout
 | ||
| 	cmd.Stderr = os.Stderr
 | ||
| 	err := cmd.Run()
 | ||
| 	if err != nil {
 | ||
| 		ps := err.(*exec.ExitError)
 | ||
| 		return int32(ps.ExitCode())
 | ||
| 	}
 | ||
| 
 | ||
| 	return 0
 | ||
| }
 | ||
| 
 | ||
| func Xsched_yield(tls *TLS) int32 {
 | ||
| 	runtime.Gosched()
 | ||
| 	return 0
 | ||
| }
 | ||
| 
 | ||
| // AtExit will attempt to run f at process exit. The execution cannot be
 | ||
| // guaranteed, neither its ordering with respect to any other handlers
 | ||
| // registered by AtExit.
 | ||
| func AtExit(f func()) {
 | ||
| 	atExitMu.Lock()
 | ||
| 	atExit = append(atExit, f)
 | ||
| 	atExitMu.Unlock()
 | ||
| }
 | ||
| 
 | ||
| func Bool64(b bool) int64 {
 | ||
| 	if b {
 | ||
| 		return 1
 | ||
| 	}
 | ||
| 
 | ||
| 	return 0
 | ||
| }
 | ||
| 
 | ||
| func Environ() uintptr {
 | ||
| 	__ccgo_environOnce.Do(func() {
 | ||
| 		Xenviron = mustAllocStrings(os.Environ())
 | ||
| 	})
 | ||
| 	return Xenviron
 | ||
| }
 | ||
| 
 | ||
| func EnvironP() uintptr {
 | ||
| 	__ccgo_environOnce.Do(func() {
 | ||
| 		Xenviron = mustAllocStrings(os.Environ())
 | ||
| 	})
 | ||
| 	return uintptr(unsafe.Pointer(&Xenviron))
 | ||
| }
 | ||
| 
 | ||
| // NewVaList is like VaList but automatically allocates the correct amount of
 | ||
| // memory for all of the items in args.
 | ||
| //
 | ||
| // The va_list return value is used to pass the constructed var args to var
 | ||
| // args accepting functions. The caller of NewVaList is responsible for freeing
 | ||
| // the va_list.
 | ||
| func NewVaList(args ...interface{}) (va_list uintptr) {
 | ||
| 	return VaList(NewVaListN(len(args)), args...)
 | ||
| }
 | ||
| 
 | ||
| // NewVaListN returns a newly allocated va_list for n items. The caller of
 | ||
| // NewVaListN is responsible for freeing the va_list.
 | ||
| func NewVaListN(n int) (va_list uintptr) {
 | ||
| 	return Xmalloc(nil, Tsize_t(8*n))
 | ||
| }
 | ||
| 
 | ||
| func SetEnviron(t *TLS, env []string) {
 | ||
| 	__ccgo_environOnce.Do(func() {
 | ||
| 		Xenviron = mustAllocStrings(env)
 | ||
| 	})
 | ||
| }
 | ||
| 
 | ||
| func Dmesg(s string, args ...interface{}) {
 | ||
| 	// nop
 | ||
| }
 | ||
| 
 | ||
| func Xalloca(tls *TLS, size Tsize_t) uintptr {
 | ||
| 	return tls.alloca(size)
 | ||
| }
 | ||
| 
 | ||
| // struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);
 | ||
| func X__cmsg_nxthdr(t *TLS, msgh, cmsg uintptr) uintptr {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| func Cover() {
 | ||
| 	runtime.Callers(2, coverPCs[:])
 | ||
| 	Covered[coverPCs[0]] = struct{}{}
 | ||
| }
 | ||
| 
 | ||
| func CoverReport(w io.Writer) error {
 | ||
| 	var a []string
 | ||
| 	pcs := make([]uintptr, 1)
 | ||
| 	for pc := range Covered {
 | ||
| 		pcs[0] = pc
 | ||
| 		frame, _ := runtime.CallersFrames(pcs).Next()
 | ||
| 		a = append(a, fmt.Sprintf("%s:%07d:%s", filepath.Base(frame.File), frame.Line, frame.Func.Name()))
 | ||
| 	}
 | ||
| 	sort.Strings(a)
 | ||
| 	_, err := fmt.Fprintf(w, "%s\n", strings.Join(a, "\n"))
 | ||
| 	return err
 | ||
| }
 | ||
| 
 | ||
| func CoverC(s string) {
 | ||
| 	CoveredC[s] = struct{}{}
 | ||
| }
 | ||
| 
 | ||
| func CoverCReport(w io.Writer) error {
 | ||
| 	var a []string
 | ||
| 	for k := range CoveredC {
 | ||
| 		a = append(a, k)
 | ||
| 	}
 | ||
| 	sort.Strings(a)
 | ||
| 	_, err := fmt.Fprintf(w, "%s\n", strings.Join(a, "\n"))
 | ||
| 	return err
 | ||
| }
 | ||
| 
 | ||
| func X__ccgo_dmesg(t *TLS, fmt uintptr, va uintptr) {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| func X__ccgo_getMutexType(tls *TLS, m uintptr) int32 { /* pthread_mutex_lock.c:3:5: */
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| func X__ccgo_in6addr_anyp(t *TLS) uintptr {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| func X__ccgo_pthreadAttrGetDetachState(tls *TLS, a uintptr) int32 { /* pthread_attr_get.c:3:5: */
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| func X__ccgo_pthreadMutexattrGettype(tls *TLS, a uintptr) int32 { /* pthread_attr_get.c:93:5: */
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // void sqlite3_log(int iErrCode, const char *zFormat, ...);
 | ||
| func X__ccgo_sqlite3_log(t *TLS, iErrCode int32, zFormat uintptr, args uintptr) {
 | ||
| 	// nop
 | ||
| }
 | ||
| 
 | ||
| // unsigned __sync_add_and_fetch_uint32(*unsigned, unsigned)
 | ||
| func X__sync_add_and_fetch_uint32(t *TLS, p uintptr, v uint32) uint32 {
 | ||
| 	return atomic.AddUint32((*uint32)(unsafe.Pointer(p)), v)
 | ||
| }
 | ||
| 
 | ||
| // unsigned __sync_sub_and_fetch_uint32(*unsigned, unsigned)
 | ||
| func X__sync_sub_and_fetch_uint32(t *TLS, p uintptr, v uint32) uint32 {
 | ||
| 	return atomic.AddUint32((*uint32)(unsafe.Pointer(p)), -v)
 | ||
| }
 | ||
| 
 | ||
| var (
 | ||
| 	randomData   = map[uintptr]*rand.Rand{}
 | ||
| 	randomDataMu sync.Mutex
 | ||
| )
 | ||
| 
 | ||
| // The initstate_r() function is like initstate(3) except that it initializes
 | ||
| // the state in the object pointed to by buf, rather than initializing the
 | ||
| // global state  variable.   Before  calling this function, the buf.state field
 | ||
| // must be initialized to NULL.  The initstate_r() function records a pointer
 | ||
| // to the statebuf argument inside the structure pointed to by buf.  Thus,
 | ||
| // state‐ buf should not be deallocated so long as buf is still in use.  (So,
 | ||
| // statebuf should typically be allocated as a static variable, or allocated on
 | ||
| // the heap using malloc(3) or similar.)
 | ||
| //
 | ||
| // char *initstate_r(unsigned int seed, char *statebuf, size_t statelen, struct random_data *buf);
 | ||
| func Xinitstate_r(t *TLS, seed uint32, statebuf uintptr, statelen Tsize_t, buf uintptr) int32 {
 | ||
| 	if buf == 0 {
 | ||
| 		panic(todo(""))
 | ||
| 	}
 | ||
| 
 | ||
| 	randomDataMu.Lock()
 | ||
| 
 | ||
| 	defer randomDataMu.Unlock()
 | ||
| 
 | ||
| 	randomData[buf] = rand.New(rand.NewSource(int64(seed)))
 | ||
| 	return 0
 | ||
| }
 | ||
| 
 | ||
| // int random_r(struct random_data *buf, int32_t *result);
 | ||
| func Xrandom_r(t *TLS, buf, result uintptr) int32 {
 | ||
| 	randomDataMu.Lock()
 | ||
| 
 | ||
| 	defer randomDataMu.Unlock()
 | ||
| 
 | ||
| 	mr := randomData[buf]
 | ||
| 	if RAND_MAX != math.MaxInt32 {
 | ||
| 		panic(todo(""))
 | ||
| 	}
 | ||
| 	*(*int32)(unsafe.Pointer(result)) = mr.Int31()
 | ||
| 	return 0
 | ||
| }
 | ||
| 
 | ||
| // void longjmp(jmp_buf env, int val);
 | ||
| func Xlongjmp(t *TLS, env uintptr, val int32) {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // void _longjmp(jmp_buf env, int val);
 | ||
| func X_longjmp(t *TLS, env uintptr, val int32) {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // int _obstack_begin (struct obstack *h, _OBSTACK_SIZE_T size, _OBSTACK_SIZE_T alignment,	void *(*chunkfun) (size_t),  void (*freefun) (void *))
 | ||
| func X_obstack_begin(t *TLS, obstack uintptr, size, alignment int32, chunkfun, freefun uintptr) int32 {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // extern void _obstack_newchunk(struct obstack *, int);
 | ||
| func X_obstack_newchunk(t *TLS, obstack uintptr, length int32) int32 {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // void obstack_free (struct obstack *h, void *obj)
 | ||
| func Xobstack_free(t *TLS, obstack, obj uintptr) {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // int obstack_vprintf (struct obstack *obstack, const char *template, va_list ap)
 | ||
| func Xobstack_vprintf(t *TLS, obstack, template, va uintptr) int32 {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // int _setjmp(jmp_buf env);
 | ||
| func X_setjmp(t *TLS, env uintptr) int32 {
 | ||
| 	return 0 //TODO
 | ||
| }
 | ||
| 
 | ||
| // int setjmp(jmp_buf env);
 | ||
| func Xsetjmp(t *TLS, env uintptr) int32 {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // int backtrace(void **buffer, int size);
 | ||
| func Xbacktrace(t *TLS, buf uintptr, size int32) int32 {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // void backtrace_symbols_fd(void *const *buffer, int size, int fd);
 | ||
| func Xbacktrace_symbols_fd(t *TLS, buffer uintptr, size, fd int32) {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // int fts_close(FTS *ftsp);
 | ||
| func Xfts_close(t *TLS, ftsp uintptr) int32 {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // FTS *fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **));
 | ||
| func Xfts_open(t *TLS, path_argv uintptr, options int32, compar uintptr) uintptr {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // FTSENT *fts_read(FTS *ftsp);
 | ||
| func Xfts64_read(t *TLS, ftsp uintptr) uintptr {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // int fts_close(FTS *ftsp);
 | ||
| func Xfts64_close(t *TLS, ftsp uintptr) int32 {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // FTS *fts_open(char * const *path_argv, int options, int (*compar)(const FTSENT **, const FTSENT **));
 | ||
| func Xfts64_open(t *TLS, path_argv uintptr, options int32, compar uintptr) uintptr {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // FTSENT *fts_read(FTS *ftsp);
 | ||
| func Xfts_read(t *TLS, ftsp uintptr) uintptr {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // FILE *popen(const char *command, const char *type);
 | ||
| func Xpopen(t *TLS, command, type1 uintptr) uintptr {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
 | ||
| func Xsysctlbyname(t *TLS, name, oldp, oldlenp, newp uintptr, newlen Tsize_t) int32 {
 | ||
| 	oldlen := *(*Tsize_t)(unsafe.Pointer(oldlenp))
 | ||
| 	switch GoString(name) {
 | ||
| 	case "hw.ncpu":
 | ||
| 		if oldlen != 4 {
 | ||
| 			panic(todo(""))
 | ||
| 		}
 | ||
| 
 | ||
| 		*(*int32)(unsafe.Pointer(oldp)) = int32(runtime.GOMAXPROCS(-1))
 | ||
| 		return 0
 | ||
| 	default:
 | ||
| 		panic(todo(""))
 | ||
| 		t.setErrno(ENOENT)
 | ||
| 		return -1
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| // void uuid_copy(uuid_t dst, uuid_t src);
 | ||
| func Xuuid_copy(t *TLS, dst, src uintptr) {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // int uuid_parse( char *in, uuid_t uu);
 | ||
| func Xuuid_parse(t *TLS, in uintptr, uu uintptr) int32 {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // void uuid_generate_random(uuid_t out);
 | ||
| func Xuuid_generate_random(t *TLS, out uintptr) {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| // void uuid_unparse(uuid_t uu, char *out);
 | ||
| func Xuuid_unparse(t *TLS, uu, out uintptr) {
 | ||
| 	panic(todo(""))
 | ||
| }
 | ||
| 
 | ||
| var Xzero_struct_address Taddress
 |