// Copyright 2020 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. package libc // import "modernc.org/libc" import ( "fmt" "io" "os" "path/filepath" "reflect" "runtime" "runtime/debug" "sort" "strconv" "strings" "sync" "sync/atomic" "syscall" "time" "unsafe" "modernc.org/libc/errno" "modernc.org/libc/signal" "modernc.org/libc/sys/types" ) const ( allocatorPageOverhead = 4 * unsafe.Sizeof(int(0)) stackHeaderSize = unsafe.Sizeof(stackHeader{}) stackSegmentSize = 1<<12 - allocatorPageOverhead uintptrSize = unsafe.Sizeof(uintptr(0)) ) var ( Covered = map[uintptr]struct{}{} CoveredC = map[string]struct{}{} fToken uintptr tid int32 atExit []func() atExitMu sync.Mutex signals [signal.NSIG]uintptr signalsMu sync.Mutex objectMu sync.Mutex objects = map[uintptr]interface{}{} tlsBalance int32 _ = origin _ = trc ) func init() { if n := stackHeaderSize; n%16 != 0 { panic(fmt.Errorf("internal error: stackHeaderSize %v == %v (mod 16)", n, n%16)) } } func origin(skip int) string { pc, fn, fl, _ := runtime.Caller(skip) f := runtime.FuncForPC(pc) var fns string if f != nil { fns = f.Name() if x := strings.LastIndex(fns, "."); x > 0 { fns = fns[x+1:] } } return fmt.Sprintf("%s:%d:%s", filepath.Base(fn), fl, fns) } func trc(s string, args ...interface{}) string { //TODO- switch { case s == "": s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...) default: s = fmt.Sprintf(s, args...) } r := fmt.Sprintf("%s: TRC %s", origin(2), s) fmt.Fprintf(os.Stdout, "%s\n", r) os.Stdout.Sync() return r } func todo(s string, args ...interface{}) string { //TODO- switch { case s == "": s = fmt.Sprintf(strings.Repeat("%v ", len(args)), args...) default: s = fmt.Sprintf(s, args...) } r := fmt.Sprintf("%s: TODOTODO %s", origin(2), s) //TODOOK if dmesgs { dmesg("%s", r) } fmt.Fprintf(os.Stdout, "%s\n", r) fmt.Fprintf(os.Stdout, "%s\n", debug.Stack()) //TODO- os.Stdout.Sync() os.Exit(1) panic("unrechable") } var coverPCs [1]uintptr //TODO not concurrent safe 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 token() uintptr { return atomic.AddUintptr(&fToken, 1) } func addObject(o interface{}) uintptr { t := token() objectMu.Lock() objects[t] = o objectMu.Unlock() return t } func getObject(t uintptr) interface{} { objectMu.Lock() o := objects[t] if o == nil { panic(todo("", t)) } objectMu.Unlock() return o } func removeObject(t uintptr) { objectMu.Lock() if _, ok := objects[t]; !ok { panic(todo("")) } delete(objects, t) objectMu.Unlock() } func (t *TLS) setErrno(err interface{}) { if memgrind { if atomic.SwapInt32(&t.reentryGuard, 1) != 0 { panic(todo("concurrent use of TLS instance %p", t)) } defer func() { if atomic.SwapInt32(&t.reentryGuard, 0) != 1 { panic(todo("concurrent use of TLS instance %p", t)) } }() } // if dmesgs { // dmesg("%v: %T(%v)\n%s", origin(1), err, err, debug.Stack()) // } again: switch x := err.(type) { case int: *(*int32)(unsafe.Pointer(t.errnop)) = int32(x) case int32: *(*int32)(unsafe.Pointer(t.errnop)) = x case *os.PathError: err = x.Err goto again case syscall.Errno: *(*int32)(unsafe.Pointer(t.errnop)) = int32(x) case *os.SyscallError: err = x.Err goto again default: panic(todo("%T", x)) } } // Close frees the resources of t. func (t *TLS) Close() { t.Free(int(unsafe.Sizeof(int32(0)))) if memgrind { if t.stackHeaderBalance != 0 { panic(todo("non zero stack header balance: %d", t.stackHeaderBalance)) } atomic.AddInt32(&tlsBalance, -1) } t.pthreadData.close(t) *t = TLS{} } // Alloc allocates n bytes of thread-local storage. It must be paired with a // call to t.Free(n), using the same n. The order matters. This is ok: // // t.Alloc(11) // t.Alloc(22) // t.Free(22) // t.Free(11) // // This is not correct: // // t.Alloc(11) // t.Alloc(22) // t.Free(11) // t.Free(22) func (t *TLS) Alloc(n int) (r uintptr) { if memgrind { if atomic.SwapInt32(&t.reentryGuard, 1) != 0 { panic(todo("concurrent use of TLS instance %p", t)) } defer func() { if atomic.SwapInt32(&t.reentryGuard, 0) != 1 { panic(todo("concurrent use of TLS instance %p", t)) } }() } n += 15 n &^= 15 if t.stack.free >= n { r = t.stack.sp t.stack.free -= n t.stack.sp += uintptr(n) return r } //if we have a next stack if nstack := t.stack.next; nstack != 0 { if (*stackHeader)(unsafe.Pointer(nstack)).free >= n { *(*stackHeader)(unsafe.Pointer(t.stack.page)) = t.stack t.stack = *(*stackHeader)(unsafe.Pointer(nstack)) r = t.stack.sp t.stack.free -= n t.stack.sp += uintptr(n) return r } nstack := *(*stackHeader)(unsafe.Pointer(t.stack.next)) for ; ; nstack = *(*stackHeader)(unsafe.Pointer(nstack.next)) { if memgrind { if atomic.AddInt32(&t.stackHeaderBalance, -1) < 0 { panic(todo("negative stack header balance")) } } Xfree(t, nstack.page) if nstack.next == 0 { break } } t.stack.next = 0 } if t.stack.page != 0 { *(*stackHeader)(unsafe.Pointer(t.stack.page)) = t.stack } rq := n + int(stackHeaderSize) if rq%int(stackSegmentSize) != 0 { rq -= rq % int(stackSegmentSize) rq += int(stackSegmentSize) } t.stack.free = rq - int(stackHeaderSize) t.stack.prev = t.stack.page rq += 15 rq &^= 15 t.stack.page = Xmalloc(t, types.Size_t(rq)) if t.stack.page == 0 { panic("OOM") } if memgrind { atomic.AddInt32(&t.stackHeaderBalance, 1) } t.stack.sp = t.stack.page + stackHeaderSize r = t.stack.sp t.stack.free -= n t.stack.sp += uintptr(n) if t.stack.prev != 0 { (*stackHeader)(unsafe.Pointer(t.stack.prev)).next = t.stack.page } return r } // this declares how many stack frames are kept alive before being freed const stackFrameKeepalive = 2 // Free deallocates n bytes of thread-local storage. See TLS.Alloc for details // on correct usage. func (t *TLS) Free(n int) { if memgrind { if atomic.SwapInt32(&t.reentryGuard, 1) != 0 { panic(todo("concurrent use of TLS instance %p", t)) } defer func() { if atomic.SwapInt32(&t.reentryGuard, 0) != 1 { panic(todo("concurrent use of TLS instance %p", t)) } }() } n += 15 n &^= 15 t.stack.free += n t.stack.sp -= uintptr(n) if t.stack.sp != t.stack.page+stackHeaderSize { return } nstack := t.stack //if we are the first one, just free all of them if t.stack.prev == 0 { for ; ; nstack = *(*stackHeader)(unsafe.Pointer(nstack.next)) { if memgrind { if atomic.AddInt32(&t.stackHeaderBalance, -1) < 0 { panic(todo("negative stack header balance")) } } Xfree(t, nstack.page) if nstack.next == 0 { break } } t.stack = stackHeader{} return } //look if we are in the last n stackframes (n=stackFrameKeepalive) //if we find something just return and set the current stack pointer to the previous one for i := 0; i < stackFrameKeepalive; i++ { if nstack.next == 0 { *((*stackHeader)(unsafe.Pointer(t.stack.page))) = t.stack t.stack = *(*stackHeader)(unsafe.Pointer(t.stack.prev)) return } nstack = *(*stackHeader)(unsafe.Pointer(nstack.next)) } //else only free the last if memgrind { if atomic.AddInt32(&t.stackHeaderBalance, -1) < 0 { panic(todo("negative stack header balance")) } } Xfree(t, nstack.page) (*stackHeader)(unsafe.Pointer(nstack.prev)).next = 0 *(*stackHeader)(unsafe.Pointer(t.stack.page)) = t.stack t.stack = *(*stackHeader)(unsafe.Pointer(t.stack.prev)) } type stackHeader struct { free int // bytes left in page page uintptr // stack page prev uintptr // prev stack page = prev stack header next uintptr // next stack page = next stack header sp uintptr // next allocation address _ stackHeaderPadding } func cString(t *TLS, s string) uintptr { //TODO- n := len(s) p := Xmalloc(t, types.Size_t(n)+1) if p == 0 { panic("OOM") } copy((*RawMem)(unsafe.Pointer(p))[:n:n], s) *(*byte)(unsafe.Pointer(p + uintptr(n))) = 0 return p } // VaList fills a varargs list at p with args and returns p. The list must // have been allocated by caller and it must not be in Go managed memory, ie. // it must be pinned. Caller is responsible for freeing the list. // // Individual arguments must be one of int, uint, int32, uint32, int64, uint64, // float64, uintptr or Intptr. Other types will panic. // // This function supports code generated by ccgo/v3. For manually constructed // var args it's recommended to use the NewVaList function instead. // // Note: The C translated to Go varargs ABI alignment for all types is 8 on all // architectures. func VaList(p uintptr, args ...interface{}) (r uintptr) { if p&7 != 0 { panic("internal error") } r = p for _, v := range args { switch x := v.(type) { case int: *(*int64)(unsafe.Pointer(p)) = int64(x) case int32: *(*int64)(unsafe.Pointer(p)) = int64(x) case int64: *(*int64)(unsafe.Pointer(p)) = x case uint: *(*uint64)(unsafe.Pointer(p)) = uint64(x) case uint16: *(*uint64)(unsafe.Pointer(p)) = uint64(x) case uint32: *(*uint64)(unsafe.Pointer(p)) = uint64(x) case uint64: *(*uint64)(unsafe.Pointer(p)) = x case float64: *(*float64)(unsafe.Pointer(p)) = x case uintptr: *(*uintptr)(unsafe.Pointer(p)) = x default: sz := reflect.TypeOf(v).Size() copy(unsafe.Slice((*byte)(unsafe.Pointer(p)), sz), unsafe.Slice((*byte)(unsafe.Pointer((*[2]uintptr)(unsafe.Pointer(&v))[1])), sz)) p += roundup(sz, 8) continue } p += 8 } return r } // 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, types.Size_t(8*n)) } // 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...) } func VaOther(app *uintptr, sz uint64) (r uintptr) { ap := *(*uintptr)(unsafe.Pointer(app)) if ap == 0 { return 0 } r = ap ap = roundup(ap+uintptr(sz), 8) *(*uintptr)(unsafe.Pointer(app)) = ap return r } func VaInt32(app *uintptr) int32 { ap := *(*uintptr)(unsafe.Pointer(app)) if ap == 0 { return 0 } ap = roundup(ap, 8) v := int32(*(*int64)(unsafe.Pointer(ap))) ap += 8 *(*uintptr)(unsafe.Pointer(app)) = ap return v } func VaUint32(app *uintptr) uint32 { ap := *(*uintptr)(unsafe.Pointer(app)) if ap == 0 { return 0 } ap = roundup(ap, 8) v := uint32(*(*uint64)(unsafe.Pointer(ap))) ap += 8 *(*uintptr)(unsafe.Pointer(app)) = ap return v } func VaInt64(app *uintptr) int64 { ap := *(*uintptr)(unsafe.Pointer(app)) if ap == 0 { return 0 } ap = roundup(ap, 8) v := *(*int64)(unsafe.Pointer(ap)) ap += 8 *(*uintptr)(unsafe.Pointer(app)) = ap return v } func VaUint64(app *uintptr) uint64 { ap := *(*uintptr)(unsafe.Pointer(app)) if ap == 0 { return 0 } ap = roundup(ap, 8) v := *(*uint64)(unsafe.Pointer(ap)) ap += 8 *(*uintptr)(unsafe.Pointer(app)) = ap return v } func VaFloat32(app *uintptr) float32 { ap := *(*uintptr)(unsafe.Pointer(app)) if ap == 0 { return 0 } ap = roundup(ap, 8) v := *(*float64)(unsafe.Pointer(ap)) ap += 8 *(*uintptr)(unsafe.Pointer(app)) = ap return float32(v) } func VaFloat64(app *uintptr) float64 { ap := *(*uintptr)(unsafe.Pointer(app)) if ap == 0 { return 0 } ap = roundup(ap, 8) v := *(*float64)(unsafe.Pointer(ap)) ap += 8 *(*uintptr)(unsafe.Pointer(app)) = ap return v } func VaUintptr(app *uintptr) uintptr { ap := *(*uintptr)(unsafe.Pointer(app)) if ap == 0 { return 0 } ap = roundup(ap, 8) v := *(*uintptr)(unsafe.Pointer(ap)) ap += 8 *(*uintptr)(unsafe.Pointer(app)) = ap return v } func roundup(n, to uintptr) uintptr { if r := n % to; r != 0 { return n + to - r } return n } 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++ } } // GoBytes returns a byte slice from a C char* having length len bytes. func GoBytes(s uintptr, len int) []byte { if len == 0 { return nil } return (*RawMem)(unsafe.Pointer(s))[:len:len] } func Bool(v bool) bool { return v } func Bool32(b bool) int32 { if b { return 1 } return 0 } func Bool64(b bool) int64 { if b { return 1 } return 0 } type sorter struct { len int base uintptr sz uintptr f func(*TLS, uintptr, uintptr) int32 t *TLS } func (s *sorter) Len() int { return s.len } func (s *sorter) Less(i, j int) bool { return s.f(s.t, s.base+uintptr(i)*s.sz, s.base+uintptr(j)*s.sz) < 0 } func (s *sorter) Swap(i, j int) { p := uintptr(s.base + uintptr(i)*s.sz) q := uintptr(s.base + uintptr(j)*s.sz) for i := 0; i < int(s.sz); i++ { *(*byte)(unsafe.Pointer(p)), *(*byte)(unsafe.Pointer(q)) = *(*byte)(unsafe.Pointer(q)), *(*byte)(unsafe.Pointer(p)) p++ q++ } } func CString(s string) (uintptr, error) { n := len(s) p := Xmalloc(nil, types.Size_t(n)+1) if p == 0 { return 0, fmt.Errorf("CString: cannot allocate %d bytes", n+1) } copy((*RawMem)(unsafe.Pointer(p))[:n:n], s) *(*byte)(unsafe.Pointer(p + uintptr(n))) = 0 return p, nil } func GetEnviron() (r []string) { for p := Environ(); ; p += unsafe.Sizeof(p) { q := *(*uintptr)(unsafe.Pointer(p)) if q == 0 { return r } r = append(r, GoString(q)) } } func strToUint64(t *TLS, s uintptr, base int32) (seenDigits, neg bool, next uintptr, n uint64, err int32) { var c byte out: for { c = *(*byte)(unsafe.Pointer(s)) switch c { case ' ', '\t', '\n', '\r', '\v', '\f': s++ case '+': s++ break out case '-': s++ neg = true break out default: break out } } for { c = *(*byte)(unsafe.Pointer(s)) var digit uint64 switch base { case 10: switch { case c >= '0' && c <= '9': seenDigits = true digit = uint64(c) - '0' default: return seenDigits, neg, s, n, 0 } case 16: if c >= 'A' && c <= 'F' { c = c + ('a' - 'A') } switch { case c >= '0' && c <= '9': seenDigits = true digit = uint64(c) - '0' case c >= 'a' && c <= 'f': seenDigits = true digit = uint64(c) - 'a' + 10 default: return seenDigits, neg, s, n, 0 } default: panic(todo("", base)) } n0 := n n = uint64(base)*n + digit if n < n0 { // overflow return seenDigits, neg, s, n0, errno.ERANGE } s++ } } func strToFloatt64(t *TLS, s uintptr, bits int) (n float64, errno int32) { var b []byte var neg bool defer func() { var err error if n, err = strconv.ParseFloat(string(b), bits); err != nil { panic(todo("")) } if neg { n = -n } }() var c byte out: for { c = *(*byte)(unsafe.Pointer(s)) switch c { case ' ', '\t', '\n', '\r', '\v', '\f': s++ case '+': s++ break out case '-': s++ neg = true break out default: break out } } for { c = *(*byte)(unsafe.Pointer(s)) switch { case c >= '0' && c <= '9': b = append(b, c) case c == '.': b = append(b, c) s++ for { c = *(*byte)(unsafe.Pointer(s)) switch { case c >= '0' && c <= '9': b = append(b, c) case c == 'e' || c == 'E': b = append(b, c) s++ for { c = *(*byte)(unsafe.Pointer(s)) switch { case c == '+' || c == '-': b = append(b, c) s++ for { c = *(*byte)(unsafe.Pointer(s)) switch { case c >= '0' && c <= '9': b = append(b, c) default: return } s++ } default: panic(todo("%q %q", b, string(c))) } } default: return } s++ } default: panic(todo("%q %q", b, string(c))) } s++ } } func parseZone(s string) (name string, off int) { _, name, off, _ = parseZoneOffset(s, false) return name, off } func parseZoneOffset(s string, offOpt bool) (string, string, int, bool) { s0 := s name := s for len(s) != 0 { switch c := s[0]; { case c >= 'A' && c <= 'Z', c >= 'a' && c <= 'z', c == '_', c == '/': s = s[1:] default: name = name[:len(name)-len(s)] if len(name) < 3 { panic(todo("%q", s0)) } if offOpt { if len(s) == 0 { return "", name, 0, false } if c := s[0]; (c < '0' || c > '9') && c != '+' && c != '-' { return s, name, 0, false } } s, off := parseOffset(s) return s, name, off, true } } return "", s0, 0, true } // [+|-]hh[:mm[:ss]] func parseOffset(s string) (string, int) { if len(s) == 0 { panic(todo("")) } k := 1 switch s[0] { case '+': // nop s = s[1:] case '-': k = -1 s = s[1:] } s, hh, ok := parseUint(s) if !ok { panic(todo("")) } n := hh * 3600 if len(s) == 0 || s[0] != ':' { return s, k * n } s = s[1:] // ':' if len(s) == 0 { panic(todo("")) } s, mm, ok := parseUint(s) if !ok { panic(todo("")) } n += mm * 60 if len(s) == 0 || s[0] != ':' { return s, k * n } s = s[1:] // ':' if len(s) == 0 { panic(todo("")) } s, ss, _ := parseUint(s) return s, k * (n + ss) } func parseUint(s string) (string, int, bool) { var ok bool var r int for len(s) != 0 { switch c := s[0]; { case c >= '0' && c <= '9': ok = true r0 := r r = 10*r + int(c) - '0' if r < r0 { panic(todo("")) } s = s[1:] default: return s, r, ok } } return s, r, ok } // https://stackoverflow.com/a/53052382 // // isTimeDST returns true if time t occurs within daylight saving time // for its time zone. func isTimeDST(t time.Time) bool { // If the most recent (within the last year) clock change // was forward then assume the change was from std to dst. hh, mm, _ := t.UTC().Clock() tClock := hh*60 + mm for m := -1; m > -12; m-- { // assume dst lasts for at least one month hh, mm, _ := t.AddDate(0, m, 0).UTC().Clock() clock := hh*60 + mm if clock != tClock { return clock > tClock } } // assume no dst return false }