mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-27 14:16:39 +00:00
1097 lines
28 KiB
Go
1097 lines
28 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 || arm64 || 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"
|
||
|
||
guuid "github.com/google/uuid"
|
||
"golang.org/x/sys/unix"
|
||
"modernc.org/libc/uuid/uuid"
|
||
"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
|
||
pendingSignals chan os.Signal
|
||
pthread uintptr // *t__pthread
|
||
pthreadCleanupItems []pthreadCleanupItem
|
||
pthreadKeyValues map[Tpthread_key_t]uintptr
|
||
sigHandlers map[int32]uintptr
|
||
sp int
|
||
stack []tlsStackSlot
|
||
|
||
ID int32
|
||
|
||
checkSignals bool
|
||
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,
|
||
sigHandlers: map[int32]uintptr{},
|
||
}
|
||
}
|
||
|
||
// 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--
|
||
if !tls.checkSignals {
|
||
return
|
||
}
|
||
|
||
select {
|
||
case sig := <-tls.pendingSignals:
|
||
signum := int32(sig.(syscall.Signal))
|
||
h, ok := tls.sigHandlers[signum]
|
||
if !ok {
|
||
break
|
||
}
|
||
|
||
switch h {
|
||
case SIG_DFL:
|
||
// nop
|
||
case SIG_IGN:
|
||
// nop
|
||
default:
|
||
(*(*func(*TLS, int32))(unsafe.Pointer(&struct{ uintptr }{h})))(tls, signum)
|
||
}
|
||
default:
|
||
// nop
|
||
}
|
||
}
|
||
|
||
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()
|
||
}
|
||
atExitHandlersMu.Lock()
|
||
for _, v := range atExitHandlers {
|
||
(*(*func(*TLS))(unsafe.Pointer(&struct{ uintptr }{v})))(tls)
|
||
}
|
||
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
|
||
|
||
func Xsignal(tls *TLS, signum int32, handler uintptr) (r uintptr) {
|
||
r, tls.sigHandlers[signum] = tls.sigHandlers[signum], handler
|
||
switch handler {
|
||
case SIG_DFL:
|
||
gosignal.Reset(syscall.Signal(signum))
|
||
case SIG_IGN:
|
||
gosignal.Ignore(syscall.Signal(signum))
|
||
default:
|
||
if tls.pendingSignals == nil {
|
||
tls.pendingSignals = make(chan os.Signal, 3)
|
||
tls.checkSignals = true
|
||
}
|
||
gosignal.Notify(tls.pendingSignals, syscall.Signal(signum))
|
||
}
|
||
return r
|
||
}
|
||
|
||
var (
|
||
atExitHandlersMu sync.Mutex
|
||
atExitHandlers []uintptr
|
||
)
|
||
|
||
func Xatexit(tls *TLS, func_ uintptr) (r int32) {
|
||
atExitHandlersMu.Lock()
|
||
atExitHandlers = append(atExitHandlers, func_)
|
||
atExitHandlersMu.Unlock()
|
||
return 0
|
||
}
|
||
|
||
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) {
|
||
if __ccgo_strace {
|
||
trc("t=%v src=%v, (%v:)", t, src, origin(2))
|
||
}
|
||
*(*uuid.Uuid_t)(unsafe.Pointer(dst)) = *(*uuid.Uuid_t)(unsafe.Pointer(src))
|
||
}
|
||
|
||
// int uuid_parse( char *in, uuid_t uu);
|
||
func Xuuid_parse(t *TLS, in uintptr, uu uintptr) int32 {
|
||
if __ccgo_strace {
|
||
trc("t=%v in=%v uu=%v, (%v:)", t, in, uu, origin(2))
|
||
}
|
||
r, err := guuid.Parse(GoString(in))
|
||
if err != nil {
|
||
return -1
|
||
}
|
||
|
||
copy((*RawMem)(unsafe.Pointer(uu))[:unsafe.Sizeof(uuid.Uuid_t{})], r[:])
|
||
return 0
|
||
}
|
||
|
||
// void uuid_generate_random(uuid_t out);
|
||
func Xuuid_generate_random(t *TLS, out uintptr) {
|
||
if __ccgo_strace {
|
||
trc("t=%v out=%v, (%v:)", t, out, origin(2))
|
||
}
|
||
x := guuid.New()
|
||
copy((*RawMem)(unsafe.Pointer(out))[:], x[:])
|
||
}
|
||
|
||
// void uuid_unparse(uuid_t uu, char *out);
|
||
func Xuuid_unparse(t *TLS, uu, out uintptr) {
|
||
if __ccgo_strace {
|
||
trc("t=%v out=%v, (%v:)", t, out, origin(2))
|
||
}
|
||
s := (*guuid.UUID)(unsafe.Pointer(uu)).String()
|
||
copy((*RawMem)(unsafe.Pointer(out))[:], s)
|
||
*(*byte)(unsafe.Pointer(out + uintptr(len(s)))) = 0
|
||
}
|
||
|
||
var Xzero_struct_address Taddress
|