Update musl to 1.1.15 (#4813)

* Revert all emscripten musl changes

* Update musl to 1.1.5

* Reapply emscripten patches

* Fix up libs to partially compile

* Revert emscripten musl include folder

* Update musl include folder to 1.1.5

* Reapply emscripten libc include changes

* Get compiling again!

* Restore alltypes.h to have emscripten-specific changes

alltypes.h is autogenerated, and we've since overridden the types

Longer-term, we should figure out a way to either autogenerate the types
to match what emscripten expects, or change emscripten's expectation to
support musl's assumptions. Probably worth including with merging the
asm&wasm ABIs.

* Update expected langinfo test output for fixes in upstream musl

* Canonicalize nans in hyperbolic test

Nans are now printed out with their sign if negative. Asm and wasm treat
these differently, so canonicalize how they print.

* Tweak how musl forwards syscalls to emscripten

Before: pass all syscall args as varargs, args are int32
After: cast all syscall args to int32, pass as varargs
This fixes and prevents aliasing issues, and is more consistent with
other architectures

* Change __syscall to syscall in isatty to set errno

POSIX specifies that isatty sets errno in the case that it fails.
__syscall doesn't set errno, and syscall does.

* Update struct_info again

* Always allocate pthread_self, even in the stub

* sys_open should call __syscall_ret

* Fix up pthread js for optimized code (remove __asm: true)

* Canonicalize more nans in testsuite

* Re-set expected langinfo output

* Update align_moar expected addresses

* Disable assert in wasm_backend llseek syscall

* Update now-passing wasm_backend tests

* Update other.test_locale to non-stub setlocale

* Fix compiling with -s USE_PTHREADS=1

* Initialize __pthread_self() when using pthread_stub

* Canonicalize nans for test_zerodiv

* Update count of globals in test_dlfcn_self

* Rework how we initialize pthread_self for non-threaded applications

* Prefer 'EMSCRIPTEN_START_FUNCS' in test_memorygrowth, because 'var TOTAL_MEMORY' can move around

* Now that CURRENT_LOCALE isn't reading off NULL, test_langinfo should expect ASCII

* Re-disable test_demangle_stacks - was spuriously passing earlier

* Explicitly pass 0 as the high bits for SYS__llseek

* Enable v8 for test_readdir now that it supports printErr

* Only use __syscall_emscripten if __EMSCRIPTEN__ is defined

* Move O_LARGEFILE as default back to libc

* libc #if to #ifdef

* Rename pthread import funcs to not rely on alias

* Consistently reference pthread_setcancelstate by alias in libc

* Stub out fcntl64 for NO_FILESYSTEM case

* Update emscripten-specific pthread struct fields

* Consistently reference pthread_testcancel by alias in libc

* Stub out SYS_rt_sigqueueinfo syscall

* Stub out SYS_sendmmsg syscall

* Stub out SYS_recvmmsg syscall

* Revert "Consistently reference pthread_testcancel by alias in libc"

This reverts commit 43563a5b141a47d4a7aa7e48c059ed0e98597fbb.

* Revert "Consistently reference pthread_setcancelstate by alias in libc"

This reverts commit 2ca2eded8a0d50bb3847cda0a99c900532f6937b.

* Correctly fix unresolved symbol: __pthread_setcancelstate

* Update library_pthread.c pthread_testcancel to match musl's expectations

* test_strftime.c -> test_strftime.cpp

* library_pthread_stub pthread_exit should call exit, not nothing

* TEMP: init pthread_self->locale with pthreads enabled

* Initialize pthread global data from libc in JS

* Move _b_lock and _b_waiters back to not emscripten-only

* Remove untrue __asm annotation from threaded pthread_self, make stub pthread_self non-asm

* Remove old unneeded pthread init exported functions

* Move pthread_self in stub case to libc C code

* Move emscripten_pthread initialization to its own file, out from locale_map.c

* Statically allocate the pthread struct and not just a pointer in the stub case

* Remove commented-out test args

* Remove misleading __asm annotation for _register_pthread_ptr

* Do module-side pthread initialization in-module

* Don't cast away volatile when calling emscripten_futex_wake in __wake

* Expose libc struct to emscripten, use that for non-main pthread initialization

* Re-add pthread_impl.h import to library_pthread.c

* Update align_moar alignment post-rebase

* Update emscripten header include in libc.c

* Remove obsolete locale preallocating code in newlocale

We previously statically allocated a single locale in the event that
we needed to dynamically allocate one during startup. musl changed their
implementation to statically allocate C_LOCALE and UTF8_LOCALE, so we
already get that behavior.
In addition this was causing invalid locales to get allocated instead of
returning NULL, because the preceding logic changed. That was caught by
other.test_locale_wrong

* Update test_llvm_lto

* Bump version to 1.37.5
diff --git a/emscripten-version.txt b/emscripten-version.txt
index c7e079d..3da2da7 100644
--- a/emscripten-version.txt
+++ b/emscripten-version.txt
@@ -1 +1 @@
-"1.37.4"
+"1.37.5"
diff --git a/src/library.js b/src/library.js
index 79d98a4..75f5051 100644
--- a/src/library.js
+++ b/src/library.js
@@ -2107,6 +2107,12 @@
     return -1;
   },
 
+  __map_file__deps: ['$ERRNO_CODES', '__setErrNo'],
+  __map_file: function(pathname, size) {
+    ___setErrNo(ERRNO_CODES.EPERM);
+    return -1;
+  },
+
   _MONTH_DAYS_REGULAR: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
   _MONTH_DAYS_LEAP: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
 
@@ -4282,7 +4288,7 @@
   _pthread_cleanup_push: function(){},
   _pthread_cleanup_pop: function(){},
   __pthread_self: function() { abort() },
-  pthread_setcancelstate: function() { return 0 },
+  __pthread_setcancelstate: function() { return 0 },
 
   // libunwind
 
diff --git a/src/library_pthread.js b/src/library_pthread.js
index 93a67de..2cbf9cc 100644
--- a/src/library_pthread.js
+++ b/src/library_pthread.js
@@ -416,6 +416,10 @@
     Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 20) >> 2, threadParams.schedPolicy);
     Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.attr }}} + 24) >> 2, threadParams.schedPrio);
 
+    var global_libc = _emscripten_get_global_libc();
+    var global_locale = global_libc + {{{ C_STRUCTS.libc.global_locale }}};
+    Atomics.store(HEAPU32, (pthread.threadInfoStruct + {{{ C_STRUCTS.pthread.locale }}}) >> 2, global_locale);
+
 #if PTHREADS_PROFILING
     PThread.createProfilerBlock(pthread.threadInfoStruct);
 #endif
@@ -735,7 +739,6 @@
   _pthread_is_main_runtime_thread: 0,
   _pthread_is_main_browser_thread: 0,
 
-  _register_pthread_ptr__asm: true,
   _register_pthread_ptr__deps: ['_pthread_ptr', '_pthread_is_main_runtime_thread', '_pthread_is_main_browser_thread'],
   _register_pthread_ptr: function(pthreadPtr, isMainBrowserThread, isMainRuntimeThread) {
     pthreadPtr = pthreadPtr|0;
@@ -747,7 +750,6 @@
   },
 
   // Public pthread_self() function which returns a unique ID for the thread.
-  pthread_self__asm: true,
   pthread_self__deps: ['_pthread_ptr'],
   pthread_self: function() {
     return __pthread_ptr|0;
diff --git a/src/library_pthread_stub.js b/src/library_pthread_stub.js
index c7e5789..164d32d 100644
--- a/src/library_pthread_stub.js
+++ b/src/library_pthread_stub.js
@@ -79,12 +79,6 @@
     return 0;
   },
 
-  pthread_self__asm: true,
-  pthread_self__sig: 'i',
-  pthread_self: function() {
-    return 0;
-  },
-
   pthread_attr_init: function(attr) {
     /* int pthread_attr_init(pthread_attr_t *attr); */
     //FIXME: should allocate a pthread_attr_t
@@ -194,7 +188,10 @@
     return {{{ cDefine('EAGAIN') }}};
   },
   pthread_cancel: function() {},
-  pthread_exit: function() {},
+  pthread_exit__deps: ['exit'],
+  pthread_exit: function(status) {
+    _exit(status);
+  },
 
   pthread_equal: function() {},
   pthread_join: function() {},
diff --git a/src/library_syscall.js b/src/library_syscall.js
index 8b2d825..423073d 100644
--- a/src/library_syscall.js
+++ b/src/library_syscall.js
@@ -332,6 +332,12 @@
         var argp = SYSCALLS.get();
         return FS.ioctl(stream, op, argp);
       }
+      case {{{ cDefine('TIOCGWINSZ') }}}: {
+        // TODO: in theory we should write to the winsize struct that gets
+        // passed in, but for now musl doesn't read anything on it
+        if (!stream.tty) return -ERRNO_CODES.ENOTTY;
+        return 0;
+      }
       default: abort('bad ioctl syscall ' + op);
     }
 #endif // NO_FILESYSTEM
@@ -833,6 +839,12 @@
     }
     return nonzero;
   },
+  __syscall178: function(which, varargs) { // rt_sigqueueinfo
+#if SYSCALL_DEBUG
+    Module.printErr('warning: ignoring SYS_rt_sigqueueinfo');
+#endif
+    return 0;
+  },
   __syscall180: function(which, varargs) { // pread64
     var stream = SYSCALLS.getStreamFromFD(), buf = SYSCALLS.get(), count = SYSCALLS.get(), zero = SYSCALLS.getZero(), offset = SYSCALLS.get64();
     return FS.read(stream, {{{ heapAndOffset('HEAP8', 'buf') }}}, count, offset);
@@ -996,6 +1008,12 @@
   },
   __syscall221__deps: ['__setErrNo'],
   __syscall221: function(which, varargs) { // fcntl64
+#if NO_FILESYSTEM
+#if SYSCALL_DEBUG
+    Module.printErr('no-op in fcntl64 syscall due to NO_FILESYSTEM');
+#endif
+    return 0;
+#else
     var stream = SYSCALLS.getStreamFromFD(), cmd = SYSCALLS.get();
     switch (cmd) {
       case {{{ cDefine('F_DUPFD') }}}: {
@@ -1044,6 +1062,7 @@
         return -ERRNO_CODES.EINVAL;
       }
     }
+#endif // NO_FILESYSTEM
   },
   __syscall265: function(which, varargs) { // clock_nanosleep
 #if SYSCALL_DEBUG
@@ -1231,6 +1250,12 @@
     var stream = SYSCALLS.getStreamFromFD(), iov = SYSCALLS.get(), iovcnt = SYSCALLS.get(), offset = SYSCALLS.get();
     return SYSCALLS.doWritev(stream, iov, iovcnt, offset);
   },
+  __syscall337: function(which, varargs) { // recvmmsg
+#if SYSCALL_DEBUG
+    Module.printErr('warning: ignoring SYS_recvmmsg');
+#endif
+    return 0;
+  },
   __syscall340: function(which, varargs) { // prlimit64
     var pid = SYSCALLS.get(), resource = SYSCALLS.get(), new_limit = SYSCALLS.get(), old_limit = SYSCALLS.get();
     if (old_limit) { // just report no limits
@@ -1241,6 +1266,12 @@
     }
     return 0;
   },
+  __syscall345: function(which, varargs) { // sendmmsg
+#if SYSCALL_DEBUG
+    Module.printErr('warning: ignoring SYS_sendmmsg');
+#endif
+    return 0;
+  },
 };
 
 if (SYSCALL_DEBUG) {
@@ -1577,6 +1608,7 @@
     SYS_inotify_init1: 332,
     SYS_preadv: 333,
     SYS_pwritev: 334,
+    SYS_recvmmsg: 337,
     SYS_prlimit64: 340,
     SYS_name_to_handle_at: 341,
     SYS_open_by_handle_at: 342,
diff --git a/src/struct_info.compiled.json b/src/struct_info.compiled.json
index 465a51e..bf639db 100644
--- a/src/struct_info.compiled.json
+++ b/src/struct_info.compiled.json
@@ -1 +1 @@
-{"structs":{"utsname":{"sysname":0,"nodename":65,"domainname":325,"machine":260,"version":195,"release":130,"__size__":390},"sockaddr":{"sa_data":2,"sa_family":0,"__size__":16},"addrinfo":{"ai_flags":0,"ai_next":28,"ai_canonname":24,"ai_socktype":8,"ai_addr":20,"ai_protocol":12,"ai_family":4,"ai_addrlen":16,"__size__":32},"timespec":{"tv_sec":0,"tv_nsec":4,"__size__":8},"utimbuf":{"modtime":4,"actime":0,"__size__":8},"EmscriptenVisibilityChangeEvent":{"hidden":0,"visibilityState":4,"__size__":8},"SDL_MouseButtonEvent":{"timestamp":4,"button":16,"state":17,"windowID":8,"which":12,"y":24,"x":20,"padding2":19,"type":0,"padding1":18,"__size__":28},"sockaddr_in":{"sin_port":2,"sin_addr":{"s_addr":4,"__size__":4},"sin_family":0,"sin_zero":8,"__size__":16},"pthread":{"tsd":116,"attr":120,"canceldisable":72,"threadStatus":0,"tsd_used":56,"pid":52,"stack":92,"cancelasync":76,"tid":48,"threadExitCode":4,"detached":80,"profilerBlock":20,"self":24,"stack_size":96,"__size__":220},"WebVRFieldOfView":{"leftDegrees":24,"upDegrees":0,"downDegrees":16,"rightDegrees":8,"__size__":32},"SDL_KeyboardEvent":{"repeat":9,"keysym":12,"state":8,"windowID":4,"__size__":28,"type":0,"padding3":11,"padding2":10},"SDL_MouseMotionEvent":{"yrel":32,"timestamp":4,"state":16,"windowID":8,"which":12,"xrel":28,"y":24,"x":20,"type":0,"__size__":36},"SDL_Rect":{"y":4,"x":0,"h":12,"w":8,"__size__":16},"itimerspec":{"it_interval":{"tv_sec":0,"tv_nsec":4,"__size__":8},"it_value":{"tv_sec":8,"tv_nsec":12,"__size__":8},"__size__":16},"iovec":{"iov_len":4,"iov_base":0,"__size__":8},"timezone":{"tz_dsttime":4,"tz_minuteswest":0,"__size__":8},"flock":{"l_whence":2,"l_type":0,"l_start":4,"__size__":16,"l_len":8,"l_pid":12},"EmscriptenOrientationChangeEvent":{"orientationIndex":0,"orientationAngle":4,"__size__":8},"EmscriptenMouseEvent":{"clientX":16,"clientY":20,"targetX":52,"buttons":42,"timestamp":0,"button":40,"targetY":56,"altKey":32,"canvasY":64,"metaKey":36,"movementX":44,"movementY":48,"shiftKey":28,"ctrlKey":24,"screenY":12,"screenX":8,"canvasX":60,"__size__":72},"SDL_ResizeEvent":{"h":8,"type":0,"w":4,"__size__":12},"tms":{"tms_stime":4,"tms_utime":0,"tms_cstime":12,"tms_cutime":8,"__size__":16},"SDL_Color":{"unused":3,"r":0,"b":2,"g":1,"__size__":4},"EmscriptenKeyboardEvent":{"code":32,"charValue":120,"locale":88,"shiftKey":72,"altKey":76,"which":160,"metaKey":80,"location":64,"key":0,"ctrlKey":68,"charCode":152,"keyCode":156,"repeat":84,"__size__":164},"rusage":{"ru_msgrcv":56,"ru_utime":{"tv_sec":0,"tv_usec":4,"__size__":8},"ru_isrss":28,"ru_stime":{"tv_sec":8,"tv_usec":12,"__size__":8},"ru_nsignals":60,"ru_nivcsw":68,"ru_msgsnd":52,"ru_nswap":40,"ru_minflt":32,"ru_nvcsw":64,"ru_ixrss":20,"ru_inblock":44,"ru_idrss":24,"ru_maxrss":16,"ru_oublock":48,"ru_majflt":36,"__size__":136},"div_t":{"quot":0,"rem":4,"__size__":8},"timeval":{"tv_sec":0,"tv_usec":4,"__size__":8},"rlimit":{"rlim_cur":0,"rlim_max":8,"__size__":16},"in6_addr":{"__in6_union":{"__s6_addr16":0,"__s6_addr":0,"__s6_addr32":0,"__size__":16},"__size__":16},"tm":{"tm_sec":0,"tm_hour":8,"tm_mday":12,"tm_isdst":32,"tm_year":20,"tm_zone":40,"tm_mon":16,"tm_yday":28,"tm_gmtoff":36,"tm_wday":24,"tm_min":4,"__size__":44},"EmscriptenWebGLContextAttributes":{"majorVersion":32,"stencil":8,"preserveDrawingBuffer":20,"failIfMajorPerformanceCaveat":28,"explicitSwapControl":44,"antialias":12,"depth":4,"minorVersion":36,"premultipliedAlpha":16,"enableExtensionsByDefault":40,"alpha":0,"preferLowPowerToHighPerformance":24,"__size__":48},"EmscriptenBatteryEvent":{"dischargingTime":8,"level":16,"charging":24,"chargingTime":0,"__size__":32},"protoent":{"p_aliases":4,"p_proto":8,"p_name":0,"__size__":12},"SDL_Surface":{"userdata":24,"locked":28,"clip_rect":36,"format":4,"h":12,"refcount":56,"map":52,"flags":0,"w":8,"pitch":16,"lock_data":32,"pixels":20,"__size__":60},"EmscriptenTouchEvent":{"touches":20,"shiftKey":8,"altKey":12,"metaKey":16,"ctrlKey":4,"__size__":1684,"numTouches":0},"EmscriptenFocusEvent":{"id":128,"nodeName":0,"__size__":256},"sockaddr_in6":{"sin6_family":0,"sin6_flowinfo":4,"sin6_scope_id":24,"sin6_addr":{"__in6_union":{"__s6_addr16":8,"__s6_addr":8,"__s6_addr32":8,"__size__":16},"__size__":16},"__size__":28,"sin6_port":2},"SDL_JoyAxisEvent":{"__size__":12,"type":0,"value":8,"which":4,"padding2":7,"padding1":6,"axis":5},"netent":{"n_name":0,"n_net":12,"n_addrtype":8,"n_aliases":4,"__size__":16},"SDL_PixelFormat":{"palette":4,"Gloss":29,"Bmask":20,"Bloss":30,"Rloss":28,"format":0,"Gshift":33,"Aloss":31,"BitsPerPixel":8,"refcount":36,"next":40,"padding":10,"Rmask":12,"Bshift":34,"Gmask":16,"BytesPerPixel":9,"Amask":24,"Rshift":32,"Ashift":35,"__size__":44},"SDL_JoyButtonEvent":{"type":0,"button":5,"state":6,"which":4,"padding1":7,"__size__":8},"EmscriptenPointerlockChangeEvent":{"id":132,"nodeName":4,"isActive":0,"__size__":260},"in_addr":{"s_addr":0,"__size__":4},"EmscriptenDeviceOrientationEvent":{"timestamp":0,"beta":16,"alpha":8,"__size__":40,"gamma":24,"absolute":32},"SDL_WindowEvent":{"data2":16,"type":0,"data1":12,"windowID":4,"__size__":20,"padding1":9,"event":8,"padding3":11,"padding2":10},"SDL_Keysym":{"scancode":0,"mod":8,"unicode":12,"sym":4,"__size__":16},"cmsghdr":{"cmsg_type":8,"cmsg_level":4,"cmsg_len":0,"__size__":12},"EmscriptenUiEvent":{"windowInnerWidth":12,"detail":0,"scrollLeft":32,"documentBodyClientHeight":8,"windowInnerHeight":16,"scrollTop":28,"windowOuterHeight":24,"windowOuterWidth":20,"documentBodyClientWidth":4,"__size__":36},"thread_profiler_block":{"threadStatus":0,"timeSpentInStatus":16,"currentStatusStartTime":8,"name":72,"__size__":104},"stat":{"st_rdev":28,"st_mtim":{"tv_sec":56,"tv_nsec":60,"__size__":8},"st_blocks":44,"st_atim":{"tv_sec":48,"tv_nsec":52,"__size__":8},"st_nlink":16,"__st_ino_truncated":8,"st_ctim":{"tv_sec":64,"tv_nsec":68,"__size__":8},"st_mode":12,"st_blksize":40,"__st_dev_padding":4,"st_dev":0,"st_size":36,"st_gid":24,"__st_rdev_padding":32,"st_uid":20,"st_ino":72,"__size__":76},"pollfd":{"fd":0,"events":4,"revents":6,"__size__":8},"WebVRPositionState":{"linearVelocity":{"y":56,"x":48,"z":64,"w":72,"__size__":32},"orientation":{"y":128,"x":120,"z":136,"w":144,"__size__":32},"timeStamp":0,"angularVelocity":{"y":160,"x":152,"z":168,"w":176,"__size__":32},"hasPosition":8,"angularAcceleration":{"y":192,"x":184,"z":200,"w":208,"__size__":32},"linearAcceleration":{"y":88,"x":80,"z":96,"w":104,"__size__":32},"hasOrientation":112,"position":{"y":24,"x":16,"z":32,"w":40,"__size__":32},"__size__":216},"dirent":{"d_name":11,"d_off":4,"d_ino":0,"d_reclen":8,"d_type":10,"__size__":268},"EmscriptenTouchPoint":{"clientX":12,"clientY":16,"identifier":0,"targetX":36,"targetY":40,"isChanged":28,"canvasY":48,"canvasX":44,"pageX":20,"pageY":24,"screenY":8,"screenX":4,"onTarget":32,"__size__":52},"EmscriptenDeviceMotionEvent":{"timestamp":0,"accelerationIncludingGravityZ":48,"accelerationIncludingGravityX":32,"accelerationIncludingGravityY":40,"accelerationY":16,"accelerationX":8,"rotationRateBeta":64,"accelerationZ":24,"rotationRateGamma":72,"rotationRateAlpha":56,"__size__":80},"SDL_AudioSpec":{"padding":10,"userdata":20,"format":4,"channels":6,"callback":16,"samples":8,"freq":0,"size":12,"silence":7,"__size__":24},"hostent":{"h_addrtype":8,"h_addr_list":16,"h_name":0,"__size__":20,"h_aliases":4,"h_length":12},"SDL_MouseWheelEvent":{"timestamp":4,"windowID":8,"which":12,"y":20,"x":16,"type":0,"__size__":24},"linger":{"l_onoff":0,"l_linger":4,"__size__":8},"SDL_version":{"major":0,"patch":2,"minor":1,"__size__":3},"statvfs":{"f_bsize":0,"f_bavail":16,"f_fsid":32,"f_favail":28,"f_files":20,"f_frsize":4,"f_blocks":8,"f_ffree":24,"f_bfree":12,"f_flag":40,"f_namemax":44,"__size__":72},"EmscriptenFullscreenChangeEvent":{"elementWidth":264,"screenWidth":272,"nodeName":8,"elementHeight":268,"fullscreenEnabled":4,"screenHeight":276,"isFullscreen":0,"id":136,"__size__":280},"EmscriptenWheelEvent":{"deltaX":72,"deltaY":80,"deltaZ":88,"deltaMode":96,"mouse":0,"__size__":104},"WebVRIntRect":{"y":4,"x":0,"height":12,"width":8,"__size__":16},"SDL_TouchFingerEvent":{"timestamp":4,"dy":36,"touchId":8,"pressure":40,"dx":32,"type":0,"y":28,"x":24,"fingerId":16,"__size__":48},"SDL_AudioCVT":{"len_ratio":32,"len_cvt":24,"rate_incr":8,"filters":40,"len":20,"needed":0,"filter_index":80,"src_format":4,"len_mult":28,"__size__":88,"buf":16,"dst_format":6},"WebVRPoint":{"y":8,"x":0,"z":16,"w":24,"__size__":32},"timeb":{"dstflag":8,"timezone":6,"time":0,"millitm":4,"__size__":12},"statfs":{"f_bsize":4,"f_bavail":16,"f_fsid":28,"f_files":20,"f_frsize":40,"f_namelen":36,"f_blocks":8,"f_ffree":24,"f_bfree":12,"f_flags":44,"__size__":64},"msghdr":{"msg_iov":8,"msg_iovlen":12,"msg_namelen":4,"msg_controllen":20,"msg_flags":24,"msg_name":0,"msg_control":16,"__size__":28},"WebVREyeParameters":{"currentFieldOfView":{"leftDegrees":152,"upDegrees":128,"downDegrees":144,"rightDegrees":136,"__size__":32},"recommendedFieldOfView":{"leftDegrees":88,"upDegrees":64,"downDegrees":80,"rightDegrees":72,"__size__":32},"eyeTranslation":{"y":104,"x":96,"z":112,"w":120,"__size__":32},"renderRect":{"y":164,"x":160,"height":172,"width":168,"__size__":16},"minimumFieldOfView":{"leftDegrees":24,"upDegrees":0,"downDegrees":16,"rightDegrees":8,"__size__":32},"maximumFieldOfView":{"leftDegrees":56,"upDegrees":32,"downDegrees":48,"rightDegrees":40,"__size__":32},"__size__":176},"SDL_Palette":{"ncolors":0,"colors":4,"version":8,"refcount":12,"__size__":16},"EmscriptenFullscreenStrategy":{"canvasResizedCallbackUserData":16,"canvasResolutionScaleMode":4,"scaleMode":0,"canvasResizedCallback":12,"filteringMode":8,"__size__":20},"EmscriptenGamepadEvent":{"index":1300,"analogButton":528,"timestamp":0,"numButtons":12,"mapping":1368,"digitalButton":1040,"connected":1296,"numAxes":8,"__size__":1432,"id":1304,"axis":16},"SDL_TextInputEvent":{"text":8,"windowID":4,"type":0,"__size__":40}},"defines":{"ETXTBSY":26,"EOF":-1,"EMSCRIPTEN_EVENT_MOUSEOVER":35,"ETOOMANYREFS":109,"ENAMETOOLONG":36,"ENOPKG":65,"UUID_TYPE_DCE_TIME":1,"_SC_XOPEN_LEGACY":129,"_SC_XOPEN_VERSION":89,"F_UNLCK":2,"_SC_BC_DIM_MAX":37,"EL3HLT":46,"S_IFDIR":16384,"EMSCRIPTEN_EVENT_KEYPRESS":1,"EINPROGRESS":115,"_SC_BARRIERS":133,"EMSCRIPTEN_EVENT_TOUCHMOVE":24,"SDL_AUDIO_ALLOW_FREQUENCY_CHANGE":1,"AUDIO_U8":8,"EAI_AGAIN":-3,"_PC_MAX_CANON":1,"ENOTSUP":95,"EFBIG":27,"O_CREAT":64,"EMSCRIPTEN_EVENT_POINTERLOCKERROR":38,"_SC_2_PBS_LOCATE":170,"EM_PROXIED_SETENV":113,"_CS_POSIX_V6_LP64_OFF64_LIBS":1126,"ENOLINK":67,"ABDAY_7":131078,"ABDAY_6":131077,"ABDAY_5":131076,"ABDAY_4":131075,"ABDAY_3":131074,"ABDAY_2":131073,"ABDAY_1":131072,"EL3RST":47,"YESEXPR":327680,"_SC_V6_ILP32_OFFBIG":177,"SDL_MINOR_VERSION":3,"EM_PROXIED_CLEARENV":112,"_SC_MEMLOCK":17,"ENOTUNIQ":76,"EMSCRIPTEN_RESULT_FAILED":-6,"ABMON_1":131086,"ELNRNG":48,"UUID_VARIANT_MICROSOFT":2,"EMSCRIPTEN_EVENT_TOUCHSTART":22,"ENOANO":55,"EMSCRIPTEN_EVENT_FOCUSIN":14,"EMSCRIPTEN_EVENT_MOUSEUP":6,"ENOPROTOOPT":92,"POLLIN":1,"S_IALLUGO":4095,"_SC_THREAD_KEYS_MAX":74,"EM_THREAD_STATUS_WAITPROXY":5,"O_RDWR":2,"EREMCHG":78,"EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED":27,"_SC_2_PBS":168,"_SC_TRACE_INHERIT":183,"_SC_REGEXP":155,"_CS_POSIX_V6_LP64_OFF64_CFLAGS":1124,"_SC_DELAYTIMER_MAX":26,"S_IWUGO":146,"S_IFREG":32768,"F_GETLK64":12,"O_DIRECTORY":65536,"EM_PROXIED_UTIMES":13,"POLLHUP":16,"S_IFMT":61440,"F_SETLK64":13,"_SC_XOPEN_CRYPT":92,"_SC_CLOCK_SELECTION":137,"_PC_CHOWN_RESTRICTED":6,"E2BIG":7,"ABMON_3":131088,"AM_STR":131110,"SDL_AUDIO_MASK_ENDIAN":4096,"ALT_DIGITS":131119,"EHOSTDOWN":112,"EBFONT":59,"ENOTEMPTY":39,"AUDIO_S16":32784,"TIOCGPGRP":21519,"EBUSY":16,"_SC_MQ_PRIO_MAX":28,"_SC_PAGE_SIZE":30,"EADDRINUSE":98,"ENOTSOCK":88,"PM_STR":131111,"O_WRONLY":1,"_SC_STREAM_MAX":5,"ABMON_9":131094,"ELIBACC":79,"S_IFIFO":4096,"EDQUOT":122,"EAI_SYSTEM":-11,"ENOENT":2,"_SC_TIMERS":11,"O_SYNC":1052672,"SEEK_END":2,"EM_THREAD_STATUS_FINISHED":6,"_PC_REC_MIN_XFER_SIZE":16,"_PC_PATH_MAX":4,"_SC_SPORADIC_SERVER":160,"ECOMM":70,"_SC_NPROCESSORS_ONLN":84,"_CS_POSIX_V6_LPBIG_OFFBIG_LIBS":1130,"_PC_MAX_INPUT":2,"_SC_VERSION":29,"_SC_XBS5_LPBIG_OFFBIG":128,"_SC_CLK_TCK":2,"ABMON_2":131087,"EXFULL":54,"ABMON_7":131092,"ABMON_6":131091,"ABMON_5":131090,"ABMON_4":131089,"ENOTDIR":20,"ABMON_8":131093,"_SC_AIO_MAX":24,"ERA":131116,"EM_PROXIED_UNSETENV":114,"_SC_THREAD_PRIO_INHERIT":80,"_PC_2_SYMLINKS":20,"_SC_XBS5_LP64_OFF64":127,"EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE":30,"ENETRESET":102,"EAFNOSUPPORT":97,"MON_2":131099,"MON_3":131100,"MON_1":131098,"EMSCRIPTEN_EVENT_DEVICEORIENTATION":16,"MON_7":131104,"MON_4":131101,"MON_5":131102,"_SC_SPAWN":159,"MON_8":131105,"MON_9":131106,"_CS_POSIX_V6_ILP32_OFF32_LDFLAGS":1117,"S_IFSOCK":49152,"S_IRUGO":292,"SOCK_DGRAM":2,"POLLERR":8,"EINVAL":22,"_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS":1128,"POLLRDNORM":64,"AUDIO_F32SYS":33056,"_SC_TRACE_SYS_MAX":244,"AI_V4MAPPED":8,"AI_NUMERICHOST":4,"_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS":1,"EHOSTUNREACH":113,"ENOCSI":50,"EPROTONOSUPPORT":93,"_SC_AIO_PRIO_DELTA_MAX":25,"_SC_MONOTONIC_CLOCK":149,"ETIME":62,"ENOTTY":25,"_SC_XOPEN_ENH_I18N":93,"EAI_SERVICE":-8,"EAGAIN":11,"F_SETLKW64":14,"EMSGSIZE":90,"ELIBEXEC":83,"_SC_MEMORY_PROTECTION":19,"EMSCRIPTEN_FULLSCREEN_SCALE_CENTER":3,"SDL_AUDIO_ALLOW_FORMAT_CHANGE":2,"ECANCELED":125,"_SC_SPIN_LOCKS":154,"_SC_XOPEN_SHM":94,"_PC_LINK_MAX":0,"TIOCSPGRP":21520,"EOPNOTSUPP":95,"EMSCRIPTEN_EVENT_MOUSEENTER":33,"EAI_FAIL":-4,"NOEXPR":327681,"_SC_FSYNC":15,"_SC_GETGR_R_SIZE_MAX":69,"EDESTADDRREQ":89,"EADDRNOTAVAIL":99,"AUDIO_S32SYS":32800,"_SC_TRACE_NAME_MAX":243,"_SC_BC_BASE_MAX":36,"EMSCRIPTEN_EVENT_CANVASRESIZED":37,"EPERM":1,"EAI_FAMILY":-6,"O_NOFOLLOW":131072,"SOCK_STREAM":1,"O_APPEND":1024,"_SC_XOPEN_STREAMS":246,"_SC_GETPW_R_SIZE_MAX":70,"MON_6":131103,"EPROTOTYPE":91,"_SC_CPUTIME":138,"EISCONN":106,"_SC_XBS5_ILP32_OFFBIG":126,"S_IFBLK":24576,"T_FMT_AMPM":131115,"SDL_PIXELFORMAT_RGBA8888":-2042224636,"F_SETLKW":14,"SDL_TOUCH_MOUSEID":-1,"EMSCRIPTEN_EVENT_SCROLL":11,"ELOOP":40,"_SC_OPEN_MAX":4,"_SC_2_FORT_RUN":50,"EMSCRIPTEN_EVENT_VISIBILITYCHANGE":21,"EREMOTE":66,"_SC_RE_DUP_MAX":44,"_SC_THREAD_PRIO_PROTECT":81,"_SC_2_PBS_CHECKPOINT":175,"_SC_2_PBS_TRACK":172,"MON_10":131107,"MON_11":131108,"MON_12":131109,"TCGETS":21505,"_SC_THREAD_PROCESS_SHARED":82,"AF_INET":2,"_SC_SHARED_MEMORY_OBJECTS":22,"F_GETFD":1,"EMSCRIPTEN_EVENT_DEVICEMOTION":17,"SDL_MIX_MAXVOLUME":128,"_PC_ALLOC_SIZE_MIN":18,"TCSETS":21506,"ELIBMAX":82,"_SC_READER_WRITER_LOCKS":153,"EMULTIHOP":72,"IPPROTO_TCP":6,"_SC_PHYS_PAGES":85,"_SC_MEMLOCK_RANGE":18,"_SC_PRIORITY_SCHEDULING":10,"T_FMT":131114,"AI_ALL":16,"_PC_VDISABLE":8,"THOUSEP":65537,"_SC_TRACE_EVENT_FILTER":182,"ERA_T_FMT":131121,"_SC_THREAD_ATTR_STACKADDR":77,"_SC_THREAD_THREADS_MAX":76,"_SC_LOGIN_NAME_MAX":71,"_SC_2_C_BIND":47,"_PC_NO_TRUNC":7,"ECONNABORTED":103,"EMSCRIPTEN_RESULT_SUCCESS":0,"_SC_SHELL":157,"EFAULT":14,"_SC_V6_LP64_OFF64":178,"_CS_GNU_LIBC_VERSION":2,"ENODATA":61,"_SC_SEM_VALUE_MAX":33,"_SC_MQ_OPEN_MAX":27,"AI_ADDRCONFIG":32,"_SC_HOST_NAME_MAX":180,"_SC_THREAD_STACK_MIN":75,"_SC_TIMEOUTS":164,"POLLOUT":4,"_SC_IPV6":235,"_SC_CHILD_MAX":1,"EDOM":33,"_SC_2_PBS_MESSAGE":171,"EILSEQ":84,"UUID_VARIANT_DCE":1,"_SC_2_C_DEV":48,"_SC_TIMER_MAX":35,"FP_ZERO":2,"EPFNOSUPPORT":96,"ENONET":64,"ECHRNG":44,"_SC_THREADS":67,"_SC_REALTIME_SIGNALS":9,"CLOCKS_PER_SEC":1000000,"ERA_D_T_FMT":131120,"ESRCH":3,"D_FMT":131113,"POLLPRI":2,"_PC_ASYNC_IO":10,"DAY_2":131080,"DAY_3":131081,"DAY_1":131079,"DAY_6":131084,"DAY_7":131085,"DAY_4":131082,"DAY_5":131083,"_SC_SYNCHRONIZED_IO":14,"EL2HLT":51,"EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF":1,"IPPROTO_UDP":17,"_SC_MAPPED_FILES":16,"EL2NSYNC":45,"_SC_NGROUPS_MAX":3,"ENOMSG":42,"EISDIR":21,"_SC_SEMAPHORES":21,"AI_NUMERICSERV":1024,"EDEADLOCK":35,"EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST":31,"EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE":29,"AUDIO_F32LSB":33056,"_SC_COLL_WEIGHTS_MAX":40,"SO_ERROR":4,"ECONNRESET":104,"AT_SYMLINK_NOFOLLOW":256,"_SC_TRACE_LOG":184,"AUDIO_U16LSB":16,"ESTRPIPE":86,"ESHUTDOWN":108,"_PC_SOCK_MAXBUF":12,"_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS":1129,"EDEADLK":35,"_CS_POSIX_V6_ILP32_OFF32_CFLAGS":1116,"EBADRQC":56,"_SC_THREAD_DESTRUCTOR_ITERATIONS":73,"_SC_TYPED_MEMORY_OBJECTS":165,"_SC_TRACE_EVENT_NAME_MAX":242,"_SC_BC_STRING_MAX":39,"_SC_2_SW_DEV":51,"FP_NAN":0,"F_SETOWN":8,"EMSCRIPTEN_EVENT_RESIZE":10,"_SC_ARG_MAX":0,"_SC_THREAD_PRIORITY_SCHEDULING":79,"F_GETLK":12,"EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF":2,"FIONREAD":21531,"_SC_THREAD_CPUTIME":139,"EMSCRIPTEN_EVENT_POINTERLOCKCHANGE":20,"EM_THREAD_STATUS_NOTSTARTED":0,"_CS_POSIX_V6_ILP32_OFF32_LIBS":1118,"EUNATCH":49,"AUDIO_S8":32776,"AUDIO_S32LSB":32800,"SDL_AUDIO_MASK_BITSIZE":255,"ERA_D_FMT":131118,"AUDIO_F32MSB":37152,"_CS_POSIX_V6_LP64_OFF64_LDFLAGS":1125,"FP_INFINITE":1,"ECHILD":10,"EAI_MEMORY":-10,"EM_PROXIED_FPATHCONF":46,"O_TRUNC":512,"ETIMEDOUT":110,"S_IRWXO":7,"EALREADY":114,"ENXIO":6,"NI_NUMERICHOST":1,"EMFILE":24,"F_GETOWN":9,"EMLINK":31,"F_SETFD":2,"ENFILE":23,"EM_PROXIED_SYSCONF":72,"EM_PROXIED_GETENV":111,"SDL_MAJOR_VERSION":1,"ENOMEM":12,"ENOSR":63,"SDL_AUDIO_ALLOW_ANY_CHANGE":7,"EOWNERDEAD":130,"_PC_PRIO_IO":11,"ELIBSCN":81,"_SC_V6_LPBIG_OFFBIG":179,"EM_PROXIED_CHROOT":37,"EMSCRIPTEN_EVENT_CLICK":4,"_SC_EXPR_NEST_MAX":42,"_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS":1120,"EBADSLT":57,"AUDIO_S16MSB":36880,"S_ISVTX":512,"EMSCRIPTEN_RESULT_DEFERRED":1,"EMSCRIPTEN_RESULT_UNKNOWN_TARGET":-4,"S_IRWXUGO":511,"EM_PROXIED_TZSET":119,"_CS_GNU_LIBPTHREAD_VERSION":3,"_PC_REC_MAX_XFER_SIZE":15,"UUID_VARIANT_OTHER":3,"EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED":32,"EM_PROXIED_PTHREAD_CREATE":137,"EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT":0,"RADIXCHAR":65536,"AF_UNSPEC":0,"ENOSTR":60,"W_OK":2,"AUDIO_S32":32800,"EACCES":13,"R_OK":4,"EM_HTML5_MEDIUM_STRING_LEN_BYTES":64,"_SC_V6_ILP32_OFF32":176,"EMSCRIPTEN_EVENT_FULLSCREENCHANGE":19,"EIO":5,"EMSCRIPTEN_RESULT_NOT_SUPPORTED":-1,"EM_PROXIED_CONFSTR":68,"_SC_SIGQUEUE_MAX":34,"EWOULDBLOCK":11,"AUDIO_U16SYS":16,"EMSCRIPTEN_EVENT_FOCUSOUT":15,"EAI_OVERFLOW":-12,"SDL_AUDIO_MASK_DATATYPE":256,"MAP_PRIVATE":2,"_SC_TZNAME_MAX":6,"_CS_PATH":0,"SEEK_SET":0,"EBADE":52,"EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED":-2,"INT_MAX":2147483647,"EMSCRIPTEN_EVENT_KEYDOWN":2,"EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH":1,"_SC_MESSAGE_PASSING":20,"_SC_THREAD_SAFE_FUNCTIONS":68,"_SC_SYMLOOP_MAX":173,"_PC_NAME_MAX":3,"O_EXCL":128,"_SC_TRACE_USER_EVENT_MAX":245,"_PC_REC_XFER_ALIGN":17,"EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT":0,"_SC_RAW_SOCKETS":236,"_SC_2_UPE":97,"EMSCRIPTEN_RESULT_NO_DATA":-7,"EMSCRIPTEN_EVENT_BLUR":12,"_SC_TTY_NAME_MAX":72,"_SC_RTSIG_MAX":31,"ESOCKTNOSUPPORT":94,"_SC_PRIORITIZED_IO":13,"_SC_XOPEN_UNIX":91,"CODESET":14,"EPIPE":32,"_PC_REC_INCR_XFER_SIZE":14,"F_SETLK":13,"_PC_FILESIZEBITS":13,"_SC_XBS5_ILP32_OFF32":125,"RAND_MAX":2147483647,"EM_PROXIED_SYSCALL":138,"ENOLCK":37,"EM_PROXIED_PUTENV":115,"AUDIO_U16":16,"EMSCRIPTEN_EVENT_MOUSELEAVE":34,"EMSCRIPTEN_EVENT_MOUSEOUT":36,"_PC_SYNC_IO":9,"EEXIST":17,"FP_NORMAL":4,"O_RDONLY":0,"_SC_SEM_NSEMS_MAX":32,"_SC_IOV_MAX":60,"EPROTO":71,"_SC_TRACE":181,"ESRMNT":69,"EM_HTML5_LONG_STRING_LEN_BYTES":128,"_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS":1121,"INADDR_LOOPBACK":2130706433,"EXDEV":18,"EM_THREAD_STATUS_RUNNING":1,"EMSCRIPTEN_EVENT_BEFOREUNLOAD":28,"EM_THREAD_STATUS_WAITFUTEX":3,"EMSCRIPTEN_RESULT_INVALID_TARGET":-3,"_SC_THREAD_SPORADIC_SERVER":161,"F_SETFL":4,"AI_PASSIVE":1,"ELIBBAD":80,"_SC_LINE_MAX":43,"D_T_FMT":131112,"ERANGE":34,"ESTALE":116,"F_DUPFD":0,"AUDIO_F32":33056,"CLOCK_MONOTONIC":1,"EMSCRIPTEN_EVENT_GAMEPADCONNECTED":26,"F_GETOWN_EX":16,"_SC_ASYNCHRONOUS_IO":12,"ENOTRECOVERABLE":131,"ENOBUFS":105,"EIDRM":43,"EMSCRIPTEN_EVENT_ORIENTATIONCHANGE":18,"CRNCYSTR":262159,"EINTR":4,"EADV":68,"ENOSYS":38,"_CS_POSIX_V6_ILP32_OFFBIG_LIBS":1122,"EM_PROXIED_UTIME":12,"F_GETFL":3,"S_IXUGO":73,"_SC_2_FORT_DEV":49,"SDL_COMPILEDVERSION":1300,"EBADMSG":74,"EUSERS":87,"CLOCK_REALTIME":0,"ENODEV":19,"AF_INET6":10,"_SC_ATEXIT_MAX":87,"_SC_SAVED_IDS":8,"SOL_SOCKET":1,"S_IFLNK":40960,"AUDIO_S16LSB":32784,"POLLNVAL":32,"EMSCRIPTEN_EVENT_TOUCHCANCEL":25,"EMSCRIPTEN_RESULT_INVALID_PARAM":-5,"EMSCRIPTEN_EVENT_MOUSEDOWN":5,"EM_THREAD_STATUS_SLEEPING":2,"_SC_JOB_CONTROL":7,"NI_NAMEREQD":8,"EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT":2,"EMSCRIPTEN_EVENT_MOUSEMOVE":8,"UUID_TYPE_DCE_RANDOM":4,"ENOTCONN":107,"_SC_ADVISORY_INFO":132,"ENETUNREACH":101,"_SC_XOPEN_REALTIME_THREADS":131,"_SC_2_LOCALEDEF":52,"_PC_SYMLINK_MAX":19,"X_OK":1,"EMSCRIPTEN_EVENT_KEYUP":3,"AI_CANONNAME":2,"UUID_VARIANT_NCS":0,"ESPIPE":29,"AUDIO_S32MSB":36896,"EMSCRIPTEN_EVENT_WHEEL":9,"SDL_AUDIO_ALLOW_CHANNELS_CHANGE":4,"_SC_XOPEN_REALTIME":130,"EAI_NONAME":-2,"_PC_PIPE_BUF":5,"EROFS":30,"EM_PROXIED_ATEXIT":110,"ECONNREFUSED":111,"_SC_2_PBS_ACCOUNTING":169,"EMSCRIPTEN_EVENT_FOCUS":13,"AUDIO_S16SYS":32784,"ENETDOWN":100,"ENOEXEC":8,"ENOSPC":28,"EBADF":9,"EAI_SOCKTYPE":-7,"EDOTDOT":73,"_SC_THREAD_ATTR_STACKSIZE":78,"EBADFD":77,"O_ACCMODE":2097155,"EBADR":53,"_SC_2_VERSION":46,"S_IFCHR":8192,"SDL_PATCHLEVEL":0,"ABMON_12":131097,"PTHREAD_KEYS_MAX":128,"ENOMEDIUM":123,"EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE":0,"AUDIO_U16MSB":4112,"EMSCRIPTEN_FULLSCREEN_FILTERING_BILINEAR":2,"_SC_2_CHAR_TERM":95,"EMSCRIPTEN_EVENT_TOUCHEND":23,"_SC_AIO_LISTIO_MAX":23,"_SC_BC_SCALE_MAX":38,"ENOTBLK":15,"EAI_BADFLAGS":-1,"EOVERFLOW":75,"EMSCRIPTEN_EVENT_DBLCLICK":7,"SDL_AUDIO_MASK_SIGNED":32768,"EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST":1,"ABMON_11":131096,"ABMON_10":131095,"AT_FDCWD":-100,"EM_HTML5_SHORT_STRING_LEN_BYTES":32}}
\ No newline at end of file
+{"structs":{"utsname":{"sysname":0,"nodename":65,"domainname":325,"machine":260,"version":195,"release":130,"__size__":390},"sockaddr":{"sa_data":2,"sa_family":0,"__size__":16},"addrinfo":{"ai_flags":0,"ai_next":28,"ai_canonname":24,"ai_socktype":8,"ai_addr":20,"ai_protocol":12,"ai_family":4,"ai_addrlen":16,"__size__":32},"timespec":{"tv_sec":0,"tv_nsec":4,"__size__":8},"utimbuf":{"modtime":4,"actime":0,"__size__":8},"EmscriptenVisibilityChangeEvent":{"hidden":0,"visibilityState":4,"__size__":8},"SDL_MouseButtonEvent":{"timestamp":4,"button":16,"state":17,"windowID":8,"which":12,"y":24,"x":20,"padding2":19,"type":0,"padding1":18,"__size__":28},"sockaddr_in":{"sin_port":2,"sin_addr":{"s_addr":4,"__size__":4},"sin_family":0,"sin_zero":8,"__size__":16},"pthread":{"tsd":116,"attr":120,"canceldisable":72,"locale":188,"threadStatus":0,"tsd_used":60,"pid":56,"stack":92,"cancelasync":76,"tid":52,"threadExitCode":4,"detached":80,"profilerBlock":20,"self":24,"stack_size":96,"__size__":244},"WebVRFieldOfView":{"leftDegrees":24,"upDegrees":0,"downDegrees":16,"rightDegrees":8,"__size__":32},"SDL_KeyboardEvent":{"repeat":9,"keysym":12,"state":8,"windowID":4,"__size__":28,"type":0,"padding3":11,"padding2":10},"SDL_MouseMotionEvent":{"yrel":32,"timestamp":4,"state":16,"windowID":8,"which":12,"xrel":28,"y":24,"x":20,"type":0,"__size__":36},"SDL_Rect":{"y":4,"x":0,"h":12,"w":8,"__size__":16},"itimerspec":{"it_interval":{"tv_sec":0,"tv_nsec":4,"__size__":8},"it_value":{"tv_sec":8,"tv_nsec":12,"__size__":8},"__size__":16},"iovec":{"iov_len":4,"iov_base":0,"__size__":8},"timezone":{"tz_dsttime":4,"tz_minuteswest":0,"__size__":8},"flock":{"l_whence":2,"l_type":0,"l_start":4,"__size__":16,"l_len":8,"l_pid":12},"EmscriptenOrientationChangeEvent":{"orientationIndex":0,"orientationAngle":4,"__size__":8},"EmscriptenMouseEvent":{"clientX":16,"clientY":20,"targetX":52,"buttons":42,"timestamp":0,"button":40,"targetY":56,"altKey":32,"canvasY":64,"metaKey":36,"movementX":44,"movementY":48,"shiftKey":28,"ctrlKey":24,"screenY":12,"screenX":8,"canvasX":60,"__size__":72},"SDL_ResizeEvent":{"h":8,"type":0,"w":4,"__size__":12},"tms":{"tms_stime":4,"tms_utime":0,"tms_cstime":12,"tms_cutime":8,"__size__":16},"SDL_Color":{"unused":3,"r":0,"b":2,"g":1,"__size__":4},"EmscriptenKeyboardEvent":{"code":32,"charValue":120,"locale":88,"shiftKey":72,"altKey":76,"which":160,"metaKey":80,"location":64,"key":0,"ctrlKey":68,"charCode":152,"keyCode":156,"repeat":84,"__size__":164},"rusage":{"ru_msgrcv":56,"ru_utime":{"tv_sec":0,"tv_usec":4,"__size__":8},"ru_isrss":28,"ru_stime":{"tv_sec":8,"tv_usec":12,"__size__":8},"ru_nsignals":60,"ru_nivcsw":68,"ru_msgsnd":52,"ru_nswap":40,"ru_minflt":32,"ru_nvcsw":64,"ru_ixrss":20,"ru_inblock":44,"ru_idrss":24,"ru_maxrss":16,"ru_oublock":48,"ru_majflt":36,"__size__":136},"div_t":{"quot":0,"rem":4,"__size__":8},"timeval":{"tv_sec":0,"tv_usec":4,"__size__":8},"rlimit":{"rlim_cur":0,"rlim_max":8,"__size__":16},"in6_addr":{"__in6_union":{"__s6_addr16":0,"__s6_addr":0,"__s6_addr32":0,"__size__":16},"__size__":16},"tm":{"tm_sec":0,"tm_hour":8,"tm_mday":12,"tm_isdst":32,"tm_year":20,"tm_zone":40,"tm_mon":16,"tm_yday":28,"tm_gmtoff":36,"tm_wday":24,"tm_min":4,"__size__":44},"EmscriptenWebGLContextAttributes":{"majorVersion":32,"stencil":8,"preserveDrawingBuffer":20,"failIfMajorPerformanceCaveat":28,"explicitSwapControl":44,"antialias":12,"depth":4,"minorVersion":36,"premultipliedAlpha":16,"enableExtensionsByDefault":40,"alpha":0,"preferLowPowerToHighPerformance":24,"__size__":48},"EmscriptenBatteryEvent":{"dischargingTime":8,"level":16,"charging":24,"chargingTime":0,"__size__":32},"protoent":{"p_aliases":4,"p_proto":8,"p_name":0,"__size__":12},"SDL_Surface":{"userdata":24,"locked":28,"clip_rect":36,"format":4,"h":12,"refcount":56,"map":52,"flags":0,"w":8,"pitch":16,"lock_data":32,"pixels":20,"__size__":60},"EmscriptenTouchEvent":{"touches":20,"shiftKey":8,"altKey":12,"metaKey":16,"ctrlKey":4,"__size__":1684,"numTouches":0},"EmscriptenFocusEvent":{"id":128,"nodeName":0,"__size__":256},"sockaddr_in6":{"sin6_family":0,"sin6_flowinfo":4,"sin6_scope_id":24,"sin6_addr":{"__in6_union":{"__s6_addr16":8,"__s6_addr":8,"__s6_addr32":8,"__size__":16},"__size__":16},"__size__":28,"sin6_port":2},"SDL_JoyAxisEvent":{"__size__":12,"type":0,"value":8,"which":4,"padding2":7,"padding1":6,"axis":5},"netent":{"n_name":0,"n_net":12,"n_addrtype":8,"n_aliases":4,"__size__":16},"SDL_PixelFormat":{"palette":4,"Gloss":29,"Bmask":20,"Bloss":30,"Rloss":28,"format":0,"Gshift":33,"Aloss":31,"BitsPerPixel":8,"refcount":36,"next":40,"padding":10,"Rmask":12,"Bshift":34,"Gmask":16,"BytesPerPixel":9,"Amask":24,"Rshift":32,"Ashift":35,"__size__":44},"SDL_JoyButtonEvent":{"type":0,"button":5,"state":6,"which":4,"padding1":7,"__size__":8},"EmscriptenPointerlockChangeEvent":{"id":132,"nodeName":4,"isActive":0,"__size__":260},"in_addr":{"s_addr":0,"__size__":4},"EmscriptenDeviceOrientationEvent":{"timestamp":0,"beta":16,"alpha":8,"__size__":40,"gamma":24,"absolute":32},"libc":{"global_locale":40,"__size__":64},"SDL_WindowEvent":{"data2":16,"type":0,"data1":12,"windowID":4,"__size__":20,"padding1":9,"event":8,"padding3":11,"padding2":10},"SDL_Keysym":{"scancode":0,"mod":8,"unicode":12,"sym":4,"__size__":16},"cmsghdr":{"cmsg_type":8,"cmsg_level":4,"cmsg_len":0,"__size__":12},"EmscriptenUiEvent":{"windowInnerWidth":12,"detail":0,"scrollLeft":32,"documentBodyClientHeight":8,"windowInnerHeight":16,"scrollTop":28,"windowOuterHeight":24,"windowOuterWidth":20,"documentBodyClientWidth":4,"__size__":36},"thread_profiler_block":{"threadStatus":0,"timeSpentInStatus":16,"currentStatusStartTime":8,"name":72,"__size__":104},"stat":{"st_rdev":28,"st_mtim":{"tv_sec":56,"tv_nsec":60,"__size__":8},"st_blocks":44,"st_atim":{"tv_sec":48,"tv_nsec":52,"__size__":8},"st_nlink":16,"__st_ino_truncated":8,"st_ctim":{"tv_sec":64,"tv_nsec":68,"__size__":8},"st_mode":12,"st_blksize":40,"__st_dev_padding":4,"st_dev":0,"st_size":36,"st_gid":24,"__st_rdev_padding":32,"st_uid":20,"st_ino":72,"__size__":76},"pollfd":{"fd":0,"events":4,"revents":6,"__size__":8},"WebVRPositionState":{"linearVelocity":{"y":56,"x":48,"z":64,"w":72,"__size__":32},"orientation":{"y":128,"x":120,"z":136,"w":144,"__size__":32},"timeStamp":0,"angularVelocity":{"y":160,"x":152,"z":168,"w":176,"__size__":32},"hasPosition":8,"angularAcceleration":{"y":192,"x":184,"z":200,"w":208,"__size__":32},"linearAcceleration":{"y":88,"x":80,"z":96,"w":104,"__size__":32},"hasOrientation":112,"position":{"y":24,"x":16,"z":32,"w":40,"__size__":32},"__size__":216},"dirent":{"d_name":11,"d_off":4,"d_ino":0,"d_reclen":8,"d_type":10,"__size__":268},"EmscriptenTouchPoint":{"clientX":12,"clientY":16,"identifier":0,"targetX":36,"targetY":40,"isChanged":28,"canvasY":48,"canvasX":44,"pageX":20,"pageY":24,"screenY":8,"screenX":4,"onTarget":32,"__size__":52},"EmscriptenDeviceMotionEvent":{"timestamp":0,"accelerationIncludingGravityZ":48,"accelerationIncludingGravityX":32,"accelerationIncludingGravityY":40,"accelerationY":16,"accelerationX":8,"rotationRateBeta":64,"accelerationZ":24,"rotationRateGamma":72,"rotationRateAlpha":56,"__size__":80},"SDL_AudioSpec":{"padding":10,"userdata":20,"format":4,"channels":6,"callback":16,"samples":8,"freq":0,"size":12,"silence":7,"__size__":24},"hostent":{"h_addrtype":8,"h_addr_list":16,"h_name":0,"__size__":20,"h_aliases":4,"h_length":12},"SDL_MouseWheelEvent":{"timestamp":4,"windowID":8,"which":12,"y":20,"x":16,"type":0,"__size__":24},"linger":{"l_onoff":0,"l_linger":4,"__size__":8},"SDL_version":{"major":0,"patch":2,"minor":1,"__size__":3},"statvfs":{"f_bsize":0,"f_bavail":16,"f_fsid":32,"f_favail":28,"f_files":20,"f_frsize":4,"f_blocks":8,"f_ffree":24,"f_bfree":12,"f_flag":40,"f_namemax":44,"__size__":72},"EmscriptenFullscreenChangeEvent":{"elementWidth":264,"screenWidth":272,"nodeName":8,"elementHeight":268,"fullscreenEnabled":4,"screenHeight":276,"isFullscreen":0,"id":136,"__size__":280},"EmscriptenWheelEvent":{"deltaX":72,"deltaY":80,"deltaZ":88,"deltaMode":96,"mouse":0,"__size__":104},"WebVRIntRect":{"y":4,"x":0,"height":12,"width":8,"__size__":16},"SDL_TouchFingerEvent":{"timestamp":4,"dy":36,"touchId":8,"pressure":40,"dx":32,"type":0,"y":28,"x":24,"fingerId":16,"__size__":48},"SDL_AudioCVT":{"len_ratio":32,"len_cvt":24,"rate_incr":8,"filters":40,"len":20,"needed":0,"filter_index":80,"src_format":4,"len_mult":28,"__size__":88,"buf":16,"dst_format":6},"WebVRPoint":{"y":8,"x":0,"z":16,"w":24,"__size__":32},"timeb":{"dstflag":8,"timezone":6,"time":0,"millitm":4,"__size__":12},"statfs":{"f_bsize":4,"f_bavail":16,"f_fsid":28,"f_files":20,"f_frsize":40,"f_namelen":36,"f_blocks":8,"f_ffree":24,"f_bfree":12,"f_flags":44,"__size__":64},"msghdr":{"msg_iov":8,"msg_iovlen":12,"msg_namelen":4,"msg_controllen":20,"msg_flags":24,"msg_name":0,"msg_control":16,"__size__":28},"WebVREyeParameters":{"currentFieldOfView":{"leftDegrees":152,"upDegrees":128,"downDegrees":144,"rightDegrees":136,"__size__":32},"recommendedFieldOfView":{"leftDegrees":88,"upDegrees":64,"downDegrees":80,"rightDegrees":72,"__size__":32},"eyeTranslation":{"y":104,"x":96,"z":112,"w":120,"__size__":32},"renderRect":{"y":164,"x":160,"height":172,"width":168,"__size__":16},"minimumFieldOfView":{"leftDegrees":24,"upDegrees":0,"downDegrees":16,"rightDegrees":8,"__size__":32},"maximumFieldOfView":{"leftDegrees":56,"upDegrees":32,"downDegrees":48,"rightDegrees":40,"__size__":32},"__size__":176},"SDL_Palette":{"ncolors":0,"colors":4,"version":8,"refcount":12,"__size__":16},"EmscriptenFullscreenStrategy":{"canvasResizedCallbackUserData":16,"canvasResolutionScaleMode":4,"scaleMode":0,"canvasResizedCallback":12,"filteringMode":8,"__size__":20},"EmscriptenGamepadEvent":{"index":1300,"analogButton":528,"timestamp":0,"numButtons":12,"mapping":1368,"digitalButton":1040,"connected":1296,"numAxes":8,"__size__":1432,"id":1304,"axis":16},"SDL_TextInputEvent":{"text":8,"windowID":4,"type":0,"__size__":40}},"defines":{"ETXTBSY":26,"EOF":-1,"EMSCRIPTEN_EVENT_MOUSEOVER":35,"ETOOMANYREFS":109,"ENAMETOOLONG":36,"ENOPKG":65,"UUID_TYPE_DCE_TIME":1,"_SC_XOPEN_LEGACY":129,"_SC_XOPEN_VERSION":89,"F_UNLCK":2,"_SC_BC_DIM_MAX":37,"EL3HLT":46,"S_IFDIR":16384,"EMSCRIPTEN_EVENT_KEYPRESS":1,"EINPROGRESS":115,"_SC_BARRIERS":133,"EMSCRIPTEN_EVENT_TOUCHMOVE":24,"SDL_AUDIO_ALLOW_FREQUENCY_CHANGE":1,"AUDIO_U8":8,"EAI_AGAIN":-3,"_PC_MAX_CANON":1,"ENOTSUP":95,"EFBIG":27,"O_CREAT":64,"EMSCRIPTEN_EVENT_POINTERLOCKERROR":38,"_SC_2_PBS_LOCATE":170,"EM_PROXIED_SETENV":113,"_CS_POSIX_V6_LP64_OFF64_LIBS":1126,"ENOLINK":67,"ABDAY_7":131078,"ABDAY_6":131077,"ABDAY_5":131076,"ABDAY_4":131075,"ABDAY_3":131074,"ABDAY_2":131073,"ABDAY_1":131072,"EL3RST":47,"YESEXPR":327680,"_SC_V6_ILP32_OFFBIG":177,"SDL_MINOR_VERSION":3,"EM_PROXIED_CLEARENV":112,"_SC_MEMLOCK":17,"ENOTUNIQ":76,"EMSCRIPTEN_RESULT_FAILED":-6,"ABMON_1":131086,"ELNRNG":48,"UUID_VARIANT_MICROSOFT":2,"EMSCRIPTEN_EVENT_TOUCHSTART":22,"ENOANO":55,"EMSCRIPTEN_EVENT_FOCUSIN":14,"EMSCRIPTEN_EVENT_MOUSEUP":6,"ENOPROTOOPT":92,"POLLIN":1,"S_IALLUGO":4095,"_SC_THREAD_KEYS_MAX":74,"EM_THREAD_STATUS_WAITPROXY":5,"O_RDWR":2,"EREMCHG":78,"EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED":27,"_SC_2_PBS":168,"_SC_TRACE_INHERIT":183,"_SC_REGEXP":155,"_CS_POSIX_V6_LP64_OFF64_CFLAGS":1124,"_SC_DELAYTIMER_MAX":26,"S_IWUGO":146,"S_IFREG":32768,"F_GETLK64":12,"O_DIRECTORY":65536,"EM_PROXIED_UTIMES":13,"POLLHUP":16,"S_IFMT":61440,"F_SETLK64":13,"_SC_XOPEN_CRYPT":92,"_SC_CLOCK_SELECTION":137,"_PC_CHOWN_RESTRICTED":6,"E2BIG":7,"ABMON_3":131088,"AM_STR":131110,"SDL_AUDIO_MASK_ENDIAN":4096,"ALT_DIGITS":131119,"EHOSTDOWN":112,"EBFONT":59,"ENOTEMPTY":39,"AUDIO_S16":32784,"TIOCGPGRP":21519,"EBUSY":16,"_SC_MQ_PRIO_MAX":28,"_SC_PAGE_SIZE":30,"EADDRINUSE":98,"ENOTSOCK":88,"PM_STR":131111,"O_WRONLY":1,"_SC_STREAM_MAX":5,"ABMON_9":131094,"ELIBACC":79,"S_IFIFO":4096,"EDQUOT":122,"EAI_SYSTEM":-11,"ENOENT":2,"_SC_TIMERS":11,"O_SYNC":1052672,"SEEK_END":2,"EM_THREAD_STATUS_FINISHED":6,"_PC_REC_MIN_XFER_SIZE":16,"_PC_PATH_MAX":4,"_SC_SPORADIC_SERVER":160,"ECOMM":70,"_SC_NPROCESSORS_ONLN":84,"_CS_POSIX_V6_LPBIG_OFFBIG_LIBS":1130,"_PC_MAX_INPUT":2,"_SC_VERSION":29,"TCGETS":21505,"_SC_CLK_TCK":2,"ABMON_2":131087,"EXFULL":54,"ABMON_7":131092,"ABMON_6":131091,"ABMON_5":131090,"ABMON_4":131089,"ENOTDIR":20,"ABMON_8":131093,"_SC_AIO_MAX":24,"ERA":131116,"EM_PROXIED_UNSETENV":114,"_SC_THREAD_PRIO_INHERIT":80,"_PC_2_SYMLINKS":20,"_SC_XBS5_LP64_OFF64":127,"EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE":30,"ENETRESET":102,"EAFNOSUPPORT":97,"MON_2":131099,"MON_3":131100,"MON_1":131098,"EMSCRIPTEN_EVENT_DEVICEORIENTATION":16,"MON_7":131104,"MON_4":131101,"MON_5":131102,"_SC_SPAWN":159,"MON_8":131105,"MON_9":131106,"_CS_POSIX_V6_ILP32_OFF32_LDFLAGS":1117,"S_IFSOCK":49152,"S_IRUGO":292,"SOCK_DGRAM":2,"POLLERR":8,"EINVAL":22,"_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS":1128,"POLLRDNORM":64,"AUDIO_F32SYS":33056,"_SC_TRACE_SYS_MAX":244,"AI_V4MAPPED":8,"AI_NUMERICHOST":4,"_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS":1,"EHOSTUNREACH":113,"ENOCSI":50,"EPROTONOSUPPORT":93,"_SC_AIO_PRIO_DELTA_MAX":25,"_SC_MONOTONIC_CLOCK":149,"ETIME":62,"ENOTTY":25,"_SC_XOPEN_ENH_I18N":93,"EAI_SERVICE":-8,"EAGAIN":11,"F_SETLKW64":14,"EMSGSIZE":90,"ELIBEXEC":83,"_SC_MEMORY_PROTECTION":19,"EMSCRIPTEN_FULLSCREEN_SCALE_CENTER":3,"SDL_AUDIO_ALLOW_FORMAT_CHANGE":2,"ECANCELED":125,"_SC_SPIN_LOCKS":154,"_SC_XOPEN_SHM":94,"_PC_LINK_MAX":0,"TIOCSPGRP":21520,"EOPNOTSUPP":95,"EMSCRIPTEN_EVENT_MOUSEENTER":33,"EAI_FAIL":-4,"NOEXPR":327681,"_SC_FSYNC":15,"_SC_GETGR_R_SIZE_MAX":69,"EDESTADDRREQ":89,"EADDRNOTAVAIL":99,"AUDIO_S32SYS":32800,"_SC_TRACE_NAME_MAX":243,"_SC_BC_BASE_MAX":36,"EMSCRIPTEN_EVENT_CANVASRESIZED":37,"EPERM":1,"EAI_FAMILY":-6,"O_NOFOLLOW":131072,"SOCK_STREAM":1,"O_APPEND":1024,"_SC_XOPEN_STREAMS":246,"_SC_GETPW_R_SIZE_MAX":70,"MON_6":131103,"EPROTOTYPE":91,"_SC_CPUTIME":138,"EISCONN":106,"_SC_XBS5_ILP32_OFFBIG":126,"S_IFBLK":24576,"T_FMT_AMPM":131115,"SDL_PIXELFORMAT_RGBA8888":-2042224636,"F_SETLKW":14,"SDL_TOUCH_MOUSEID":-1,"EMSCRIPTEN_EVENT_SCROLL":11,"ELOOP":40,"_SC_OPEN_MAX":4,"_SC_2_FORT_RUN":50,"EMSCRIPTEN_EVENT_VISIBILITYCHANGE":21,"EREMOTE":66,"_SC_RE_DUP_MAX":44,"_SC_THREAD_PRIO_PROTECT":81,"_SC_2_PBS_CHECKPOINT":175,"_SC_2_PBS_TRACK":172,"MON_10":131107,"MON_11":131108,"MON_12":131109,"_SC_XBS5_LPBIG_OFFBIG":128,"_SC_THREAD_PROCESS_SHARED":82,"AF_INET":2,"_SC_SHARED_MEMORY_OBJECTS":22,"F_GETFD":1,"EMSCRIPTEN_EVENT_DEVICEMOTION":17,"SDL_MIX_MAXVOLUME":128,"_PC_ALLOC_SIZE_MIN":18,"TCSETS":21506,"ELIBMAX":82,"_SC_READER_WRITER_LOCKS":153,"EMULTIHOP":72,"IPPROTO_TCP":6,"_SC_PHYS_PAGES":85,"_SC_MEMLOCK_RANGE":18,"_SC_PRIORITY_SCHEDULING":10,"T_FMT":131114,"AI_ALL":16,"_PC_VDISABLE":8,"THOUSEP":65537,"_SC_TRACE_EVENT_FILTER":182,"ERA_T_FMT":131121,"_SC_THREAD_ATTR_STACKADDR":77,"_SC_THREAD_THREADS_MAX":76,"_SC_LOGIN_NAME_MAX":71,"_SC_2_C_BIND":47,"_PC_NO_TRUNC":7,"ECONNABORTED":103,"EMSCRIPTEN_RESULT_SUCCESS":0,"_SC_SHELL":157,"EFAULT":14,"O_LARGEFILE":32768,"_SC_V6_LP64_OFF64":178,"_CS_GNU_LIBC_VERSION":2,"ENODATA":61,"_SC_SEM_VALUE_MAX":33,"_SC_MQ_OPEN_MAX":27,"AI_ADDRCONFIG":32,"_SC_HOST_NAME_MAX":180,"_SC_THREAD_STACK_MIN":75,"_SC_TIMEOUTS":164,"POLLOUT":4,"_SC_IPV6":235,"_SC_CHILD_MAX":1,"EDOM":33,"_SC_2_PBS_MESSAGE":171,"EILSEQ":84,"UUID_VARIANT_DCE":1,"_SC_2_C_DEV":48,"_SC_TIMER_MAX":35,"FP_ZERO":2,"EPFNOSUPPORT":96,"ENONET":64,"ECHRNG":44,"_SC_THREADS":67,"_SC_REALTIME_SIGNALS":9,"CLOCKS_PER_SEC":1000000,"ERA_D_T_FMT":131120,"ESRCH":3,"D_FMT":131113,"POLLPRI":2,"_PC_ASYNC_IO":10,"DAY_2":131080,"DAY_3":131081,"DAY_1":131079,"DAY_6":131084,"DAY_7":131085,"DAY_4":131082,"DAY_5":131083,"_SC_SYNCHRONIZED_IO":14,"EL2HLT":51,"EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF":1,"IPPROTO_UDP":17,"_SC_MAPPED_FILES":16,"EL2NSYNC":45,"_SC_NGROUPS_MAX":3,"ENOMSG":42,"EISDIR":21,"_SC_SEMAPHORES":21,"AI_NUMERICSERV":1024,"EDEADLOCK":35,"EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST":31,"EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE":29,"AUDIO_F32LSB":33056,"_SC_COLL_WEIGHTS_MAX":40,"SO_ERROR":4,"ECONNRESET":104,"AT_SYMLINK_NOFOLLOW":256,"_SC_TRACE_LOG":184,"AUDIO_U16LSB":16,"ESTRPIPE":86,"ESHUTDOWN":108,"_PC_SOCK_MAXBUF":12,"_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS":1129,"EDEADLK":35,"_CS_POSIX_V6_ILP32_OFF32_CFLAGS":1116,"EBADRQC":56,"_SC_THREAD_DESTRUCTOR_ITERATIONS":73,"_SC_TYPED_MEMORY_OBJECTS":165,"_SC_TRACE_EVENT_NAME_MAX":242,"_SC_BC_STRING_MAX":39,"_SC_2_SW_DEV":51,"FP_NAN":0,"F_SETOWN":8,"EMSCRIPTEN_EVENT_RESIZE":10,"_SC_ARG_MAX":0,"_SC_THREAD_PRIORITY_SCHEDULING":79,"F_GETLK":12,"EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF":2,"FIONREAD":21531,"_SC_THREAD_CPUTIME":139,"EMSCRIPTEN_EVENT_POINTERLOCKCHANGE":20,"EM_THREAD_STATUS_NOTSTARTED":0,"_CS_POSIX_V6_ILP32_OFF32_LIBS":1118,"EUNATCH":49,"AUDIO_S8":32776,"AUDIO_S32LSB":32800,"SDL_AUDIO_MASK_BITSIZE":255,"ERA_D_FMT":131118,"AUDIO_F32MSB":37152,"_CS_POSIX_V6_LP64_OFF64_LDFLAGS":1125,"FP_INFINITE":1,"ECHILD":10,"EAI_MEMORY":-10,"EM_PROXIED_FPATHCONF":46,"O_TRUNC":512,"ETIMEDOUT":110,"S_IRWXO":7,"EALREADY":114,"ENXIO":6,"NI_NUMERICHOST":1,"EMFILE":24,"F_GETOWN":9,"EMLINK":31,"F_SETFD":2,"ENFILE":23,"EM_PROXIED_SYSCONF":72,"EM_PROXIED_GETENV":111,"SDL_MAJOR_VERSION":1,"ENOMEM":12,"ENOSR":63,"SDL_AUDIO_ALLOW_ANY_CHANGE":7,"EOWNERDEAD":130,"_PC_PRIO_IO":11,"ELIBSCN":81,"_SC_V6_LPBIG_OFFBIG":179,"EM_PROXIED_CHROOT":37,"EMSCRIPTEN_EVENT_CLICK":4,"_SC_EXPR_NEST_MAX":42,"_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS":1120,"EBADSLT":57,"AUDIO_S16MSB":36880,"S_ISVTX":512,"EMSCRIPTEN_RESULT_DEFERRED":1,"EMSCRIPTEN_RESULT_UNKNOWN_TARGET":-4,"S_IRWXUGO":511,"EM_PROXIED_TZSET":119,"_CS_GNU_LIBPTHREAD_VERSION":3,"_PC_REC_MAX_XFER_SIZE":15,"UUID_VARIANT_OTHER":3,"EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED":32,"EM_PROXIED_PTHREAD_CREATE":137,"EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT":0,"RADIXCHAR":65536,"AF_UNSPEC":0,"ENOSTR":60,"W_OK":2,"AUDIO_S32":32800,"EACCES":13,"R_OK":4,"EM_HTML5_MEDIUM_STRING_LEN_BYTES":64,"_SC_V6_ILP32_OFF32":176,"EMSCRIPTEN_EVENT_FULLSCREENCHANGE":19,"EIO":5,"EMSCRIPTEN_RESULT_NOT_SUPPORTED":-1,"EM_PROXIED_CONFSTR":68,"_SC_SIGQUEUE_MAX":34,"EWOULDBLOCK":11,"AUDIO_U16SYS":16,"EMSCRIPTEN_EVENT_FOCUSOUT":15,"EAI_OVERFLOW":-12,"SDL_AUDIO_MASK_DATATYPE":256,"MAP_PRIVATE":2,"_SC_TZNAME_MAX":6,"_CS_PATH":0,"SEEK_SET":0,"EBADE":52,"EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED":-2,"INT_MAX":2147483647,"EMSCRIPTEN_EVENT_KEYDOWN":2,"EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH":1,"_SC_MESSAGE_PASSING":20,"_SC_THREAD_SAFE_FUNCTIONS":68,"_SC_SYMLOOP_MAX":173,"_PC_NAME_MAX":3,"O_EXCL":128,"_SC_TRACE_USER_EVENT_MAX":245,"_PC_REC_XFER_ALIGN":17,"EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT":0,"_SC_RAW_SOCKETS":236,"_SC_2_UPE":97,"EMSCRIPTEN_RESULT_NO_DATA":-7,"EMSCRIPTEN_EVENT_BLUR":12,"_SC_TTY_NAME_MAX":72,"_SC_RTSIG_MAX":31,"ESOCKTNOSUPPORT":94,"_SC_PRIORITIZED_IO":13,"_SC_XOPEN_UNIX":91,"CODESET":14,"EPIPE":32,"_PC_REC_INCR_XFER_SIZE":14,"F_SETLK":13,"_PC_FILESIZEBITS":13,"_SC_XBS5_ILP32_OFF32":125,"RAND_MAX":2147483647,"EM_PROXIED_SYSCALL":138,"ENOLCK":37,"EM_PROXIED_PUTENV":115,"AUDIO_U16":16,"EMSCRIPTEN_EVENT_MOUSELEAVE":34,"EMSCRIPTEN_EVENT_MOUSEOUT":36,"_PC_SYNC_IO":9,"EEXIST":17,"FP_NORMAL":4,"O_RDONLY":0,"_SC_SEM_NSEMS_MAX":32,"_SC_IOV_MAX":60,"EPROTO":71,"_SC_TRACE":181,"ESRMNT":69,"EM_HTML5_LONG_STRING_LEN_BYTES":128,"_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS":1121,"INADDR_LOOPBACK":2130706433,"EXDEV":18,"TIOCGWINSZ":21523,"EM_THREAD_STATUS_RUNNING":1,"EMSCRIPTEN_EVENT_BEFOREUNLOAD":28,"EM_THREAD_STATUS_WAITFUTEX":3,"EMSCRIPTEN_RESULT_INVALID_TARGET":-3,"_SC_THREAD_SPORADIC_SERVER":161,"F_SETFL":4,"AI_PASSIVE":1,"ELIBBAD":80,"_SC_LINE_MAX":43,"D_T_FMT":131112,"ERANGE":34,"ESTALE":116,"F_DUPFD":0,"AUDIO_F32":33056,"CLOCK_MONOTONIC":1,"EMSCRIPTEN_EVENT_GAMEPADCONNECTED":26,"F_GETOWN_EX":16,"_SC_ASYNCHRONOUS_IO":12,"ENOTRECOVERABLE":131,"ENOBUFS":105,"EIDRM":43,"EMSCRIPTEN_EVENT_ORIENTATIONCHANGE":18,"CRNCYSTR":262159,"EINTR":4,"EADV":68,"ENOSYS":38,"_CS_POSIX_V6_ILP32_OFFBIG_LIBS":1122,"EM_PROXIED_UTIME":12,"F_GETFL":3,"S_IXUGO":73,"_SC_2_FORT_DEV":49,"SDL_COMPILEDVERSION":1300,"EBADMSG":74,"EUSERS":87,"CLOCK_REALTIME":0,"ENODEV":19,"AF_INET6":10,"_SC_ATEXIT_MAX":87,"_SC_SAVED_IDS":8,"SOL_SOCKET":1,"S_IFLNK":40960,"AUDIO_S16LSB":32784,"POLLNVAL":32,"EMSCRIPTEN_EVENT_TOUCHCANCEL":25,"EMSCRIPTEN_RESULT_INVALID_PARAM":-5,"EMSCRIPTEN_EVENT_MOUSEDOWN":5,"EM_THREAD_STATUS_SLEEPING":2,"_SC_JOB_CONTROL":7,"NI_NAMEREQD":8,"EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT":2,"EMSCRIPTEN_EVENT_MOUSEMOVE":8,"UUID_TYPE_DCE_RANDOM":4,"ENOTCONN":107,"_SC_ADVISORY_INFO":132,"ENETUNREACH":101,"_SC_XOPEN_REALTIME_THREADS":131,"_SC_2_LOCALEDEF":52,"_PC_SYMLINK_MAX":19,"X_OK":1,"EMSCRIPTEN_EVENT_KEYUP":3,"AI_CANONNAME":2,"UUID_VARIANT_NCS":0,"ESPIPE":29,"AUDIO_S32MSB":36896,"EMSCRIPTEN_EVENT_WHEEL":9,"SDL_AUDIO_ALLOW_CHANNELS_CHANGE":4,"_SC_XOPEN_REALTIME":130,"EAI_NONAME":-2,"_PC_PIPE_BUF":5,"EROFS":30,"EM_PROXIED_ATEXIT":110,"ECONNREFUSED":111,"_SC_2_PBS_ACCOUNTING":169,"EMSCRIPTEN_EVENT_FOCUS":13,"AUDIO_S16SYS":32784,"ENETDOWN":100,"ENOEXEC":8,"ENOSPC":28,"EBADF":9,"EAI_SOCKTYPE":-7,"EDOTDOT":73,"_SC_THREAD_ATTR_STACKSIZE":78,"EBADFD":77,"O_ACCMODE":2097155,"EBADR":53,"_SC_2_VERSION":46,"S_IFCHR":8192,"SDL_PATCHLEVEL":0,"ABMON_12":131097,"PTHREAD_KEYS_MAX":128,"ENOMEDIUM":123,"EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE":0,"AUDIO_U16MSB":4112,"EMSCRIPTEN_FULLSCREEN_FILTERING_BILINEAR":2,"_SC_2_CHAR_TERM":95,"EMSCRIPTEN_EVENT_TOUCHEND":23,"_SC_AIO_LISTIO_MAX":23,"_SC_BC_SCALE_MAX":38,"ENOTBLK":15,"EAI_BADFLAGS":-1,"EOVERFLOW":75,"EMSCRIPTEN_EVENT_DBLCLICK":7,"SDL_AUDIO_MASK_SIGNED":32768,"EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST":1,"ABMON_11":131096,"ABMON_10":131095,"AT_FDCWD":-100,"EM_HTML5_SHORT_STRING_LEN_BYTES":32}}
\ No newline at end of file
diff --git a/src/struct_info.json b/src/struct_info.json
index d4cbfc1..853e396 100644
--- a/src/struct_info.json
+++ b/src/struct_info.json
@@ -467,7 +467,8 @@
             "F_GETOWN_EX",
             "F_SETFD", 
             "O_EXCL", 
-            "F_GETFL"
+            "F_GETFL",
+            "O_LARGEFILE"
         ],
         "structs": {}
     }, 
@@ -491,7 +492,8 @@
             "TCGETS",
             "TCSETS",
             "TIOCGPGRP",
-            "TIOCSPGRP"
+            "TIOCSPGRP",
+            "TIOCGWINSZ"
         ],
         "structs": {}
     }, 
@@ -1485,7 +1487,17 @@
               "tid",
               "pid",
               "canceldisable",
-              "cancelasync"
+              "cancelasync",
+              "locale"
+            ]
+        },
+        "defines": []
+    },
+    {
+        "file": "../lib/libc/musl/src/internal/libc.h",
+        "structs": {
+            "libc": [
+              "global_locale"
             ]
         },
         "defines": []
diff --git a/system/include/emscripten/threading.h b/system/include/emscripten/threading.h
index a23b240..6d97db5 100644
--- a/system/include/emscripten/threading.h
+++ b/system/include/emscripten/threading.h
@@ -80,9 +80,9 @@
 uint32_t emscripten_atomic_xor_u32(void/*uint32_t*/ *addr, uint32_t val);
 uint64_t emscripten_atomic_xor_u64(void/*uint64_t*/ *addr, uint64_t val); // Emulated with locks, very slow!!
 
-int emscripten_futex_wait(void/*uint32_t*/ *addr, uint32_t val, double maxWaitMilliseconds);
-int emscripten_futex_wake(void/*uint32_t*/ *addr, int count);
-int emscripten_futex_wake_or_requeue(void/*uint32_t*/ *addr, int count, void/*uint32_t*/ *addr2, int cmpValue);
+int emscripten_futex_wait(volatile void/*uint32_t*/ *addr, uint32_t val, double maxWaitMilliseconds);
+int emscripten_futex_wake(volatile void/*uint32_t*/ *addr, int count);
+int emscripten_futex_wake_or_requeue(volatile void/*uint32_t*/ *addr, int count, volatile void/*uint32_t*/ *addr2, int cmpValue);
 
 typedef union em_variant_val
 {
diff --git a/system/include/libc/aio.h b/system/include/libc/aio.h
index d9330eb..19bc28a 100644
--- a/system/include/libc/aio.h
+++ b/system/include/libc/aio.h
@@ -21,7 +21,7 @@
 	struct sigevent aio_sigevent;
 	void *__td;
 	int __lock[2];
-	int __err;
+	volatile int __err;
 	ssize_t __ret;
 	off_t aio_offset;
 	void *__next, *__prev;
diff --git a/system/include/libc/alltypes.h.in b/system/include/libc/alltypes.h.in
index c4ca5d5..6a9c105 100644
--- a/system/include/libc/alltypes.h.in
+++ b/system/include/libc/alltypes.h.in
@@ -28,6 +28,7 @@
 TYPEDEF unsigned _Int64 fsblkcnt_t;
 TYPEDEF unsigned _Int64 fsfilcnt_t;
 
+TYPEDEF unsigned wint_t;
 TYPEDEF unsigned long wctype_t;
 
 TYPEDEF void * timer_t;
@@ -58,6 +59,8 @@
 
 TYPEDEF struct _IO_FILE FILE;
 
+TYPEDEF struct __mbstate_t { unsigned __opaque1, __opaque2; } mbstate_t;
+
 TYPEDEF struct __locale_struct * locale_t;
 
 TYPEDEF struct __sigset_t { unsigned long __bits[128/sizeof(long)]; } sigset_t;
diff --git a/system/include/libc/arpa/nameser.h b/system/include/libc/arpa/nameser.h
index b9ee665..581925a 100644
--- a/system/include/libc/arpa/nameser.h
+++ b/system/include/libc/arpa/nameser.h
@@ -1,6 +1,11 @@
 #ifndef _ARPA_NAMESER_H
 #define _ARPA_NAMESER_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
 #include <stdint.h>
 
 #define __NAMESER	19991006
@@ -48,6 +53,8 @@
 #define ns_msg_end(handle) ((handle)._eom + 0)
 #define ns_msg_size(handle) ((handle)._eom - (handle)._msg)
 #define ns_msg_count(handle, section) ((handle)._counts[section] + 0)
+#define ns_msg_getflag(handle, flag) \
+	(((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift)
 
 typedef	struct __ns_rr {
 	char		name[NS_MAXDNAME];
@@ -296,43 +303,20 @@
 #define NS_OPT_DNSSEC_OK        0x8000U
 #define NS_OPT_NSID		3
 
-#define NS_GET16(s, cp) do { \
-	register const unsigned char *t_cp = (const unsigned char *)(cp); \
-	(s) = ((uint16_t)t_cp[0] << 8) \
-	    | ((uint16_t)t_cp[1]) \
-	    ; \
-	(cp) += NS_INT16SZ; \
-} while (0)
+#define NS_GET16(s, cp) (void)((s) = ns_get16(((cp)+=2)-2))
+#define NS_GET32(l, cp) (void)((l) = ns_get32(((cp)+=4)-4))
+#define NS_PUT16(s, cp) ns_put16((s), ((cp)+=2)-2)
+#define NS_PUT32(l, cp) ns_put32((l), ((cp)+=4)-4)
 
-#define NS_GET32(l, cp) do { \
-	register const unsigned char *t_cp = (const unsigned char *)(cp); \
-	(l) = ((uint32_t)t_cp[0] << 24) \
-	    | ((uint32_t)t_cp[1] << 16) \
-	    | ((uint32_t)t_cp[2] << 8) \
-	    | ((uint32_t)t_cp[3]) \
-	    ; \
-	(cp) += NS_INT32SZ; \
-} while (0)
+unsigned ns_get16(const unsigned char *);
+unsigned long ns_get32(const unsigned char *);
+void ns_put16(unsigned, unsigned char *);
+void ns_put32(unsigned long, unsigned char *);
 
-#define NS_PUT16(s, cp) do { \
-	register uint16_t t_s = (uint16_t)(s); \
-	register unsigned char *t_cp = (unsigned char *)(cp); \
-	*t_cp++ = t_s >> 8; \
-	*t_cp   = t_s; \
-	(cp) += NS_INT16SZ; \
-} while (0)
-
-#define NS_PUT32(l, cp) do { \
-	register uint32_t t_l = (uint32_t)(l); \
-	register unsigned char *t_cp = (unsigned char *)(cp); \
-	*t_cp++ = t_l >> 24; \
-	*t_cp++ = t_l >> 16; \
-	*t_cp++ = t_l >> 8; \
-	*t_cp   = t_l; \
-	(cp) += NS_INT32SZ; \
-} while (0)
-
-
+int ns_initparse(const unsigned char *, int, ns_msg *);
+int ns_parserr(ns_msg *, ns_sect, int, ns_rr *);
+int ns_skiprr(const unsigned char *, const unsigned char *, ns_sect, int);
+int ns_name_uncompress(const unsigned char *, const unsigned char *, const unsigned char *, char *, size_t);
 
 
 #define	__BIND		19950621
@@ -464,4 +448,8 @@
 #define	PUTSHORT		NS_PUT16
 #define	PUTLONG			NS_PUT32
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/system/include/libc/assert.h b/system/include/libc/assert.h
index fae53fc..1ae0ea5 100644
--- a/system/include/libc/assert.h
+++ b/system/include/libc/assert.h
@@ -8,11 +8,15 @@
 #define assert(x) ((void)((x) || (__assert_fail(#x, __FILE__, __LINE__, __func__),0)))
 #endif
 
+#if __STDC_VERSION__ >= 201112L && !defined(__cplusplus)
+#define static_assert _Static_assert
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#if __EMSCRIPTEN__
+#ifdef __EMSCRIPTEN__
 _Noreturn
 #endif
 void __assert_fail (const char *, const char *, int, const char *);
diff --git a/system/include/libc/complex.h b/system/include/libc/complex.h
index 13a45c5..008b3c7 100644
--- a/system/include/libc/complex.h
+++ b/system/include/libc/complex.h
@@ -7,9 +7,9 @@
 
 #define complex _Complex
 #ifdef __GNUC__
-#define _Complex_I (__extension__ 1.0fi)
+#define _Complex_I (__extension__ (0.0f+1.0fi))
 #else
-#define _Complex_I 1.0fi
+#define _Complex_I (0.0f+1.0fi)
 #endif
 #define I _Complex_I
 
@@ -101,8 +101,9 @@
 float crealf(float complex);
 long double creall(long double complex);
 
+#ifndef __cplusplus
 #define __CIMAG(x, t) \
-	((union { _Complex t __z; t __xy[2]; }){(_Complex t)(x)}.__xy[1])
+	(+(union { _Complex t __z; t __xy[2]; }){(_Complex t)(x)}.__xy[1])
 
 #define creal(x) ((double)(x))
 #define crealf(x) ((float)(x))
@@ -111,13 +112,20 @@
 #define cimag(x) __CIMAG(x, double)
 #define cimagf(x) __CIMAG(x, float)
 #define cimagl(x) __CIMAG(x, long double)
+#endif
 
-#define __CMPLX(x, y, t) \
-	((union { _Complex t __z; t __xy[2]; }){.__xy = {(x),(y)}}.__z)
-
+#if __STDC_VERSION__ >= 201112L
+#if defined(_Imaginary_I)
+#define __CMPLX(x, y, t) ((t)(x) + _Imaginary_I*(t)(y))
+#elif defined(__clang__)
+#define __CMPLX(x, y, t) (+(_Complex t){ (t)(x), (t)(y) })
+#else
+#define __CMPLX(x, y, t) (__builtin_complex((t)(x), (t)(y)))
+#endif
 #define CMPLX(x, y) __CMPLX(x, y, double)
 #define CMPLXF(x, y) __CMPLX(x, y, float)
 #define CMPLXL(x, y) __CMPLX(x, y, long double)
+#endif
 
 #ifdef __cplusplus
 }
diff --git a/system/include/libc/ctype.h b/system/include/libc/ctype.h
index 8f0d168..7936536 100644
--- a/system/include/libc/ctype.h
+++ b/system/include/libc/ctype.h
@@ -22,13 +22,20 @@
 int   tolower(int);
 int   toupper(int);
 
-#define isalpha(a) ((((unsigned)(a)|32)-'a') < 26)
-#define isdigit(a) (((unsigned)(a)-'0') < 10)
-#define islower(a) (((unsigned)(a)-'a') < 26)
-#define isupper(a) (((unsigned)(a)-'A') < 26)
-#define isprint(a) (((unsigned)(a)-0x20) < 0x5f)
-#define isgraph(a) (((unsigned)(a)-0x21) < 0x5e)
+#ifndef __cplusplus
+static __inline int __isspace(int _c)
+{
+	return _c == ' ' || (unsigned)_c-'\t' < 5;
+}
 
+#define isalpha(a) (0 ? isalpha(a) : (((unsigned)(a)|32)-'a') < 26)
+#define isdigit(a) (0 ? isdigit(a) : ((unsigned)(a)-'0') < 10)
+#define islower(a) (0 ? islower(a) : ((unsigned)(a)-'a') < 26)
+#define isupper(a) (0 ? isupper(a) : ((unsigned)(a)-'A') < 26)
+#define isprint(a) (0 ? isprint(a) : ((unsigned)(a)-0x20) < 0x5f)
+#define isgraph(a) (0 ? isgraph(a) : ((unsigned)(a)-0x21) < 0x5e)
+#define isspace(a) __isspace(a)
+#endif
 
 
 #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
@@ -57,6 +64,7 @@
 int   toascii(int);
 #define _tolower(a) ((a)|0x20)
 #define _toupper(a) ((a)&0x5f)
+#define isascii(a) (0 ? isascii(a) : (unsigned)(a) < 128)
 
 #endif
 
diff --git a/system/include/libc/dirent.h b/system/include/libc/dirent.h
index 5aa8510..006a360 100644
--- a/system/include/libc/dirent.h
+++ b/system/include/libc/dirent.h
@@ -17,8 +17,7 @@
 
 typedef struct __dirstream DIR;
 
-struct dirent
-{
+struct dirent {
 	ino_t d_ino;
 	off_t d_off;
 	unsigned short d_reclen;
diff --git a/system/include/libc/elf.h b/system/include/libc/elf.h
index 4d8c0c8..0721d63 100644
--- a/system/include/libc/elf.h
+++ b/system/include/libc/elf.h
@@ -209,9 +209,11 @@
 #define EM_MN10300	89
 #define EM_MN10200	90
 #define EM_PJ		91
+#define EM_OR1K		92
 #define EM_OPENRISC	92
 #define EM_ARC_A5	93
 #define EM_XTENSA	94
+#define EM_ALTERA_NIOS2 113
 #define EM_AARCH64	183
 #define EM_TILEPRO	188
 #define EM_MICROBLAZE	189
@@ -316,10 +318,31 @@
 
 #define SHF_GROUP	     (1 << 9)
 #define SHF_TLS		     (1 << 10)
+#define SHF_COMPRESSED	     (1 << 11)
 #define SHF_MASKOS	     0x0ff00000
 #define SHF_MASKPROC	     0xf0000000
 #define SHF_ORDERED	     (1 << 30)
-#define SHF_EXCLUDE	     (1 << 31)
+#define SHF_EXCLUDE	     (1U << 31)
+
+typedef struct {
+  Elf32_Word	ch_type;
+  Elf32_Word	ch_size;
+  Elf32_Word	ch_addralign;
+} Elf32_Chdr;
+
+typedef struct {
+  Elf64_Word	ch_type;
+  Elf64_Word	ch_reserved;
+  Elf64_Xword	ch_size;
+  Elf64_Xword	ch_addralign;
+} Elf64_Chdr;
+
+#define ELFCOMPRESS_ZLIB	1
+#define ELFCOMPRESS_LOOS	0x60000000
+#define ELFCOMPRESS_HIOS	0x6fffffff
+#define ELFCOMPRESS_LOPROC	0x70000000
+#define ELFCOMPRESS_HIPROC	0x7fffffff
+
 
 #define GRP_COMDAT	0x1
 
@@ -409,8 +432,7 @@
 
 
 
-typedef struct
-{
+typedef struct {
   Elf32_Addr	r_offset;
   Elf32_Word	r_info;
 } Elf32_Rel;
@@ -541,6 +563,7 @@
 #define NT_ARM_TLS	0x401
 #define NT_ARM_HW_BREAK	0x402
 #define NT_ARM_HW_WATCH	0x403
+#define NT_ARM_SYSTEM_CALL	0x404
 #define NT_METAG_CBUF	0x500
 #define NT_METAG_RPIPE	0x501
 #define NT_METAG_TLS	0x502
@@ -1020,7 +1043,8 @@
 #define R_386_TLS_DESC_CALL 40
 #define R_386_TLS_DESC     41
 #define R_386_IRELATIVE	   42
-#define R_386_NUM	   43
+#define R_386_GOT32X	   43
+#define R_386_NUM	   44
 
 
 
@@ -1153,6 +1177,7 @@
 #define EF_MIPS_64BIT_WHIRL 16
 #define EF_MIPS_ABI2	    32
 #define EF_MIPS_ABI_ON32    64
+#define EF_MIPS_FP64	    512
 #define EF_MIPS_NAN2008     1024
 #define EF_MIPS_ARCH	    0xf0000000
 
@@ -1397,6 +1422,7 @@
 #define PT_MIPS_REGINFO	0x70000000
 #define PT_MIPS_RTPROC  0x70000001
 #define PT_MIPS_OPTIONS 0x70000002
+#define PT_MIPS_ABIFLAGS 0x70000003
 
 
 
@@ -1460,7 +1486,8 @@
 #define DT_MIPS_PLTGOT	     0x70000032
 
 #define DT_MIPS_RWPLT        0x70000034
-#define DT_MIPS_NUM	     0x35
+#define DT_MIPS_RLD_MAP_REL  0x70000035
+#define DT_MIPS_NUM	     0x36
 
 
 
@@ -1483,8 +1510,7 @@
 
 
 
-typedef struct
-{
+typedef struct {
   Elf32_Word l_name;
   Elf32_Word l_time_stamp;
   Elf32_Word l_checksum;
@@ -1492,8 +1518,7 @@
   Elf32_Word l_flags;
 } Elf32_Lib;
 
-typedef struct
-{
+typedef struct {
   Elf64_Word l_name;
   Elf64_Word l_time_stamp;
   Elf64_Word l_checksum;
@@ -1516,7 +1541,73 @@
 
 typedef Elf32_Addr Elf32_Conflict;
 
+typedef struct {
+  Elf32_Half version;
+  unsigned char isa_level;
+  unsigned char isa_rev;
+  unsigned char gpr_size;
+  unsigned char cpr1_size;
+  unsigned char cpr2_size;
+  unsigned char fp_abi;
+  Elf32_Word isa_ext;
+  Elf32_Word ases;
+  Elf32_Word flags1;
+  Elf32_Word flags2;
+} Elf_MIPS_ABIFlags_v0;
 
+#define MIPS_AFL_REG_NONE	0x00
+#define MIPS_AFL_REG_32		0x01
+#define MIPS_AFL_REG_64		0x02
+#define MIPS_AFL_REG_128	0x03
+
+#define MIPS_AFL_ASE_DSP	0x00000001
+#define MIPS_AFL_ASE_DSPR2	0x00000002
+#define MIPS_AFL_ASE_EVA	0x00000004
+#define MIPS_AFL_ASE_MCU	0x00000008
+#define MIPS_AFL_ASE_MDMX	0x00000010
+#define MIPS_AFL_ASE_MIPS3D	0x00000020
+#define MIPS_AFL_ASE_MT		0x00000040
+#define MIPS_AFL_ASE_SMARTMIPS	0x00000080
+#define MIPS_AFL_ASE_VIRT	0x00000100
+#define MIPS_AFL_ASE_MSA	0x00000200
+#define MIPS_AFL_ASE_MIPS16	0x00000400
+#define MIPS_AFL_ASE_MICROMIPS	0x00000800
+#define MIPS_AFL_ASE_XPA	0x00001000
+#define MIPS_AFL_ASE_MASK	0x00001fff
+
+#define MIPS_AFL_EXT_XLR	  1
+#define MIPS_AFL_EXT_OCTEON2	  2
+#define MIPS_AFL_EXT_OCTEONP	  3
+#define MIPS_AFL_EXT_LOONGSON_3A  4
+#define MIPS_AFL_EXT_OCTEON	  5
+#define MIPS_AFL_EXT_5900	  6
+#define MIPS_AFL_EXT_4650	  7
+#define MIPS_AFL_EXT_4010	  8
+#define MIPS_AFL_EXT_4100	  9
+#define MIPS_AFL_EXT_3900	  10
+#define MIPS_AFL_EXT_10000	  11
+#define MIPS_AFL_EXT_SB1	  12
+#define MIPS_AFL_EXT_4111	  13
+#define MIPS_AFL_EXT_4120	  14
+#define MIPS_AFL_EXT_5400	  15
+#define MIPS_AFL_EXT_5500	  16
+#define MIPS_AFL_EXT_LOONGSON_2E  17
+#define MIPS_AFL_EXT_LOONGSON_2F  18
+
+#define MIPS_AFL_FLAGS1_ODDSPREG  1
+
+enum
+{
+  Val_GNU_MIPS_ABI_FP_ANY = 0,
+  Val_GNU_MIPS_ABI_FP_DOUBLE = 1,
+  Val_GNU_MIPS_ABI_FP_SINGLE = 2,
+  Val_GNU_MIPS_ABI_FP_SOFT = 3,
+  Val_GNU_MIPS_ABI_FP_OLD_64 = 4,
+  Val_GNU_MIPS_ABI_FP_XX = 5,
+  Val_GNU_MIPS_ABI_FP_64 = 6,
+  Val_GNU_MIPS_ABI_FP_64A = 7,
+  Val_GNU_MIPS_ABI_FP_MAX = 7
+};
 
 
 
@@ -1857,7 +1948,8 @@
 #define R_PPC_GOT_DTPREL16_LO	92
 #define R_PPC_GOT_DTPREL16_HI	93
 #define R_PPC_GOT_DTPREL16_HA	94
-
+#define R_PPC_TLSGD		95
+#define R_PPC_TLSLD		96
 
 
 #define R_PPC_EMB_NADDR32	101
@@ -1900,7 +1992,10 @@
 
 
 #define DT_PPC_GOT		(DT_LOPROC + 0)
-#define DT_PPC_NUM		1
+#define DT_PPC_OPT		(DT_LOPROC + 1)
+#define DT_PPC_NUM		2
+
+#define PPC_OPT_TLS		1
 
 
 #define R_PPC64_NONE		R_PPC_NONE
@@ -2013,6 +2108,15 @@
 #define R_PPC64_DTPREL16_HIGHERA 104
 #define R_PPC64_DTPREL16_HIGHEST 105
 #define R_PPC64_DTPREL16_HIGHESTA 106
+#define R_PPC64_TLSGD		107
+#define R_PPC64_TLSLD		108
+#define R_PPC64_TOCSAVE		109
+#define R_PPC64_ADDR16_HIGH	110
+#define R_PPC64_ADDR16_HIGHA	111
+#define R_PPC64_TPREL16_HIGH	112
+#define R_PPC64_TPREL16_HIGHA	113
+#define R_PPC64_DTPREL16_HIGH	114
+#define R_PPC64_DTPREL16_HIGHA	115
 
 
 #define R_PPC64_JMP_IREL	247
@@ -2022,14 +2126,20 @@
 #define R_PPC64_REL16_HI	251
 #define R_PPC64_REL16_HA	252
 
+#define EF_PPC64_ABI	3
 
 #define DT_PPC64_GLINK  (DT_LOPROC + 0)
 #define DT_PPC64_OPD	(DT_LOPROC + 1)
 #define DT_PPC64_OPDSZ	(DT_LOPROC + 2)
-#define DT_PPC64_NUM    3
+#define DT_PPC64_OPT	(DT_LOPROC + 3)
+#define DT_PPC64_NUM	4
 
+#define PPC64_OPT_TLS		1
+#define PPC64_OPT_MULTI_TOC	2
 
-
+#define STO_PPC64_LOCAL_BIT	5
+#define STO_PPC64_LOCAL_MASK	0xe0
+#define PPC64_LOCAL_ENTRY_OFFSET(x) (1 << (((x)&0xe0)>>5) & 0xfc)
 
 
 #define EF_ARM_RELEXEC		0x01
@@ -2089,8 +2199,17 @@
 #define SHT_ARM_PREEMPTMAP	(SHT_LOPROC + 2)
 #define SHT_ARM_ATTRIBUTES	(SHT_LOPROC + 3)
 
-
 #define R_AARCH64_NONE            0
+#define R_AARCH64_P32_ABS32	1
+#define R_AARCH64_P32_COPY	180
+#define R_AARCH64_P32_GLOB_DAT	181
+#define R_AARCH64_P32_JUMP_SLOT	182
+#define R_AARCH64_P32_RELATIVE	183
+#define R_AARCH64_P32_TLS_DTPMOD 184
+#define R_AARCH64_P32_TLS_DTPREL 185
+#define R_AARCH64_P32_TLS_TPREL	186
+#define R_AARCH64_P32_TLSDESC	187
+#define R_AARCH64_P32_IRELATIVE	188
 #define R_AARCH64_ABS64         257
 #define R_AARCH64_ABS32         258
 #define R_AARCH64_ABS16		259
@@ -2208,8 +2327,11 @@
 #define R_AARCH64_GLOB_DAT     1025
 #define R_AARCH64_JUMP_SLOT    1026
 #define R_AARCH64_RELATIVE     1027
+#define R_AARCH64_TLS_DTPMOD   1028
 #define R_AARCH64_TLS_DTPMOD64 1028
+#define R_AARCH64_TLS_DTPREL   1029
 #define R_AARCH64_TLS_DTPREL64 1029
+#define R_AARCH64_TLS_TPREL    1030
 #define R_AARCH64_TLS_TPREL64  1030
 #define R_AARCH64_TLSDESC      1031
 
@@ -2459,7 +2581,28 @@
 #define R_IA64_LTOFF_DTPREL22	0xba
 
 
-
+#define EF_SH_MACH_MASK		0x1f
+#define EF_SH_UNKNOWN		0x0
+#define EF_SH1			0x1
+#define EF_SH2			0x2
+#define EF_SH3			0x3
+#define EF_SH_DSP		0x4
+#define EF_SH3_DSP		0x5
+#define EF_SH4AL_DSP		0x6
+#define EF_SH3E			0x8
+#define EF_SH4			0x9
+#define EF_SH2E			0xb
+#define EF_SH4A			0xc
+#define EF_SH2A			0xd
+#define EF_SH4_NOFPU		0x10
+#define EF_SH4A_NOFPU		0x11
+#define EF_SH4_NOMMU_NOFPU	0x12
+#define EF_SH2A_NOFPU		0x13
+#define EF_SH3_NOMMU		0x14
+#define EF_SH2A_SH4_NOFPU	0x15
+#define EF_SH2A_SH3_NOFPU	0x16
+#define EF_SH2A_SH4		0x17
+#define EF_SH2A_SH3E		0x18
 
 #define	R_SH_NONE		0
 #define	R_SH_DIR32		1
@@ -2498,6 +2641,14 @@
 #define	R_SH_RELATIVE		165
 #define	R_SH_GOTOFF		166
 #define	R_SH_GOTPC		167
+#define	R_SH_GOT20		201
+#define	R_SH_GOTOFF20		202
+#define	R_SH_GOTFUNCDESC	203
+#define	R_SH_GOTFUNCDEST20	204
+#define	R_SH_GOTOFFFUNCDESC	205
+#define	R_SH_GOTOFFFUNCDEST20	206
+#define	R_SH_FUNCDESC		207
+#define	R_SH_FUNCDESC_VALUE	208
 
 #define	R_SH_NUM		256
 
@@ -2657,7 +2808,9 @@
 #define R_X86_64_TLSDESC        36
 #define R_X86_64_IRELATIVE	37
 #define R_X86_64_RELATIVE64	38
-#define R_X86_64_NUM		39
+#define R_X86_64_GOTPCRELX	41
+#define R_X86_64_REX_GOTPCRELX	42
+#define R_X86_64_NUM		43
 
 
 
@@ -2775,6 +2928,91 @@
 #define R_MICROBLAZE_TLSGOTTPREL32 28
 #define R_MICROBLAZE_TLSTPREL32	 29
 
+#define DT_NIOS2_GP             0x70000002
+
+#define R_NIOS2_NONE		0
+#define R_NIOS2_S16		1
+#define R_NIOS2_U16		2
+#define R_NIOS2_PCREL16		3
+#define R_NIOS2_CALL26		4
+#define R_NIOS2_IMM5		5
+#define R_NIOS2_CACHE_OPX	6
+#define R_NIOS2_IMM6		7
+#define R_NIOS2_IMM8		8
+#define R_NIOS2_HI16		9
+#define R_NIOS2_LO16		10
+#define R_NIOS2_HIADJ16		11
+#define R_NIOS2_BFD_RELOC_32	12
+#define R_NIOS2_BFD_RELOC_16	13
+#define R_NIOS2_BFD_RELOC_8	14
+#define R_NIOS2_GPREL		15
+#define R_NIOS2_GNU_VTINHERIT	16
+#define R_NIOS2_GNU_VTENTRY	17
+#define R_NIOS2_UJMP		18
+#define R_NIOS2_CJMP		19
+#define R_NIOS2_CALLR		20
+#define R_NIOS2_ALIGN		21
+#define R_NIOS2_GOT16		22
+#define R_NIOS2_CALL16		23
+#define R_NIOS2_GOTOFF_LO	24
+#define R_NIOS2_GOTOFF_HA	25
+#define R_NIOS2_PCREL_LO	26
+#define R_NIOS2_PCREL_HA	27
+#define R_NIOS2_TLS_GD16	28
+#define R_NIOS2_TLS_LDM16	29
+#define R_NIOS2_TLS_LDO16	30
+#define R_NIOS2_TLS_IE16	31
+#define R_NIOS2_TLS_LE16	32
+#define R_NIOS2_TLS_DTPMOD	33
+#define R_NIOS2_TLS_DTPREL	34
+#define R_NIOS2_TLS_TPREL	35
+#define R_NIOS2_COPY		36
+#define R_NIOS2_GLOB_DAT	37
+#define R_NIOS2_JUMP_SLOT	38
+#define R_NIOS2_RELATIVE	39
+#define R_NIOS2_GOTOFF		40
+#define R_NIOS2_CALL26_NOAT	41
+#define R_NIOS2_GOT_LO		42
+#define R_NIOS2_GOT_HA		43
+#define R_NIOS2_CALL_LO		44
+#define R_NIOS2_CALL_HA		45
+
+#define R_OR1K_NONE		0
+#define R_OR1K_32		1
+#define R_OR1K_16		2
+#define R_OR1K_8		3
+#define R_OR1K_LO_16_IN_INSN	4
+#define R_OR1K_HI_16_IN_INSN	5
+#define R_OR1K_INSN_REL_26	6
+#define R_OR1K_GNU_VTENTRY	7
+#define R_OR1K_GNU_VTINHERIT	8
+#define R_OR1K_32_PCREL		9
+#define R_OR1K_16_PCREL		10
+#define R_OR1K_8_PCREL		11
+#define R_OR1K_GOTPC_HI16	12
+#define R_OR1K_GOTPC_LO16	13
+#define R_OR1K_GOT16		14
+#define R_OR1K_PLT26		15
+#define R_OR1K_GOTOFF_HI16	16
+#define R_OR1K_GOTOFF_LO16	17
+#define R_OR1K_COPY		18
+#define R_OR1K_GLOB_DAT		19
+#define R_OR1K_JMP_SLOT		20
+#define R_OR1K_RELATIVE		21
+#define R_OR1K_TLS_GD_HI16	22
+#define R_OR1K_TLS_GD_LO16	23
+#define R_OR1K_TLS_LDM_HI16	24
+#define R_OR1K_TLS_LDM_LO16	25
+#define R_OR1K_TLS_LDO_HI16	26
+#define R_OR1K_TLS_LDO_LO16	27
+#define R_OR1K_TLS_IE_HI16	28
+#define R_OR1K_TLS_IE_LO16	29
+#define R_OR1K_TLS_LE_HI16	30
+#define R_OR1K_TLS_LE_LO16	31
+#define R_OR1K_TLS_TPOFF	32
+#define R_OR1K_TLS_DTPOFF	33
+#define R_OR1K_TLS_DTPMOD	34
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/system/include/libc/errno.h b/system/include/libc/errno.h
index 0361b33..93f5f6e 100644
--- a/system/include/libc/errno.h
+++ b/system/include/libc/errno.h
@@ -9,9 +9,6 @@
 
 #include <bits/errno.h>
 
-#ifdef __GNUC__
-__attribute__((const))
-#endif
 int *__errno_location(void);
 #define errno (*__errno_location())
 
diff --git a/system/include/libc/fcntl.h b/system/include/libc/fcntl.h
index 2d8fa6e..e683e4d 100644
--- a/system/include/libc/fcntl.h
+++ b/system/include/libc/fcntl.h
@@ -21,8 +21,7 @@
 
 #include <bits/fcntl.h>
 
-struct flock
-{
+struct flock {
 	short l_type;
 	short l_whence;
 	off_t l_start;
@@ -37,15 +36,18 @@
 int posix_fadvise(int, off_t, off_t, int);
 int posix_fallocate(int, off_t, off_t);
 
-#define O_SEARCH  010000000
-#define O_EXEC    010000000
-#define O_PATH    010000000
+#define O_SEARCH  O_PATH
+#define O_EXEC    O_PATH
 
 #define O_ACCMODE (03|O_SEARCH)
 #define O_RDONLY  00
 #define O_WRONLY  01
 #define O_RDWR    02
 
+#define F_OFD_GETLK 36
+#define F_OFD_SETLK 37
+#define F_OFD_SETLKW 38
+
 #define F_DUPFD_CLOEXEC 1030
 
 #define F_RDLCK 0
@@ -117,6 +119,13 @@
 #define F_CANCELLK	1029
 #define F_SETPIPE_SZ	1031
 #define F_GETPIPE_SZ	1032
+#define F_ADD_SEALS	1033
+#define F_GET_SEALS	1034
+
+#define F_SEAL_SEAL	0x0001
+#define F_SEAL_SHRINK	0x0002
+#define F_SEAL_GROW	0x0004
+#define F_SEAL_WRITE	0x0008
 
 #define DN_ACCESS	0x00000001
 #define DN_MODIFY	0x00000002
diff --git a/system/include/libc/features.h b/system/include/libc/features.h
index 294c61d..3cc3e57 100644
--- a/system/include/libc/features.h
+++ b/system/include/libc/features.h
@@ -1,10 +1,14 @@
 #ifndef _FEATURES_H
 #define _FEATURES_H
 
-#ifdef _ALL_SOURCE
+#if defined(_ALL_SOURCE) && !defined(_GNU_SOURCE)
 #define _GNU_SOURCE 1
 #endif
 
+#if defined(_DEFAULT_SOURCE) && !defined(_BSD_SOURCE)
+#define _BSD_SOURCE 1
+#endif
+
 #if !defined(_POSIX_SOURCE) && !defined(_POSIX_C_SOURCE) \
  && !defined(_XOPEN_SOURCE) && !defined(_GNU_SOURCE) \
  && !defined(_BSD_SOURCE) && !defined(__STRICT_ANSI__)
diff --git a/system/include/libc/float.h b/system/include/libc/float.h
index 2b2ad39..713aadb 100644
--- a/system/include/libc/float.h
+++ b/system/include/libc/float.h
@@ -1,6 +1,13 @@
 #ifndef _FLOAT_H
 #define _FLOAT_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int __flt_rounds(void);
+#define FLT_ROUNDS (__flt_rounds())
+
 #define FLT_RADIX 2
 
 #define FLT_TRUE_MIN 1.40129846432481707092e-45F
@@ -11,8 +18,10 @@
 #define FLT_MANT_DIG 24
 #define FLT_MIN_EXP (-125)
 #define FLT_MAX_EXP 128
+#define FLT_HAS_SUBNORM 1
 
 #define FLT_DIG 6
+#define FLT_DECIMAL_DIG 9
 #define FLT_MIN_10_EXP (-37)
 #define FLT_MAX_10_EXP 38
 
@@ -24,11 +33,20 @@
 #define DBL_MANT_DIG 53
 #define DBL_MIN_EXP (-1021)
 #define DBL_MAX_EXP 1024
+#define DBL_HAS_SUBNORM 1
 
 #define DBL_DIG 15
+#define DBL_DECIMAL_DIG 17
 #define DBL_MIN_10_EXP (-307)
 #define DBL_MAX_10_EXP 308
 
+#define LDBL_HAS_SUBNORM 1
+#define LDBL_DECIMAL_DIG DECIMAL_DIG
+
 #include <bits/float.h>
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/system/include/libc/fmtmsg.h b/system/include/libc/fmtmsg.h
new file mode 100644
index 0000000..d944b06
--- /dev/null
+++ b/system/include/libc/fmtmsg.h
@@ -0,0 +1,47 @@
+#ifndef _FMTMSG_H
+#define _FMTMSG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MM_HARD		1
+#define MM_SOFT		2
+#define MM_FIRM		4
+
+#define MM_APPL		8
+#define MM_UTIL		16
+#define MM_OPSYS	32
+
+#define MM_RECOVER	64
+#define MM_NRECOV	128
+
+#define MM_PRINT	256
+#define MM_CONSOLE	512
+
+#define MM_NULLMC	0L
+
+#define MM_HALT		1
+#define MM_ERROR	2
+#define MM_WARNING	3
+#define MM_INFO		4
+#define MM_NOSEV	0
+
+#define MM_OK		0
+#define MM_NOTOK	(-1)
+#define MM_NOMSG	1
+#define MM_NOCON	4
+
+#define MM_NULLLBL	((char*)0)
+#define MM_NULLTXT	((char*)0)
+#define MM_NULLACT	((char*)0)
+#define MM_NULLTAG	((char*)0)
+#define MM_NULLSEV	0
+
+int fmtmsg(long, const char *, int, const char *, const char *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/system/include/libc/ftw.h b/system/include/libc/ftw.h
index c8eadbc..b15c062 100644
--- a/system/include/libc/ftw.h
+++ b/system/include/libc/ftw.h
@@ -21,8 +21,7 @@
 #define FTW_CHDIR 4
 #define FTW_DEPTH 8
 
-struct FTW
-{
+struct FTW {
 	int base;
 	int level;
 };
diff --git a/system/include/libc/getopt.h b/system/include/libc/getopt.h
index c1d0df9..35cbd35 100644
--- a/system/include/libc/getopt.h
+++ b/system/include/libc/getopt.h
@@ -9,8 +9,7 @@
 extern char *optarg;
 extern int optind, opterr, optopt, optreset;
 
-struct option
-{
+struct option {
 	const char *name;
 	int has_arg;
 	int *flag;
diff --git a/system/include/libc/glob.h b/system/include/libc/glob.h
index 9fbbaa6..76f6c1c 100644
--- a/system/include/libc/glob.h
+++ b/system/include/libc/glob.h
@@ -39,6 +39,7 @@
 #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
 #define glob64 glob
 #define globfree64 globfree
+#define glob64_t glob_t
 #endif
 
 #ifdef __cplusplus
diff --git a/system/include/libc/grp.h b/system/include/libc/grp.h
index b331d32..358181b 100644
--- a/system/include/libc/grp.h
+++ b/system/include/libc/grp.h
@@ -16,8 +16,7 @@
 
 #include <bits/alltypes.h>
 
-struct group
-{
+struct group {
 	char *gr_name;
 	char *gr_passwd;
 	gid_t gr_gid;
diff --git a/system/include/libc/libintl.h b/system/include/libc/libintl.h
index a2dada6..6a707bf 100644
--- a/system/include/libc/libintl.h
+++ b/system/include/libc/libintl.h
@@ -8,16 +8,24 @@
 #define __USE_GNU_GETTEXT 1
 #define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 1 : -1)
 
-char *gettext(const char *);
-char *dgettext(const char *, const char *);
-char *dcgettext(const char *, const char *, int);
-char *ngettext(const char *, const char *, unsigned long);
-char *dngettext(const char *, const char *, const char *, unsigned long);
-char *dcngettext(const char *, const char *, const char *, unsigned long, int);
+#if __GNUC__ >= 3
+#define __fa(n) __attribute__ ((__format_arg__ (n)))
+#else
+#define __fa(n)
+#endif
+
+char *gettext(const char *) __fa(1);
+char *dgettext(const char *, const char *) __fa(2);
+char *dcgettext(const char *, const char *, int) __fa(2);
+char *ngettext(const char *, const char *, unsigned long) __fa(1) __fa(2);
+char *dngettext(const char *, const char *, const char *, unsigned long) __fa(2) __fa(3);
+char *dcngettext(const char *, const char *, const char *, unsigned long, int) __fa(2) __fa(3);
 char *textdomain(const char *);
 char *bindtextdomain (const char *, const char *);
 char *bind_textdomain_codeset(const char *, const char *);
 
+#undef __fa
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/system/include/libc/malloc.h b/system/include/libc/malloc.h
index e69de29..35f8b19 100644
--- a/system/include/libc/malloc.h
+++ b/system/include/libc/malloc.h
@@ -0,0 +1,25 @@
+#ifndef _MALLOC_H
+#define _MALLOC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __NEED_size_t
+
+#include <bits/alltypes.h>
+
+void *malloc (size_t);
+void *calloc (size_t, size_t);
+void *realloc (void *, size_t);
+void free (void *);
+void *valloc (size_t);
+void *memalign(size_t, size_t);
+
+size_t malloc_usable_size(void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/system/include/libc/mntent.h b/system/include/libc/mntent.h
index d03c414..3492a1d 100644
--- a/system/include/libc/mntent.h
+++ b/system/include/libc/mntent.h
@@ -20,8 +20,7 @@
 #define MNTOPT_NOSUID	"nosuid"
 #define MNTOPT_NOAUTO	"noauto"
 
-struct mntent
-{
+struct mntent {
 	char *mnt_fsname;
 	char *mnt_dir;
 	char *mnt_type;
diff --git a/system/include/libc/net/if.h b/system/include/libc/net/if.h
index 3f4fc09..1a4059d 100644
--- a/system/include/libc/net/if.h
+++ b/system/include/libc/net/if.h
@@ -9,8 +9,7 @@
 
 #define IF_NAMESIZE 16
 
-struct if_nameindex
-{
+struct if_nameindex {
 	unsigned int if_index;
 	char *if_name;
 };
diff --git a/system/include/libc/netdb.h b/system/include/libc/netdb.h
index dfc70e2..967ca21 100644
--- a/system/include/libc/netdb.h
+++ b/system/include/libc/netdb.h
@@ -13,8 +13,7 @@
 #include <bits/alltypes.h>
 #endif
 
-struct addrinfo
-{
+struct addrinfo {
 	int ai_flags;
 	int ai_family;
 	int ai_socktype;
@@ -41,7 +40,7 @@
 #define NI_NOFQDN       0x04
 #define NI_NAMEREQD     0x08
 #define NI_DGRAM        0x10
-/*#define NI_NUMERICSCOPE */
+#define NI_NUMERICSCOPE 0x100
 
 #define EAI_BADFLAGS   -1
 #define EAI_NONAME     -2
@@ -62,16 +61,14 @@
 
 /* Legacy functions follow (marked OBsolete in SUS) */
 
-struct netent
-{
+struct netent {
 	char *n_name;
 	char **n_aliases;
 	int n_addrtype;
 	uint32_t n_net;
 };
 
-struct hostent
-{
+struct hostent {
 	char *h_name;
 	char **h_aliases;
 	int h_addrtype;
@@ -80,16 +77,14 @@
 };
 #define h_addr h_addr_list[0]
 
-struct servent
-{
+struct servent {
 	char *s_name;
 	char **s_aliases;
 	int s_port;
 	char *s_proto;
 };
 
-struct protoent
-{
+struct protoent {
 	char *p_name;
 	char **p_aliases;
 	int p_proto;
@@ -122,9 +117,6 @@
  || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE+0 < 700)
 struct hostent *gethostbyname (const char *);
 struct hostent *gethostbyaddr (const void *, socklen_t, int);
-#ifdef __GNUC__
-__attribute__((const))
-#endif
 int *__h_errno_location(void);
 #define h_errno (*__h_errno_location())
 #define HOST_NOT_FOUND 1
diff --git a/system/include/libc/netinet/ether.h b/system/include/libc/netinet/ether.h
index d64c9ef..eec7e53 100644
--- a/system/include/libc/netinet/ether.h
+++ b/system/include/libc/netinet/ether.h
@@ -1,6 +1,10 @@
 #ifndef _NETINET_ETHER_H
 #define _NETINET_ETHER_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <netinet/if_ether.h>
 
 char *ether_ntoa (const struct ether_addr *);
@@ -11,4 +15,8 @@
 int ether_ntohost(char *, const struct ether_addr *);
 int ether_hostton(const char *, struct ether_addr *);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/system/include/libc/netinet/if_ether.h b/system/include/libc/netinet/if_ether.h
index 34f9be5..4c2322b 100644
--- a/system/include/libc/netinet/if_ether.h
+++ b/system/include/libc/netinet/if_ether.h
@@ -39,11 +39,11 @@
 #define ETH_P_PAUSE	0x8808
 #define ETH_P_SLOW	0x8809
 #define ETH_P_WCCP	0x883E
-#define ETH_P_PPP_DISC	0x8863
-#define ETH_P_PPP_SES	0x8864
 #define ETH_P_MPLS_UC	0x8847
 #define ETH_P_MPLS_MC	0x8848
 #define ETH_P_ATMMPOA	0x884c
+#define ETH_P_PPP_DISC	0x8863
+#define ETH_P_PPP_SES	0x8864
 #define ETH_P_LINK_CTL	0x886c
 #define ETH_P_ATMFATE	0x8884
 #define ETH_P_PAE	0x888E
@@ -51,6 +51,7 @@
 #define ETH_P_8021AD	0x88A8
 #define ETH_P_802_EX1	0x88B5
 #define ETH_P_TIPC	0x88CA
+#define ETH_P_MACSEC	0x88E5
 #define ETH_P_8021AH	0x88E7
 #define ETH_P_MVRP	0x88F5
 #define ETH_P_1588	0x88F7
@@ -58,6 +59,8 @@
 #define ETH_P_FCOE	0x8906
 #define ETH_P_TDLS	0x890D
 #define ETH_P_FIP	0x8914
+#define ETH_P_80221	0x8917
+#define ETH_P_LOOPBACK	0x9000
 #define ETH_P_QINQ1	0x9100
 #define ETH_P_QINQ2	0x9200
 #define ETH_P_QINQ3	0x9300
diff --git a/system/include/libc/netinet/in.h b/system/include/libc/netinet/in.h
index ee6e19f..f6bb77b 100644
--- a/system/include/libc/netinet/in.h
+++ b/system/include/libc/netinet/in.h
@@ -13,16 +13,14 @@
 typedef uint32_t in_addr_t;
 struct in_addr { in_addr_t s_addr; };
 
-struct sockaddr_in
-{
+struct sockaddr_in {
 	sa_family_t sin_family;
 	in_port_t sin_port;
 	struct in_addr sin_addr;
 	uint8_t sin_zero[8];
 };
 
-struct in6_addr
-{
+struct in6_addr {
 	union {
 		uint8_t __s6_addr[16];
 		uint16_t __s6_addr16[8];
@@ -33,8 +31,7 @@
 #define s6_addr16 __in6_union.__s6_addr16
 #define s6_addr32 __in6_union.__s6_addr32
 
-struct sockaddr_in6
-{
+struct sockaddr_in6 {
 	sa_family_t     sin6_family;
 	in_port_t       sin6_port;
 	uint32_t        sin6_flowinfo;
@@ -42,8 +39,7 @@
 	uint32_t        sin6_scope_id;
 };
 
-struct ipv6_mreq
-{
+struct ipv6_mreq {
 	struct in6_addr ipv6mr_multiaddr;
 	unsigned        ipv6mr_interface;
 };
@@ -103,6 +99,7 @@
 #define IPPROTO_SCTP     132
 #define IPPROTO_MH       135
 #define IPPROTO_UDPLITE  136
+#define IPPROTO_MPLS     137
 #define IPPROTO_RAW      255
 #define IPPROTO_MAX      256
 
@@ -198,6 +195,9 @@
 #define IP_ORIGDSTADDR     20
 #define IP_RECVORIGDSTADDR IP_ORIGDSTADDR
 #define IP_MINTTL          21
+#define IP_NODEFRAG        22
+#define IP_CHECKSUM        23
+#define IP_BIND_ADDRESS_NO_PORT 24
 #define IP_MULTICAST_IF    32
 #define IP_MULTICAST_TTL   33
 #define IP_MULTICAST_LOOP  34
@@ -218,13 +218,13 @@
 #define IP_PMTUDISC_DO     2
 #define IP_PMTUDISC_PROBE  3
 #define IP_PMTUDISC_INTERFACE 4
+#define IP_PMTUDISC_OMIT   5
 
 #define IP_DEFAULT_MULTICAST_TTL        1
 #define IP_DEFAULT_MULTICAST_LOOP       1
 #define IP_MAX_MEMBERSHIPS              20
 
-struct ip_opts
-{
+struct ip_opts {
 	struct in_addr ip_dst;
 	char ip_opts[40];
 };
@@ -242,14 +242,12 @@
 #define MCAST_EXCLUDE 0
 #define MCAST_INCLUDE 1
 
-struct ip_mreq
-{
+struct ip_mreq {
 	struct in_addr imr_multiaddr;
 	struct in_addr imr_interface;
 };
 
-struct ip_mreqn
-{
+struct ip_mreqn {
 	struct in_addr imr_multiaddr;
 	struct in_addr imr_address;
 	int imr_ifindex;
@@ -294,21 +292,18 @@
 	(sizeof(struct group_filter) - sizeof(struct sockaddr_storage) \
 	+ (numsrc) * sizeof(struct sockaddr_storage))
 
-struct in_pktinfo
-{
+struct in_pktinfo {
 	int ipi_ifindex;
 	struct in_addr ipi_spec_dst;
 	struct in_addr ipi_addr;
 };
 
-struct in6_pktinfo
-{
+struct in6_pktinfo {
 	struct in6_addr ipi6_addr;
 	unsigned ipi6_ifindex;
 };
 
-struct ip6_mtuinfo
-{
+struct ip6_mtuinfo {
 	struct sockaddr_in6 ip6m_addr;
 	uint32_t ip6m_mtu;
 };
@@ -339,6 +334,7 @@
 #define IPV6_LEAVE_ANYCAST      28
 #define IPV6_IPSEC_POLICY       34
 #define IPV6_XFRM_POLICY        35
+#define IPV6_HDRINCL            36
 
 #define IPV6_RECVPKTINFO        49
 #define IPV6_PKTINFO            50
@@ -351,9 +347,18 @@
 #define IPV6_RTHDR              57
 #define IPV6_RECVDSTOPTS        58
 #define IPV6_DSTOPTS            59
-
+#define IPV6_RECVPATHMTU        60
+#define IPV6_PATHMTU            61
+#define IPV6_DONTFRAG           62
 #define IPV6_RECVTCLASS         66
 #define IPV6_TCLASS             67
+#define IPV6_AUTOFLOWLABEL      70
+#define IPV6_ADDR_PREFERENCES   72
+#define IPV6_MINHOPCOUNT        73
+#define IPV6_ORIGDSTADDR        74
+#define IPV6_RECVORIGDSTADDR    IPV6_ORIGDSTADDR
+#define IPV6_TRANSPARENT        75
+#define IPV6_UNICAST_IF         76
 
 #define IPV6_ADD_MEMBERSHIP     IPV6_JOIN_GROUP
 #define IPV6_DROP_MEMBERSHIP    IPV6_LEAVE_GROUP
@@ -364,6 +369,16 @@
 #define IPV6_PMTUDISC_WANT      1
 #define IPV6_PMTUDISC_DO        2
 #define IPV6_PMTUDISC_PROBE     3
+#define IPV6_PMTUDISC_INTERFACE 4
+#define IPV6_PMTUDISC_OMIT      5
+
+#define IPV6_PREFER_SRC_TMP            0x0001
+#define IPV6_PREFER_SRC_PUBLIC         0x0002
+#define IPV6_PREFER_SRC_PUBTMP_DEFAULT 0x0100
+#define IPV6_PREFER_SRC_COA            0x0004
+#define IPV6_PREFER_SRC_HOME           0x0400
+#define IPV6_PREFER_SRC_CGA            0x0008
+#define IPV6_PREFER_SRC_NONCGA         0x0800
 
 #define IPV6_RTHDR_LOOSE        0
 #define IPV6_RTHDR_STRICT       1
diff --git a/system/include/libc/netinet/ip.h b/system/include/libc/netinet/ip.h
index 4118741..d7fa8d5 100644
--- a/system/include/libc/netinet/ip.h
+++ b/system/include/libc/netinet/ip.h
@@ -104,6 +104,18 @@
 #define	IPTOS_DSCP_AF43		0x98
 #define	IPTOS_DSCP_EF		0xb8
 
+#define	IPTOS_CLASS_MASK	0xe0
+#define	IPTOS_CLASS(x)		((x) & IPTOS_CLASS_MASK)
+#define	IPTOS_CLASS_CS0		0x00
+#define	IPTOS_CLASS_CS1		0x20
+#define	IPTOS_CLASS_CS2		0x40
+#define	IPTOS_CLASS_CS3		0x60
+#define	IPTOS_CLASS_CS4		0x80
+#define	IPTOS_CLASS_CS5		0xa0
+#define	IPTOS_CLASS_CS6		0xc0
+#define	IPTOS_CLASS_CS7		0xe0
+#define	IPTOS_CLASS_DEFAULT	IPTOS_CLASS_CS0
+
 #define	IPTOS_TOS_MASK		0x1E
 #define	IPTOS_TOS(tos)		((tos) & IPTOS_TOS_MASK)
 #define	IPTOS_LOWDELAY		0x10
diff --git a/system/include/libc/netinet/tcp.h b/system/include/libc/netinet/tcp.h
index d3db042..9ea64c4 100644
--- a/system/include/libc/netinet/tcp.h
+++ b/system/include/libc/netinet/tcp.h
@@ -27,6 +27,9 @@
 #define TCP_FASTOPEN     23
 #define TCP_TIMESTAMP    24
 #define TCP_NOTSENT_LOWAT 25
+#define TCP_CC_INFO      26
+#define TCP_SAVE_SYN     27
+#define TCP_SAVED_SYN    28
 
 #define TCP_ESTABLISHED  1
 #define TCP_SYN_SENT     2
@@ -41,7 +44,20 @@
 #define TCP_CLOSING      11
 
 #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+#define TCPOPT_EOL              0
+#define TCPOPT_NOP              1
+#define TCPOPT_MAXSEG           2
+#define TCPOPT_WINDOW           3
+#define TCPOPT_SACK_PERMITTED   4
+#define TCPOPT_SACK             5
+#define TCPOPT_TIMESTAMP        8
+#define TCPOLEN_SACK_PERMITTED  2
+#define TCPOLEN_WINDOW          3
+#define TCPOLEN_MAXSEG          4
+#define TCPOLEN_TIMESTAMP       10
+
 #define SOL_TCP 6
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <stdint.h>
@@ -129,8 +145,7 @@
 #define TCP_CA_Recovery		3
 #define TCP_CA_Loss		4
 
-struct tcp_info
-{
+struct tcp_info {
 	uint8_t tcpi_state;
 	uint8_t tcpi_ca_state;
 	uint8_t tcpi_retransmits;
@@ -162,12 +177,21 @@
 	uint32_t tcpi_rcv_rtt;
 	uint32_t tcpi_rcv_space;
 	uint32_t tcpi_total_retrans;
+	uint64_t tcpi_pacing_rate;
+	uint64_t tcpi_max_pacing_rate;
+	uint64_t tcpi_bytes_acked;
+	uint64_t tcpi_bytes_received;
+	uint32_t tcpi_segs_out;
+	uint32_t tcpi_segs_in;
+	uint32_t tcpi_notsent_bytes;
+	uint32_t tcpi_min_rtt;
+	uint32_t tcpi_data_segs_in;
+	uint32_t tcpi_data_segs_out;
 };
 
 #define TCP_MD5SIG_MAXKEYLEN    80
 
-struct tcp_md5sig
-{
+struct tcp_md5sig {
 	struct sockaddr_storage tcpm_addr;
 	uint16_t __tcpm_pad1;
 	uint16_t tcpm_keylen;
diff --git a/system/include/libc/netpacket/packet.h b/system/include/libc/netpacket/packet.h
index fa53712..f2210ce 100644
--- a/system/include/libc/netpacket/packet.h
+++ b/system/include/libc/netpacket/packet.h
@@ -32,10 +32,27 @@
 #define	PACKET_RECV_OUTPUT		3
 #define	PACKET_RX_RING			5
 #define	PACKET_STATISTICS		6
+#define PACKET_COPY_THRESH		7
+#define PACKET_AUXDATA			8
+#define PACKET_ORIGDEV			9
+#define PACKET_VERSION			10
+#define PACKET_HDRLEN			11
+#define PACKET_RESERVE			12
+#define PACKET_TX_RING			13
+#define PACKET_LOSS			14
+#define PACKET_VNET_HDR			15
+#define PACKET_TX_TIMESTAMP		16
+#define PACKET_TIMESTAMP		17
+#define PACKET_FANOUT			18
+#define PACKET_TX_HAS_OFF		19
+#define PACKET_QDISC_BYPASS		20
+#define PACKET_ROLLOVER_STATS		21
+#define PACKET_FANOUT_DATA		22
 
 #define PACKET_MR_MULTICAST	0
 #define PACKET_MR_PROMISC	1
 #define PACKET_MR_ALLMULTI	2
+#define PACKET_MR_UNICAST	3
 
 #ifdef __cplusplus
 }
diff --git a/system/include/libc/poll.h b/system/include/libc/poll.h
index 7af7372..daccc76 100644
--- a/system/include/libc/poll.h
+++ b/system/include/libc/poll.h
@@ -28,8 +28,7 @@
 
 typedef unsigned long nfds_t;
 
-struct pollfd
-{
+struct pollfd {
 	int fd;
 	short events;
 	short revents;
diff --git a/system/include/libc/pthread.h b/system/include/libc/pthread.h
index 0e92c28..378ae29 100644
--- a/system/include/libc/pthread.h
+++ b/system/include/libc/pthread.h
@@ -63,6 +63,7 @@
 
 #define PTHREAD_CANCEL_ENABLE 0
 #define PTHREAD_CANCEL_DISABLE 1
+#define PTHREAD_CANCEL_MASKED 2
 
 #define PTHREAD_CANCEL_DEFERRED 0
 #define PTHREAD_CANCEL_ASYNCHRONOUS 1
@@ -78,13 +79,12 @@
 _Noreturn void pthread_exit(void *);
 int pthread_join(pthread_t, void **);
 
-#ifdef __GNUC__
-__attribute__((const))
-#endif
 pthread_t pthread_self(void);
 
 int pthread_equal(pthread_t, pthread_t);
+#ifndef __cplusplus
 #define pthread_equal(x,y) ((x)==(y))
+#endif
 
 int pthread_setcancelstate(int, int *);
 int pthread_setcanceltype(int, int *);
@@ -221,6 +221,8 @@
 int pthread_getaffinity_np(pthread_t, size_t, struct cpu_set_t *);
 int pthread_setaffinity_np(pthread_t, size_t, const struct cpu_set_t *);
 int pthread_getattr_np(pthread_t, pthread_attr_t *);
+int pthread_tryjoin_np(pthread_t, void **);
+int pthread_timedjoin_np(pthread_t, void **, const struct timespec *);
 #endif
 
 #ifdef __cplusplus
diff --git a/system/include/libc/pwd.h b/system/include/libc/pwd.h
index 55d9d42..07a5871 100644
--- a/system/include/libc/pwd.h
+++ b/system/include/libc/pwd.h
@@ -17,8 +17,7 @@
 
 #include <bits/alltypes.h>
 
-struct passwd
-{
+struct passwd {
 	char *pw_name;
 	char *pw_passwd;
 	uid_t pw_uid;
diff --git a/system/include/libc/sched.h b/system/include/libc/sched.h
index 0246675..af82d6c 100644
--- a/system/include/libc/sched.h
+++ b/system/include/libc/sched.h
@@ -38,6 +38,7 @@
 #define SCHED_RR 2
 #define SCHED_BATCH 3
 #define SCHED_IDLE 5
+#define SCHED_DEADLINE 6
 #define SCHED_RESET_ON_FORK 0x40000000
 
 #ifdef _GNU_SOURCE
@@ -58,6 +59,7 @@
 #define CLONE_DETACHED	0x00400000
 #define CLONE_UNTRACED	0x00800000
 #define CLONE_CHILD_SETTID	0x01000000
+#define CLONE_NEWCGROUP	0x02000000
 #define CLONE_NEWUTS	0x04000000
 #define CLONE_NEWIPC	0x08000000
 #define CLONE_NEWUSER	0x10000000
@@ -75,6 +77,7 @@
 
 typedef struct cpu_set_t { unsigned long __bits[128/sizeof(long)]; } cpu_set_t;
 int __sched_cpucount(size_t, const cpu_set_t *);
+int sched_getcpu(void);
 int sched_getaffinity(pid_t, size_t, cpu_set_t *);
 int sched_setaffinity(pid_t, size_t, const cpu_set_t *);
 
diff --git a/system/include/libc/search.h b/system/include/libc/search.h
index 27f6107..02e407e 100644
--- a/system/include/libc/search.h
+++ b/system/include/libc/search.h
@@ -22,6 +22,18 @@
 void hdestroy(void);
 ENTRY *hsearch(ENTRY, ACTION);
 
+#ifdef _GNU_SOURCE
+struct hsearch_data {
+	struct __tab *__tab;
+	unsigned int __unused1;
+	unsigned int __unused2;
+};
+
+int hcreate_r(size_t, struct hsearch_data *);
+void hdestroy_r(struct hsearch_data *);
+int hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *);
+#endif
+
 void insque(void *, void *);
 void remque(void *);
 
diff --git a/system/include/libc/semaphore.h b/system/include/libc/semaphore.h
index 20d46f0..277c47d 100644
--- a/system/include/libc/semaphore.h
+++ b/system/include/libc/semaphore.h
@@ -15,7 +15,7 @@
 #define SEM_FAILED ((sem_t *)0)
 
 typedef struct {
-	int __val[4*sizeof(long)/sizeof(int)];
+	volatile int __val[4*sizeof(long)/sizeof(int)];
 } sem_t;
 
 int    sem_close(sem_t *);
diff --git a/system/include/libc/setjmp.h b/system/include/libc/setjmp.h
index 0da27de..2d43abf 100644
--- a/system/include/libc/setjmp.h
+++ b/system/include/libc/setjmp.h
@@ -33,7 +33,6 @@
 _Noreturn void longjmp (jmp_buf, int);
 
 #define setjmp setjmp
-#define longjmp longjmp
 
 #ifdef __cplusplus
 }
diff --git a/system/include/libc/signal.h b/system/include/libc/signal.h
index 3fb21b2..a16094c 100644
--- a/system/include/libc/signal.h
+++ b/system/include/libc/signal.h
@@ -27,8 +27,6 @@
 
 #include <bits/alltypes.h>
 
-#define SIG_HOLD ((void (*)(int)) 2)
-
 #define SIG_BLOCK     0
 #define SIG_UNBLOCK   1
 #define SIG_SETMASK   2
@@ -43,6 +41,18 @@
 #define SI_USER 0
 #define SI_KERNEL 128
 
+typedef struct sigaltstack stack_t;
+
+#endif
+
+#include <bits/signal.h>
+
+#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
+ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
+ || defined(_BSD_SOURCE)
+
+#define SIG_HOLD ((void (*)(int)) 2)
+
 #define FPE_INTDIV 1
 #define FPE_INTOVF 2
 #define FPE_FLTDIV 3
@@ -63,6 +73,8 @@
 
 #define SEGV_MAPERR 1
 #define SEGV_ACCERR 2
+#define SEGV_BNDERR 3
+#define SEGV_PKUERR 4
 
 #define BUS_ADRALN 1
 #define BUS_ADRERR 2
@@ -77,34 +89,48 @@
 #define CLD_STOPPED 5
 #define CLD_CONTINUED 6
 
-typedef struct sigaltstack stack_t;
-
 union sigval {
 	int sival_int;
 	void *sival_ptr;
 };
 
 typedef struct {
+#ifdef __SI_SWAP_ERRNO_CODE
+	int si_signo, si_code, si_errno;
+#else
 	int si_signo, si_errno, si_code;
+#endif
 	union {
 		char __pad[128 - 2*sizeof(int) - sizeof(long)];
 		struct {
-			pid_t si_pid;
-			uid_t si_uid;
-			union sigval si_sigval;
-		} __rt;
-		struct {
-			unsigned int si_timer1, si_timer2;
-		} __timer;
-		struct {
-			pid_t si_pid;
-			uid_t si_uid;
-			int si_status;
-			clock_t si_utime, si_stime;
-		} __sigchld;
+			union {
+				struct {
+					pid_t si_pid;
+					uid_t si_uid;
+				} __piduid;
+				struct {
+					int si_timerid;
+					int si_overrun;
+				} __timer;
+			} __first;
+			union {
+				union sigval si_value;
+				struct {
+					int si_status;
+					clock_t si_utime, si_stime;
+				} __sigchld;
+			} __second;
+		} __si_common;
 		struct {
 			void *si_addr;
 			short si_addr_lsb;
+			union {
+				struct {
+					void *si_lower;
+					void *si_upper;
+				} __addr_bnd;
+				unsigned si_pkey;
+			} __first;
 		} __sigfault;
 		struct {
 			long si_band;
@@ -117,20 +143,23 @@
 		} __sigsys;
 	} __si_fields;
 } siginfo_t;
-#define si_pid     __si_fields.__sigchld.si_pid
-#define si_uid     __si_fields.__sigchld.si_uid
-#define si_status  __si_fields.__sigchld.si_status
-#define si_utime   __si_fields.__sigchld.si_utime
-#define si_stime   __si_fields.__sigchld.si_stime
-#define si_value   __si_fields.__rt.si_sigval
+#define si_pid     __si_fields.__si_common.__first.__piduid.si_pid
+#define si_uid     __si_fields.__si_common.__first.__piduid.si_uid
+#define si_status  __si_fields.__si_common.__second.__sigchld.si_status
+#define si_utime   __si_fields.__si_common.__second.__sigchld.si_utime
+#define si_stime   __si_fields.__si_common.__second.__sigchld.si_stime
+#define si_value   __si_fields.__si_common.__second.si_value
 #define si_addr    __si_fields.__sigfault.si_addr
 #define si_addr_lsb __si_fields.__sigfault.si_addr_lsb
+#define si_lower   __si_fields.__sigfault.__first.__addr_bnd.si_lower
+#define si_upper   __si_fields.__sigfault.__first.__addr_bnd.si_upper
+#define si_pkey    __si_fields.__sigfault.__first.si_pkey
 #define si_band    __si_fields.__sigpoll.si_band
 #define si_fd      __si_fields.__sigpoll.si_fd
-#define si_timer1  __si_fields.__timer.si_timer1
-#define si_timer2  __si_fields.__timer.si_timer2
-#define si_ptr     __si_fields.__rt.si_sigval.sival_ptr
-#define si_int     __si_fields.__rt.si_sigval.sival_int
+#define si_timerid __si_fields.__si_common.__first.__timer.si_timerid
+#define si_overrun __si_fields.__si_common.__first.__timer.si_overrun
+#define si_ptr     si_value.sival_ptr
+#define si_int     si_value.sival_int
 #define si_call_addr __si_fields.__sigsys.si_call_addr
 #define si_syscall __si_fields.__sigsys.si_syscall
 #define si_arch    __si_fields.__sigsys.si_arch
@@ -191,7 +220,7 @@
 
 #endif
 
-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
+#if defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
 int killpg(pid_t, int);
 int sigaltstack(const stack_t *__restrict, stack_t *__restrict);
 int sighold(int);
@@ -210,8 +239,6 @@
 #define POLL_HUP 6
 #define SS_ONSTACK    1
 #define SS_DISABLE    2
-#define MINSIGSTKSZ 2048
-#define SIGSTKSZ 8192
 #endif
 
 #if defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
@@ -230,8 +257,6 @@
 #define SA_ONESHOT SA_RESETHAND
 #endif
 
-#include <bits/signal.h>
-
 #define SIG_ERR  ((void (*)(int))-1)
 #define SIG_DFL  ((void (*)(int)) 0)
 #define SIG_IGN  ((void (*)(int)) 1)
diff --git a/system/include/libc/stdalign.h b/system/include/libc/stdalign.h
index b6e50ae..2cc94be 100644
--- a/system/include/libc/stdalign.h
+++ b/system/include/libc/stdalign.h
@@ -1,6 +1,8 @@
 #ifndef _STDALIGN_H
 #define _STDALIGN_H
 
+#ifndef __cplusplus
+
 /* this whole header only works in C11 or with compiler extensions */
 #if __STDC_VERSION__ < 201112L && defined( __GNUC__)
 #define _Alignas(t) __attribute__((__aligned__(t)))
@@ -9,6 +11,9 @@
 
 #define alignas _Alignas
 #define alignof _Alignof
+
+#endif
+
 #define __alignas_is_defined 1
 #define __alignof_is_defined 1
 
diff --git a/system/include/libc/stdc-predef.h b/system/include/libc/stdc-predef.h
new file mode 100644
index 0000000..f8cd4b8
--- /dev/null
+++ b/system/include/libc/stdc-predef.h
@@ -0,0 +1,10 @@
+#ifndef _STDC_PREDEF_H
+#define _STDC_PREDEF_H
+
+#define __STDC_ISO_10646__ 201206L
+
+#if !defined(__GCC_IEC_559) || __GCC_IEC_559 > 0
+#define __STDC_IEC_559__ 1
+#endif
+
+#endif
diff --git a/system/include/libc/stddef.h b/system/include/libc/stddef.h
index 0a32919..bd75385 100644
--- a/system/include/libc/stddef.h
+++ b/system/include/libc/stddef.h
@@ -10,6 +10,9 @@
 #define __NEED_ptrdiff_t
 #define __NEED_size_t
 #define __NEED_wchar_t
+#if __STDC_VERSION__ >= 201112L || __cplusplus >= 201103L
+#define __NEED_max_align_t
+#endif
 
 #include <bits/alltypes.h>
 
diff --git a/system/include/libc/stdint.h b/system/include/libc/stdint.h
index 518d05b..a296819 100644
--- a/system/include/libc/stdint.h
+++ b/system/include/libc/stdint.h
@@ -47,8 +47,8 @@
 
 #define UINT8_MAX  (0xff)
 #define UINT16_MAX (0xffff)
-#define UINT32_MAX (0xffffffff)
-#define UINT64_MAX (0xffffffffffffffff)
+#define UINT32_MAX (0xffffffffu)
+#define UINT64_MAX (0xffffffffffffffffu)
 
 #define INT_FAST8_MIN   INT8_MIN
 #define INT_FAST64_MIN  INT64_MIN
diff --git a/system/include/libc/stdlib.h b/system/include/libc/stdlib.h
index f034c6e..d2c911f 100644
--- a/system/include/libc/stdlib.h
+++ b/system/include/libc/stdlib.h
@@ -76,7 +76,8 @@
 #define EXIT_FAILURE 1
 #define EXIT_SUCCESS 0
 
-#define MB_CUR_MAX ((size_t)+4)
+size_t __ctype_get_mb_cur_max(void);
+#define MB_CUR_MAX (__ctype_get_mb_cur_max())
 
 #define RAND_MAX (0x7fffffff)
 
@@ -114,9 +115,6 @@
 void srandom (unsigned int);
 char *initstate (unsigned int, char *, size_t);
 char *setstate (char *);
-#endif
-
-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
 int putenv (char *);
 int posix_openpt (int);
 int grantpt (int);
@@ -144,12 +142,12 @@
 void *valloc (size_t);
 void *memalign(size_t, size_t);
 int getloadavg(double *, int);
+int clearenv(void);
 #define WCOREDUMP(s) ((s) & 0x80)
 #define WIFCONTINUED(s) ((s) == 0xffff)
 #endif
 
 #ifdef _GNU_SOURCE
-int clearenv(void);
 int ptsname_r(int, char *, size_t);
 char *ecvt(double, int, int *, int *);
 char *fcvt(double, int, int *, int *);
diff --git a/system/include/libc/stdnoreturn.h b/system/include/libc/stdnoreturn.h
index 60d924a..5c6aeeb 100644
--- a/system/include/libc/stdnoreturn.h
+++ b/system/include/libc/stdnoreturn.h
@@ -1,5 +1,7 @@
 #ifndef _STDNORETURN_H
 #define _STDNORETURN_H
+#ifndef __cplusplus
 #include <features.h>
 #define noreturn _Noreturn
 #endif
+#endif
diff --git a/system/include/libc/strings.h b/system/include/libc/strings.h
index 2b7e086..db0960b 100644
--- a/system/include/libc/strings.h
+++ b/system/include/libc/strings.h
@@ -20,7 +20,11 @@
 char *rindex (const char *, int);
 #endif
 
+#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)  || defined(_BSD_SOURCE)
 int ffs (int);
+int ffsl (long);
+int ffsll (long long);
+#endif
 
 int strcasecmp (const char *, const char *);
 int strncasecmp (const char *, const char *, size_t);
diff --git a/system/include/libc/sys/acct.h b/system/include/libc/sys/acct.h
index ee576c4..9b0ba36 100644
--- a/system/include/libc/sys/acct.h
+++ b/system/include/libc/sys/acct.h
@@ -14,8 +14,7 @@
 
 typedef uint16_t comp_t;
 
-struct acct
-{
+struct acct {
 	char ac_flag;
 	uint16_t ac_uid;
 	uint16_t ac_gid;
@@ -36,8 +35,7 @@
 };
 
 
-struct acct_v3
-{
+struct acct_v3 {
 	char ac_flag;
 	char ac_version;
 	uint16_t ac_tty;
diff --git a/system/include/libc/sys/auxv.h b/system/include/libc/sys/auxv.h
new file mode 100644
index 0000000..6dcf9ad
--- /dev/null
+++ b/system/include/libc/sys/auxv.h
@@ -0,0 +1,16 @@
+#ifndef _SYS_AUXV_H
+#define _SYS_AUXV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <elf.h>
+
+unsigned long getauxval(unsigned long);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/system/include/libc/sys/epoll.h b/system/include/libc/sys/epoll.h
index 1f0312e..ffe2311 100644
--- a/system/include/libc/sys/epoll.h
+++ b/system/include/libc/sys/epoll.h
@@ -28,6 +28,7 @@
 #define EPOLLERR 0x008
 #define EPOLLHUP 0x010
 #define EPOLLRDHUP 0x2000
+#define EPOLLEXCLUSIVE (1U<<28)
 #define EPOLLWAKEUP (1U<<29)
 #define EPOLLONESHOT (1U<<30)
 #define EPOLLET (1U<<31)
diff --git a/system/include/libc/sys/mman.h b/system/include/libc/sys/mman.h
index a34448a..8a5149c 100644
--- a/system/include/libc/sys/mman.h
+++ b/system/include/libc/sys/mman.h
@@ -16,6 +16,66 @@
 
 #include <bits/alltypes.h>
 
+#define MAP_FAILED ((void *) -1)
+
+#define MAP_SHARED     0x01
+#define MAP_PRIVATE    0x02
+#define MAP_TYPE       0x0f
+#define MAP_FIXED      0x10
+#define MAP_ANON       0x20
+#define MAP_ANONYMOUS  MAP_ANON
+#define MAP_NORESERVE  0x4000
+#define MAP_GROWSDOWN  0x0100
+#define MAP_DENYWRITE  0x0800
+#define MAP_EXECUTABLE 0x1000
+#define MAP_LOCKED     0x2000
+#define MAP_POPULATE   0x8000
+#define MAP_NONBLOCK   0x10000
+#define MAP_STACK      0x20000
+#define MAP_HUGETLB    0x40000
+#define MAP_FILE       0
+
+#define PROT_NONE      0
+#define PROT_READ      1
+#define PROT_WRITE     2
+#define PROT_EXEC      4
+#define PROT_GROWSDOWN 0x01000000
+#define PROT_GROWSUP   0x02000000
+
+#define MS_ASYNC       1
+#define MS_INVALIDATE  2
+#define MS_SYNC        4
+
+#define MCL_CURRENT    1
+#define MCL_FUTURE     2
+#define MCL_ONFAULT    4
+
+#define POSIX_MADV_NORMAL     0
+#define POSIX_MADV_RANDOM     1
+#define POSIX_MADV_SEQUENTIAL 2
+#define POSIX_MADV_WILLNEED   3
+#define POSIX_MADV_DONTNEED   4
+
+#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+#define MADV_NORMAL      0
+#define MADV_RANDOM      1
+#define MADV_SEQUENTIAL  2
+#define MADV_WILLNEED    3
+#define MADV_DONTNEED    4
+#define MADV_FREE        8
+#define MADV_REMOVE      9
+#define MADV_DONTFORK    10
+#define MADV_DOFORK      11
+#define MADV_MERGEABLE   12
+#define MADV_UNMERGEABLE 13
+#define MADV_HUGEPAGE    14
+#define MADV_NOHUGEPAGE  15
+#define MADV_DONTDUMP    16
+#define MADV_DODUMP      17
+#define MADV_HWPOISON    100
+#define MADV_SOFT_OFFLINE 101
+#endif
+
 #include <bits/mman.h>
 
 void *mmap (void *, size_t, int, int, int, off_t);
@@ -32,11 +92,14 @@
 int munlockall (void);
 
 #ifdef _GNU_SOURCE
+#define MREMAP_MAYMOVE 1
+#define MREMAP_FIXED 2
 void *mremap (void *, size_t, size_t, int, ...);
 int remap_file_pages (void *, size_t, int, size_t, int);
 #endif
 
 #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+#define MLOCK_ONFAULT   0x01
 int madvise (void *, size_t, int);
 int mincore (void *, size_t, unsigned char *);
 #endif
diff --git a/system/include/libc/sys/mount.h b/system/include/libc/sys/mount.h
index 1e1907f..6674e65 100644
--- a/system/include/libc/sys/mount.h
+++ b/system/include/libc/sys/mount.h
@@ -46,12 +46,13 @@
 #define MS_KERNMOUNT   (1<<22)
 #define MS_I_VERSION   (1<<23)
 #define MS_STRICTATIME (1<<24)
+#define MS_LAZYTIME    (1<<25)
 #define MS_NOSEC       (1<<28)
 #define MS_BORN        (1<<29)
 #define MS_ACTIVE      (1<<30)
 #define MS_NOUSER      (1U<<31)
 
-#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION)
+#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME)
 
 #define MS_MGC_VAL 0xc0ed0000
 #define MS_MGC_MSK 0xffff0000
diff --git a/system/include/libc/sys/prctl.h b/system/include/libc/sys/prctl.h
index d41ff0f..24f4f8b 100644
--- a/system/include/libc/sys/prctl.h
+++ b/system/include/libc/sys/prctl.h
@@ -5,6 +5,8 @@
 extern "C" {
 #endif
 
+#include <stdint.h>
+
 #define PR_SET_PDEATHSIG  1
 #define PR_GET_PDEATHSIG  2
 #define PR_GET_DUMPABLE   3
@@ -80,6 +82,25 @@
 #define PR_SET_MM_ENV_END              11
 #define PR_SET_MM_AUXV                 12
 #define PR_SET_MM_EXE_FILE             13
+#define PR_SET_MM_MAP                  14
+#define PR_SET_MM_MAP_SIZE             15
+
+struct prctl_mm_map {
+	uint64_t start_code;
+	uint64_t end_code;
+	uint64_t start_data;
+	uint64_t end_data;
+	uint64_t start_brk;
+	uint64_t brk;
+	uint64_t start_stack;
+	uint64_t arg_start;
+	uint64_t arg_end;
+	uint64_t env_start;
+	uint64_t env_end;
+	uint64_t *auxv;
+	uint32_t auxv_size;
+	uint32_t exe_fd;
+};
 
 #define PR_SET_PTRACER 0x59616d61
 #define PR_SET_PTRACER_ANY (-1UL)
@@ -92,6 +113,23 @@
 
 #define PR_GET_TID_ADDRESS      40
 
+#define PR_SET_THP_DISABLE      41
+#define PR_GET_THP_DISABLE      42
+
+#define PR_MPX_ENABLE_MANAGEMENT  43
+#define PR_MPX_DISABLE_MANAGEMENT 44
+
+#define PR_SET_FP_MODE          45
+#define PR_GET_FP_MODE          46
+#define PR_FP_MODE_FR (1 << 0)
+#define PR_FP_MODE_FRE (1 << 1)
+
+#define PR_CAP_AMBIENT          47
+#define PR_CAP_AMBIENT_IS_SET   1
+#define PR_CAP_AMBIENT_RAISE    2
+#define PR_CAP_AMBIENT_LOWER    3
+#define PR_CAP_AMBIENT_CLEAR_ALL 4
+
 int prctl (int, ...);
 
 #ifdef __cplusplus
diff --git a/system/include/libc/sys/procfs.h b/system/include/libc/sys/procfs.h
index f7936c4..e23bf1a 100644
--- a/system/include/libc/sys/procfs.h
+++ b/system/include/libc/sys/procfs.h
@@ -33,8 +33,7 @@
 
 #define ELF_PRARGSZ 80
 
-struct elf_prpsinfo
-	{
+struct elf_prpsinfo {
 	char pr_state;
 	char pr_sname;
 	char pr_zomb;
diff --git a/system/include/libc/sys/ptrace.h b/system/include/libc/sys/ptrace.h
index a133e66..d9d4540 100644
--- a/system/include/libc/sys/ptrace.h
+++ b/system/include/libc/sys/ptrace.h
@@ -39,6 +39,7 @@
 #define PTRACE_PEEKSIGINFO 0x4209
 #define PTRACE_GETSIGMASK 0x420a
 #define PTRACE_SETSIGMASK 0x420b
+#define PTRACE_SECCOMP_GET_FILTER 0x420c
 
 #define PT_READ_I PTRACE_PEEKTEXT
 #define PT_READ_D PTRACE_PEEKDATA
@@ -72,7 +73,8 @@
 #define PTRACE_O_TRACEEXIT      0x00000040
 #define PTRACE_O_TRACESECCOMP   0x00000080
 #define PTRACE_O_EXITKILL       0x00100000
-#define PTRACE_O_MASK           0x001000ff
+#define PTRACE_O_SUSPEND_SECCOMP 0x00200000
+#define PTRACE_O_MASK           0x003000ff
 
 #define PTRACE_EVENT_FORK 1
 #define PTRACE_EVENT_VFORK 2
diff --git a/system/include/libc/sys/quota.h b/system/include/libc/sys/quota.h
index a06f335..3ed7378 100644
--- a/system/include/libc/sys/quota.h
+++ b/system/include/libc/sys/quota.h
@@ -57,8 +57,7 @@
 #define QIF_TIMES	(QIF_BTIME | QIF_ITIME)
 #define QIF_ALL		(QIF_LIMITS | QIF_USAGE | QIF_TIMES)
 
-struct dqblk
-{
+struct dqblk {
 	uint64_t dqb_bhardlimit;
 	uint64_t dqb_bsoftlimit;
 	uint64_t dqb_curspace;
@@ -87,8 +86,7 @@
 #define IIF_FLAGS	4
 #define IIF_ALL		(IIF_BGRACE | IIF_IGRACE | IIF_FLAGS)
 
-struct dqinfo
-{
+struct dqinfo {
 	uint64_t dqi_bgrace;
 	uint64_t dqi_igrace;
 	uint32_t dqi_flags;
diff --git a/system/include/libc/sys/resource.h b/system/include/libc/sys/resource.h
index 58392d6..70d793d 100644
--- a/system/include/libc/sys/resource.h
+++ b/system/include/libc/sys/resource.h
@@ -19,14 +19,12 @@
 
 typedef unsigned long long rlim_t;
 
-struct rlimit
-{
+struct rlimit {
 	rlim_t rlim_cur;
 	rlim_t rlim_max;
 };
 
-struct rusage
-{
+struct rusage {
 	struct timeval ru_utime;
 	struct timeval ru_stime;
 	/* linux extentions, but useful */
@@ -68,7 +66,8 @@
 #define PRIO_USER    2
 
 #define RUSAGE_SELF     0
-#define RUSAGE_CHILDREN 1
+#define RUSAGE_CHILDREN (-1)
+#define RUSAGE_THREAD   1
 
 #define RLIM_INFINITY (~0ULL)
 #define RLIM_SAVED_CUR RLIM_INFINITY
@@ -96,6 +95,9 @@
 #define RLIM_NLIMITS RLIMIT_NLIMITS
 
 #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
+#define RLIM64_INFINITY RLIM_INFINITY
+#define RLIM64_SAVED_CUR RLIM_SAVED_CUR
+#define RLIM64_SAVED_MAX RLIM_SAVED_MAX
 #define getrlimit64 getrlimit
 #define setrlimit64 setrlimit
 #define rlimit64 rlimit
diff --git a/system/include/libc/sys/select.h b/system/include/libc/sys/select.h
index e25257d..d34cbf1 100644
--- a/system/include/libc/sys/select.h
+++ b/system/include/libc/sys/select.h
@@ -19,8 +19,7 @@
 
 typedef unsigned long fd_mask;
 
-typedef struct
-{
+typedef struct {
 	unsigned long fds_bits[FD_SETSIZE / 8 / sizeof(long)];
 } fd_set;
 
diff --git a/system/include/libc/sys/socket.h b/system/include/libc/sys/socket.h
index 8e8c9e9..59ab1e2 100644
--- a/system/include/libc/sys/socket.h
+++ b/system/include/libc/sys/socket.h
@@ -20,16 +20,24 @@
 #include <bits/socket.h>
 
 #ifdef _GNU_SOURCE
-struct ucred
-{
+struct ucred {
 	pid_t pid;
 	uid_t uid;
 	gid_t gid;
 };
+
+struct mmsghdr {
+	struct msghdr msg_hdr;
+	unsigned int  msg_len;
+};
+
+struct timespec;
+
+int sendmmsg (int, struct mmsghdr *, unsigned int, unsigned int);
+int recvmmsg (int, struct mmsghdr *, unsigned int, unsigned int, struct timespec *);
 #endif
 
-struct linger
-{
+struct linger {
 	int l_onoff;
 	int l_linger;
 };
@@ -85,6 +93,7 @@
 #define PF_WANPIPE      25
 #define PF_LLC          26
 #define PF_IB           27
+#define PF_MPLS         28
 #define PF_CAN          29
 #define PF_TIPC         30
 #define PF_BLUETOOTH    31
@@ -97,7 +106,8 @@
 #define PF_ALG          38
 #define PF_NFC          39
 #define PF_VSOCK        40
-#define PF_MAX          41
+#define PF_KCM          41
+#define PF_MAX          42
 
 #define AF_UNSPEC       PF_UNSPEC
 #define AF_LOCAL        PF_LOCAL
@@ -130,6 +140,7 @@
 #define AF_WANPIPE      PF_WANPIPE
 #define AF_LLC          PF_LLC
 #define AF_IB           PF_IB
+#define AF_MPLS         PF_MPLS
 #define AF_CAN          PF_CAN
 #define AF_TIPC         PF_TIPC
 #define AF_BLUETOOTH    PF_BLUETOOTH
@@ -142,6 +153,7 @@
 #define AF_ALG          PF_ALG
 #define AF_NFC          PF_NFC
 #define AF_VSOCK        PF_VSOCK
+#define AF_KCM          PF_KCM
 #define AF_MAX          PF_MAX
 
 #ifndef SO_DEBUG
@@ -166,8 +178,11 @@
 #define SO_SNDLOWAT     19
 #define SO_RCVTIMEO     20
 #define SO_SNDTIMEO     21
+#define SO_ACCEPTCONN   30
 #define SO_SNDBUFFORCE  32
 #define SO_RCVBUFFORCE  33
+#define SO_PROTOCOL     38
+#define SO_DOMAIN       39
 #endif
 
 #define SO_SECURITY_AUTHENTICATION              22
@@ -184,7 +199,6 @@
 #define SO_TIMESTAMP            29
 #define SCM_TIMESTAMP           SO_TIMESTAMP
 
-#define SO_ACCEPTCONN           30
 #define SO_PEERSEC              31
 #define SO_PASSSEC              34
 #define SO_TIMESTAMPNS          35
@@ -192,8 +206,6 @@
 #define SO_MARK                 36
 #define SO_TIMESTAMPING         37
 #define SCM_TIMESTAMPING        SO_TIMESTAMPING
-#define SO_PROTOCOL             38
-#define SO_DOMAIN               39
 #define SO_RXQ_OVFL             40
 #define SO_WIFI_STATUS          41
 #define SCM_WIFI_STATUS         SO_WIFI_STATUS
@@ -203,6 +215,13 @@
 #define SO_SELECT_ERR_QUEUE     45
 #define SO_BUSY_POLL            46
 #define SO_MAX_PACING_RATE      47
+#define SO_BPF_EXTENSIONS       48
+#define SO_INCOMING_CPU         49
+#define SO_ATTACH_BPF           50
+#define SO_DETACH_BPF           SO_DETACH_FILTER
+#define SO_ATTACH_REUSEPORT_CBPF 51
+#define SO_ATTACH_REUSEPORT_EBPF 52
+#define SO_CNX_ADVICE           53
 
 #ifndef SOL_SOCKET
 #define SOL_SOCKET      1
@@ -219,6 +238,21 @@
 #define SOL_ATM         264
 #define SOL_AAL         265
 #define SOL_IRDA        266
+#define SOL_NETBEUI     267
+#define SOL_LLC         268
+#define SOL_DCCP        269
+#define SOL_NETLINK     270
+#define SOL_TIPC        271
+#define SOL_RXRPC       272
+#define SOL_PPPOL2TP    273
+#define SOL_BLUETOOTH   274
+#define SOL_PNPIPE      275
+#define SOL_RDS         276
+#define SOL_IUCV        277
+#define SOL_CAIF        278
+#define SOL_ALG         279
+#define SOL_NFC         280
+#define SOL_KCM         281
 
 #define SOMAXCONN       128
 
@@ -239,6 +273,8 @@
 #define MSG_NOSIGNAL  0x4000
 #define MSG_MORE      0x8000
 #define MSG_WAITFORONE 0x10000
+#define MSG_BATCH     0x40000
+#define MSG_FASTOPEN  0x20000000
 #define MSG_CMSG_CLOEXEC 0x40000000
 
 #define __CMSG_LEN(cmsg) (((cmsg)->cmsg_len + sizeof(long) - 1) & ~(long)(sizeof(long) - 1))
@@ -246,9 +282,9 @@
 #define __MHDR_END(mhdr) ((unsigned char *)(mhdr)->msg_control + (mhdr)->msg_controllen)
 
 #define CMSG_DATA(cmsg) ((unsigned char *) (((struct cmsghdr *)(cmsg)) + 1))
-#define CMSG_NXTHDR(mhdr, cmsg) ((cmsg)->cmsg_len < sizeof (struct cmsghdr) ? (struct cmsghdr *)0 : \
-        (__CMSG_NEXT(cmsg) + sizeof (struct cmsghdr) >= __MHDR_END(mhdr) ? (struct cmsghdr *)0 : \
-        ((struct cmsghdr *)__CMSG_NEXT(cmsg))))
+#define CMSG_NXTHDR(mhdr, cmsg) ((cmsg)->cmsg_len < sizeof (struct cmsghdr) || \
+	__CMSG_LEN(cmsg) + sizeof(struct cmsghdr) >= __MHDR_END(mhdr) - (unsigned char *)(cmsg) \
+	? 0 : (struct cmsghdr *)__CMSG_NEXT(cmsg))
 #define CMSG_FIRSTHDR(mhdr) ((size_t) (mhdr)->msg_controllen >= sizeof (struct cmsghdr) ? (struct cmsghdr *) (mhdr)->msg_control : (struct cmsghdr *) 0)
 
 #define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1))
@@ -258,17 +294,15 @@
 #define SCM_RIGHTS      0x01
 #define SCM_CREDENTIALS 0x02
 
-struct sockaddr
-{
+struct sockaddr {
 	sa_family_t sa_family;
 	char sa_data[14];
 };
 
-struct sockaddr_storage
-{
+struct sockaddr_storage {
 	sa_family_t ss_family;
+	char __ss_padding[128-sizeof(long)-sizeof(sa_family_t)];
 	unsigned long __ss_align;
-	char __ss_padding[128-2*sizeof(unsigned long)];
 };
 
 int socket (int, int, int);
diff --git a/system/include/libc/sys/stat.h b/system/include/libc/sys/stat.h
index c6abab5..82a6490 100644
--- a/system/include/libc/sys/stat.h
+++ b/system/include/libc/sys/stat.h
@@ -100,8 +100,9 @@
 #define fstat64 fstat
 #define lstat64 lstat
 #define fstatat64 fstatat
-#define blksize64_t blksize_t
 #define blkcnt64_t blkcnt_t
+#define fsblkcnt64_t fsblkcnt_t
+#define fsfilcnt64_t fsfilcnt_t
 #define ino64_t ino_t
 #define off64_t off_t
 #endif
diff --git a/system/include/libc/sys/time.h b/system/include/libc/sys/time.h
index bfe1414..e4b379a 100644
--- a/system/include/libc/sys/time.h
+++ b/system/include/libc/sys/time.h
@@ -17,8 +17,7 @@
 #define ITIMER_VIRTUAL 1
 #define ITIMER_PROF    2
 
-struct itimerval
-{
+struct itimerval {
 	struct timeval it_interval;
 	struct timeval it_value;
 };
diff --git a/system/include/libc/sys/timerfd.h b/system/include/libc/sys/timerfd.h
index df645fe..9724d90 100644
--- a/system/include/libc/sys/timerfd.h
+++ b/system/include/libc/sys/timerfd.h
@@ -13,6 +13,8 @@
 
 #define TFD_TIMER_ABSTIME 1
 
+struct itimerspec;
+
 int timerfd_create(int, int);
 int timerfd_settime(int, int, const struct itimerspec *, struct itimerspec *);
 int timerfd_gettime(int, struct itimerspec *);
diff --git a/system/include/libc/sys/times.h b/system/include/libc/sys/times.h
index cc55e57..80a5052 100644
--- a/system/include/libc/sys/times.h
+++ b/system/include/libc/sys/times.h
@@ -8,8 +8,7 @@
 #define __NEED_clock_t
 #include <bits/alltypes.h>
 
-struct tms
-{
+struct tms {
 	clock_t tms_utime;
 	clock_t tms_stime;
 	clock_t tms_cutime;
diff --git a/system/include/libc/sys/types.h b/system/include/libc/sys/types.h
index 27170f6..75e489c 100644
--- a/system/include/libc/sys/types.h
+++ b/system/include/libc/sys/types.h
@@ -20,11 +20,6 @@
 #define __NEED_timer_t
 #define __NEED_clockid_t
 
-#define __NEED_int8_t
-#define __NEED_int16_t
-#define __NEED_int32_t
-#define __NEED_int64_t
-
 #define __NEED_blkcnt_t
 #define __NEED_fsblkcnt_t
 #define __NEED_fsfilcnt_t
@@ -49,19 +44,22 @@
 #define __NEED_pthread_key_t
 #define __NEED_pthread_once_t
 #define __NEED_useconds_t
-#define __NEED_u_int64_t
 
 #if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+#define __NEED_int8_t
+#define __NEED_int16_t
+#define __NEED_int32_t
+#define __NEED_int64_t
+#define __NEED_u_int64_t
 #define __NEED_register_t
 #endif
 
 #include <bits/alltypes.h>
 
+#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
 typedef unsigned char u_int8_t;
 typedef unsigned short u_int16_t;
 typedef unsigned u_int32_t;
-
-#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
 typedef char *caddr_t;
 typedef unsigned char u_char;
 typedef unsigned short u_short, ushort;
@@ -75,7 +73,6 @@
 #endif
 
 #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
-#define blksize64_t blksize_t
 #define blkcnt64_t blkcnt_t
 #define fsblkcnt64_t fsblkcnt_t
 #define fsfilcnt64_t fsfilcnt_t
@@ -87,5 +84,3 @@
 }
 #endif
 #endif
-
-
diff --git a/system/include/libc/sys/un.h b/system/include/libc/sys/un.h
index 7494f1a..1a3193a 100644
--- a/system/include/libc/sys/un.h
+++ b/system/include/libc/sys/un.h
@@ -14,8 +14,7 @@
 
 #include <bits/alltypes.h>
 
-struct sockaddr_un
-{
+struct sockaddr_un {
 	sa_family_t sun_family;
 	char sun_path[108];
 };
diff --git a/system/include/libc/sys/utsname.h b/system/include/libc/sys/utsname.h
index 6b9ea97..2c80fb5 100644
--- a/system/include/libc/sys/utsname.h
+++ b/system/include/libc/sys/utsname.h
@@ -7,8 +7,7 @@
 
 #include <features.h>
 
-struct utsname
-{
+struct utsname {
 	char sysname[65];
 	char nodename[65];
 	char release[65];
diff --git a/system/include/libc/threads.h b/system/include/libc/threads.h
new file mode 100644
index 0000000..0179482
--- /dev/null
+++ b/system/include/libc/threads.h
@@ -0,0 +1,87 @@
+#ifndef _THREADS_H
+#define _THREADS_H
+
+#include <features.h>
+#include <time.h>
+
+#ifdef __cplusplus
+extern "C" {
+typedef unsigned long thrd_t;
+#else
+typedef struct __pthread *thrd_t;
+#define thread_local _Thread_local
+#endif
+
+typedef int once_flag;
+typedef unsigned tss_t;
+typedef int (*thrd_start_t)(void *);
+typedef void (*tss_dtor_t)(void *);
+
+#define __NEED_cnd_t
+#define __NEED_mtx_t
+
+#include <bits/alltypes.h>
+
+#define TSS_DTOR_ITERATIONS 4
+
+enum {
+	thrd_success  = 0,
+	thrd_busy     = 1,
+	thrd_error    = 2,
+	thrd_nomem    = 3,
+	thrd_timedout = 4,
+};
+
+enum {
+	mtx_plain     = 0,
+	mtx_recursive = 1,
+	mtx_timed     = 2,
+};
+
+#define ONCE_FLAG_INIT 0
+
+int thrd_create(thrd_t *, thrd_start_t, void *);
+_Noreturn void thrd_exit(int);
+
+int thrd_detach(thrd_t);
+int thrd_join(thrd_t, int *);
+
+int thrd_sleep(const struct timespec *, struct timespec *);
+void thrd_yield(void);
+
+thrd_t thrd_current(void);
+int thrd_equal(thrd_t, thrd_t);
+#ifndef __cplusplus
+#define thrd_equal(A, B) ((A) == (B))
+#endif
+
+void call_once(once_flag *, void (*)(void));
+
+int mtx_init(mtx_t *, int);
+void mtx_destroy(mtx_t *);
+
+int mtx_lock(mtx_t *);
+int mtx_timedlock(mtx_t *__restrict, const struct timespec *__restrict);
+int mtx_trylock(mtx_t *);
+int mtx_unlock(mtx_t *);
+
+int cnd_init(cnd_t *);
+void cnd_destroy(cnd_t *);
+
+int cnd_broadcast(cnd_t *);
+int cnd_signal(cnd_t *);
+
+int cnd_timedwait(cnd_t *__restrict, mtx_t *__restrict, const struct timespec *__restrict);
+int cnd_wait(cnd_t *, mtx_t *);
+
+int tss_create(tss_t *, tss_dtor_t);
+void tss_delete(tss_t key);
+
+int tss_set(tss_t, void *);
+void *tss_get(tss_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/system/include/libc/time.h b/system/include/libc/time.h
index dc88070..a408679 100644
--- a/system/include/libc/time.h
+++ b/system/include/libc/time.h
@@ -17,11 +17,11 @@
 #define __NEED_size_t
 #define __NEED_time_t
 #define __NEED_clock_t
+#define __NEED_struct_timespec
 
 #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
  || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
  || defined(_BSD_SOURCE)
-#define __NEED_struct_timespec
 #define __NEED_clockid_t
 #define __NEED_timer_t
 #define __NEED_pid_t
@@ -35,8 +35,7 @@
 #define __tm_zone tm_zone
 #endif
 
-struct tm
-{
+struct tm {
 	int tm_sec;
 	int tm_min;
 	int tm_hour;
@@ -59,9 +58,11 @@
 struct tm *localtime (const time_t *);
 char *asctime (const struct tm *);
 char *ctime (const time_t *);
+int timespec_get(struct timespec *, int);
 
 #define CLOCKS_PER_SEC 1000000L
 
+#define TIME_UTC 1
 
 #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
  || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
@@ -76,8 +77,7 @@
 
 void tzset (void);
 
-struct itimerspec
-{
+struct itimerspec {
 	struct timespec it_interval;
 	struct timespec it_value;
 };
@@ -114,7 +114,7 @@
 #endif
 
 
-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
+#if defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
 char *strptime (const char *__restrict, const char *__restrict, struct tm *__restrict);
 extern int daylight;
 extern long timezone;
diff --git a/system/include/libc/uchar.h b/system/include/libc/uchar.h
new file mode 100644
index 0000000..8dabf1e
--- /dev/null
+++ b/system/include/libc/uchar.h
@@ -0,0 +1,27 @@
+#ifndef _UCHAR_H
+#define _UCHAR_H
+
+#ifdef __cplusplus
+extern "C" {
+#else
+typedef unsigned short char16_t;
+typedef unsigned char32_t;
+#endif
+
+#define __NEED_mbstate_t
+#define __NEED_size_t
+
+#include <features.h>
+#include <bits/alltypes.h>
+
+size_t c16rtomb(char *__restrict, char16_t, mbstate_t *__restrict);
+size_t mbrtoc16(char16_t *__restrict, const char *__restrict, size_t, mbstate_t *__restrict);
+
+size_t c32rtomb(char *__restrict, char32_t, mbstate_t *__restrict);
+size_t mbrtoc32(char32_t *__restrict, const char *__restrict, size_t, mbstate_t *__restrict);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/system/include/libc/unistd.h b/system/include/libc/unistd.h
index 2ceef55..c7cafaa 100644
--- a/system/include/libc/unistd.h
+++ b/system/include/libc/unistd.h
@@ -140,9 +140,6 @@
 long gethostid(void);
 int nice(int);
 void sync(void);
-#endif
-
-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
 pid_t setpgrp(void);
 char *crypt(const char *, const char *);
 void encrypt(char *, int);
@@ -177,6 +174,8 @@
 char *getusershell(void);
 int acct(const char *);
 /* XXX EMSCRIPTEN long syscall(long, ...); */
+int execvpe(const char *, char *const [], char *const []);
+int issetugid(void);
 #endif
 
 #ifdef _GNU_SOURCE
@@ -186,7 +185,7 @@
 int getresuid(uid_t *, uid_t *, uid_t *);
 int getresgid(gid_t *, gid_t *, gid_t *);
 char *get_current_dir_name(void);
-void syncfs(int);
+int syncfs(int);
 int euidaccess(const char *, int);
 int eaccess(const char *, int);
 #endif
@@ -320,11 +319,9 @@
 #define _SC_BC_SCALE_MAX	38
 #define _SC_BC_STRING_MAX	39
 #define _SC_COLL_WEIGHTS_MAX	40
-#define _SC_EQUIV_CLASS_MAX	41
 #define _SC_EXPR_NEST_MAX	42
 #define _SC_LINE_MAX	43
 #define _SC_RE_DUP_MAX	44
-#define _SC_CHARCLASS_NAME_MAX	45
 #define _SC_2_VERSION	46
 #define _SC_2_C_BIND	47
 #define _SC_2_C_DEV	48
@@ -332,21 +329,8 @@
 #define _SC_2_FORT_RUN	50
 #define _SC_2_SW_DEV	51
 #define _SC_2_LOCALEDEF	52
-#define _SC_PII	53
-#define _SC_PII_XTI	54
-#define _SC_PII_SOCKET	55
-#define _SC_PII_INTERNET	56
-#define _SC_PII_OSI	57
-#define _SC_POLL	58
-#define _SC_SELECT	59
 #define _SC_UIO_MAXIOV	60 /* !! */
 #define _SC_IOV_MAX	60
-#define _SC_PII_INTERNET_STREAM	61
-#define _SC_PII_INTERNET_DGRAM	62
-#define _SC_PII_OSI_COTS	63
-#define _SC_PII_OSI_CLTS	64
-#define _SC_PII_OSI_M	65
-#define _SC_T_IOV_MAX	66
 #define _SC_THREADS	67
 #define _SC_THREAD_SAFE_FUNCTIONS	68
 #define _SC_GETGR_R_SIZE_MAX	69
@@ -376,35 +360,11 @@
 #define _SC_XOPEN_ENH_I18N	93
 #define _SC_XOPEN_SHM	94
 #define _SC_2_CHAR_TERM	95
-#define _SC_2_C_VERSION	96
 #define _SC_2_UPE	97
 #define _SC_XOPEN_XPG2	98
 #define _SC_XOPEN_XPG3	99
 #define _SC_XOPEN_XPG4	100
-#define _SC_CHAR_BIT	101
-#define _SC_CHAR_MAX	102
-#define _SC_CHAR_MIN	103
-#define _SC_INT_MAX	104
-#define _SC_INT_MIN	105
-#define _SC_LONG_BIT	106
-#define _SC_WORD_BIT	107
-#define _SC_MB_LEN_MAX	108
 #define _SC_NZERO	109
-#define _SC_SSIZE_MAX	110
-#define _SC_SCHAR_MAX	111
-#define _SC_SCHAR_MIN	112
-#define _SC_SHRT_MAX	113
-#define _SC_SHRT_MIN	114
-#define _SC_UCHAR_MAX	115
-#define _SC_UINT_MAX	116
-#define _SC_ULONG_MAX	117
-#define _SC_USHRT_MAX	118
-#define _SC_NL_ARGMAX	119
-#define _SC_NL_LANGMAX	120
-#define _SC_NL_MSGMAX	121
-#define _SC_NL_NMAX	122
-#define _SC_NL_SETMAX	123
-#define _SC_NL_TEXTMAX	124
 #define _SC_XBS5_ILP32_OFF32	125
 #define _SC_XBS5_ILP32_OFFBIG	126
 #define _SC_XBS5_LP64_OFF64	127
@@ -414,40 +374,19 @@
 #define _SC_XOPEN_REALTIME_THREADS	131
 #define _SC_ADVISORY_INFO	132
 #define _SC_BARRIERS	133
-#define _SC_BASE	134
-#define _SC_C_LANG_SUPPORT	135
-#define _SC_C_LANG_SUPPORT_R	136
 #define _SC_CLOCK_SELECTION	137
 #define _SC_CPUTIME	138
 #define _SC_THREAD_CPUTIME	139
-#define _SC_DEVICE_IO	140
-#define _SC_DEVICE_SPECIFIC	141
-#define _SC_DEVICE_SPECIFIC_R	142
-#define _SC_FD_MGMT	143
-#define _SC_FIFO	144
-#define _SC_PIPE	145
-#define _SC_FILE_ATTRIBUTES	146
-#define _SC_FILE_LOCKING	147
-#define _SC_FILE_SYSTEM	148
 #define _SC_MONOTONIC_CLOCK	149
-#define _SC_MULTI_PROCESS	150
-#define _SC_SINGLE_PROCESS	151
-#define _SC_NETWORKING	152
 #define _SC_READER_WRITER_LOCKS	153
 #define _SC_SPIN_LOCKS	154
 #define _SC_REGEXP	155
-#define _SC_REGEX_VERSION	156
 #define _SC_SHELL	157
-#define _SC_SIGNALS	158
 #define _SC_SPAWN	159
 #define _SC_SPORADIC_SERVER	160
 #define _SC_THREAD_SPORADIC_SERVER	161
-#define _SC_SYSTEM_DATABASE	162
-#define _SC_SYSTEM_DATABASE_R	163
 #define _SC_TIMEOUTS	164
 #define _SC_TYPED_MEMORY_OBJECTS	165
-#define _SC_USER_GROUPS	166
-#define _SC_USER_GROUPS_R	167
 #define _SC_2_PBS	168
 #define _SC_2_PBS_ACCOUNTING	169
 #define _SC_2_PBS_LOCATE	170
diff --git a/system/include/libc/utime.h b/system/include/libc/utime.h
index ec82e0f..dd5ff92 100644
--- a/system/include/libc/utime.h
+++ b/system/include/libc/utime.h
@@ -9,8 +9,7 @@
 
 #include <bits/alltypes.h>
 
-struct utimbuf
-{
+struct utimbuf {
 	time_t actime;
 	time_t modtime;
 };
diff --git a/system/include/libc/utmp.h b/system/include/libc/utmp.h
index e9ba23e..48a400d 100644
--- a/system/include/libc/utmp.h
+++ b/system/include/libc/utmp.h
@@ -22,7 +22,6 @@
 #define ut_name ut_user
 #define ut_addr ut_addr_v6[0]
 #define utmp utmpx
-#define utmpname(x) (-1)
 #define e_exit __e_exit
 #define e_termination __e_termination
 
@@ -34,6 +33,9 @@
 void         setutent(void);
 
 void updwtmp(const char *, const struct utmp *);
+int utmpname(const char *);
+
+int login_tty(int);
 
 #define _PATH_UTMP "/dev/null/utmp"
 #define _PATH_WTMP "/dev/null/wtmp"
diff --git a/system/include/libc/utmpx.h b/system/include/libc/utmpx.h
index f0c3b01..9e5cc95 100644
--- a/system/include/libc/utmpx.h
+++ b/system/include/libc/utmpx.h
@@ -14,8 +14,7 @@
 
 #include <bits/alltypes.h>
 
-struct utmpx
-{
+struct utmpx {
 	short ut_type;
 	pid_t ut_pid;
 	char ut_line[32];
@@ -43,6 +42,7 @@
 #define e_exit __e_exit
 #define e_termination __e_termination
 void updwtmpx(const char *, const struct utmpx *);
+int utmpxname(const char *);
 #endif
 
 #define EMPTY           0
diff --git a/system/include/libc/wchar.h b/system/include/libc/wchar.h
index 9fd967c..0167dce 100644
--- a/system/include/libc/wchar.h
+++ b/system/include/libc/wchar.h
@@ -12,6 +12,7 @@
 #define __NEED_size_t
 #define __NEED_wchar_t
 #define __NEED_wint_t
+#define __NEED_mbstate_t
 
 #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
  || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
@@ -42,11 +43,6 @@
 #undef WEOF
 #define WEOF 0xffffffffU
 
-typedef struct __mbstate_t
-{
-	unsigned __opaque1, __opaque2;
-} mbstate_t;
-
 wchar_t *wcscpy (wchar_t *__restrict, const wchar_t *__restrict);
 wchar_t *wcsncpy (wchar_t *__restrict, const wchar_t *__restrict, size_t);
 
@@ -176,8 +172,11 @@
 wint_t    towlower(wint_t);
 wint_t    towupper(wint_t);
 wctype_t  wctype(const char *);
+
+#ifndef __cplusplus
 #undef iswdigit
-#define iswdigit(a) ((unsigned)(a)-'0' < 10)
+#define iswdigit(a) (0 ? iswdigit(a) : ((unsigned)(a)-'0') < 10)
+#endif
 #endif
 
 #ifdef __cplusplus
diff --git a/system/include/libc/wctype.h b/system/include/libc/wctype.h
index 3ac24f1..bc2420d 100644
--- a/system/include/libc/wctype.h
+++ b/system/include/libc/wctype.h
@@ -43,8 +43,10 @@
 wctrans_t wctrans(const char *);
 wctype_t  wctype(const char *);
 
+#ifndef __cplusplus
 #undef iswdigit
-#define iswdigit(a) (((unsigned)(a)-L'0') < 10)
+#define iswdigit(a) (0 ? iswdigit(a) : ((unsigned)(a)-'0') < 10)
+#endif
 
 #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
  || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
diff --git a/system/include/libc/wordexp.h b/system/include/libc/wordexp.h
index d12081e..5460002 100644
--- a/system/include/libc/wordexp.h
+++ b/system/include/libc/wordexp.h
@@ -18,8 +18,7 @@
 #define WRDE_SHOWERR 16
 #define WRDE_UNDEF   32
 
-typedef struct
-{
+typedef struct {
 	size_t we_wordc;
 	char **we_wordv;
 	size_t we_offs;
diff --git a/system/lib/libc/musl/COPYRIGHT b/system/lib/libc/musl/COPYRIGHT
index a8a38a5..f0ee3b7 100644
--- a/system/lib/libc/musl/COPYRIGHT
+++ b/system/lib/libc/musl/COPYRIGHT
@@ -25,31 +25,54 @@
 
 Authors/contributors include:
 
+Alex Dowad
+Alexander Monakov
 Anthony G. Basile
 Arvid Picciani
 Bobby Bingham
 Boris Brezillon
+Brent Cook
 Chris Spiegel
+Clément Vasseur
+Daniel Micay
+Denys Vlasenko
 Emil Renner Berthing
+Felix Fietkau
+Felix Janda
+Gianluca Anzolin
+Hauke Mehrtens
 Hiltjo Posthuma
 Isaac Dunham
+Jaydeep Patil
 Jens Gustedt
 Jeremy Huntwork
+Jo-Philipp Wich
+Joakim Sindholt
 John Spencer
+Josiah Worcester
 Justin Cormack
+Khem Raj
+Kylie McClain
 Luca Barbato
 Luka Perkov
+M Farkas-Dyck (Strake)
+Mahesh Bodapati
 Michael Forney
+Natanael Copa
 Nicholas J. Kain
 orc
 Pascal Cuoq
+Petr Hosek
 Pierre Carrier
 Rich Felker
 Richard Pennington
+Shiz
+sin
 Solar Designer
-Strake
+Stefan Kristiansson
 Szabolcs Nagy
 Timo Teräs
+Trutz Behn
 Valentin Ochs
 William Haddon
 
@@ -71,14 +94,14 @@
 and labelled as such in comments in the individual source files. All
 have been licensed under extremely permissive terms.
 
-The ARM memcpy code (src/string/armel/memcpy.s) is Copyright © 2008
+The ARM memcpy code (src/string/arm/memcpy_el.S) is Copyright © 2008
 The Android Open Source Project and is licensed under a two-clause BSD
 license. It was taken from Bionic libc, used on Android.
 
-The implementation of DES for crypt (src/misc/crypt_des.c) is
+The implementation of DES for crypt (src/crypt/crypt_des.c) is
 Copyright © 1994 David Burren. It is licensed under a BSD license.
 
-The implementation of blowfish crypt (src/misc/crypt_blowfish.c) was
+The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was
 originally written by Solar Designer and placed into the public
 domain. The code also comes with a fallback permissive license for use
 in jurisdictions that may not recognize the public domain.
@@ -92,16 +115,17 @@
 and/or distribute this code for any purpose with or without fee is
 hereby granted. There is no warranty."
 
-The x86_64 port was written by Nicholas J. Kain. Several files (crt)
-were released into the public domain; others are licensed under the
-standard MIT license terms at the top of this file. See individual
-files for their copyright status.
+The x86_64 port was written by Nicholas J. Kain and is licensed under
+the standard MIT terms.
 
 The mips and microblaze ports were originally written by Richard
 Pennington for use in the ellcc project. The original code was adapted
 by Rich Felker for build system and code conventions during upstream
 integration. It is licensed under the standard MIT terms.
 
+The mips64 port was contributed by Imagination Technologies and is
+licensed under the standard MIT terms.
+
 The powerpc port was also originally written by Richard Pennington,
 and later supplemented and integrated by John Spencer. It is licensed
 under the standard MIT terms.
@@ -114,15 +138,26 @@
 omission of copyright and license comments in each file is in the
 interest of source tree size.
 
-All public header files (include/* and arch/*/bits/*) should be
-treated as Public Domain as they intentionally contain no content
-which can be covered by copyright. Some source modules may fall in
-this category as well. If you believe that a file is so trivial that
-it should be in the Public Domain, please contact the authors and
-request an explicit statement releasing it from copyright.
+In addition, permission is hereby granted for all public header files
+(include/* and arch/*/bits/*) and crt files intended to be linked into
+applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit
+the copyright notice and permission notice otherwise required by the
+license, and to use these files without any requirement of
+attribution. These files include substantial contributions from:
 
-The following files are trivial, believed not to be copyrightable in
-the first place, and hereby explicitly released to the Public Domain:
+Bobby Bingham
+John Spencer
+Nicholas J. Kain
+Rich Felker
+Richard Pennington
+Stefan Kristiansson
+Szabolcs Nagy
 
-All public headers: include/*, arch/*/bits/*
-Startup files: crt/*
+all of whom have explicitly granted such permission.
+
+This file previously contained text expressing a belief that most of
+the files covered by the above exception were sufficiently trivial not
+to be subject to copyright, resulting in confusion over whether it
+negated the permissions granted in the license. In the spirit of
+permissive licensing, and of not having licensing issues being an
+obstacle to adoption, that text has been removed.
diff --git a/system/lib/libc/musl/INSTALL b/system/lib/libc/musl/INSTALL
index 9f295d5..526c3f6 100644
--- a/system/lib/libc/musl/INSTALL
+++ b/system/lib/libc/musl/INSTALL
@@ -18,25 +18,18 @@
 
 The only build-time prerequisites for musl are GNU Make and a
 freestanding C99 compiler toolchain targeting the desired instruction
-set architecture and ABI, with support for gcc-style inline assembly,
-weak aliases, and stand-alone assembly source files.
+set architecture and ABI, with support for a minimal subset of "GNU C"
+extensions consisting mainly of gcc-style inline assembly, weak
+aliases, hidden visibility, and stand-alone assembly source files.
+
+GCC, LLVM/clang, Firm/cparser, and PCC have all successfully built
+musl, but GCC is the most widely used/tested. Recent compiler (and
+binutils) versions should be used if possible since some older
+versions have bugs which affect musl.
 
 The system used to build musl does not need to be Linux-based, nor do
 the Linux kernel headers need to be available.
 
-If support for dynamic linking is desired, some further requirements
-are placed on the compiler and linker. In particular, the linker must
-support the -Bsymbolic-functions option.
-
-At present, GCC 4.6 or later is the recommended compiler for building
-musl. Any earlier version of GCC with full C99 support should also
-work, but may be subject to minor floating point conformance issues on
-i386 targets. Sufficiently recent versions of PCC and LLVM/clang are
-also believed to work, but have not been tested as heavily; prior to
-Fall 2012, both had known bugs that affected musl. Firm/cparser is
-also believed to work but lacks support for producing shared
-libraries.
-
 
 
 Supported Targets
@@ -50,12 +43,17 @@
       the `cmpxchg` instruction is added
 
 * x86_64
+    * ILP32 ABI (x32) is available as a separate arch but is still
+      experimental
 
 * ARM
     * EABI, standard or hard-float VFP variant
     * Little-endian default; big-endian variants also supported
     * Compiler toolchains only support armv4t and later
 
+* AArch64
+    * Little-endian default; big-endian variants also supported
+
 * MIPS
     * ABI is o32
     * Big-endian default; little-endian variants also supported
@@ -64,6 +62,12 @@
     * MIPS2 or later, or kernel emulation of ll/sc (standard in Linux)
       is required
 
+* MIPS64
+    * ABI is n64 (LP64)
+    * Big-endian default; little-endian variants also supported
+    * Default ABI variant uses FPU registers; alternate soft-float ABI
+      that does not use FPU registers or instructions is available
+
 * PowerPC
     * Only 32-bit is supported
     * Compiler toolchain must provide 64-bit long double, not IBM
@@ -71,21 +75,18 @@
     * For dynamic linking, compiler toolchain must be configured for
       "secure PLT" variant
 
+* SuperH (SH)
+    * Standard ELF ABI or FDPIC ABI (shared-text without MMU)
+    * Little-endian by default; big-engian variant also supported
+    * Full FPU ABI or soft-float ABI is supported, but the
+      single-precision-only FPU ABI is not
+
 * Microblaze
     * Big-endian default; little-endian variants also supported
     * Soft-float
     * Requires support for lwx/swx instructions
 
-The following additional targets are available for build, but may not
-work correctly and may not yet have ABI stability:
-
-* SuperH (SH)
-    * Little-endian by default; big-engian variant also supported
-    * Full FPU ABI or soft-float ABI is supported, but the
-      single-precision-only FPU ABI is not supported (musl always
-      requires IEEE single and double to be supported)
-
-* x32 (x86_64 ILP32 ABI)
+* OpenRISC 1000 (or1k)
 
 
 
diff --git a/system/lib/libc/musl/Makefile b/system/lib/libc/musl/Makefile
index 0ab0bfd..8246b78 100644
--- a/system/lib/libc/musl/Makefile
+++ b/system/lib/libc/musl/Makefile
@@ -8,6 +8,7 @@
 # Do not make changes here.
 #
 
+srcdir = .
 exec_prefix = /usr/local
 bindir = $(exec_prefix)/bin
 
@@ -16,138 +17,183 @@
 libdir = $(prefix)/lib
 syslibdir = /lib
 
-SRCS = $(sort $(wildcard src/*/*.c arch/$(ARCH)/src/*.c))
-OBJS = $(SRCS:.c=.o)
-LOBJS = $(OBJS:.o=.lo)
-GENH = include/bits/alltypes.h
-GENH_INT = src/internal/version.h
-IMPH = src/internal/stdio_impl.h src/internal/pthread_impl.h src/internal/libc.h
+SRC_DIRS = $(addprefix $(srcdir)/,src/* crt ldso)
+BASE_GLOBS = $(addsuffix /*.c,$(SRC_DIRS))
+ARCH_GLOBS = $(addsuffix /$(ARCH)/*.[csS],$(SRC_DIRS))
+BASE_SRCS = $(sort $(wildcard $(BASE_GLOBS)))
+ARCH_SRCS = $(sort $(wildcard $(ARCH_GLOBS)))
+BASE_OBJS = $(patsubst $(srcdir)/%,%.o,$(basename $(BASE_SRCS)))
+ARCH_OBJS = $(patsubst $(srcdir)/%,%.o,$(basename $(ARCH_SRCS)))
+REPLACED_OBJS = $(sort $(subst /$(ARCH)/,/,$(ARCH_OBJS)))
+ALL_OBJS = $(addprefix obj/, $(filter-out $(REPLACED_OBJS), $(sort $(BASE_OBJS) $(ARCH_OBJS))))
 
-LDFLAGS = 
+LIBC_OBJS = $(filter obj/src/%,$(ALL_OBJS))
+LDSO_OBJS = $(filter obj/ldso/%,$(ALL_OBJS:%.o=%.lo))
+CRT_OBJS = $(filter obj/crt/%,$(ALL_OBJS))
+
+AOBJS = $(LIBC_OBJS)
+LOBJS = $(LIBC_OBJS:.o=.lo)
+GENH = obj/include/bits/alltypes.h obj/include/bits/syscall.h
+GENH_INT = obj/src/internal/version.h
+IMPH = $(addprefix $(srcdir)/, src/internal/stdio_impl.h src/internal/pthread_impl.h src/internal/libc.h)
+
+LDFLAGS =
+LDFLAGS_AUTO =
 LIBCC = -lgcc
 CPPFLAGS =
-CFLAGS = -Os -pipe
+CFLAGS =
+CFLAGS_AUTO = -Os -pipe
 CFLAGS_C99FSE = -std=c99 -ffreestanding -nostdinc 
 
 CFLAGS_ALL = $(CFLAGS_C99FSE)
-CFLAGS_ALL += -D_XOPEN_SOURCE=700 -I./arch/$(ARCH) -I./src/internal -I./include
-CFLAGS_ALL += $(CPPFLAGS) $(CFLAGS)
-CFLAGS_ALL_STATIC = $(CFLAGS_ALL)
-CFLAGS_ALL_SHARED = $(CFLAGS_ALL) -fPIC -DSHARED
+CFLAGS_ALL += -D_XOPEN_SOURCE=700 -I$(srcdir)/arch/$(ARCH) -I$(srcdir)/arch/generic -Iobj/src/internal -I$(srcdir)/src/internal -Iobj/include -I$(srcdir)/include
+CFLAGS_ALL += $(CPPFLAGS) $(CFLAGS_AUTO) $(CFLAGS)
+
+LDFLAGS_ALL = $(LDFLAGS_AUTO) $(LDFLAGS)
 
 AR      = $(CROSS_COMPILE)ar
 RANLIB  = $(CROSS_COMPILE)ranlib
-INSTALL = ./tools/install.sh
+INSTALL = $(srcdir)/tools/install.sh
 
-ARCH_INCLUDES = $(wildcard arch/$(ARCH)/bits/*.h)
-ALL_INCLUDES = $(sort $(wildcard include/*.h include/*/*.h) $(GENH) $(ARCH_INCLUDES:arch/$(ARCH)/%=include/%))
+ARCH_INCLUDES = $(wildcard $(srcdir)/arch/$(ARCH)/bits/*.h)
+GENERIC_INCLUDES = $(wildcard $(srcdir)/arch/generic/bits/*.h)
+INCLUDES = $(wildcard $(srcdir)/include/*.h $(srcdir)/include/*/*.h)
+ALL_INCLUDES = $(sort $(INCLUDES:$(srcdir)/%=%) $(GENH:obj/%=%) $(ARCH_INCLUDES:$(srcdir)/arch/$(ARCH)/%=include/%) $(GENERIC_INCLUDES:$(srcdir)/arch/generic/%=include/%))
 
 EMPTY_LIB_NAMES = m rt pthread crypt util xnet resolv dl
 EMPTY_LIBS = $(EMPTY_LIB_NAMES:%=lib/lib%.a)
-CRT_LIBS = lib/crt1.o lib/Scrt1.o lib/crti.o lib/crtn.o
+CRT_LIBS = $(addprefix lib/,$(notdir $(CRT_OBJS)))
 STATIC_LIBS = lib/libc.a
 SHARED_LIBS = lib/libc.so
 TOOL_LIBS = lib/musl-gcc.specs
 ALL_LIBS = $(CRT_LIBS) $(STATIC_LIBS) $(SHARED_LIBS) $(EMPTY_LIBS) $(TOOL_LIBS)
-ALL_TOOLS = tools/musl-gcc
+ALL_TOOLS = obj/musl-gcc
+
+WRAPCC_GCC = gcc
+WRAPCC_CLANG = clang
 
 LDSO_PATHNAME = $(syslibdir)/ld-musl-$(ARCH)$(SUBARCH).so.1
 
 -include config.mak
 
+ifeq ($(ARCH),)
+
+all:
+	@echo "Please set ARCH in config.mak before running make."
+	@exit 1
+
+else
+
 all: $(ALL_LIBS) $(ALL_TOOLS)
 
-install: install-libs install-headers install-tools
+OBJ_DIRS = $(sort $(patsubst %/,%,$(dir $(ALL_LIBS) $(ALL_TOOLS) $(ALL_OBJS) $(GENH) $(GENH_INT))) obj/include)
 
-clean:
-	rm -f crt/*.o
-	rm -f $(OBJS)
-	rm -f $(LOBJS)
-	rm -f $(ALL_LIBS) lib/*.[ao] lib/*.so
-	rm -f $(ALL_TOOLS)
-	rm -f $(GENH) $(GENH_INT)
-	rm -f include/bits
+$(ALL_LIBS) $(ALL_TOOLS) $(ALL_OBJS) $(ALL_OBJS:%.o=%.lo) $(GENH) $(GENH_INT): | $(OBJ_DIRS)
 
-distclean: clean
-	rm -f config.mak
+$(OBJ_DIRS):
+	mkdir -p $@
 
-include/bits:
-	@test "$(ARCH)" || { echo "Please set ARCH in config.mak before running make." ; exit 1 ; }
-	ln -sf ../arch/$(ARCH)/bits $@
+obj/include/bits/alltypes.h: $(srcdir)/arch/$(ARCH)/bits/alltypes.h.in $(srcdir)/include/alltypes.h.in $(srcdir)/tools/mkalltypes.sed
+	sed -f $(srcdir)/tools/mkalltypes.sed $(srcdir)/arch/$(ARCH)/bits/alltypes.h.in $(srcdir)/include/alltypes.h.in > $@
 
-include/bits/alltypes.h.in: include/bits
+obj/include/bits/syscall.h: $(srcdir)/arch/$(ARCH)/bits/syscall.h.in
+	cp $< $@
+	sed -n -e s/__NR_/SYS_/p < $< >> $@
 
-include/bits/alltypes.h: include/bits/alltypes.h.in include/alltypes.h.in tools/mkalltypes.sed
-	sed -f tools/mkalltypes.sed include/bits/alltypes.h.in include/alltypes.h.in > $@
+obj/src/internal/version.h: $(wildcard $(srcdir)/VERSION $(srcdir)/.git)
+	printf '#define VERSION "%s"\n' "$$(cd $(srcdir); sh tools/version.sh)" > $@
 
-src/internal/version.h: $(wildcard VERSION .git)
-	printf '#define VERSION "%s"\n' "$$(sh tools/version.sh)" > $@
+obj/src/internal/version.o obj/src/internal/version.lo: obj/src/internal/version.h
 
-src/internal/version.lo: src/internal/version.h
+obj/crt/rcrt1.o obj/ldso/dlstart.lo obj/ldso/dynlink.lo: $(srcdir)/src/internal/dynlink.h $(srcdir)/arch/$(ARCH)/reloc.h
 
-src/ldso/dynlink.lo: arch/$(ARCH)/reloc.h
+obj/crt/crt1.o obj/crt/scrt1.o obj/crt/rcrt1.o obj/ldso/dlstart.lo: $(srcdir)/arch/$(ARCH)/crt_arch.h
 
-crt/crt1.o crt/Scrt1.o: $(wildcard arch/$(ARCH)/crt_arch.h)
+obj/crt/rcrt1.o: $(srcdir)/ldso/dlstart.c
 
-crt/Scrt1.o: CFLAGS += -fPIC
+obj/crt/Scrt1.o obj/crt/rcrt1.o: CFLAGS_ALL += -fPIC
 
-OPTIMIZE_SRCS = $(wildcard $(OPTIMIZE_GLOBS:%=src/%))
-$(OPTIMIZE_SRCS:%.c=%.o) $(OPTIMIZE_SRCS:%.c=%.lo): CFLAGS += -O3
+obj/crt/$(ARCH)/crti.o: $(srcdir)/crt/$(ARCH)/crti.s
+
+obj/crt/$(ARCH)/crtn.o: $(srcdir)/crt/$(ARCH)/crtn.s
+
+OPTIMIZE_SRCS = $(wildcard $(OPTIMIZE_GLOBS:%=$(srcdir)/src/%))
+$(OPTIMIZE_SRCS:$(srcdir)/%.c=obj/%.o) $(OPTIMIZE_SRCS:$(srcdir)/%.c=obj/%.lo): CFLAGS += -O3
 
 MEMOPS_SRCS = src/string/memcpy.c src/string/memmove.c src/string/memcmp.c src/string/memset.c
-$(MEMOPS_SRCS:%.c=%.o) $(MEMOPS_SRCS:%.c=%.lo): CFLAGS += $(CFLAGS_MEMOPS)
+$(MEMOPS_SRCS:%.c=obj/%.o) $(MEMOPS_SRCS:%.c=obj/%.lo): CFLAGS_ALL += $(CFLAGS_MEMOPS)
 
-# This incantation ensures that changes to any subarch asm files will
-# force the corresponding object file to be rebuilt, even if the implicit
-# rule below goes indirectly through a .sub file.
-define mkasmdep
-$(dir $(patsubst %/,%,$(dir $(1))))$(notdir $(1:.s=.o)): $(1)
-endef
-$(foreach s,$(wildcard src/*/$(ARCH)*/*.s),$(eval $(call mkasmdep,$(s))))
+NOSSP_SRCS = $(wildcard crt/*.c) \
+	src/env/__libc_start_main.c src/env/__init_tls.c \
+	src/env/__stack_chk_fail.c \
+	src/thread/__set_thread_area.c src/thread/$(ARCH)/__set_thread_area.c \
+	src/string/memset.c src/string/$(ARCH)/memset.c \
+	src/string/memcpy.c src/string/$(ARCH)/memcpy.c \
+	ldso/dlstart.c ldso/dynlink.c
+$(NOSSP_SRCS:%.c=obj/%.o) $(NOSSP_SRCS:%.c=obj/%.lo): CFLAGS_ALL += $(CFLAGS_NOSSP)
 
-%.o: $(ARCH)$(ASMSUBARCH)/%.sub
-	$(CC) $(CFLAGS_ALL_STATIC) -c -o $@ $(dir $<)$(shell cat $<)
+$(CRT_OBJS): CFLAGS_ALL += -DCRT
 
-%.o: $(ARCH)/%.s
-	$(CC) $(CFLAGS_ALL_STATIC) -c -o $@ $<
+$(LOBJS) $(LDSO_OBJS): CFLAGS_ALL += -fPIC
 
-%.o: %.c $(GENH) $(IMPH)
-	$(CC) $(CFLAGS_ALL_STATIC) -c -o $@ $<
+CC_CMD = $(CC) $(CFLAGS_ALL) -c -o $@ $<
 
-%.lo: $(ARCH)$(ASMSUBARCH)/%.sub
-	$(CC) $(CFLAGS_ALL_SHARED) -c -o $@ $(dir $<)$(shell cat $<)
+# Choose invocation of assembler to be used
+ifeq ($(ADD_CFI),yes)
+	AS_CMD = LC_ALL=C awk -f $(srcdir)/tools/add-cfi.common.awk -f $(srcdir)/tools/add-cfi.$(ARCH).awk $< | $(CC) $(CFLAGS_ALL) -x assembler -c -o $@ -
+else
+	AS_CMD = $(CC_CMD)
+endif
 
-%.lo: $(ARCH)/%.s
-	$(CC) $(CFLAGS_ALL_SHARED) -c -o $@ $<
+obj/%.o: $(srcdir)/%.s
+	$(AS_CMD)
 
-%.lo: %.c $(GENH) $(IMPH)
-	$(CC) $(CFLAGS_ALL_SHARED) -c -o $@ $<
+obj/%.o: $(srcdir)/%.S
+	$(CC_CMD)
 
-lib/libc.so: $(LOBJS)
-	$(CC) $(CFLAGS_ALL_SHARED) $(LDFLAGS) -nostdlib -shared \
-	-Wl,-e,_start -Wl,-Bsymbolic-functions \
-	-o $@ $(LOBJS) $(LIBCC)
+obj/%.o: $(srcdir)/%.c $(GENH) $(IMPH)
+	$(CC_CMD)
 
-lib/libc.a: $(OBJS)
+obj/%.lo: $(srcdir)/%.s
+	$(AS_CMD)
+
+obj/%.lo: $(srcdir)/%.S
+	$(CC_CMD)
+
+obj/%.lo: $(srcdir)/%.c $(GENH) $(IMPH)
+	$(CC_CMD)
+
+lib/libc.so: $(LOBJS) $(LDSO_OBJS)
+	$(CC) $(CFLAGS_ALL) $(LDFLAGS_ALL) -nostdlib -shared \
+	-Wl,-e,_dlstart -o $@ $(LOBJS) $(LDSO_OBJS) $(LIBCC)
+
+lib/libc.a: $(AOBJS)
 	rm -f $@
-	$(AR) rc $@ $(OBJS)
+	$(AR) rc $@ $(AOBJS)
 	$(RANLIB) $@
 
 $(EMPTY_LIBS):
 	rm -f $@
 	$(AR) rc $@
 
-lib/%.o: crt/%.o
+lib/%.o: obj/crt/$(ARCH)/%.o
 	cp $< $@
 
-lib/musl-gcc.specs: tools/musl-gcc.specs.sh config.mak
+lib/%.o: obj/crt/%.o
+	cp $< $@
+
+lib/musl-gcc.specs: $(srcdir)/tools/musl-gcc.specs.sh config.mak
 	sh $< "$(includedir)" "$(libdir)" "$(LDSO_PATHNAME)" > $@
 
-tools/musl-gcc: config.mak
-	printf '#!/bin/sh\nexec "$${REALGCC:-gcc}" "$$@" -specs "%s/musl-gcc.specs"\n' "$(libdir)" > $@
+obj/musl-gcc: config.mak
+	printf '#!/bin/sh\nexec "$${REALGCC:-$(WRAPCC_GCC)}" "$$@" -specs "%s/musl-gcc.specs"\n' "$(libdir)" > $@
 	chmod +x $@
 
-$(DESTDIR)$(bindir)/%: tools/%
+obj/%-clang: $(srcdir)/tools/%-clang.in config.mak
+	sed -e 's!@CC@!$(WRAPCC_CLANG)!g' -e 's!@PREFIX@!$(prefix)!g' -e 's!@INCDIR@!$(includedir)!g' -e 's!@LIBDIR@!$(libdir)!g' -e 's!@LDSO@!$(LDSO_PATHNAME)!g' $< > $@
+	chmod +x $@
+
+$(DESTDIR)$(bindir)/%: obj/%
 	$(INSTALL) -D $< $@
 
 $(DESTDIR)$(libdir)/%.so: lib/%.so
@@ -156,10 +202,16 @@
 $(DESTDIR)$(libdir)/%: lib/%
 	$(INSTALL) -D -m 644 $< $@
 
-$(DESTDIR)$(includedir)/bits/%: arch/$(ARCH)/bits/%
+$(DESTDIR)$(includedir)/bits/%: $(srcdir)/arch/$(ARCH)/bits/%
 	$(INSTALL) -D -m 644 $< $@
 
-$(DESTDIR)$(includedir)/%: include/%
+$(DESTDIR)$(includedir)/bits/%: $(srcdir)/arch/generic/bits/%
+	$(INSTALL) -D -m 644 $< $@
+
+$(DESTDIR)$(includedir)/bits/%: obj/include/bits/%
+	$(INSTALL) -D -m 644 $< $@
+
+$(DESTDIR)$(includedir)/%: $(srcdir)/include/%
 	$(INSTALL) -D -m 644 $< $@
 
 $(DESTDIR)$(LDSO_PATHNAME): $(DESTDIR)$(libdir)/libc.so
@@ -169,10 +221,22 @@
 
 install-headers: $(ALL_INCLUDES:include/%=$(DESTDIR)$(includedir)/%)
 
-install-tools: $(ALL_TOOLS:tools/%=$(DESTDIR)$(bindir)/%)
+install-tools: $(ALL_TOOLS:obj/%=$(DESTDIR)$(bindir)/%)
 
+install: install-libs install-headers install-tools
 
+musl-git-%.tar.gz: .git
+	 git --git-dir=$(srcdir)/.git archive --format=tar.gz --prefix=$(patsubst %.tar.gz,%,$@)/ -o $@ $(patsubst musl-git-%.tar.gz,%,$@)
 
-.PRECIOUS: $(CRT_LIBS:lib/%=crt/%)
+musl-%.tar.gz: .git
+	 git --git-dir=$(srcdir)/.git archive --format=tar.gz --prefix=$(patsubst %.tar.gz,%,$@)/ -o $@ v$(patsubst musl-%.tar.gz,%,$@)
+
+endif
+
+clean:
+	rm -rf obj lib
+
+distclean: clean
+	rm -f config.mak
 
 .PHONY: all clean install install-libs install-headers install-tools
diff --git a/system/lib/libc/musl/README b/system/lib/libc/musl/README
index dd61bd5..a30eb11 100644
--- a/system/lib/libc/musl/README
+++ b/system/lib/libc/musl/README
@@ -10,7 +10,7 @@
 safety. musl is built on the principle that these goals are best
 achieved through simple code that is easy to understand and maintain.
 
-The 1.0 release series for musl features coverage for all interfaces
+The 1.1 release series for musl features coverage for all interfaces
 defined in ISO C99 and POSIX 2008 base, along with a number of
 non-standardized interfaces for compatibility with Linux, BSD, and
 glibc functionality.
diff --git a/system/lib/libc/musl/VERSION b/system/lib/libc/musl/VERSION
index 90a27f9..645377e 100644
--- a/system/lib/libc/musl/VERSION
+++ b/system/lib/libc/musl/VERSION
@@ -1 +1 @@
-1.0.5
+1.1.15
diff --git a/system/lib/libc/musl/WHATSNEW b/system/lib/libc/musl/WHATSNEW
index f476208..be688cb 100644
--- a/system/lib/libc/musl/WHATSNEW
+++ b/system/lib/libc/musl/WHATSNEW
@@ -1187,7 +1187,15 @@
 
 
 
-1.0.1 release notes
+1.1.0 release notes
+
+new features:
+- relro memory protection in dynamic linker
+- malloc can now extend heap with mmap if brk fails
+- vdso clock_gettime/gettimeofday/time acceleration on x86_64
+- thread/library-safe versions of search.h functions (nonstandard)
+- getauxval function (nonstandard)
+- sysconf extensions to query physical memory size
 
 bugs fixed:
 - floating point printf output corruption from carry into uninitialized slot
@@ -1195,6 +1203,7 @@
 - printf %g failure to strip trailing zeros in some cases
 - search past end of haystack in memmem
 - off-by-one error in confstr return value
+- crashes in some near-empty static programs that use stack protector
 - deadlock race in pthread_once
 - non-working clock_gettime fallback for old kernels
 
@@ -1202,10 +1211,16 @@
 - crash from missing syscall asm register clobbers on real microblaze kernel
 - crash in all nontrivial dynamic linker use on microblaze
 - incorrect rlimit constants on mips
+- broken, possibly dangerous, use of getrlimit syscall on x32 in sysconf
 
 
 
-1.0.2 release notes
+1.1.1 release notes
+
+new features:
+- new options --preload and --library-path to dynamic linker
+- public execvpe function (nonstandard extension)
+- iconv support for cp437 and cp850
 
 bugs fixed:
 - false negatives with some periodic needles in strstr, wcsstr, and memmem
@@ -1217,35 +1232,89 @@
 compatibility:
 - configure now detects serious constant-folding bug in gcc 4.9.0
 - removed __yield symbol (unused) that clashed with some compilers
+- improvements to sysconf's handling of unsupported/invalid arguments
 
 arch-specific bugs fixed:
 - misdetection of superh ABI variant by configure on gcc 3.x
 - missing SO_RCVBUFFORCE and SO_SNDBUFFORCE in mips socket.h
+- build regression on armv6 and later with -mthumb
 
 
 
-1.0.3 release notes
+1.1.2 release notes
+
+new features:
+- multi-protocol matches (tcp and udp) in getaddrinfo
+- support for AI_V4MAPPED and AI_ALL flags to getaddrinfo
+- reverse name lookups from /etc/hosts
+- reverse service lookups from /etc/services
+- support for service aliases in /etc/services
+- ipsec and tunneling protocols to getprotoent-family functions
+- res_send, res_mkquery, res_querydomain, and dn_comp functions
+- ipv6 scope id handling for link-local scope addresses
+- previously-unimplemented %C and %y in strptime now work
+- vdso clock_gettime acceleration on i386 (new kernel feature)
+- better O_CLOEXEC/SOCK_CLOEXEC fallbacks for old kernels
 
 bugs fixed:
 - buffer overflow in dns response parsing (CVE-2014-3484)
 - possible infinite loop in dns response parsing
-- fix multiple validation issues in dns response label parsing
 - sendfile off_t 32/64-bit size mismatch
 - incorrect end pointer in some cases when wcsrtombs stops early
 - incorrect if_nametoindex return value when interface does not exist
 - dummy "ent" function aliases that possibly shadowed real ones
+- tmpfile fd leak on memory exhaustion
+- getaddrinfo returning EAI_NONAME for some transient failures
 
 arch-specific bugs fixed:
 - broken kernel side RLIM_INFINITY on mips
+- incorrect syscall argument 6/7 types for pselect on x32
 
 
 
-1.0.4 release notes
+1.1.3 release notes
+
+new features:
+- address sorting in getaddrinfo, etc. modeled on rfc 3484/6724
+- default timezone taken from /etc/localtime when $TZ is unset
+- getopt double-colon extension for optional arguments
+- support for TLSDESC-based (gnu2) TLS dialect on i386 and x86_64
+- sendmmsg/recvmmsg (linux-specific)
+- fmtmsg (last mandatory XSI function that was missing)
+
+compatibility:
+- treat dns rcode=2 as temporary failure, not negative result
+- working thread-pointer for pre-2.6 kernels on i386
+- further ABI-compat symbols: __xmknod[at], __sysv_signal
+
+bugs fixed:
+- memmem false positives/false negatives/crashes from invalid logic
+- gethostby*_r not setting result pointer to null on failure
+- aliasing violations in syscall.h SYSLOG_NAMES feature
+- fanotify_mark syscall arguments wrong
+
+arch-specific bugs fixed:
+- various subtle relocation bugs in powerpc and sh dynamic linker
+
+
+
+1.1.4 release notes
+
+new features:
+- experimental locale support for LC_MESSAGES and LC_TIME
+- non-stub gettext family functions for message translation
+- or1k (OpenRISC 1000) port
+- syslog options LOG_CONS and LOG_PERROR
+- issetugid function (from OpenBSD)
+- improved if_nameindex and getifaddrs functions
 
 compatibility:
 - work around bug #61144 in gcc 4.9.0 and 4.9.1
+- support getauxval(AT_SECURE) even on kernels without AT_SECURE
 
 bugs fixed:
+- empty dynamic linker error messages (regression in 1.1.3)
+- if_nameindex omitted unconfigured and ipv6-only interfaces
 - incorrect return value for fwide function
 - failure of wide printf/scanf functions to set wide orientation
 - multiple issues in legacy function getpass
@@ -1254,52 +1323,410 @@
 - crash in regexec for nonzero nmatch argument with REG_NOSUB
 - minor bugs in rarely-used nl_langinfo item lookups
 
-- memmem false positives/false negatives/crashes from invalid logic
-- gethostby*_r not setting result pointer to null on failure
-- aliasing violations in syscall.h SYSLOG_NAMES feature
-- fanotify_mark syscall arguments wrong
-
 arch-specific bugs fixed:
+- broken relocations in mips dynamic linker (regression in 1.1.3)
 - register state corruption in setjmp asm for microblaze
 - broken struct stat st_ino field on microblaze
 - broken struct stat st_dev field on big endian mips
 - broken asm register constraints in atomics on powerpc
-- missing barriers in atomics on mips, powerpc, and microblaze
-
-- TLS relocation bug in powerpc dynamic linker
+- missing barriers in atomics on mips, powerpc, microblaze, and sh
 
 
 
-1.0.5 release notes
+1.1.5 release notes
+
+new features:
+- full C11 coverage (threads, UTF-16/32 API, timespec_get, etc.)
+- malloc_usable_size function (nonstandard)
+- support for new F_OFD_* fcntl operations (linux 3.15, POSIX-future)
+- new _DEFAULT_SOURCE feature test macro to request default profile
+
+performance:
+- private-futex support
+- redesigned cond var implementation with major performance improvement
+- tweaked spinning in userspace before performing futex waits
+
+bugs fixed:
+- failure of dn_expand to null-terminate name for crafted DNS packets
+- corruption of cond var mutex state when switching mutexes
+- use of uninitialized memory with application-provided thread stacks
+- false ownership of orphaned mutexes due to tid reuse
+- possible failure-to-wake for robust mutexes on owner death
+- subtle errors in robust mutex unrecoverable status handling
+- missing memory/compiler barrier spinning to obtain locks
+- wrong behavior in various zero-length stdio operations
+- buffer overflow in swab with odd argument
+- incorrect sequence generation in the rand48 family of prng functions
+- missing cancellation check in non-wait paths of sem_wait, pthread_join
+- missing barrier in pthread_once fast path
+- memory leak in regexec when input contains illegal sequence
+- various parser bugs in regcomp
+- wrong return value on overflow in some strtoul-family functions
+- broken CPU_EQUAL macro in sched.h
+- dlerror not working in static-linked programs
+- mishandling of negative non-whole-hour TZ offsets
+- incorrect case mappings for U+00DF
+- namespace pollution via accidentally-non-static function named "dummy"
+- missing __fpclassifyl and __signbitl definitions for ld64 archs
+
+
+
+1.1.6 release notes
+
+new features:
+- getopt '-' flag for processing non-option arguments
+- getopt_long argument permutation extension
+- getopt_long abbreviated options
+- ns_parserr and related DNS-packet-parsing functions
+- fnmatch FNM_CASEFOLD extension
+- support for translation of getopt error messages
+- login_tty function (legacy)
+
+performance:
+- efficient atomics on armv7+ targets
+- pthread_once shrink-wrapping of fast path
 
 compatibility:
+- baseline arm binaries now work on new cpus/kernels without kuser_helper
 - dynamic linker now honors DT_RUNPATH without DT_RPATH (new binutils)
+- arm asm is now compatible with clang's internal assembler
+- suppress macro implementations of functions when headers are used in C++
+- increased message length limit for syslog
+
+bugs fixed:
+- open ignored file creation mode argument for O_TMPFILE
+- wrong printf formatting for %#.0o with value zero
+- missing private state for uchar.h functions (null ps pointer)
+- sched_getaffinity left uninitialized data in output bit array
+- wrong return values for pthread_getaffinity_np and pthread_setaffinity_np
+- buggy handling of multibyte option chars with arguments in getopt
+- printf failed to report or stop on write errors
+- printf failed to honor '+' modifier when printing NANs
+- wcsnrtombs returned the wrong value in one code path
+- syslog failed to check for connect error
+- multi-threaded set*id() had spurious failures from ugly workaround code
+- various minor header conformance bugs (signedness, constant expressions, ...)
+
+arch-specific bugs fixed:
+- on or1k, some syscalls with 64-bit arguments were broken (misaligned)
+- usage of sahf instruction on x86_64 crashed on some early cpu models
+
+
+
+1.1.7 release notes
+
+new features:
+- alternate passwd/group backend support via nscd protocol
+- masked cancellation mode extension (experimental)
+- aio cancellation
+- aarch64 port (experimental)
+
+performance:
+- significant memset asm optimizations on i386 and x86_64
+
+compatibility:
+- suppress EINTR in semaphores for old kernels where futex restart is broken
+- always set optarg in getopt_long
+- support SOCK_RAW socket type in getaddrinfo
+- report success instead of EINPROGRESS when close is interrupted
+
+bugs fixed:
+- multithreaded set*id() was not async-signal safe, had various race bugs
+- getspnam_r returned results for partial username matches
+- wordexp bad character checker mis-counted parentheses
+- close on fd with pending aio could lead to file corruption
+- old aio implementation had numerous conformance bugs
+- malloc init code could deadlock due to race condition
+- pthread_exit did not disable cancellation
+- pthread_cond_wait could wrongly consume signal on cancellation
+- execvp wrongly stopped path search on EACCESS
+- fsync, fdatasync, and msync were not honored as cancellation points
+- fchmodat was subject to fd leak race (missing O_CLOEXEC)
+- fchmodat failed to report EOPNOTSUPP in race path
+- passwd/group lookup functions had various minor error-reporting bugs
+- isatty had false-positives/device-state-corruption for OSS sound devices
+- configure script failed to detect gcc with translated messages
+- FLT_ROUNDS macro failed to reflect rounding mode changes in fenv
+
+arch-specific bugs fixed:
+- mips fesetenv did not handle FE_DFL_ENV
+- mips POLLWRNORM and POLLWRBAND macros had wrong values
+- x32 pthread synchronization object type definitions were wrong
+- powerpc minimum signal stack size was insufficient
+
+
+
+1.1.8 release notes
 
 bugs fixed:
 - stack-based buffer overflow in inet_pton (CVE-2015-1817)
-- regcomp mishandling of high bytes after backslash
+- regcomp crash/mem-corruption with illegal bytes after backslash
+- regcomp wrongly allowed backrefs in ER
 - regcomp miscompiled character class brace-repetitions
-- use of uninitialized memory with application-provided thread stacks
-- failure of dn_expand to null-terminate name for crafted DNS packets
-- buffer overflow in swab with odd argument
-- missing barrier in pthread_once fast path
-- wordexp bad character checker mis-counted parentheses
-- malloc init code could deadlock due to race condition
-- getspnam_r returned results for partial username matches
-- mishandling of negative non-whole-hour TZ offsets
-- printf failed to report or stop on write errors
-- syslog failed to check for connect error
-- fchmodat was subject to fd leak race (missing O_CLOEXEC)
-- fchmodat failed to report EOPNOTSUPP in race path
-- wrong behavior in various zero-length stdio operations
-- wrong return value on overflow in some strtoul-family functions
-- incorrect sequence generation in the rand48 family of prng functions
-- wrong printf formatting for %#.0o with value zero
-- sched_getaffinity left uninitialized data in output bit array
-- wrong return values for pthread_getaffinity_np and pthread_setaffinity_np
-- broken CPU_EQUAL macro in sched.h
+- regcomp wrongly processed \0 as an unmatchable backref
+- new FLT_ROUNDS definition failed to work in C++ code
 
 arch-specific bugs fixed:
-- usage of sahf instruction on x86_64 crashed on some early cpu models
-- mips fesetenv did not handle FE_DFL_ENV
-- mips POLLWRNORM and POLLWRBAND macros had wrong values
+- aarch64 was missing max_align_t definition
+
+
+
+1.1.9 release notes
+
+new features:
+- ability to protect libc code itself with stack protector
+- sigsetjmp now restores signal mask after restoring context, not before
+- thread-local dlerror status/messages
+- dlerror messages are no longer truncated
+- diagnostics for constraint violations with ctype.h macros
+
+optimizations:
+- reduce cost of PIC on archs where PLT calls need a fixed GOT register
+- spin locks no longer constantly invalidate cache lines while spinning
+- code size reduction in static-linked TLS init
+
+bugs fixed:
+- failure to process robust mutexes on detached-thread exit
+- possible memory corruption due to robust mutex list on detached-thread exit
+- crash on memory exhaustion in getgr* internals
+- misaligned memory accesses in static binaries with low-alignment TLS blocks
+- multiple cases of wrongful path search continuation after transient failure
+- small memory leak on failure of dlopen with RPATH $ORIGIN
+- several small math bugs related to exception flags with non-finite args
+- mmap leak in sem_open failure path for link call
+- duplocale clobbered new locale struct with memcpy of old
+- futimes crashed with null timeval argument
+
+arch-specific bugs fixed:
+- stack protector spuriously aborted after forking on x32
+- stack protector spuriously aborted with flockfile on powerpc
+- theoretically-possible clobbering of syscall return value on mips
+- random thread-pointer setup failure on sh (uninitialized return value)
+- possible crash in dlsym on sh due to incorrectly-computed branch target
+- broken fesetenv(FE_DFL_ENV) on mips
+- dynamic linker name for sh ignored fpu/nofpu and endianness
+- various minor aarch64 bugs
+- dangling pointers in x32 syscall timespec fixup code
+
+
+
+1.1.10 release notes
+
+new features:
+- fail-safe (allocation-free) C locale for newlocale to return
+- all locale categories track requested locale name
+- rcrt1.o start file for static PIE
+
+optimizations:
+- inline atomics for sh4a
+- removed heavy atomics from locale-related code paths
+- removed global data accesses from CURRENT_LOCALE macro & callers
+- dynamic linker stage 1 size reduction
+
+compatibility:
+- better configure detection of unsupported compiler options
+- support for more relocation types in libc.so, not currently used
+- iconv_open accepts "" and "CHAR" as aliases for native (UTF-8)
+- additional LFS64 macros in sys/resource.h
+
+regressions fixed:
+- dynamic linker crash on NONE-type relocations (only mips affected)
+- inability to build as thumb2 on arm
+- failure to run under qemu-i386 user-level emulation
+- inability to access globals from libc on powerpc
+- PIE link errors in Scrt1.o under unusual usage on some archs
+
+other bugs fixed:
+- failure of ungetc/ungetwc to work on FILE streams in EOF state
+- possible null pointer dereference in gettext
+- possible initial stack misalignment on mips with PIE
+
+
+
+1.1.11 release notes
+
+new features:
+- byte-based C locale
+- vdso clock_gettime on arm
+- musl-clang wrapper
+- sh2 nommu target support
+
+performance:
+- major speed-up for dynamic linker symbol lookups with GNU hash
+
+compatibility:
+- strverscmp now matches GNU behavior in corner cases
+- empty TZ environment variable gives GMT rather than system default
+- reconnection on syslog server socket loss (syslogd restart)
+- mmap fallback in simple_malloc when brk fails
+- support for %m and %s with null pointers in wide printf variants
+- call frame information in i386 asm for improved debugger support
+
+bugs fixed:
+- spurious errors from pwd/grp functions when nscd backend is absent
+- possible invalid access on calloc with simple_malloc
+- null pointer dereferences after calling uselocale((locale_t)0)
+- erroneous support for cancellation in stdio caused data loss
+- inconsistent handling of atexit called from atexit handler
+- missing locking in error paths for ungetwc
+- btowc mishandling of out-of-range non-EOF inputs
+- negated return value of ns_skiprr, failure in related functions
+- incorrect void return type for syncfs, missing error status
+- possible failure of tempnam due to missing null termination
+- negated tm_gmtoff field in struct tm
+- off-by-one error in getsubopt leaving equals sign in value result
+
+arch-specific bugs fixed:
+- soft deadlocks on i386/x86_64 due to missing barrier in internal locks
+- regression in arm pre-v7 support for kernels with kuser helper removed
+- runaway PC on mips detached thread exit (due to kernel regression)
+- mismatched ABI for local-dynamic model TLS on mips and powerpc
+- incorrect value of some SO_* constants on mips
+- broken 64-bit syscall argument passing on aarch64
+
+
+
+1.1.12 release notes
+
+new features:
+- fdpic abi on sh2 for shareable text segment without mmu
+- general fdpic elf support in dynamic linker
+- CFI generation for x86_64 asm source files
+- protection against silently building a libc.so with missing symbols
+
+compatibility:
+- nl_langinfo(CODESET) now returns "ASCII" in byte-based C locale
+- fixed build regression due to buggy .SECONDARY in some GNU make versions
+- additional arm eabi functions needed by llvm arm backend
+- added format argument attributes to gettext function prototypes
+- static PIE no longer requires linking with -E/-rdynamic
+- eliminated spurious protected-data warnings linking against libc.so
+- avoided spurious fpu asm errors with some armhf toolchains
+
+bugs fixed:
+- fclose of stdin/stdout caused deadlock at exit
+- missing memory barrier in pthread_join
+- open_[w]memstream produced no buffer when no writes took place
+- uninitialized scopeid in address lookups from hosts file and ip literals
+- ip literals for mismatching family (v4 vs v6) were queried as hostnames
+- possible crash on OOM in regcomp
+- incorrect contents in localeconv structure (-1 instead of CHAR_MAX)
+- strftime mishandling of out-of-range struct tm members
+- wrongful attribute((const)) on pthread_self and errno location function
+
+arch-specific bugs fixed:
+- arm crt1 entry point failed to align stack pointer in some cases
+- mips fesetround failed to actually set rounding mode
+- i386 asm source CFI generation had multiple bugs
+
+
+
+1.1.13 release notes
+
+new features:
+- out-of-tree builds
+- search domains in resolv.conf
+- sh arch supports j-core (j2) cas.l atomics
+- dynamic linker includes arch/abi in output when run as a command
+- header support for new kernel features through linux 4.4
+- mips vdso clock_gettime support
+- regex BRE extensions: \|, \+, \?
+
+performance:
+- improved atomics performance on all archs with ll/sc model
+- atomic instructions are now inlined on armv6
+- use fpu sqrt for arm softfp abi on targets with vfp
+
+compatibility:
+- getnameinfo now accepts sockaddr sizes larger than needed
+- new default CFLAGS/LDFLAGS avoid entire classes of toolchain bugs
+- explicit use of float_t/double_t avoids compiler float spill bugs
+- i386 max_align_t definition now works with g++ 4.7's pseudo-c++11
+- all known protocols are added to protoent functions
+- stub utmpname, utmpxname functions
+- linker support for -Bsymbolic-functions is no longer mandatory
+- regex parsing size limits increased
+- malloc_usable_size now accepts null pointer input
+
+bugs fixed:
+- potential single-byte heap overflow in getdelim
+- mishandling of transient failure opening hosts, services, resolv.conf
+- mremap was sometimes able to allocate objects larger than PTRDIFF_MAX
+- nl_langinfo wrongly returned NULL instead of "" for invalid items
+- out-of-bounds dynamic tls allocation due to pointer/index scaling error
+- getifaddrs misreported point-to-point interface addresses
+- tdelete left tsearch trees misbalanced
+- tsearch crashed on allocation failure
+- tsearch, tfind, and tdelete failed to handle null pointer input
+- passing signal number 0 to sigaction resulted in a crash
+- getdelim updated caller's size wrongly when realloc failed
+- getdelim realloc strategy was wasteful
+- if_nametoindex returned wrong value on failure
+- missing ssp-suppression for some source files called from early-init
+- various minor resolv.conf parsing bugs
+- fwrite wrongly reported success on write errors in line-buffered flush
+- fwrite and fread wrongly returned nmemb (not 0) when size was 0
+
+nommu-specific bugs fix:
+- failure to zero bss in FDPIC shared library loader
+- unsafe writes to read-only file mapping in non-FDPIC library loader
+
+arch-specific bugs fixed:
+- sh[eb]-nofpu-fdpic was using fpu-dependent setjmp/longjmp variants
+- dynamic linker path file name was wrong for arm "softfp" targets
+- mips siginfo_t and related macros were defined incorrectly
+- possibly misaligned pointer globals on arm (from an asm source file)
+- mips dynamic linker failed to provide info needed by debugger
+- mips cancellation asm wrongly assumed validity of $gp register value
+
+
+
+1.1.14 release notes
+
+regressions fixed:
+- treatment of empty string argument as error by puts and fputs
+- make clean and distclean failure in unconfigured trees
+- sh/fdpic dynamic linker entry point hang due to wrong code
+- armhf (and arm softfp model) build failure with clang
+
+other bugs fixed:
+- wrongly clamping (rather than failing) excessive rounds in crypt-sha*
+
+
+
+1.1.15 release notes
+
+new features:
+- mips64 (full 64-bit and n32) port
+- mips r6 isa support (subarch for mips, mips64, and mipsn32 archs)
+- powerpc64 port
+- powerpc (32-bit) soft-float ABI support (subarch)
+- pthread_tryjoin_np and pthread_timedjoin_np (nonstandard extensions)
+- header-level support for linux 4.5 and 4.6 features
+- sched_getcpu (nonstandard extension) support, including vdso version
+- __STDC_ISO_10646__, __STDC_IEC_559__ macros predefined via stdc-predef.h
+- support for new elf/arch features in elf.h
+
+compatibility:
+- configure now correctly chooses cross-prefix based on build/host/target
+- abort now successfully terminates pid 1 in a container (or top-level)
+
+bugs fixed:
+- memmem read past end of haystack, possible false positives or crashes
+- buffer underflow (reverse-overflow) in ungetwc
+- double-free under certain usage of putenv
+- incorrect treatment by regcomp of * at start of BRE subexpression
+- gethostbyname[2][_r] produced ip addresses in misaligned buffers
+- looking up some invalid hostnames caused malformed dns queries
+- lookups from hosts file were inconsistent with non-matching family
+- missing h_length value in gethostbyaddr results
+- a64l function produced wrong-signed results on 64-bit archs
+- broken padding of string formats to width in wide printf variants
+- wrong results for expf(-NAN) and exp2f(-NAN)
+- wrong value for RUSAGE_CHILDREN prevented it from working
+- abort failed to provide abnormal termination with SIGABRT blocked
+
+arch-specific bugs fixed:
+- broken posix_fadvise on arm and powerpc (32-bit)
+- thread structure/dtv corruption on powerpc at thread startup
+- various wrong mips and powerpc ioctl and termios constant values
diff --git a/system/lib/libc/musl/arch/emscripten/atomic.h b/system/lib/libc/musl/arch/emscripten/atomic_arch.h
similarity index 81%
rename from system/lib/libc/musl/arch/emscripten/atomic.h
rename to system/lib/libc/musl/arch/emscripten/atomic_arch.h
index 52cc54d..d700042 100644
--- a/system/lib/libc/musl/arch/emscripten/atomic.h
+++ b/system/lib/libc/musl/arch/emscripten/atomic_arch.h
@@ -5,6 +5,7 @@
 #include <emscripten.h>
 #include <emscripten/threading.h>
 
+#define a_ctz_l a_ctz_l
 static inline int a_ctz_l(unsigned long x)
 {
 	if (x == 0)
@@ -18,6 +19,7 @@
 	return nTrailingZeros;
 }
 
+#define a_ctz_64 a_ctz_64
 static inline int a_ctz_64(uint64_t x)
 {
 	uint32_t lo = (uint32_t)x;
@@ -27,52 +29,62 @@
 		return a_ctz_l((unsigned long)lo);
 }
 
+#define a_and_64 a_and_64
 static inline void a_and_64(volatile uint64_t *p, uint64_t v)
 {
 	*p &= v;
 }
 
+#define a_or_64 a_or_64
 static inline void a_or_64(volatile uint64_t *p, uint64_t v)
 {
 	*p |= v;
 }
 
 #ifdef __EMSCRIPTEN_PTHREADS__
+#define a_store_l a_store_l
 static inline void a_store_l(volatile void *p, long x)
 {
 	emscripten_atomic_store_u32((void*)p, x);
 }
 
+#define a_or_l a_or_l
 static inline void a_or_l(volatile void *p, long v)
 {
 	emscripten_atomic_or_u32((void*)p, v);
 }
 
+#define a_cas_p a_cas_p
 static inline void *a_cas_p(volatile void *p, void *t, void *s)
 {
 	return (void*)emscripten_atomic_cas_u32(p, (uint32_t)t, (uint32_t)s);
 }
 
+#define a_cas_l a_cas_l
 static inline long a_cas_l(volatile void *p, long t, long s)
 {
 	return emscripten_atomic_cas_u32(p, t, s);
 }
 
+#define a_cas a_cas
 static inline int a_cas(volatile int *p, int t, int s)
 {
 	return emscripten_atomic_cas_u32(p, t, s);
 }
 
+#define a_or a_or
 static inline void a_or(volatile void *p, int v)
 {
 	emscripten_atomic_or_u32((void*)p, v);
 }
 
+#define a_and a_and
 static inline void a_and(volatile void *p, int v)
 {
 	emscripten_atomic_and_u32((void*)p, v);
 }
 
+#define a_swap a_swap
 static inline int a_swap(volatile int *x, int v)
 {
 	int old;
@@ -82,36 +94,43 @@
 	return old;
 }
 
+#define a_fetch_add a_fetch_add
 static inline int a_fetch_add(volatile int *x, int v)
 {
 	return emscripten_atomic_add_u32(x, v);
 }
 
+#define a_inc a_inc
 static inline void a_inc(volatile int *x)
 {
 	emscripten_atomic_add_u32((void*)x, 1);
 }
 
+#define a_dec a_dec
 static inline void a_dec(volatile int *x)
 {
 	emscripten_atomic_sub_u32((void*)x, 1);
 }
 
+#define a_store a_store
 static inline void a_store(volatile int *p, int x)
 {
 	emscripten_atomic_store_u32((void*)p, x);
 }
 #else // __EMSCRIPTEN_PTHREADS__
+#define a_store_l a_store_l
 static inline void a_store_l(volatile void *p, long x)
 {
 	*(long*)p = x;
 }
 
+#define a_or_l a_or_l
 static inline void a_or_l(volatile void *p, long v)
 {
 	*(long*)p |= v;
 }
 
+#define a_cas_p a_cas_p
 static inline void *a_cas_p(volatile void *p, void *t, void *s)
 {
 	if (*(long*)p == t)
@@ -119,6 +138,7 @@
 	return t;
 }
 
+#define a_cas_l a_cas_l
 static inline long a_cas_l(volatile void *p, long t, long s)
 {
 	if (*(long*)p == t)
@@ -126,6 +146,7 @@
 	return t;
 }
 
+#define a_cas a_cas
 static inline int a_cas(volatile int *p, int t, int s)
 {
 	if (*p == t)
@@ -133,31 +154,37 @@
 	return t;
 }
 
+#define a_or a_or
 static inline void a_or(volatile void *p, int v)
 {
 	*(int*)p |= v;
 }
 
+#define a_and a_and
 static inline void a_and(volatile void *p, int v)
 {
 	*(int*)p &= v;
 }
 
+#define a_inc a_inc
 static inline void a_inc(volatile int *x)
 {
 	++*x;
 }
 
+#define a_dec a_dec
 static inline void a_dec(volatile int *x)
 {
 	--*x;
 }
 
+#define a_store a_store
 static inline void a_store(volatile int *p, int x)
 {
 	*p = x;
 }
 
+#define a_swap a_swap
 static inline int a_swap(volatile int *x, int v)
 {
 	int old;
@@ -166,16 +193,19 @@
 	return old;
 }
 
+#define a_fetch_add a_fetch_add
 static inline int a_fetch_add(volatile int *x, int v)
 {
   return __sync_fetch_and_add(x, v);
 }
 #endif
 
+#define a_spin a_spin
 static inline void a_spin()
 {
 }
 
+#define a_crash a_crash
 static inline void a_crash()
 {
   EM_ASM( abort() );
diff --git a/system/lib/libc/musl/arch/emscripten/bits/alltypes.h b/system/lib/libc/musl/arch/emscripten/bits/alltypes.h
index c7d8109..29f724f 100644
--- a/system/lib/libc/musl/arch/emscripten/bits/alltypes.h
+++ b/system/lib/libc/musl/arch/emscripten/bits/alltypes.h
@@ -84,41 +84,44 @@
 
 #if defined(__NEED_pthread_attr_t) && !defined(__DEFINED_pthread_attr_t)
 #ifdef __EMSCRIPTEN__
-// For canvas transfer implementation in Emscripten, use an extra 10th control field
+// For canvas transfer implementation in Emscripten, use an extra 11th control field
 // to pass a pointer to a string denoting the WebGL canvases to transfer.
-typedef struct { union { int __i[10]; unsigned __s[10]; } __u; } pthread_attr_t;
+typedef struct { union { int __i[11]; volatile int __vi[11]; unsigned __s[11]; } __u; } pthread_attr_t;
 #else
-typedef struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t;
+typedef struct { union { int __i[10]; volatile int __vi[10]; unsigned __s[10]; } __u; } pthread_attr_t;
 #endif
 #define __DEFINED_pthread_attr_t
 #endif
 
 #if defined(__NEED_pthread_mutex_t) && !defined(__DEFINED_pthread_mutex_t)
 #ifdef __EMSCRIPTEN__
-// For mutex implementation in Emscripten, need to use an extra seventh control field
+// For mutex implementation in Emscripten, need to use an extra eighth control field
 // to hold a temporary futex wait & wake location, designated as mutex->_m_addr.
-typedef struct { union { int __i[7]; void *__p[7]; } __u; } pthread_mutex_t;
+typedef struct { union { int __i[8]; volatile int __vi[8]; volatile void *__p[8]; } __u; } pthread_mutex_t;
 #else
-typedef struct { union { int __i[6]; void *__p[6]; } __u; } pthread_mutex_t;
+typedef struct { union { int __i[7]; volatile int __vi[7]; volatile void *__p[7]; } __u; } pthread_mutex_t;
 #endif
+typedef pthread_mutex_t mtx_t;
 #define __DEFINED_pthread_mutex_t
 #endif
 
 #if defined(__NEED_pthread_cond_t) && !defined(__DEFINED_pthread_cond_t)
-typedef struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t;
+typedef struct { union { int __i[12]; volatile int __vi[12]; void *__p[12]; } __u; } pthread_cond_t;
+typedef pthread_cond_t cnd_t;
 #define __DEFINED_pthread_cond_t
 #endif
 
 #if defined(__NEED_pthread_rwlock_t) && !defined(__DEFINED_pthread_rwlock_t)
-typedef struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t;
+typedef struct { union { int __i[8]; volatile int __vi[8]; void *__p[8]; } __u; } pthread_rwlock_t;
 #define __DEFINED_pthread_rwlock_t
 #endif
 
 #if defined(__NEED_pthread_barrier_t) && !defined(__DEFINED_pthread_barrier_t)
-typedef struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t;
+typedef struct { union { int __i[5]; volatile int __vi[5]; void *__p[5]; } __u; } pthread_barrier_t;
 #define __DEFINED_pthread_barrier_t
 #endif
 
+
 #if defined(__NEED_size_t) && !defined(__DEFINED_size_t)
 typedef unsigned _Addr size_t;
 #define __DEFINED_size_t
@@ -256,6 +259,10 @@
 #define __DEFINED_fsfilcnt_t
 #endif
 
+#if defined(__NEED_wint_t) && !defined(__DEFINED_wint_t)
+typedef unsigned wint_t;
+#define __DEFINED_wint_t
+#endif
 
 #if defined(__NEED_wctype_t) && !defined(__DEFINED_wctype_t)
 typedef unsigned long wctype_t;
@@ -375,6 +382,12 @@
 #endif
 
 
+#if defined(__NEED_mbstate_t) && !defined(__DEFINED_mbstate_t)
+typedef struct __mbstate_t { unsigned __opaque1, __opaque2; } mbstate_t;
+#define __DEFINED_mbstate_t
+#endif
+
+
 #if defined(__NEED_locale_t) && !defined(__DEFINED_locale_t)
 typedef struct __locale_struct * locale_t;
 #define __DEFINED_locale_t
diff --git a/system/lib/libc/musl/arch/emscripten/bits/fcntl.h b/system/lib/libc/musl/arch/emscripten/bits/fcntl.h
index 97bdfd6..900601b 100644
--- a/system/lib/libc/musl/arch/emscripten/bits/fcntl.h
+++ b/system/lib/libc/musl/arch/emscripten/bits/fcntl.h
@@ -15,6 +15,7 @@
 #define O_DIRECT     040000
 #define O_LARGEFILE 0100000
 #define O_NOATIME  01000000
+#define O_PATH    010000000
 #define O_TMPFILE 020000000
 #define O_NDELAY O_NONBLOCK
 
diff --git a/system/lib/libc/musl/arch/emscripten/bits/syscall.h b/system/lib/libc/musl/arch/emscripten/bits/syscall.h
index e24bb5c..a6e10f0 100644
--- a/system/lib/libc/musl/arch/emscripten/bits/syscall.h
+++ b/system/lib/libc/musl/arch/emscripten/bits/syscall.h
@@ -333,6 +333,7 @@
 #define __NR_inotify_init1	332
 #define __NR_preadv		333
 #define __NR_pwritev		334
+#define __NR_recvmmsg		337
 #define __NR_prlimit64		340
 #define __NR_name_to_handle_at	341
 #define __NR_open_by_handle_at	342
@@ -683,6 +684,7 @@
 #define SYS_inotify_init1	332
 #define SYS_preadv		333
 #define SYS_pwritev		334
+#define SYS_recvmmsg		337
 #define SYS_prlimit64		340
 #define SYS_name_to_handle_at	341
 #define SYS_open_by_handle_at	342
diff --git a/system/lib/libc/musl/arch/emscripten/syscall_arch.h b/system/lib/libc/musl/arch/emscripten/syscall_arch.h
index 9315991..81aa9a6 100644
--- a/system/lib/libc/musl/arch/emscripten/syscall_arch.h
+++ b/system/lib/libc/musl/arch/emscripten/syscall_arch.h
@@ -88,6 +88,7 @@
 long __syscall153(int which, ...);
 long __syscall163(int which, ...);
 long __syscall168(int which, ...);
+long __syscall178(int which, ...);
 long __syscall180(int which, ...);
 long __syscall181(int which, ...);
 long __syscall183(int which, ...);
@@ -140,7 +141,11 @@
 long __syscall331(int which, ...);
 long __syscall333(int which, ...);
 long __syscall334(int which, ...);
+long __syscall337(int which, ...);
 long __syscall340(int which, ...);
+long __syscall345(int which, ...);
+
+#undef SYS_futimesat
 
 #ifdef __cplusplus
 }
diff --git a/system/lib/libc/musl/configure b/system/lib/libc/musl/configure
index f3ea041..928455e 100755
--- a/system/lib/libc/musl/configure
+++ b/system/lib/libc/musl/configure
@@ -9,6 +9,9 @@
 
 Defaults for the options are specified in brackets.
 
+Configuration:
+  --srcdir=DIR            source directory [detected]
+
 Installation directories:
   --prefix=PREFIX         main installation prefix [/usr/local/musl]
   --exec-prefix=EPREFIX   installation prefix for executable files [PREFIX]
@@ -22,12 +25,14 @@
 System types:
   --target=TARGET         configure to run on target TARGET [detected]
   --host=HOST             same as --target
+  --build=BUILD           build system type; used only to infer cross-compiling
 
 Optional features:
   --enable-optimize=...   optimize listed components for speed over size [auto]
   --enable-debug          build with debugging information [disabled]
   --enable-warnings       build with recommended warnings flags [disabled]
-  --enable-gcc-wrapper    build musl-gcc toolchain wrapper [auto]
+  --enable-visibility     use global visibility options to optimize PIC [auto]
+  --enable-wrapper=...    build given musl toolchain wrapper [auto]
   --disable-shared        inhibit building shared library [enabled]
   --disable-static        inhibit building static library [enabled]
 
@@ -79,7 +84,7 @@
 tryflag () {
 printf "checking whether compiler accepts %s... " "$2"
 echo "typedef int x;" > "$tmpc"
-if $CC "$2" -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+if $CC $CFLAGS_TRY $2 -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
 printf "yes\n"
 eval "$1=\"\${$1} \$2\""
 eval "$1=\${$1# }"
@@ -93,7 +98,7 @@
 tryldflag () {
 printf "checking whether linker accepts %s... " "$2"
 echo "typedef int x;" > "$tmpc"
-if $CC -nostdlib -shared "$2" -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+if $CC $LDFLAGS_TRY -nostdlib -shared "$2" -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
 printf "yes\n"
 eval "$1=\"\${$1} \$2\""
 eval "$1=\${$1# }"
@@ -111,25 +116,36 @@
 CFLAGS_C99FSE=
 CFLAGS_AUTO=
 CFLAGS_MEMOPS=
+CFLAGS_NOSSP=
+CFLAGS_TRY=
 LDFLAGS_AUTO=
+LDFLAGS_TRY=
 OPTIMIZE_GLOBS=
+srcdir=
 prefix=/usr/local/musl
 exec_prefix='$(prefix)'
 bindir='$(exec_prefix)/bin'
 libdir='$(prefix)/lib'
 includedir='$(prefix)/include'
 syslibdir='/lib'
+tools=
+tool_libs=
+build=
 target=
 optimize=auto
 debug=no
 warnings=no
-shared=yes
+visibility=auto
+shared=auto
 static=yes
 wrapper=auto
+gcc_wrapper=no
+clang_wrapper=no
 
 for arg ; do
 case "$arg" in
---help) usage ;;
+--help|-h) usage ;;
+--srcdir=*) srcdir=${arg#*=} ;;
 --prefix=*) prefix=${arg#*=} ;;
 --exec-prefix=*) exec_prefix=${arg#*=} ;;
 --bindir=*) bindir=${arg#*=} ;;
@@ -147,10 +163,18 @@
 --disable-debug|--enable-debug=no) debug=no ;;
 --enable-warnings|--enable-warnings=yes) warnings=yes ;;
 --disable-warnings|--enable-warnings=no) warnings=no ;;
---enable-gcc-wrapper|--enable-gcc-wrapper=yes) wrapper=yes ;;
+--enable-visibility|--enable-visibility=yes) visibility=yes ;;
+--disable-visibility|--enable-visibility=no) visibility=no ;;
+--enable-wrapper|--enable-wrapper=yes) wrapper=detect ;;
+--enable-wrapper=all) wrapper=yes ; gcc_wrapper=yes ; clang_wrapper=yes ;;
+--enable-wrapper=gcc) wrapper=yes ; gcc_wrapper=yes ;;
+--enable-wrapper=clang) wrapper=yes ; clang_wrapper=yes ;;
+--disable-wrapper|--enable-wrapper=no) wrapper=no ;;
+--enable-gcc-wrapper|--enable-gcc-wrapper=yes) wrapper=yes ; gcc_wrapper=yes ;;
 --disable-gcc-wrapper|--enable-gcc-wrapper=no) wrapper=no ;;
---enable-*|--disable-*|--with-*|--without-*|--*dir=*|--build=*) ;;
+--enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;;
 --host=*|--target=*) target=${arg#*=} ;;
+--build=*) build=${arg#*=} ;;
 -* ) echo "$0: unknown option $arg" ;;
 CC=*) CC=${arg#*=} ;;
 CFLAGS=*) CFLAGS=${arg#*=} ;;
@@ -159,15 +183,27 @@
 CROSS_COMPILE=*) CROSS_COMPILE=${arg#*=} ;;
 LIBCC=*) LIBCC=${arg#*=} ;;
 *=*) ;;
-*) target=$arg ;;
+*) build=$arg ; target=$arg ;;
 esac
 done
 
-for i in prefix exec_prefix bindir libdir includedir syslibdir ; do
+for i in srcdir prefix exec_prefix bindir libdir includedir syslibdir ; do
 stripdir $i
 done
 
 #
+# Get the source dir for out-of-tree builds
+#
+if test -z "$srcdir" ; then
+srcdir="${0%/configure}"
+stripdir srcdir
+fi
+abs_builddir="$(pwd)" || fail "$0: cannot determine working directory"
+abs_srcdir="$(cd $srcdir && pwd)" || fail "$0: invalid source directory $srcdir"
+test "$abs_srcdir" = "$abs_builddir" && srcdir=.
+test "$srcdir" != "." -a -f Makefile -a ! -h Makefile && fail "$0: Makefile already exists in the working directory"
+
+#
 # Get a temp filename we can use
 #
 i=0
@@ -181,6 +217,15 @@
 trap 'rm "$tmpc"' EXIT INT QUIT TERM HUP
 
 #
+# Check whether we are cross-compiling, and set a default
+# CROSS_COMPILE prefix if none was provided.
+#
+test "$target" && \
+test "$target" != "$build" && \
+test -z "$CROSS_COMPILE" && \
+CROSS_COMPILE="$target-"
+
+#
 # Find a C compiler to use
 #
 printf "checking for C compiler... "
@@ -200,36 +245,59 @@
 fi
 
 #
-# Need to know if the compiler is gcc to decide whether to build the
-# musl-gcc wrapper, and for critical bug detection in some gcc versions.
+# Figure out options to force errors on unknown flags.
 #
-printf "checking whether compiler is gcc... "
-if fnmatch '*gcc\ version*' "$($CC -v 2>&1)" ; then
-cc_is_gcc=yes
+tryflag   CFLAGS_TRY  -Werror=unknown-warning-option
+tryflag   CFLAGS_TRY  -Werror=unused-command-line-argument
+tryldflag LDFLAGS_TRY -Werror=unknown-warning-option
+tryldflag LDFLAGS_TRY -Werror=unused-command-line-argument
+
+#
+# Need to know if the compiler is gcc or clang to decide which toolchain
+# wrappers to build.
+#
+printf "checking for C compiler family... "
+cc_ver="$(LC_ALL=C $CC -v 2>&1)"
+cc_family=unknown
+if fnmatch '*gcc\ version*' "$cc_ver" ; then
+cc_family=gcc
+elif fnmatch '*clang\ version*' "$cc_ver" ; then
+cc_family=clang
+fi
+echo "$cc_family"
+
+#
+# Figure out toolchain wrapper to build
+#
+if test "$wrapper" = auto -o "$wrapper" = detect ; then
+echo "#include <stdlib.h>" > "$tmpc"
+echo "#if ! __GLIBC__" >> "$tmpc"
+echo "#error no" >> "$tmpc"
+echo "#endif" >> "$tmpc"
+printf "checking for toolchain wrapper to build... "
+if test "$wrapper" = auto && ! $CC -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+echo "none"
+elif test "$cc_family" = gcc ; then
+gcc_wrapper=yes
+echo "gcc"
+elif test "$cc_family" = clang ; then
+clang_wrapper=yes
+echo "clang"
 else
-cc_is_gcc=no
+echo "none"
+if test "$wrapper" = detect ; then
+fail "$0: could not find an appropriate toolchain wrapper"
 fi
-echo "$cc_is_gcc"
-
-#
-# Only build musl-gcc wrapper if toolchain does not already target musl
-#
-if test "$wrapper" = auto ; then
-printf "checking whether to build musl-gcc wrapper... "
-if test "$cc_is_gcc" = yes ; then
-wrapper=yes
-while read line ; do
-case "$line" in */ld-musl-*) wrapper=no ;; esac
-done <<EOF
-$($CC -dumpspecs)
-EOF
-else
-wrapper=no
 fi
-echo "$wrapper"
 fi
 
-
+if test "$gcc_wrapper" = yes ; then
+tools="$tools obj/musl-gcc"
+tool_libs="$tool_libs lib/musl-gcc.specs"
+fi
+if test "$clang_wrapper" = yes ; then
+tools="$tools obj/musl-clang obj/ld.musl-clang"
+fi
 
 #
 # Find the target architecture
@@ -243,13 +311,18 @@
 #
 case "$target" in
 # Catch these early to simplify matching for 32-bit archs
-mips64*|powerpc64*) fail "$0: unsupported target \"$target\"" ;;
 arm*) ARCH=arm ;;
+aarch64*) ARCH=aarch64 ;;
+i?86-nt32*) ARCH=nt32 ;;
 i?86*) ARCH=i386 ;;
 x86_64-x32*|x32*|x86_64*x32) ARCH=x32 ;;
+x86_64-nt64*) ARCH=nt64 ;;
 x86_64*) ARCH=x86_64 ;;
+mips64*) ARCH=mips64 ;;
 mips*) ARCH=mips ;;
 microblaze*) ARCH=microblaze ;;
+or1k*) ARCH=or1k ;;
+powerpc64*) ARCH=powerpc64 ;;
 powerpc*) ARCH=powerpc ;;
 sh[1-9bel-]*|sh|superh*) ARCH=sh ;;
 asmjs-unknown-emscripten) ARCH=emscripten ;;
@@ -281,7 +354,7 @@
 #endif
 x;
 EOF
-if $CC $CFLAGS_C99FSE -I./arch/$ARCH -I./include $CPPFLAGS $CFLAGS \
+if $CC $CFLAGS_C99FSE $CPPFLAGS $CFLAGS \
   -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
 printf "no\n"
 else
@@ -290,6 +363,20 @@
 fi
 
 #
+# The GNU toolchain defaults to assuming unmarked files need an
+# executable stack, potentially exposing vulnerabilities in programs
+# linked with such object files. Fix this.
+#
+tryflag CFLAGS_C99FSE -Wa,--noexecstack
+
+#
+# Check for options to disable stack protector, which needs to be
+# disabled for a few early-bootstrap translation units. If not found,
+# this is not an error; we assume the toolchain does not do ssp.
+#
+tryflag CFLAGS_NOSSP -fno-stack-protector
+
+#
 # Check for options that may be needed to prevent the compiler from
 # generating self-referential versions of memcpy,, memmove, memcmp,
 # and memset. Really, we should add a check to determine if this
@@ -299,12 +386,25 @@
 # XXX EMSCRIPTEN tryflag CFLAGS_MEMOPS -fno-tree-loop-distribute-patterns
 
 #
-# If debugging is explicitly enabled, don't auto-enable optimizations
+# Enable debugging if requessted.
 #
-if test "$debug" = yes ; then
-CFLAGS_AUTO=-g
-test "$optimize" = auto && optimize=no
+test "$debug" = yes && CFLAGS_AUTO=-g
+
+#
+# Preprocess asm files to add extra debugging information if debug is
+# enabled, our assembler supports the needed directives, and the
+# preprocessing script has been written for our architecture.
+#
+printf "checking whether we should preprocess assembly to add debugging information... "
+if fnmatch '-g*|*\ -g*' "$CFLAGS_AUTO $CFLAGS" &&
+   test -f "tools/add-cfi.$ARCH.awk" &&
+   printf ".file 1 \"srcfile.s\"\n.line 1\n.cfi_startproc\n.cfi_endproc" | $CC -g -x assembler -c -o /dev/null 2>/dev/null -
+then
+  ADD_CFI=yes
+else
+  ADD_CFI=no
 fi
+printf "%s\n" "$ADD_CFI"
 
 #
 # Possibly add a -O option to CFLAGS and select modules to optimize with
@@ -370,21 +470,27 @@
 tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables
 
 #
-# The GNU toolchain defaults to assuming unmarked files need an
-# executable stack, potentially exposing vulnerabilities in programs
-# linked with such object files. Fix this.
+# Attempt to put each function and each data object in its own
+# section. This both allows additional size optimizations at link
+# time and works around a dangerous class of compiler/assembler bugs
+# whereby relative address expressions are constant-folded by the
+# assembler even when one or more of the symbols involved is
+# replaceable. See gas pr 18561 and gcc pr 66609, 68178, etc.
 #
-tryflag CFLAGS_AUTO -Wa,--noexecstack
+tryflag CFLAGS_AUTO -ffunction-sections
+tryflag CFLAGS_AUTO -fdata-sections
 
 #
 # On x86, make sure we don't have incompatible instruction set
 # extensions enabled by default. This is bad for making static binaries.
 # We cheat and use i486 rather than i386 because i386 really does not
 # work anyway (issues with atomic ops).
+# Some build environments pass -march and -mtune options via CC, so
+# check both CC and CFLAGS.
 #
 if test "$ARCH" = "i386" ; then
-fnmatch '-march=*|*\ -march=*' "$CFLAGS" || tryldflag CFLAGS_AUTO -march=i486
-fnmatch '-mtune=*|*\ -mtune=*' "$CFLAGS" || tryldflag CFLAGS_AUTO -mtune=generic
+fnmatch '-march=*|*\ -march=*' "$CC $CFLAGS" || tryldflag CFLAGS_AUTO -march=i486
+fnmatch '-mtune=*|*\ -mtune=*' "$CC $CFLAGS" || tryldflag CFLAGS_AUTO -mtune=generic
 fi
 
 #
@@ -409,16 +515,76 @@
 tryflag CFLAGS_AUTO -Wno-pointer-to-int-cast
 fi
 
+if test "x$visibility" = xauto ; then
+# This test checks toolchain support for several things:
+# - the -include option
+# - the attributes/pragmas used in vis.h
+# - linking code that takes the address of protected symbols
+# - gcc 3.x bug that wrongly claims declarations mismatch
+printf "checking whether global visibility preinclude works... "
+cat > "$tmpc" <<EOF
+__attribute__((__visibility__("default")))
+extern struct a *const x;
+typedef struct a b;
+extern b *const x;
+b *const x;
+int (*fp)(void);
+int foo(void) { }
+int bar(void) { fp = foo; return foo(); }
+EOF
+if $CC $CFLAGS_C99FSE $CPPFLAGS $CFLAGS \
+  -DSHARED -fPIC -I$srcdir/src/internal -include vis.h \
+  -nostdlib -shared -Wl,-Bsymbolic-functions \
+  -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+visibility=yes
+else
+visibility=no
+fi
+printf "%s\n" "$visibility"
+fi
+
+if test "x$visibility" = xyes ; then
+CFLAGS_AUTO="$CFLAGS_AUTO -include vis.h"
+CFLAGS_AUTO="${CFLAGS_AUTO# }"
+fi
+
+# Determine if the compiler produces position-independent code (PIC)
+# by default. If so, we don't need to compile separate object files
+# for libc.a and libc.so.
+if trycppif __PIC__ "$CFLAGS_C99FSE $CPPFLAGS $CFLAGS" ; then
+pic_default=yes
+else
+pic_default=no
+fi
+
+# Reduce space lost to padding for alignment purposes by sorting data
+# objects according to their alignment reqirements. This approximates
+# optimal packing.
+tryldflag LDFLAGS_AUTO -Wl,--sort-section,alignment
+tryldflag LDFLAGS_AUTO -Wl,--sort-common
+
+# When linking shared library, drop dummy weak definitions that were
+# replaced by strong definitions from other translation units.
+tryldflag LDFLAGS_AUTO -Wl,--gc-sections
+
 # Some patched GCC builds have these defaults messed up...
-tryflag CFLAGS_AUTO -fno-stack-protector
 tryldflag LDFLAGS_AUTO -Wl,--hash-style=both
 
-# Disable dynamic linking if ld is broken and can't do -Bsymbolic-functions
-LDFLAGS_DUMMY=
-tryldflag LDFLAGS_DUMMY -Wl,-Bsymbolic-functions || {
-printf "warning: disabling dynamic linking support\n"
-shared=no
-}
+# Prevent linking if there are undefined symbols; if any exist,
+# libc.so will crash at runtime during relocation processing.
+# The common way this can happen is failure to link the compiler
+# runtime library; implementation error is also a possibility.
+tryldflag LDFLAGS_AUTO -Wl,--no-undefined
+
+# Avoid exporting symbols from compiler runtime libraries. They
+# should be hidden anyway, but some toolchains including old gcc
+# versions built without shared library support and pcc are broken.
+tryldflag LDFLAGS_AUTO -Wl,--exclude-libs=ALL
+
+# Linking with -Bsymbolic-functions is no longer mandatory for
+# the dynamic linker to work, but enable it if it works as
+# a linking optimization.
+tryldflag LDFLAGS_AUTO -Wl,-Bsymbolic-functions
 
 # Find compiler runtime library
 test -z "$LIBCC" && tryldflag LIBCC -lgcc && tryldflag LIBCC -lgcc_eh
@@ -429,7 +595,7 @@
 
 # Figure out arch variants for archs with variants
 SUBARCH=
-t="$CFLAGS_C99FSE $CPPFLAGS $CFLAGS_AUTO $CFLAGS"
+t="$CFLAGS_C99FSE $CPPFLAGS $CFLAGS"
 
 if test "$ARCH" = "x86_64" ; then
 trycppif __ILP32__ "$t" && ARCH=x32
@@ -438,17 +604,56 @@
 if test "$ARCH" = "arm" ; then
 trycppif __ARMEB__ "$t" && SUBARCH=${SUBARCH}eb
 trycppif __ARM_PCS_VFP "$t" && SUBARCH=${SUBARCH}hf
+# Versions of clang up until at least 3.8 have the wrong constraint codes
+# for floating point operands to inline asm. Detect this so the affected
+# source files can just disable the asm.
+if test "$cc_family" = clang ; then
+printf "checking whether clang's vfp asm constraints work... "
+echo 'float f(float x) { __asm__("":"+t"(x)); return x; }' > "$tmpc"
+if $CC $CFLAGS_C99FSE $CPPFLAGS $CFLAGS -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+printf "yes\n"
+else
+printf "no\n"
+CFLAGS_AUTO="$CFLAGS_AUTO -DBROKEN_VFP_ASM"
+CFLAGS_AUTO="${CFLAGS_AUTO# }"
+fi
+fi
+fi
+
+if test "$ARCH" = "aarch64" ; then
+trycppif __AARCH64EB__ "$t" && SUBARCH=${SUBARCH}_be
 fi
 
 if test "$ARCH" = "mips" ; then
+trycppif "__mips_isa_rev >= 6" "$t" && SUBARCH=${SUBARCH}r6
 trycppif "_MIPSEL || __MIPSEL || __MIPSEL__" "$t" && SUBARCH=${SUBARCH}el
 trycppif __mips_soft_float "$t" && SUBARCH=${SUBARCH}-sf
 fi
 
+if test "$ARCH" = "mips64" ; then
+trycppif "_MIPS_SIM != _ABI64" "$t" && ARCH=mipsn32
+trycppif "__mips_isa_rev >= 6" "$t" && SUBARCH=${SUBARCH}r6
+trycppif "_MIPSEL || __MIPSEL || __MIPSEL__" "$t" && SUBARCH=${SUBARCH}el
+trycppif __mips_soft_float "$t" && SUBARCH=${SUBARCH}-sf
+fi
+
+if test "$ARCH" = "powerpc" ; then
+trycppif "__NO_FPRS__ && !_SOFT_FLOAT" "$t" && fail \
+  "$0: error: compiler's floating point configuration is unsupported"
+trycppif _SOFT_FLOAT "$t" && SUBARCH=${SUBARCH}-sf
+fi
+
 test "$ARCH" = "microblaze" && trycppif __MICROBLAZEEL__ "$t" \
 && SUBARCH=${SUBARCH}el
 
+if test "$ARCH" = "powerpc64" ; then
+trycppif "_CALL_ELF == 2" "$t" || fail "$0: error: unsupported powerpc64 ABI"
+trycppif __LITTLE_ENDIAN__ "$t" && SUBARCH=${SUBARCH}le
+trycppif _SOFT_FLOAT "$t" && fail "$0: error: soft-float not supported on powerpc64"
+fi
+
 if test "$ARCH" = "sh" ; then
+tryflag CFLAGS_AUTO -Wa,--isa=any
 trycppif __BIG_ENDIAN__ "$t" && SUBARCH=${SUBARCH}eb
 if trycppif "__SH_FPU_ANY__ || __SH4__" "$t" ; then
 # Some sh configurations are broken and replace double with float
@@ -465,6 +670,9 @@
 else
 SUBARCH=${SUBARCH}-nofpu
 fi
+if trycppif __SH_FDPIC__ "$t" ; then
+SUBARCH=${SUBARCH}-fdpic
+fi
 fi
 
 test "$SUBARCH" \
@@ -488,14 +696,25 @@
 echo '#if LDBL_MANT_DIG == 53' >> "$tmpc"
 echo 'typedef char ldcheck[9-(int)sizeof(long double)];' >> "$tmpc"
 echo '#endif' >> "$tmpc"
-if $CC $CFLAGS_C99FSE -I./arch/$ARCH -I./include $CPPFLAGS $CFLAGS \
-  -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
+if $CC $CFLAGS_C99FSE \
+  -I$srcdir/arch/$ARCH -I$srcdir/arch/generic -I$srcdir/include \
+  $CPPFLAGS $CFLAGS -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then
 printf "yes\n"
 else
 printf "no\n"
 fail "$0: error: unsupported long double type"
 fi
 
+#
+# Some build systems globally pass in broken CFLAGS like -ffast-math
+# for all packages. On recent GCC we can detect this and error out
+# early rather than producing a seriously-broken math library.
+#
+if trycppif "__FAST_MATH__" \
+  "$CFLAGS_C99FSE $CPPFLAGS $CFLAGS" ; then
+fail "$0: error: compiler has broken floating point; check CFLAGS"
+fi
+
 printf "creating config.mak... "
 
 cmdline=$(quote "$0")
@@ -511,6 +730,7 @@
 ARCH = $ARCH
 SUBARCH = $SUBARCH
 ASMSUBARCH = $ASMSUBARCH
+srcdir = $srcdir
 prefix = $prefix
 exec_prefix = $exec_prefix
 bindir = $bindir
@@ -518,19 +738,28 @@
 includedir = $includedir
 syslibdir = $syslibdir
 CC = $CC
-CFLAGS = $CFLAGS_AUTO $CFLAGS
+CFLAGS = $CFLAGS
+CFLAGS_AUTO = $CFLAGS_AUTO
 CFLAGS_C99FSE = $CFLAGS_C99FSE
 CFLAGS_MEMOPS = $CFLAGS_MEMOPS
+CFLAGS_NOSSP = $CFLAGS_NOSSP
 CPPFLAGS = $CPPFLAGS
-LDFLAGS = $LDFLAGS_AUTO $LDFLAGS
+LDFLAGS = $LDFLAGS
+LDFLAGS_AUTO = $LDFLAGS_AUTO
 CROSS_COMPILE = $CROSS_COMPILE
 LIBCC = $LIBCC
 OPTIMIZE_GLOBS = $OPTIMIZE_GLOBS
+ALL_TOOLS = $tools
+TOOL_LIBS = $tool_libs
+ADD_CFI = $ADD_CFI
 EOF
 test "x$static" = xno && echo "STATIC_LIBS ="
 test "x$shared" = xno && echo "SHARED_LIBS ="
-test "x$wrapper" = xno && echo "ALL_TOOLS ="
-test "x$wrapper" = xno && echo "TOOL_LIBS ="
+test "x$cc_family" = xgcc && echo 'WRAPCC_GCC = $(CC)'
+test "x$cc_family" = xclang && echo 'WRAPCC_CLANG = $(CC)'
+test "x$pic_default" = xyes && echo 'AOBJS = $(LOBJS)'
 exec 1>&3 3>&-
 
+test "$srcdir" = "." || ln -sf $srcdir/Makefile .
+
 printf "done\n"
diff --git a/system/lib/libc/musl/ldso/dlstart.c b/system/lib/libc/musl/ldso/dlstart.c
new file mode 100644
index 0000000..4dbe178
--- /dev/null
+++ b/system/lib/libc/musl/ldso/dlstart.c
@@ -0,0 +1,148 @@
+#include <stddef.h>
+#include "dynlink.h"
+
+#ifndef START
+#define START "_dlstart"
+#endif
+
+#define SHARED
+
+#include "crt_arch.h"
+
+#ifndef GETFUNCSYM
+#define GETFUNCSYM(fp, sym, got) do { \
+	__attribute__((__visibility__("hidden"))) void sym(); \
+	static void (*static_func_ptr)() = sym; \
+	__asm__ __volatile__ ( "" : "+m"(static_func_ptr) : : "memory"); \
+	*(fp) = static_func_ptr; } while(0)
+#endif
+
+__attribute__((__visibility__("hidden")))
+void _dlstart_c(size_t *sp, size_t *dynv)
+{
+	size_t i, aux[AUX_CNT], dyn[DYN_CNT];
+	size_t *rel, rel_size, base;
+
+	int argc = *sp;
+	char **argv = (void *)(sp+1);
+
+	for (i=argc+1; argv[i]; i++);
+	size_t *auxv = (void *)(argv+i+1);
+
+	for (i=0; i<AUX_CNT; i++) aux[i] = 0;
+	for (i=0; auxv[i]; i+=2) if (auxv[i]<AUX_CNT)
+		aux[auxv[i]] = auxv[i+1];
+
+#if DL_FDPIC
+	struct fdpic_loadseg *segs, fakeseg;
+	size_t j;
+	if (dynv) {
+		/* crt_arch.h entry point asm is responsible for reserving
+		 * space and moving the extra fdpic arguments to the stack
+		 * vector where they are easily accessible from C. */
+		segs = ((struct fdpic_loadmap *)(sp[-1] ? sp[-1] : sp[-2]))->segs;
+	} else {
+		/* If dynv is null, the entry point was started from loader
+		 * that is not fdpic-aware. We can assume normal fixed-
+		 * displacement ELF loading was performed, but when ldso was
+		 * run as a command, finding the Ehdr is a heursitic: we
+		 * have to assume Phdrs start in the first 4k of the file. */
+		base = aux[AT_BASE];
+		if (!base) base = aux[AT_PHDR] & -4096;
+		segs = &fakeseg;
+		segs[0].addr = base;
+		segs[0].p_vaddr = 0;
+		segs[0].p_memsz = -1;
+		Ehdr *eh = (void *)base;
+		Phdr *ph = (void *)(base + eh->e_phoff);
+		size_t phnum = eh->e_phnum;
+		size_t phent = eh->e_phentsize;
+		while (phnum-- && ph->p_type != PT_DYNAMIC)
+			ph = (void *)((size_t)ph + phent);
+		dynv = (void *)(base + ph->p_vaddr);
+	}
+#endif
+
+	for (i=0; i<DYN_CNT; i++) dyn[i] = 0;
+	for (i=0; dynv[i]; i+=2) if (dynv[i]<DYN_CNT)
+		dyn[dynv[i]] = dynv[i+1];
+
+#if DL_FDPIC
+	for (i=0; i<DYN_CNT; i++) {
+		if (i==DT_RELASZ || i==DT_RELSZ) continue;
+		if (!dyn[i]) continue;
+		for (j=0; dyn[i]-segs[j].p_vaddr >= segs[j].p_memsz; j++);
+		dyn[i] += segs[j].addr - segs[j].p_vaddr;
+	}
+	base = 0;
+
+	const Sym *syms = (void *)dyn[DT_SYMTAB];
+
+	rel = (void *)dyn[DT_RELA];
+	rel_size = dyn[DT_RELASZ];
+	for (; rel_size; rel+=3, rel_size-=3*sizeof(size_t)) {
+		if (!IS_RELATIVE(rel[1], syms)) continue;
+		for (j=0; rel[0]-segs[j].p_vaddr >= segs[j].p_memsz; j++);
+		size_t *rel_addr = (void *)
+			(rel[0] + segs[j].addr - segs[j].p_vaddr);
+		if (R_TYPE(rel[1]) == REL_FUNCDESC_VAL) {
+			*rel_addr += segs[rel_addr[1]].addr
+				- segs[rel_addr[1]].p_vaddr
+				+ syms[R_SYM(rel[1])].st_value;
+			rel_addr[1] = dyn[DT_PLTGOT];
+		} else {
+			size_t val = syms[R_SYM(rel[1])].st_value;
+			for (j=0; val-segs[j].p_vaddr >= segs[j].p_memsz; j++);
+			*rel_addr = rel[2] + segs[j].addr - segs[j].p_vaddr + val;
+		}
+	}
+#else
+	/* If the dynamic linker is invoked as a command, its load
+	 * address is not available in the aux vector. Instead, compute
+	 * the load address as the difference between &_DYNAMIC and the
+	 * virtual address in the PT_DYNAMIC program header. */
+	base = aux[AT_BASE];
+	if (!base) {
+		size_t phnum = aux[AT_PHNUM];
+		size_t phentsize = aux[AT_PHENT];
+		Phdr *ph = (void *)aux[AT_PHDR];
+		for (i=phnum; i--; ph = (void *)((char *)ph + phentsize)) {
+			if (ph->p_type == PT_DYNAMIC) {
+				base = (size_t)dynv - ph->p_vaddr;
+				break;
+			}
+		}
+	}
+
+	/* MIPS uses an ugly packed form for GOT relocations. Since we
+	 * can't make function calls yet and the code is tiny anyway,
+	 * it's simply inlined here. */
+	if (NEED_MIPS_GOT_RELOCS) {
+		size_t local_cnt = 0;
+		size_t *got = (void *)(base + dyn[DT_PLTGOT]);
+		for (i=0; dynv[i]; i+=2) if (dynv[i]==DT_MIPS_LOCAL_GOTNO)
+			local_cnt = dynv[i+1];
+		for (i=0; i<local_cnt; i++) got[i] += base;
+	}
+
+	rel = (void *)(base+dyn[DT_REL]);
+	rel_size = dyn[DT_RELSZ];
+	for (; rel_size; rel+=2, rel_size-=2*sizeof(size_t)) {
+		if (!IS_RELATIVE(rel[1], 0)) continue;
+		size_t *rel_addr = (void *)(base + rel[0]);
+		*rel_addr += base;
+	}
+
+	rel = (void *)(base+dyn[DT_RELA]);
+	rel_size = dyn[DT_RELASZ];
+	for (; rel_size; rel+=3, rel_size-=3*sizeof(size_t)) {
+		if (!IS_RELATIVE(rel[1], 0)) continue;
+		size_t *rel_addr = (void *)(base + rel[0]);
+		*rel_addr = base + rel[2];
+	}
+#endif
+
+	stage2_func dls2;
+	GETFUNCSYM(&dls2, __dls2, base+dyn[DT_PLTGOT]);
+	dls2((void *)base, sp);
+}
diff --git a/system/lib/libc/musl/ldso/dynlink.c b/system/lib/libc/musl/ldso/dynlink.c
new file mode 100644
index 0000000..e458f38
--- /dev/null
+++ b/system/lib/libc/musl/ldso/dynlink.c
@@ -0,0 +1,1934 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <elf.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <link.h>
+#include <setjmp.h>
+#include <pthread.h>
+#include <ctype.h>
+#include <dlfcn.h>
+#include "pthread_impl.h"
+#include "libc.h"
+#include "dynlink.h"
+
+static void error(const char *, ...);
+
+#define MAXP2(a,b) (-(-(a)&-(b)))
+#define ALIGN(x,y) ((x)+(y)-1 & -(y))
+
+struct debug {
+	int ver;
+	void *head;
+	void (*bp)(void);
+	int state;
+	void *base;
+};
+
+struct td_index {
+	size_t args[2];
+	struct td_index *next;
+};
+
+struct dso {
+#if DL_FDPIC
+	struct fdpic_loadmap *loadmap;
+#else
+	unsigned char *base;
+#endif
+	char *name;
+	size_t *dynv;
+	struct dso *next, *prev;
+
+	Phdr *phdr;
+	int phnum;
+	size_t phentsize;
+	int refcnt;
+	Sym *syms;
+	uint32_t *hashtab;
+	uint32_t *ghashtab;
+	int16_t *versym;
+	char *strings;
+	unsigned char *map;
+	size_t map_len;
+	dev_t dev;
+	ino_t ino;
+	signed char global;
+	char relocated;
+	char constructed;
+	char kernel_mapped;
+	struct dso **deps, *needed_by;
+	char *rpath_orig, *rpath;
+	struct tls_module tls;
+	size_t tls_id;
+	size_t relro_start, relro_end;
+	void **new_dtv;
+	unsigned char *new_tls;
+	volatile int new_dtv_idx, new_tls_idx;
+	struct td_index *td_index;
+	struct dso *fini_next;
+	char *shortname;
+#if DL_FDPIC
+	unsigned char *base;
+#else
+	struct fdpic_loadmap *loadmap;
+#endif
+	struct funcdesc {
+		void *addr;
+		size_t *got;
+	} *funcdescs;
+	size_t *got;
+	char buf[];
+};
+
+struct symdef {
+	Sym *sym;
+	struct dso *dso;
+};
+
+int __init_tp(void *);
+void __init_libc(char **, char *);
+void *__copy_tls(unsigned char *);
+
+__attribute__((__visibility__("hidden")))
+const char *__libc_get_version(void);
+
+static struct builtin_tls {
+	char c;
+	struct pthread pt;
+	void *space[16];
+} builtin_tls[1];
+#define MIN_TLS_ALIGN offsetof(struct builtin_tls, pt)
+
+#define ADDEND_LIMIT 4096
+static size_t *saved_addends, *apply_addends_to;
+
+static struct dso ldso;
+static struct dso *head, *tail, *fini_head;
+static char *env_path, *sys_path;
+static unsigned long long gencnt;
+static int runtime;
+static int ldd_mode;
+static int ldso_fail;
+static int noload;
+static jmp_buf *rtld_fail;
+static pthread_rwlock_t lock;
+static struct debug debug;
+static struct tls_module *tls_tail;
+static size_t tls_cnt, tls_offset, tls_align = MIN_TLS_ALIGN;
+static size_t static_tls_cnt;
+static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE };
+static struct fdpic_loadmap *app_loadmap;
+static struct fdpic_dummy_loadmap app_dummy_loadmap;
+
+struct debug *_dl_debug_addr = &debug;
+
+__attribute__((__visibility__("hidden")))
+void (*const __init_array_start)(void)=0, (*const __fini_array_start)(void)=0;
+
+__attribute__((__visibility__("hidden")))
+extern void (*const __init_array_end)(void), (*const __fini_array_end)(void);
+
+weak_alias(__init_array_start, __init_array_end);
+weak_alias(__fini_array_start, __fini_array_end);
+
+static int dl_strcmp(const char *l, const char *r)
+{
+	for (; *l==*r && *l; l++, r++);
+	return *(unsigned char *)l - *(unsigned char *)r;
+}
+#define strcmp(l,r) dl_strcmp(l,r)
+
+/* Compute load address for a virtual address in a given dso. */
+#if DL_FDPIC
+static void *laddr(const struct dso *p, size_t v)
+{
+	size_t j=0;
+	if (!p->loadmap) return p->base + v;
+	for (j=0; v-p->loadmap->segs[j].p_vaddr >= p->loadmap->segs[j].p_memsz; j++);
+	return (void *)(v - p->loadmap->segs[j].p_vaddr + p->loadmap->segs[j].addr);
+}
+#define fpaddr(p, v) ((void (*)())&(struct funcdesc){ \
+	laddr(p, v), (p)->got })
+#else
+#define laddr(p, v) (void *)((p)->base + (v))
+#define fpaddr(p, v) ((void (*)())laddr(p, v))
+#endif
+
+static void decode_vec(size_t *v, size_t *a, size_t cnt)
+{
+	size_t i;
+	for (i=0; i<cnt; i++) a[i] = 0;
+	for (; v[0]; v+=2) if (v[0]-1<cnt-1) {
+		a[0] |= 1UL<<v[0];
+		a[v[0]] = v[1];
+	}
+}
+
+static int search_vec(size_t *v, size_t *r, size_t key)
+{
+	for (; v[0]!=key; v+=2)
+		if (!v[0]) return 0;
+	*r = v[1];
+	return 1;
+}
+
+static uint32_t sysv_hash(const char *s0)
+{
+	const unsigned char *s = (void *)s0;
+	uint_fast32_t h = 0;
+	while (*s) {
+		h = 16*h + *s++;
+		h ^= h>>24 & 0xf0;
+	}
+	return h & 0xfffffff;
+}
+
+static uint32_t gnu_hash(const char *s0)
+{
+	const unsigned char *s = (void *)s0;
+	uint_fast32_t h = 5381;
+	for (; *s; s++)
+		h += h*32 + *s;
+	return h;
+}
+
+static Sym *sysv_lookup(const char *s, uint32_t h, struct dso *dso)
+{
+	size_t i;
+	Sym *syms = dso->syms;
+	uint32_t *hashtab = dso->hashtab;
+	char *strings = dso->strings;
+	for (i=hashtab[2+h%hashtab[0]]; i; i=hashtab[2+hashtab[0]+i]) {
+		if ((!dso->versym || dso->versym[i] >= 0)
+		    && (!strcmp(s, strings+syms[i].st_name)))
+			return syms+i;
+	}
+	return 0;
+}
+
+static Sym *gnu_lookup(uint32_t h1, uint32_t *hashtab, struct dso *dso, const char *s)
+{
+	uint32_t nbuckets = hashtab[0];
+	uint32_t *buckets = hashtab + 4 + hashtab[2]*(sizeof(size_t)/4);
+	uint32_t i = buckets[h1 % nbuckets];
+
+	if (!i) return 0;
+
+	uint32_t *hashval = buckets + nbuckets + (i - hashtab[1]);
+
+	for (h1 |= 1; ; i++) {
+		uint32_t h2 = *hashval++;
+		if ((h1 == (h2|1)) && (!dso->versym || dso->versym[i] >= 0)
+		    && !strcmp(s, dso->strings + dso->syms[i].st_name))
+			return dso->syms+i;
+		if (h2 & 1) break;
+	}
+
+	return 0;
+}
+
+static Sym *gnu_lookup_filtered(uint32_t h1, uint32_t *hashtab, struct dso *dso, const char *s, uint32_t fofs, size_t fmask)
+{
+	const size_t *bloomwords = (const void *)(hashtab+4);
+	size_t f = bloomwords[fofs & (hashtab[2]-1)];
+	if (!(f & fmask)) return 0;
+
+	f >>= (h1 >> hashtab[3]) % (8 * sizeof f);
+	if (!(f & 1)) return 0;
+
+	return gnu_lookup(h1, hashtab, dso, s);
+}
+
+#define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON | 1<<STT_TLS)
+#define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE)
+
+#ifndef ARCH_SYM_REJECT_UND
+#define ARCH_SYM_REJECT_UND(s) 0
+#endif
+
+static struct symdef find_sym(struct dso *dso, const char *s, int need_def)
+{
+	uint32_t h = 0, gh, gho, *ght;
+	size_t ghm = 0;
+	struct symdef def = {0};
+	for (; dso; dso=dso->next) {
+		Sym *sym;
+		if (!dso->global) continue;
+		if ((ght = dso->ghashtab)) {
+			if (!ghm) {
+				gh = gnu_hash(s);
+				int maskbits = 8 * sizeof ghm;
+				gho = gh / maskbits;
+				ghm = 1ul << gh % maskbits;
+			}
+			sym = gnu_lookup_filtered(gh, ght, dso, s, gho, ghm);
+		} else {
+			if (!h) h = sysv_hash(s);
+			sym = sysv_lookup(s, h, dso);
+		}
+		if (!sym) continue;
+		if (!sym->st_shndx)
+			if (need_def || (sym->st_info&0xf) == STT_TLS
+			    || ARCH_SYM_REJECT_UND(sym))
+				continue;
+		if (!sym->st_value)
+			if ((sym->st_info&0xf) != STT_TLS)
+				continue;
+		if (!(1<<(sym->st_info&0xf) & OK_TYPES)) continue;
+		if (!(1<<(sym->st_info>>4) & OK_BINDS)) continue;
+
+		if (def.sym && sym->st_info>>4 == STB_WEAK) continue;
+		def.sym = sym;
+		def.dso = dso;
+		if (sym->st_info>>4 == STB_GLOBAL) break;
+	}
+	return def;
+}
+
+__attribute__((__visibility__("hidden")))
+ptrdiff_t __tlsdesc_static(), __tlsdesc_dynamic();
+
+static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stride)
+{
+	unsigned char *base = dso->base;
+	Sym *syms = dso->syms;
+	char *strings = dso->strings;
+	Sym *sym;
+	const char *name;
+	void *ctx;
+	int type;
+	int sym_index;
+	struct symdef def;
+	size_t *reloc_addr;
+	size_t sym_val;
+	size_t tls_val;
+	size_t addend;
+	int skip_relative = 0, reuse_addends = 0, save_slot = 0;
+
+	if (dso == &ldso) {
+		/* Only ldso's REL table needs addend saving/reuse. */
+		if (rel == apply_addends_to)
+			reuse_addends = 1;
+		skip_relative = 1;
+	}
+
+	for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) {
+		if (skip_relative && IS_RELATIVE(rel[1], dso->syms)) continue;
+		type = R_TYPE(rel[1]);
+		if (type == REL_NONE) continue;
+		sym_index = R_SYM(rel[1]);
+		reloc_addr = laddr(dso, rel[0]);
+		if (sym_index) {
+			sym = syms + sym_index;
+			name = strings + sym->st_name;
+			ctx = type==REL_COPY ? head->next : head;
+			def = (sym->st_info&0xf) == STT_SECTION
+				? (struct symdef){ .dso = dso, .sym = sym }
+				: find_sym(ctx, name, type==REL_PLT);
+			if (!def.sym && (sym->st_shndx != SHN_UNDEF
+			    || sym->st_info>>4 != STB_WEAK)) {
+				error("Error relocating %s: %s: symbol not found",
+					dso->name, name);
+				if (runtime) longjmp(*rtld_fail, 1);
+				continue;
+			}
+		} else {
+			sym = 0;
+			def.sym = 0;
+			def.dso = dso;
+		}
+
+		if (stride > 2) {
+			addend = rel[2];
+		} else if (type==REL_GOT || type==REL_PLT|| type==REL_COPY) {
+			addend = 0;
+		} else if (reuse_addends) {
+			/* Save original addend in stage 2 where the dso
+			 * chain consists of just ldso; otherwise read back
+			 * saved addend since the inline one was clobbered. */
+			if (head==&ldso)
+				saved_addends[save_slot] = *reloc_addr;
+			addend = saved_addends[save_slot++];
+		} else {
+			addend = *reloc_addr;
+		}
+
+		sym_val = def.sym ? (size_t)laddr(def.dso, def.sym->st_value) : 0;
+		tls_val = def.sym ? def.sym->st_value : 0;
+
+		switch(type) {
+		case REL_NONE:
+			break;
+		case REL_OFFSET:
+			addend -= (size_t)reloc_addr;
+		case REL_SYMBOLIC:
+		case REL_GOT:
+		case REL_PLT:
+			*reloc_addr = sym_val + addend;
+			break;
+		case REL_RELATIVE:
+			*reloc_addr = (size_t)base + addend;
+			break;
+		case REL_SYM_OR_REL:
+			if (sym) *reloc_addr = sym_val + addend;
+			else *reloc_addr = (size_t)base + addend;
+			break;
+		case REL_COPY:
+			memcpy(reloc_addr, (void *)sym_val, sym->st_size);
+			break;
+		case REL_OFFSET32:
+			*(uint32_t *)reloc_addr = sym_val + addend
+				- (size_t)reloc_addr;
+			break;
+		case REL_FUNCDESC:
+			*reloc_addr = def.sym ? (size_t)(def.dso->funcdescs
+				+ (def.sym - def.dso->syms)) : 0;
+			break;
+		case REL_FUNCDESC_VAL:
+			if ((sym->st_info&0xf) == STT_SECTION) *reloc_addr += sym_val;
+			else *reloc_addr = sym_val;
+			reloc_addr[1] = def.sym ? (size_t)def.dso->got : 0;
+			break;
+		case REL_DTPMOD:
+			*reloc_addr = def.dso->tls_id;
+			break;
+		case REL_DTPOFF:
+			*reloc_addr = tls_val + addend - DTP_OFFSET;
+			break;
+#ifdef TLS_ABOVE_TP
+		case REL_TPOFF:
+			*reloc_addr = tls_val + def.dso->tls.offset + TPOFF_K + addend;
+			break;
+#else
+		case REL_TPOFF:
+			*reloc_addr = tls_val - def.dso->tls.offset + addend;
+			break;
+		case REL_TPOFF_NEG:
+			*reloc_addr = def.dso->tls.offset - tls_val + addend;
+			break;
+#endif
+		case REL_TLSDESC:
+			if (stride<3) addend = reloc_addr[1];
+			if (runtime && def.dso->tls_id >= static_tls_cnt) {
+				struct td_index *new = malloc(sizeof *new);
+				if (!new) {
+					error(
+					"Error relocating %s: cannot allocate TLSDESC for %s",
+					dso->name, sym ? name : "(local)" );
+					longjmp(*rtld_fail, 1);
+				}
+				new->next = dso->td_index;
+				dso->td_index = new;
+				new->args[0] = def.dso->tls_id;
+				new->args[1] = tls_val + addend;
+				reloc_addr[0] = (size_t)__tlsdesc_dynamic;
+				reloc_addr[1] = (size_t)new;
+			} else {
+				reloc_addr[0] = (size_t)__tlsdesc_static;
+#ifdef TLS_ABOVE_TP
+				reloc_addr[1] = tls_val + def.dso->tls.offset
+					+ TPOFF_K + addend;
+#else
+				reloc_addr[1] = tls_val - def.dso->tls.offset
+					+ addend;
+#endif
+			}
+			break;
+		default:
+			error("Error relocating %s: unsupported relocation type %d",
+				dso->name, type);
+			if (runtime) longjmp(*rtld_fail, 1);
+			continue;
+		}
+	}
+}
+
+/* A huge hack: to make up for the wastefulness of shared libraries
+ * needing at least a page of dirty memory even if they have no global
+ * data, we reclaim the gaps at the beginning and end of writable maps
+ * and "donate" them to the heap by setting up minimal malloc
+ * structures and then freeing them. */
+
+static void reclaim(struct dso *dso, size_t start, size_t end)
+{
+	size_t *a, *z;
+	if (start >= dso->relro_start && start < dso->relro_end) start = dso->relro_end;
+	if (end   >= dso->relro_start && end   < dso->relro_end) end = dso->relro_start;
+	start = start + 6*sizeof(size_t)-1 & -4*sizeof(size_t);
+	end = (end & -4*sizeof(size_t)) - 2*sizeof(size_t);
+	if (start>end || end-start < 4*sizeof(size_t)) return;
+	a = laddr(dso, start);
+	z = laddr(dso, end);
+	a[-2] = 1;
+	a[-1] = z[0] = end-start + 2*sizeof(size_t) | 1;
+	z[1] = 1;
+	free(a);
+}
+
+static void reclaim_gaps(struct dso *dso)
+{
+	Phdr *ph = dso->phdr;
+	size_t phcnt = dso->phnum;
+
+	if (DL_FDPIC) return; // FIXME
+	for (; phcnt--; ph=(void *)((char *)ph+dso->phentsize)) {
+		if (ph->p_type!=PT_LOAD) continue;
+		if ((ph->p_flags&(PF_R|PF_W))!=(PF_R|PF_W)) continue;
+		reclaim(dso, ph->p_vaddr & -PAGE_SIZE, ph->p_vaddr);
+		reclaim(dso, ph->p_vaddr+ph->p_memsz,
+			ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE);
+	}
+}
+
+static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t off)
+{
+	static int no_map_fixed;
+	char *q;
+	if (!no_map_fixed) {
+		q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);
+		if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL)
+			return q;
+		no_map_fixed = 1;
+	}
+	/* Fallbacks for MAP_FIXED failure on NOMMU kernels. */
+	if (flags & MAP_ANONYMOUS) {
+		memset(p, 0, n);
+		return p;
+	}
+	ssize_t r;
+	if (lseek(fd, off, SEEK_SET) < 0) return MAP_FAILED;
+	for (q=p; n; q+=r, off+=r, n-=r) {
+		r = read(fd, q, n);
+		if (r < 0 && errno != EINTR) return MAP_FAILED;
+		if (!r) {
+			memset(q, 0, n);
+			break;
+		}
+	}
+	return p;
+}
+
+static void unmap_library(struct dso *dso)
+{
+	if (dso->loadmap) {
+		size_t i;
+		for (i=0; i<dso->loadmap->nsegs; i++) {
+			if (!dso->loadmap->segs[i].p_memsz)
+				continue;
+			munmap((void *)dso->loadmap->segs[i].addr,
+				dso->loadmap->segs[i].p_memsz);
+		}
+		free(dso->loadmap);
+	} else if (dso->map && dso->map_len) {
+		munmap(dso->map, dso->map_len);
+	}
+}
+
+static void *map_library(int fd, struct dso *dso)
+{
+	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
+	void *allocated_buf=0;
+	size_t phsize;
+	size_t addr_min=SIZE_MAX, addr_max=0, map_len;
+	size_t this_min, this_max;
+	size_t nsegs = 0;
+	off_t off_start;
+	Ehdr *eh;
+	Phdr *ph, *ph0;
+	unsigned prot;
+	unsigned char *map=MAP_FAILED, *base;
+	size_t dyn=0;
+	size_t tls_image=0;
+	size_t i;
+
+	ssize_t l = read(fd, buf, sizeof buf);
+	eh = buf;
+	if (l<0) return 0;
+	if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
+		goto noexec;
+	phsize = eh->e_phentsize * eh->e_phnum;
+	if (phsize > sizeof buf - sizeof *eh) {
+		allocated_buf = malloc(phsize);
+		if (!allocated_buf) return 0;
+		l = pread(fd, allocated_buf, phsize, eh->e_phoff);
+		if (l < 0) goto error;
+		if (l != phsize) goto noexec;
+		ph = ph0 = allocated_buf;
+	} else if (eh->e_phoff + phsize > l) {
+		l = pread(fd, buf+1, phsize, eh->e_phoff);
+		if (l < 0) goto error;
+		if (l != phsize) goto noexec;
+		ph = ph0 = (void *)(buf + 1);
+	} else {
+		ph = ph0 = (void *)((char *)buf + eh->e_phoff);
+	}
+	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
+		if (ph->p_type == PT_DYNAMIC) {
+			dyn = ph->p_vaddr;
+		} else if (ph->p_type == PT_TLS) {
+			tls_image = ph->p_vaddr;
+			dso->tls.align = ph->p_align;
+			dso->tls.len = ph->p_filesz;
+			dso->tls.size = ph->p_memsz;
+		} else if (ph->p_type == PT_GNU_RELRO) {
+			dso->relro_start = ph->p_vaddr & -PAGE_SIZE;
+			dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
+		}
+		if (ph->p_type != PT_LOAD) continue;
+		nsegs++;
+		if (ph->p_vaddr < addr_min) {
+			addr_min = ph->p_vaddr;
+			off_start = ph->p_offset;
+			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
+				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
+				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
+		}
+		if (ph->p_vaddr+ph->p_memsz > addr_max) {
+			addr_max = ph->p_vaddr+ph->p_memsz;
+		}
+	}
+	if (!dyn) goto noexec;
+	if (DL_FDPIC && !(eh->e_flags & FDPIC_CONSTDISP_FLAG)) {
+		dso->loadmap = calloc(1, sizeof *dso->loadmap
+			+ nsegs * sizeof *dso->loadmap->segs);
+		if (!dso->loadmap) goto error;
+		dso->loadmap->nsegs = nsegs;
+		for (ph=ph0, i=0; i<nsegs; ph=(void *)((char *)ph+eh->e_phentsize)) {
+			if (ph->p_type != PT_LOAD) continue;
+			prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
+				((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
+				((ph->p_flags&PF_X) ? PROT_EXEC : 0));
+			map = mmap(0, ph->p_memsz + (ph->p_vaddr & PAGE_SIZE-1),
+				prot, MAP_PRIVATE,
+				fd, ph->p_offset & -PAGE_SIZE);
+			if (map == MAP_FAILED) {
+				unmap_library(dso);
+				goto error;
+			}
+			dso->loadmap->segs[i].addr = (size_t)map +
+				(ph->p_vaddr & PAGE_SIZE-1);
+			dso->loadmap->segs[i].p_vaddr = ph->p_vaddr;
+			dso->loadmap->segs[i].p_memsz = ph->p_memsz;
+			i++;
+			if (prot & PROT_WRITE) {
+				size_t brk = (ph->p_vaddr & PAGE_SIZE-1)
+					+ ph->p_filesz;
+				size_t pgbrk = brk + PAGE_SIZE-1 & -PAGE_SIZE;
+				size_t pgend = brk + ph->p_memsz - ph->p_filesz
+					+ PAGE_SIZE-1 & -PAGE_SIZE;
+				if (pgend > pgbrk && mmap_fixed(map+pgbrk,
+					pgend-pgbrk, prot,
+					MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
+					-1, off_start) == MAP_FAILED)
+					goto error;
+				memset(map + brk, 0, pgbrk-brk);
+			}
+		}
+		map = (void *)dso->loadmap->segs[0].addr;
+		map_len = 0;
+		goto done_mapping;
+	}
+	addr_max += PAGE_SIZE-1;
+	addr_max &= -PAGE_SIZE;
+	addr_min &= -PAGE_SIZE;
+	off_start &= -PAGE_SIZE;
+	map_len = addr_max - addr_min + off_start;
+	/* The first time, we map too much, possibly even more than
+	 * the length of the file. This is okay because we will not
+	 * use the invalid part; we just need to reserve the right
+	 * amount of virtual address space to map over later. */
+	map = DL_NOMMU_SUPPORT
+		? mmap((void *)addr_min, map_len, PROT_READ|PROT_WRITE|PROT_EXEC,
+			MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
+		: mmap((void *)addr_min, map_len, prot,
+			MAP_PRIVATE, fd, off_start);
+	if (map==MAP_FAILED) goto error;
+	dso->map = map;
+	dso->map_len = map_len;
+	/* If the loaded file is not relocatable and the requested address is
+	 * not available, then the load operation must fail. */
+	if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) {
+		errno = EBUSY;
+		goto error;
+	}
+	base = map - addr_min;
+	dso->phdr = 0;
+	dso->phnum = 0;
+	for (ph=ph0, i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
+		if (ph->p_type != PT_LOAD) continue;
+		/* Check if the programs headers are in this load segment, and
+		 * if so, record the address for use by dl_iterate_phdr. */
+		if (!dso->phdr && eh->e_phoff >= ph->p_offset
+		    && eh->e_phoff+phsize <= ph->p_offset+ph->p_filesz) {
+			dso->phdr = (void *)(base + ph->p_vaddr
+				+ (eh->e_phoff-ph->p_offset));
+			dso->phnum = eh->e_phnum;
+			dso->phentsize = eh->e_phentsize;
+		}
+		/* Reuse the existing mapping for the lowest-address LOAD */
+		if ((ph->p_vaddr & -PAGE_SIZE) == addr_min && !DL_NOMMU_SUPPORT)
+			continue;
+		this_min = ph->p_vaddr & -PAGE_SIZE;
+		this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE;
+		off_start = ph->p_offset & -PAGE_SIZE;
+		prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) |
+			((ph->p_flags&PF_W) ? PROT_WRITE: 0) |
+			((ph->p_flags&PF_X) ? PROT_EXEC : 0));
+		if (mmap_fixed(base+this_min, this_max-this_min, prot, MAP_PRIVATE|MAP_FIXED, fd, off_start) == MAP_FAILED)
+			goto error;
+		if (ph->p_memsz > ph->p_filesz) {
+			size_t brk = (size_t)base+ph->p_vaddr+ph->p_filesz;
+			size_t pgbrk = brk+PAGE_SIZE-1 & -PAGE_SIZE;
+			memset((void *)brk, 0, pgbrk-brk & PAGE_SIZE-1);
+			if (pgbrk-(size_t)base < this_max && mmap_fixed((void *)pgbrk, (size_t)base+this_max-pgbrk, prot, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED)
+				goto error;
+		}
+	}
+	for (i=0; ((size_t *)(base+dyn))[i]; i+=2)
+		if (((size_t *)(base+dyn))[i]==DT_TEXTREL) {
+			if (mprotect(map, map_len, PROT_READ|PROT_WRITE|PROT_EXEC)
+			    && errno != ENOSYS)
+				goto error;
+			break;
+		}
+done_mapping:
+	dso->base = base;
+	dso->dynv = laddr(dso, dyn);
+	if (dso->tls.size) dso->tls.image = laddr(dso, tls_image);
+	if (!runtime) reclaim_gaps(dso);
+	free(allocated_buf);
+	return map;
+noexec:
+	errno = ENOEXEC;
+error:
+	if (map!=MAP_FAILED) unmap_library(dso);
+	free(allocated_buf);
+	return 0;
+}
+
+static int path_open(const char *name, const char *s, char *buf, size_t buf_size)
+{
+	size_t l;
+	int fd;
+	for (;;) {
+		s += strspn(s, ":\n");
+		l = strcspn(s, ":\n");
+		if (l-1 >= INT_MAX) return -1;
+		if (snprintf(buf, buf_size, "%.*s/%s", (int)l, s, name) < buf_size) {
+			if ((fd = open(buf, O_RDONLY|O_CLOEXEC))>=0) return fd;
+			switch (errno) {
+			case ENOENT:
+			case ENOTDIR:
+			case EACCES:
+			case ENAMETOOLONG:
+				break;
+			default:
+				/* Any negative value but -1 will inhibit
+				 * futher path search. */
+				return -2;
+			}
+		}
+		s += l;
+	}
+}
+
+static int fixup_rpath(struct dso *p, char *buf, size_t buf_size)
+{
+	size_t n, l;
+	const char *s, *t, *origin;
+	char *d;
+	if (p->rpath || !p->rpath_orig) return 0;
+	if (!strchr(p->rpath_orig, '$')) {
+		p->rpath = p->rpath_orig;
+		return 0;
+	}
+	n = 0;
+	s = p->rpath_orig;
+	while ((t=strchr(s, '$'))) {
+		if (strncmp(t, "$ORIGIN", 7) && strncmp(t, "${ORIGIN}", 9))
+			return 0;
+		s = t+1;
+		n++;
+	}
+	if (n > SSIZE_MAX/PATH_MAX) return 0;
+
+	if (p->kernel_mapped) {
+		/* $ORIGIN searches cannot be performed for the main program
+		 * when it is suid/sgid/AT_SECURE. This is because the
+		 * pathname is under the control of the caller of execve.
+		 * For libraries, however, $ORIGIN can be processed safely
+		 * since the library's pathname came from a trusted source
+		 * (either system paths or a call to dlopen). */
+		if (libc.secure)
+			return 0;
+		l = readlink("/proc/self/exe", buf, buf_size);
+		if (l == -1) switch (errno) {
+		case ENOENT:
+		case ENOTDIR:
+		case EACCES:
+			break;
+		default:
+			return -1;
+		}
+		if (l >= buf_size)
+			return 0;
+		buf[l] = 0;
+		origin = buf;
+	} else {
+		origin = p->name;
+	}
+	t = strrchr(origin, '/');
+	l = t ? t-origin : 0;
+	p->rpath = malloc(strlen(p->rpath_orig) + n*l + 1);
+	if (!p->rpath) return -1;
+
+	d = p->rpath;
+	s = p->rpath_orig;
+	while ((t=strchr(s, '$'))) {
+		memcpy(d, s, t-s);
+		d += t-s;
+		memcpy(d, origin, l);
+		d += l;
+		/* It was determined previously that the '$' is followed
+		 * either by "ORIGIN" or "{ORIGIN}". */
+		s = t + 7 + 2*(t[1]=='{');
+	}
+	strcpy(d, s);
+	return 0;
+}
+
+static void decode_dyn(struct dso *p)
+{
+	size_t dyn[DYN_CNT];
+	decode_vec(p->dynv, dyn, DYN_CNT);
+	p->syms = laddr(p, dyn[DT_SYMTAB]);
+	p->strings = laddr(p, dyn[DT_STRTAB]);
+	if (dyn[0]&(1<<DT_HASH))
+		p->hashtab = laddr(p, dyn[DT_HASH]);
+	if (dyn[0]&(1<<DT_RPATH))
+		p->rpath_orig = p->strings + dyn[DT_RPATH];
+	if (dyn[0]&(1<<DT_RUNPATH))
+		p->rpath_orig = p->strings + dyn[DT_RUNPATH];
+	if (dyn[0]&(1<<DT_PLTGOT))
+		p->got = laddr(p, dyn[DT_PLTGOT]);
+	if (search_vec(p->dynv, dyn, DT_GNU_HASH))
+		p->ghashtab = laddr(p, *dyn);
+	if (search_vec(p->dynv, dyn, DT_VERSYM))
+		p->versym = laddr(p, *dyn);
+}
+
+static size_t count_syms(struct dso *p)
+{
+	if (p->hashtab) return p->hashtab[1];
+
+	size_t nsym, i;
+	uint32_t *buckets = p->ghashtab + 4 + (p->ghashtab[2]*sizeof(size_t)/4);
+	uint32_t *hashval;
+	for (i = nsym = 0; i < p->ghashtab[0]; i++) {
+		if (buckets[i] > nsym)
+			nsym = buckets[i];
+	}
+	if (nsym) {
+		hashval = buckets + p->ghashtab[0] + (nsym - p->ghashtab[1]);
+		do nsym++;
+		while (!(*hashval++ & 1));
+	}
+	return nsym;
+}
+
+static void *dl_mmap(size_t n)
+{
+	void *p;
+	int prot = PROT_READ|PROT_WRITE, flags = MAP_ANONYMOUS|MAP_PRIVATE;
+#ifdef SYS_mmap2
+	p = (void *)__syscall(SYS_mmap2, 0, n, prot, flags, -1, 0);
+#else
+	p = (void *)__syscall(SYS_mmap, 0, n, prot, flags, -1, 0);
+#endif
+	return p == MAP_FAILED ? 0 : p;
+}
+
+static void makefuncdescs(struct dso *p)
+{
+	static int self_done;
+	size_t nsym = count_syms(p);
+	size_t i, size = nsym * sizeof(*p->funcdescs);
+
+	if (!self_done) {
+		p->funcdescs = dl_mmap(size);
+		self_done = 1;
+	} else {
+		p->funcdescs = malloc(size);
+	}
+	if (!p->funcdescs) {
+		if (!runtime) a_crash();
+		error("Error allocating function descriptors for %s", p->name);
+		longjmp(*rtld_fail, 1);
+	}
+	for (i=0; i<nsym; i++) {
+		if ((p->syms[i].st_info&0xf)==STT_FUNC && p->syms[i].st_shndx) {
+			p->funcdescs[i].addr = laddr(p, p->syms[i].st_value);
+			p->funcdescs[i].got = p->got;
+		} else {
+			p->funcdescs[i].addr = 0;
+			p->funcdescs[i].got = 0;
+		}
+	}
+}
+
+static struct dso *load_library(const char *name, struct dso *needed_by)
+{
+	char buf[2*NAME_MAX+2];
+	const char *pathname;
+	unsigned char *map;
+	struct dso *p, temp_dso = {0};
+	int fd;
+	struct stat st;
+	size_t alloc_size;
+	int n_th = 0;
+	int is_self = 0;
+
+	if (!*name) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	/* Catch and block attempts to reload the implementation itself */
+	if (name[0]=='l' && name[1]=='i' && name[2]=='b') {
+		static const char *rp, reserved[] =
+			"c\0pthread\0rt\0m\0dl\0util\0xnet\0";
+		char *z = strchr(name, '.');
+		if (z) {
+			size_t l = z-name;
+			for (rp=reserved; *rp && strncmp(name+3, rp, l-3); rp+=strlen(rp)+1);
+			if (*rp) {
+				if (ldd_mode) {
+					/* Track which names have been resolved
+					 * and only report each one once. */
+					static unsigned reported;
+					unsigned mask = 1U<<(rp-reserved);
+					if (!(reported & mask)) {
+						reported |= mask;
+						dprintf(1, "\t%s => %s (%p)\n",
+							name, ldso.name,
+							ldso.base);
+					}
+				}
+				is_self = 1;
+			}
+		}
+	}
+	if (!strcmp(name, ldso.name)) is_self = 1;
+	if (is_self) {
+		if (!ldso.prev) {
+			tail->next = &ldso;
+			ldso.prev = tail;
+			tail = ldso.next ? ldso.next : &ldso;
+		}
+		return &ldso;
+	}
+	if (strchr(name, '/')) {
+		pathname = name;
+		fd = open(name, O_RDONLY|O_CLOEXEC);
+	} else {
+		/* Search for the name to see if it's already loaded */
+		for (p=head->next; p; p=p->next) {
+			if (p->shortname && !strcmp(p->shortname, name)) {
+				p->refcnt++;
+				return p;
+			}
+		}
+		if (strlen(name) > NAME_MAX) return 0;
+		fd = -1;
+		if (env_path) fd = path_open(name, env_path, buf, sizeof buf);
+		for (p=needed_by; fd == -1 && p; p=p->needed_by) {
+			if (fixup_rpath(p, buf, sizeof buf) < 0)
+				fd = -2; /* Inhibit further search. */
+			if (p->rpath)
+				fd = path_open(name, p->rpath, buf, sizeof buf);
+		}
+		if (fd == -1) {
+			if (!sys_path) {
+				char *prefix = 0;
+				size_t prefix_len;
+				if (ldso.name[0]=='/') {
+					char *s, *t, *z;
+					for (s=t=z=ldso.name; *s; s++)
+						if (*s=='/') z=t, t=s;
+					prefix_len = z-ldso.name;
+					if (prefix_len < PATH_MAX)
+						prefix = ldso.name;
+				}
+				if (!prefix) {
+					prefix = "";
+					prefix_len = 0;
+				}
+				char etc_ldso_path[prefix_len + 1
+					+ sizeof "/etc/ld-musl-" LDSO_ARCH ".path"];
+				snprintf(etc_ldso_path, sizeof etc_ldso_path,
+					"%.*s/etc/ld-musl-" LDSO_ARCH ".path",
+					(int)prefix_len, prefix);
+				FILE *f = fopen(etc_ldso_path, "rbe");
+				if (f) {
+					if (getdelim(&sys_path, (size_t[1]){0}, 0, f) <= 0) {
+						free(sys_path);
+						sys_path = "";
+					}
+					fclose(f);
+				} else if (errno != ENOENT) {
+					sys_path = "";
+				}
+			}
+			if (!sys_path) sys_path = "/lib:/usr/local/lib:/usr/lib";
+			fd = path_open(name, sys_path, buf, sizeof buf);
+		}
+		pathname = buf;
+	}
+	if (fd < 0) return 0;
+	if (fstat(fd, &st) < 0) {
+		close(fd);
+		return 0;
+	}
+	for (p=head->next; p; p=p->next) {
+		if (p->dev == st.st_dev && p->ino == st.st_ino) {
+			/* If this library was previously loaded with a
+			 * pathname but a search found the same inode,
+			 * setup its shortname so it can be found by name. */
+			if (!p->shortname && pathname != name)
+				p->shortname = strrchr(p->name, '/')+1;
+			close(fd);
+			p->refcnt++;
+			return p;
+		}
+	}
+	map = noload ? 0 : map_library(fd, &temp_dso);
+	close(fd);
+	if (!map) return 0;
+
+	/* Allocate storage for the new DSO. When there is TLS, this
+	 * storage must include a reservation for all pre-existing
+	 * threads to obtain copies of both the new TLS, and an
+	 * extended DTV capable of storing an additional slot for
+	 * the newly-loaded DSO. */
+	alloc_size = sizeof *p + strlen(pathname) + 1;
+	if (runtime && temp_dso.tls.image) {
+		size_t per_th = temp_dso.tls.size + temp_dso.tls.align
+			+ sizeof(void *) * (tls_cnt+3);
+		n_th = libc.threads_minus_1 + 1;
+		if (n_th > SSIZE_MAX / per_th) alloc_size = SIZE_MAX;
+		else alloc_size += n_th * per_th;
+	}
+	p = calloc(1, alloc_size);
+	if (!p) {
+		unmap_library(&temp_dso);
+		return 0;
+	}
+	memcpy(p, &temp_dso, sizeof temp_dso);
+	decode_dyn(p);
+	p->dev = st.st_dev;
+	p->ino = st.st_ino;
+	p->refcnt = 1;
+	p->needed_by = needed_by;
+	p->name = p->buf;
+	strcpy(p->name, pathname);
+	/* Add a shortname only if name arg was not an explicit pathname. */
+	if (pathname != name) p->shortname = strrchr(p->name, '/')+1;
+	if (p->tls.image) {
+		p->tls_id = ++tls_cnt;
+		tls_align = MAXP2(tls_align, p->tls.align);
+#ifdef TLS_ABOVE_TP
+		p->tls.offset = tls_offset + ( (tls_align-1) &
+			-(tls_offset + (uintptr_t)p->tls.image) );
+		tls_offset += p->tls.size;
+#else
+		tls_offset += p->tls.size + p->tls.align - 1;
+		tls_offset -= (tls_offset + (uintptr_t)p->tls.image)
+			& (p->tls.align-1);
+		p->tls.offset = tls_offset;
+#endif
+		p->new_dtv = (void *)(-sizeof(size_t) &
+			(uintptr_t)(p->name+strlen(p->name)+sizeof(size_t)));
+		p->new_tls = (void *)(p->new_dtv + n_th*(tls_cnt+1));
+		if (tls_tail) tls_tail->next = &p->tls;
+		else libc.tls_head = &p->tls;
+		tls_tail = &p->tls;
+	}
+
+	tail->next = p;
+	p->prev = tail;
+	tail = p;
+
+	if (DL_FDPIC) makefuncdescs(p);
+
+	if (ldd_mode) dprintf(1, "\t%s => %s (%p)\n", name, pathname, p->base);
+
+	return p;
+}
+
+static void load_deps(struct dso *p)
+{
+	size_t i, ndeps=0;
+	struct dso ***deps = &p->deps, **tmp, *dep;
+	for (; p; p=p->next) {
+		for (i=0; p->dynv[i]; i+=2) {
+			if (p->dynv[i] != DT_NEEDED) continue;
+			dep = load_library(p->strings + p->dynv[i+1], p);
+			if (!dep) {
+				error("Error loading shared library %s: %m (needed by %s)",
+					p->strings + p->dynv[i+1], p->name);
+				if (runtime) longjmp(*rtld_fail, 1);
+				continue;
+			}
+			if (runtime) {
+				tmp = realloc(*deps, sizeof(*tmp)*(ndeps+2));
+				if (!tmp) longjmp(*rtld_fail, 1);
+				tmp[ndeps++] = dep;
+				tmp[ndeps] = 0;
+				*deps = tmp;
+			}
+		}
+	}
+}
+
+static void load_preload(char *s)
+{
+	int tmp;
+	char *z;
+	for (z=s; *z; s=z) {
+		for (   ; *s && (isspace(*s) || *s==':'); s++);
+		for (z=s; *z && !isspace(*z) && *z!=':'; z++);
+		tmp = *z;
+		*z = 0;
+		load_library(s, 0);
+		*z = tmp;
+	}
+}
+
+static void make_global(struct dso *p)
+{
+	for (; p; p=p->next) p->global = 1;
+}
+
+static void do_mips_relocs(struct dso *p, size_t *got)
+{
+	size_t i, j, rel[2];
+	unsigned char *base = p->base;
+	i=0; search_vec(p->dynv, &i, DT_MIPS_LOCAL_GOTNO);
+	if (p==&ldso) {
+		got += i;
+	} else {
+		while (i--) *got++ += (size_t)base;
+	}
+	j=0; search_vec(p->dynv, &j, DT_MIPS_GOTSYM);
+	i=0; search_vec(p->dynv, &i, DT_MIPS_SYMTABNO);
+	Sym *sym = p->syms + j;
+	rel[0] = (unsigned char *)got - base;
+	for (i-=j; i; i--, sym++, rel[0]+=sizeof(size_t)) {
+		rel[1] = R_INFO(sym-p->syms, R_MIPS_JUMP_SLOT);
+		do_relocs(p, rel, sizeof rel, 2);
+	}
+}
+
+static void reloc_all(struct dso *p)
+{
+	size_t dyn[DYN_CNT];
+	for (; p; p=p->next) {
+		if (p->relocated) continue;
+		decode_vec(p->dynv, dyn, DYN_CNT);
+		if (NEED_MIPS_GOT_RELOCS)
+			do_mips_relocs(p, laddr(p, dyn[DT_PLTGOT]));
+		do_relocs(p, laddr(p, dyn[DT_JMPREL]), dyn[DT_PLTRELSZ],
+			2+(dyn[DT_PLTREL]==DT_RELA));
+		do_relocs(p, laddr(p, dyn[DT_REL]), dyn[DT_RELSZ], 2);
+		do_relocs(p, laddr(p, dyn[DT_RELA]), dyn[DT_RELASZ], 3);
+
+		if (head != &ldso && p->relro_start != p->relro_end &&
+		    mprotect(laddr(p, p->relro_start), p->relro_end-p->relro_start, PROT_READ)
+		    && errno != ENOSYS) {
+			error("Error relocating %s: RELRO protection failed: %m",
+				p->name);
+			if (runtime) longjmp(*rtld_fail, 1);
+		}
+
+		p->relocated = 1;
+	}
+}
+
+static void kernel_mapped_dso(struct dso *p)
+{
+	size_t min_addr = -1, max_addr = 0, cnt;
+	Phdr *ph = p->phdr;
+	for (cnt = p->phnum; cnt--; ph = (void *)((char *)ph + p->phentsize)) {
+		if (ph->p_type == PT_DYNAMIC) {
+			p->dynv = laddr(p, ph->p_vaddr);
+		} else if (ph->p_type == PT_GNU_RELRO) {
+			p->relro_start = ph->p_vaddr & -PAGE_SIZE;
+			p->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
+		}
+		if (ph->p_type != PT_LOAD) continue;
+		if (ph->p_vaddr < min_addr)
+			min_addr = ph->p_vaddr;
+		if (ph->p_vaddr+ph->p_memsz > max_addr)
+			max_addr = ph->p_vaddr+ph->p_memsz;
+	}
+	min_addr &= -PAGE_SIZE;
+	max_addr = (max_addr + PAGE_SIZE-1) & -PAGE_SIZE;
+	p->map = p->base + min_addr;
+	p->map_len = max_addr - min_addr;
+	p->kernel_mapped = 1;
+}
+
+void __libc_exit_fini()
+{
+	struct dso *p;
+	size_t dyn[DYN_CNT];
+	for (p=fini_head; p; p=p->fini_next) {
+		if (!p->constructed) continue;
+		decode_vec(p->dynv, dyn, DYN_CNT);
+		if (dyn[0] & (1<<DT_FINI_ARRAY)) {
+			size_t n = dyn[DT_FINI_ARRAYSZ]/sizeof(size_t);
+			size_t *fn = (size_t *)laddr(p, dyn[DT_FINI_ARRAY])+n;
+			while (n--) ((void (*)(void))*--fn)();
+		}
+#ifndef NO_LEGACY_INITFINI
+		if ((dyn[0] & (1<<DT_FINI)) && dyn[DT_FINI])
+			fpaddr(p, dyn[DT_FINI])();
+#endif
+	}
+}
+
+static void do_init_fini(struct dso *p)
+{
+	size_t dyn[DYN_CNT];
+	int need_locking = libc.threads_minus_1;
+	/* Allow recursive calls that arise when a library calls
+	 * dlopen from one of its constructors, but block any
+	 * other threads until all ctors have finished. */
+	if (need_locking) pthread_mutex_lock(&init_fini_lock);
+	for (; p; p=p->prev) {
+		if (p->constructed) continue;
+		p->constructed = 1;
+		decode_vec(p->dynv, dyn, DYN_CNT);
+		if (dyn[0] & ((1<<DT_FINI) | (1<<DT_FINI_ARRAY))) {
+			p->fini_next = fini_head;
+			fini_head = p;
+		}
+#ifndef NO_LEGACY_INITFINI
+		if ((dyn[0] & (1<<DT_INIT)) && dyn[DT_INIT])
+			fpaddr(p, dyn[DT_INIT])();
+#endif
+		if (dyn[0] & (1<<DT_INIT_ARRAY)) {
+			size_t n = dyn[DT_INIT_ARRAYSZ]/sizeof(size_t);
+			size_t *fn = laddr(p, dyn[DT_INIT_ARRAY]);
+			while (n--) ((void (*)(void))*fn++)();
+		}
+		if (!need_locking && libc.threads_minus_1) {
+			need_locking = 1;
+			pthread_mutex_lock(&init_fini_lock);
+		}
+	}
+	if (need_locking) pthread_mutex_unlock(&init_fini_lock);
+}
+
+void __libc_start_init(void)
+{
+	do_init_fini(tail);
+}
+
+static void dl_debug_state(void)
+{
+}
+
+weak_alias(dl_debug_state, _dl_debug_state);
+
+void __init_tls(size_t *auxv)
+{
+}
+
+__attribute__((__visibility__("hidden")))
+void *__tls_get_new(size_t *v)
+{
+	pthread_t self = __pthread_self();
+
+	/* Block signals to make accessing new TLS async-signal-safe */
+	sigset_t set;
+	__block_all_sigs(&set);
+	if (v[0]<=(size_t)self->dtv[0]) {
+		__restore_sigs(&set);
+		return (char *)self->dtv[v[0]]+v[1]+DTP_OFFSET;
+	}
+
+	/* This is safe without any locks held because, if the caller
+	 * is able to request the Nth entry of the DTV, the DSO list
+	 * must be valid at least that far out and it was synchronized
+	 * at program startup or by an already-completed call to dlopen. */
+	struct dso *p;
+	for (p=head; p->tls_id != v[0]; p=p->next);
+
+	/* Get new DTV space from new DSO if needed */
+	if (v[0] > (size_t)self->dtv[0]) {
+		void **newdtv = p->new_dtv +
+			(v[0]+1)*a_fetch_add(&p->new_dtv_idx,1);
+		memcpy(newdtv, self->dtv,
+			((size_t)self->dtv[0]+1) * sizeof(void *));
+		newdtv[0] = (void *)v[0];
+		self->dtv = self->dtv_copy = newdtv;
+	}
+
+	/* Get new TLS memory from all new DSOs up to the requested one */
+	unsigned char *mem;
+	for (p=head; ; p=p->next) {
+		if (!p->tls_id || self->dtv[p->tls_id]) continue;
+		mem = p->new_tls + (p->tls.size + p->tls.align)
+			* a_fetch_add(&p->new_tls_idx,1);
+		mem += ((uintptr_t)p->tls.image - (uintptr_t)mem)
+			& (p->tls.align-1);
+		self->dtv[p->tls_id] = mem;
+		memcpy(mem, p->tls.image, p->tls.len);
+		if (p->tls_id == v[0]) break;
+	}
+	__restore_sigs(&set);
+	return mem + v[1] + DTP_OFFSET;
+}
+
+static void update_tls_size()
+{
+	libc.tls_cnt = tls_cnt;
+	libc.tls_align = tls_align;
+	libc.tls_size = ALIGN(
+		(1+tls_cnt) * sizeof(void *) +
+		tls_offset +
+		sizeof(struct pthread) +
+		tls_align * 2,
+	tls_align);
+}
+
+/* Stage 1 of the dynamic linker is defined in dlstart.c. It calls the
+ * following stage 2 and stage 3 functions via primitive symbolic lookup
+ * since it does not have access to their addresses to begin with. */
+
+/* Stage 2 of the dynamic linker is called after relative relocations 
+ * have been processed. It can make function calls to static functions
+ * and access string literals and static data, but cannot use extern
+ * symbols. Its job is to perform symbolic relocations on the dynamic
+ * linker itself, but some of the relocations performed may need to be
+ * replaced later due to copy relocations in the main program. */
+
+__attribute__((__visibility__("hidden")))
+void __dls2(unsigned char *base, size_t *sp)
+{
+	if (DL_FDPIC) {
+		void *p1 = (void *)sp[-2];
+		void *p2 = (void *)sp[-1];
+		if (!p1) {
+			size_t *auxv, aux[AUX_CNT];
+			for (auxv=sp+1+*sp+1; *auxv; auxv++); auxv++;
+			decode_vec(auxv, aux, AUX_CNT);
+			if (aux[AT_BASE]) ldso.base = (void *)aux[AT_BASE];
+			else ldso.base = (void *)(aux[AT_PHDR] & -4096);
+		}
+		app_loadmap = p2 ? p1 : 0;
+		ldso.loadmap = p2 ? p2 : p1;
+		ldso.base = laddr(&ldso, 0);
+	} else {
+		ldso.base = base;
+	}
+	Ehdr *ehdr = (void *)ldso.base;
+	ldso.name = ldso.shortname = "libc.so";
+	ldso.global = 1;
+	ldso.phnum = ehdr->e_phnum;
+	ldso.phdr = laddr(&ldso, ehdr->e_phoff);
+	ldso.phentsize = ehdr->e_phentsize;
+	kernel_mapped_dso(&ldso);
+	decode_dyn(&ldso);
+
+	if (DL_FDPIC) makefuncdescs(&ldso);
+
+	/* Prepare storage for to save clobbered REL addends so they
+	 * can be reused in stage 3. There should be very few. If
+	 * something goes wrong and there are a huge number, abort
+	 * instead of risking stack overflow. */
+	size_t dyn[DYN_CNT];
+	decode_vec(ldso.dynv, dyn, DYN_CNT);
+	size_t *rel = laddr(&ldso, dyn[DT_REL]);
+	size_t rel_size = dyn[DT_RELSZ];
+	size_t symbolic_rel_cnt = 0;
+	apply_addends_to = rel;
+	for (; rel_size; rel+=2, rel_size-=2*sizeof(size_t))
+		if (!IS_RELATIVE(rel[1], ldso.syms)) symbolic_rel_cnt++;
+	if (symbolic_rel_cnt >= ADDEND_LIMIT) a_crash();
+	size_t addends[symbolic_rel_cnt+1];
+	saved_addends = addends;
+
+	head = &ldso;
+	reloc_all(&ldso);
+
+	ldso.relocated = 0;
+
+	/* Call dynamic linker stage-3, __dls3, looking it up
+	 * symbolically as a barrier against moving the address
+	 * load across the above relocation processing. */
+	struct symdef dls3_def = find_sym(&ldso, "__dls3", 0);
+	if (DL_FDPIC) ((stage3_func)&ldso.funcdescs[dls3_def.sym-ldso.syms])(sp);
+	else ((stage3_func)laddr(&ldso, dls3_def.sym->st_value))(sp);
+}
+
+/* Stage 3 of the dynamic linker is called with the dynamic linker/libc
+ * fully functional. Its job is to load (if not already loaded) and
+ * process dependencies and relocations for the main application and
+ * transfer control to its entry point. */
+
+_Noreturn void __dls3(size_t *sp)
+{
+	static struct dso app, vdso;
+	size_t aux[AUX_CNT], *auxv;
+	size_t i;
+	char *env_preload=0;
+	size_t vdso_base;
+	int argc = *sp;
+	char **argv = (void *)(sp+1);
+	char **argv_orig = argv;
+	char **envp = argv+argc+1;
+
+	/* Find aux vector just past environ[] and use it to initialize
+	 * global data that may be needed before we can make syscalls. */
+	__environ = envp;
+	for (i=argc+1; argv[i]; i++);
+	libc.auxv = auxv = (void *)(argv+i+1);
+	decode_vec(auxv, aux, AUX_CNT);
+	__hwcap = aux[AT_HWCAP];
+	libc.page_size = aux[AT_PAGESZ];
+	libc.secure = ((aux[0]&0x7800)!=0x7800 || aux[AT_UID]!=aux[AT_EUID]
+		|| aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]);
+
+	/* Setup early thread pointer in builtin_tls for ldso/libc itself to
+	 * use during dynamic linking. If possible it will also serve as the
+	 * thread pointer at runtime. */
+	libc.tls_size = sizeof builtin_tls;
+	libc.tls_align = tls_align;
+	if (__init_tp(__copy_tls((void *)builtin_tls)) < 0) {
+		a_crash();
+	}
+
+	/* Only trust user/env if kernel says we're not suid/sgid */
+	if (!libc.secure) {
+		env_path = getenv("LD_LIBRARY_PATH");
+		env_preload = getenv("LD_PRELOAD");
+	}
+
+	/* If the main program was already loaded by the kernel,
+	 * AT_PHDR will point to some location other than the dynamic
+	 * linker's program headers. */
+	if (aux[AT_PHDR] != (size_t)ldso.phdr) {
+		size_t interp_off = 0;
+		size_t tls_image = 0;
+		/* Find load address of the main program, via AT_PHDR vs PT_PHDR. */
+		Phdr *phdr = app.phdr = (void *)aux[AT_PHDR];
+		app.phnum = aux[AT_PHNUM];
+		app.phentsize = aux[AT_PHENT];
+		for (i=aux[AT_PHNUM]; i; i--, phdr=(void *)((char *)phdr + aux[AT_PHENT])) {
+			if (phdr->p_type == PT_PHDR)
+				app.base = (void *)(aux[AT_PHDR] - phdr->p_vaddr);
+			else if (phdr->p_type == PT_INTERP)
+				interp_off = (size_t)phdr->p_vaddr;
+			else if (phdr->p_type == PT_TLS) {
+				tls_image = phdr->p_vaddr;
+				app.tls.len = phdr->p_filesz;
+				app.tls.size = phdr->p_memsz;
+				app.tls.align = phdr->p_align;
+			}
+		}
+		if (DL_FDPIC) app.loadmap = app_loadmap;
+		if (app.tls.size) app.tls.image = laddr(&app, tls_image);
+		if (interp_off) ldso.name = laddr(&app, interp_off);
+		if ((aux[0] & (1UL<<AT_EXECFN))
+		    && strncmp((char *)aux[AT_EXECFN], "/proc/", 6))
+			app.name = (char *)aux[AT_EXECFN];
+		else
+			app.name = argv[0];
+		kernel_mapped_dso(&app);
+	} else {
+		int fd;
+		char *ldname = argv[0];
+		size_t l = strlen(ldname);
+		if (l >= 3 && !strcmp(ldname+l-3, "ldd")) ldd_mode = 1;
+		argv++;
+		while (argv[0] && argv[0][0]=='-' && argv[0][1]=='-') {
+			char *opt = argv[0]+2;
+			*argv++ = (void *)-1;
+			if (!*opt) {
+				break;
+			} else if (!memcmp(opt, "list", 5)) {
+				ldd_mode = 1;
+			} else if (!memcmp(opt, "library-path", 12)) {
+				if (opt[12]=='=') env_path = opt+13;
+				else if (opt[12]) *argv = 0;
+				else if (*argv) env_path = *argv++;
+			} else if (!memcmp(opt, "preload", 7)) {
+				if (opt[7]=='=') env_preload = opt+8;
+				else if (opt[7]) *argv = 0;
+				else if (*argv) env_preload = *argv++;
+			} else {
+				argv[0] = 0;
+			}
+		}
+		argv[-1] = (void *)(argc - (argv-argv_orig));
+		if (!argv[0]) {
+			dprintf(2, "musl libc (" LDSO_ARCH ")\n"
+				"Version %s\n"
+				"Dynamic Program Loader\n"
+				"Usage: %s [options] [--] pathname%s\n",
+				__libc_get_version(), ldname,
+				ldd_mode ? "" : " [args]");
+			_exit(1);
+		}
+		fd = open(argv[0], O_RDONLY);
+		if (fd < 0) {
+			dprintf(2, "%s: cannot load %s: %s\n", ldname, argv[0], strerror(errno));
+			_exit(1);
+		}
+		runtime = 1;
+		Ehdr *ehdr = (void *)map_library(fd, &app);
+		if (!ehdr) {
+			dprintf(2, "%s: %s: Not a valid dynamic program\n", ldname, argv[0]);
+			_exit(1);
+		}
+		runtime = 0;
+		close(fd);
+		ldso.name = ldname;
+		app.name = argv[0];
+		aux[AT_ENTRY] = (size_t)laddr(&app, ehdr->e_entry);
+		/* Find the name that would have been used for the dynamic
+		 * linker had ldd not taken its place. */
+		if (ldd_mode) {
+			for (i=0; i<app.phnum; i++) {
+				if (app.phdr[i].p_type == PT_INTERP)
+					ldso.name = laddr(&app, app.phdr[i].p_vaddr);
+			}
+			dprintf(1, "\t%s (%p)\n", ldso.name, ldso.base);
+		}
+	}
+	if (app.tls.size) {
+		libc.tls_head = tls_tail = &app.tls;
+		app.tls_id = tls_cnt = 1;
+#ifdef TLS_ABOVE_TP
+		app.tls.offset = 0;
+		tls_offset = app.tls.size
+			+ ( -((uintptr_t)app.tls.image + app.tls.size)
+			& (app.tls.align-1) );
+#else
+		tls_offset = app.tls.offset = app.tls.size
+			+ ( -((uintptr_t)app.tls.image + app.tls.size)
+			& (app.tls.align-1) );
+#endif
+		tls_align = MAXP2(tls_align, app.tls.align);
+	}
+	app.global = 1;
+	decode_dyn(&app);
+	if (DL_FDPIC) {
+		makefuncdescs(&app);
+		if (!app.loadmap) {
+			app.loadmap = (void *)&app_dummy_loadmap;
+			app.loadmap->nsegs = 1;
+			app.loadmap->segs[0].addr = (size_t)app.map;
+			app.loadmap->segs[0].p_vaddr = (size_t)app.map
+				- (size_t)app.base;
+			app.loadmap->segs[0].p_memsz = app.map_len;
+		}
+		argv[-3] = (void *)app.loadmap;
+	}
+
+	/* Attach to vdso, if provided by the kernel */
+	if (search_vec(auxv, &vdso_base, AT_SYSINFO_EHDR)) {
+		Ehdr *ehdr = (void *)vdso_base;
+		Phdr *phdr = vdso.phdr = (void *)(vdso_base + ehdr->e_phoff);
+		vdso.phnum = ehdr->e_phnum;
+		vdso.phentsize = ehdr->e_phentsize;
+		for (i=ehdr->e_phnum; i; i--, phdr=(void *)((char *)phdr + ehdr->e_phentsize)) {
+			if (phdr->p_type == PT_DYNAMIC)
+				vdso.dynv = (void *)(vdso_base + phdr->p_offset);
+			if (phdr->p_type == PT_LOAD)
+				vdso.base = (void *)(vdso_base - phdr->p_vaddr + phdr->p_offset);
+		}
+		vdso.name = "";
+		vdso.shortname = "linux-gate.so.1";
+		vdso.global = 1;
+		vdso.relocated = 1;
+		decode_dyn(&vdso);
+		vdso.prev = &ldso;
+		ldso.next = &vdso;
+	}
+
+	/* Initial dso chain consists only of the app. */
+	head = tail = &app;
+
+	/* Donate unused parts of app and library mapping to malloc */
+	reclaim_gaps(&app);
+	reclaim_gaps(&ldso);
+
+	/* Load preload/needed libraries, add their symbols to the global
+	 * namespace, and perform all remaining relocations. */
+	if (env_preload) load_preload(env_preload);
+	load_deps(&app);
+	make_global(&app);
+
+	for (i=0; app.dynv[i]; i+=2) {
+		if (!DT_DEBUG_INDIRECT && app.dynv[i]==DT_DEBUG)
+			app.dynv[i+1] = (size_t)&debug;
+		if (DT_DEBUG_INDIRECT && app.dynv[i]==DT_DEBUG_INDIRECT) {
+			size_t *ptr = (size_t *) app.dynv[i+1];
+			*ptr = (size_t)&debug;
+		}
+	}
+
+	/* The main program must be relocated LAST since it may contin
+	 * copy relocations which depend on libraries' relocations. */
+	reloc_all(app.next);
+	reloc_all(&app);
+
+	update_tls_size();
+	if (libc.tls_size > sizeof builtin_tls || tls_align > MIN_TLS_ALIGN) {
+		void *initial_tls = calloc(libc.tls_size, 1);
+		if (!initial_tls) {
+			dprintf(2, "%s: Error getting %zu bytes thread-local storage: %m\n",
+				argv[0], libc.tls_size);
+			_exit(127);
+		}
+		if (__init_tp(__copy_tls(initial_tls)) < 0) {
+			a_crash();
+		}
+	} else {
+		size_t tmp_tls_size = libc.tls_size;
+		pthread_t self = __pthread_self();
+		/* Temporarily set the tls size to the full size of
+		 * builtin_tls so that __copy_tls will use the same layout
+		 * as it did for before. Then check, just to be safe. */
+		libc.tls_size = sizeof builtin_tls;
+		if (__copy_tls((void*)builtin_tls) != self) a_crash();
+		libc.tls_size = tmp_tls_size;
+	}
+	static_tls_cnt = tls_cnt;
+
+	if (ldso_fail) _exit(127);
+	if (ldd_mode) _exit(0);
+
+	/* Switch to runtime mode: any further failures in the dynamic
+	 * linker are a reportable failure rather than a fatal startup
+	 * error. */
+	runtime = 1;
+
+	debug.ver = 1;
+	debug.bp = dl_debug_state;
+	debug.head = head;
+	debug.base = ldso.base;
+	debug.state = 0;
+	_dl_debug_state();
+
+	errno = 0;
+
+	CRTJMP((void *)aux[AT_ENTRY], argv-1);
+	for(;;);
+}
+
+void *dlopen(const char *file, int mode)
+{
+	struct dso *volatile p, *orig_tail, *next;
+	struct tls_module *orig_tls_tail;
+	size_t orig_tls_cnt, orig_tls_offset, orig_tls_align;
+	size_t i;
+	int cs;
+	jmp_buf jb;
+
+	if (!file) return head;
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+	pthread_rwlock_wrlock(&lock);
+	__inhibit_ptc();
+
+	p = 0;
+	orig_tls_tail = tls_tail;
+	orig_tls_cnt = tls_cnt;
+	orig_tls_offset = tls_offset;
+	orig_tls_align = tls_align;
+	orig_tail = tail;
+	noload = mode & RTLD_NOLOAD;
+
+	rtld_fail = &jb;
+	if (setjmp(*rtld_fail)) {
+		/* Clean up anything new that was (partially) loaded */
+		if (p && p->deps) for (i=0; p->deps[i]; i++)
+			if (p->deps[i]->global < 0)
+				p->deps[i]->global = 0;
+		for (p=orig_tail->next; p; p=next) {
+			next = p->next;
+			while (p->td_index) {
+				void *tmp = p->td_index->next;
+				free(p->td_index);
+				p->td_index = tmp;
+			}
+			free(p->funcdescs);
+			if (p->rpath != p->rpath_orig)
+				free(p->rpath);
+			free(p->deps);
+			unmap_library(p);
+			free(p);
+		}
+		if (!orig_tls_tail) libc.tls_head = 0;
+		tls_tail = orig_tls_tail;
+		tls_cnt = orig_tls_cnt;
+		tls_offset = orig_tls_offset;
+		tls_align = orig_tls_align;
+		tail = orig_tail;
+		tail->next = 0;
+		p = 0;
+		goto end;
+	} else p = load_library(file, head);
+
+	if (!p) {
+		error(noload ?
+			"Library %s is not already loaded" :
+			"Error loading shared library %s: %m",
+			file);
+		goto end;
+	}
+
+	/* First load handling */
+	if (!p->deps) {
+		load_deps(p);
+		if (p->deps) for (i=0; p->deps[i]; i++)
+			if (!p->deps[i]->global)
+				p->deps[i]->global = -1;
+		if (!p->global) p->global = -1;
+		reloc_all(p);
+		if (p->deps) for (i=0; p->deps[i]; i++)
+			if (p->deps[i]->global < 0)
+				p->deps[i]->global = 0;
+		if (p->global < 0) p->global = 0;
+	}
+
+	if (mode & RTLD_GLOBAL) {
+		if (p->deps) for (i=0; p->deps[i]; i++)
+			p->deps[i]->global = 1;
+		p->global = 1;
+	}
+
+	update_tls_size();
+	_dl_debug_state();
+	orig_tail = tail;
+end:
+	__release_ptc();
+	if (p) gencnt++;
+	pthread_rwlock_unlock(&lock);
+	if (p) do_init_fini(orig_tail);
+	pthread_setcancelstate(cs, 0);
+	return p;
+}
+
+__attribute__((__visibility__("hidden")))
+int __dl_invalid_handle(void *h)
+{
+	struct dso *p;
+	for (p=head; p; p=p->next) if (h==p) return 0;
+	error("Invalid library handle %p", (void *)h);
+	return 1;
+}
+
+static void *addr2dso(size_t a)
+{
+	struct dso *p;
+	size_t i;
+	if (DL_FDPIC) for (p=head; p; p=p->next) {
+		i = count_syms(p);
+		if (a-(size_t)p->funcdescs < i*sizeof(*p->funcdescs))
+			return p;
+	}
+	for (p=head; p; p=p->next) {
+		if (DL_FDPIC && p->loadmap) {
+			for (i=0; i<p->loadmap->nsegs; i++) {
+				if (a-p->loadmap->segs[i].p_vaddr
+				    < p->loadmap->segs[i].p_memsz)
+					return p;
+			}
+		} else {
+			if (a-(size_t)p->map < p->map_len)
+				return p;
+		}
+	}
+	return 0;
+}
+
+void *__tls_get_addr(size_t *);
+
+static void *do_dlsym(struct dso *p, const char *s, void *ra)
+{
+	size_t i;
+	uint32_t h = 0, gh = 0, *ght;
+	Sym *sym;
+	if (p == head || p == RTLD_DEFAULT || p == RTLD_NEXT) {
+		if (p == RTLD_DEFAULT) {
+			p = head;
+		} else if (p == RTLD_NEXT) {
+			p = addr2dso((size_t)ra);
+			if (!p) p=head;
+			p = p->next;
+		}
+		struct symdef def = find_sym(p, s, 0);
+		if (!def.sym) goto failed;
+		if ((def.sym->st_info&0xf) == STT_TLS)
+			return __tls_get_addr((size_t []){def.dso->tls_id, def.sym->st_value});
+		if (DL_FDPIC && (def.sym->st_info&0xf) == STT_FUNC)
+			return def.dso->funcdescs + (def.sym - def.dso->syms);
+		return laddr(def.dso, def.sym->st_value);
+	}
+	if (__dl_invalid_handle(p))
+		return 0;
+	if ((ght = p->ghashtab)) {
+		gh = gnu_hash(s);
+		sym = gnu_lookup(gh, ght, p, s);
+	} else {
+		h = sysv_hash(s);
+		sym = sysv_lookup(s, h, p);
+	}
+	if (sym && (sym->st_info&0xf) == STT_TLS)
+		return __tls_get_addr((size_t []){p->tls_id, sym->st_value});
+	if (DL_FDPIC && sym && sym->st_shndx && (sym->st_info&0xf) == STT_FUNC)
+		return p->funcdescs + (sym - p->syms);
+	if (sym && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES))
+		return laddr(p, sym->st_value);
+	if (p->deps) for (i=0; p->deps[i]; i++) {
+		if ((ght = p->deps[i]->ghashtab)) {
+			if (!gh) gh = gnu_hash(s);
+			sym = gnu_lookup(gh, ght, p->deps[i], s);
+		} else {
+			if (!h) h = sysv_hash(s);
+			sym = sysv_lookup(s, h, p->deps[i]);
+		}
+		if (sym && (sym->st_info&0xf) == STT_TLS)
+			return __tls_get_addr((size_t []){p->deps[i]->tls_id, sym->st_value});
+		if (DL_FDPIC && sym && sym->st_shndx && (sym->st_info&0xf) == STT_FUNC)
+			return p->deps[i]->funcdescs + (sym - p->deps[i]->syms);
+		if (sym && sym->st_value && (1<<(sym->st_info&0xf) & OK_TYPES))
+			return laddr(p->deps[i], sym->st_value);
+	}
+failed:
+	error("Symbol not found: %s", s);
+	return 0;
+}
+
+int dladdr(const void *addr, Dl_info *info)
+{
+	struct dso *p;
+	Sym *sym, *bestsym;
+	uint32_t nsym;
+	char *strings;
+	void *best = 0;
+
+	pthread_rwlock_rdlock(&lock);
+	p = addr2dso((size_t)addr);
+	pthread_rwlock_unlock(&lock);
+
+	if (!p) return 0;
+
+	sym = p->syms;
+	strings = p->strings;
+	nsym = count_syms(p);
+
+	if (DL_FDPIC) {
+		size_t idx = ((size_t)addr-(size_t)p->funcdescs)
+			/ sizeof(*p->funcdescs);
+		if (idx < nsym && (sym[idx].st_info&0xf) == STT_FUNC) {
+			best = p->funcdescs + idx;
+			bestsym = sym + idx;
+		}
+	}
+
+	if (!best) for (; nsym; nsym--, sym++) {
+		if (sym->st_value
+		 && (1<<(sym->st_info&0xf) & OK_TYPES)
+		 && (1<<(sym->st_info>>4) & OK_BINDS)) {
+			void *symaddr = laddr(p, sym->st_value);
+			if (symaddr > addr || symaddr < best)
+				continue;
+			best = symaddr;
+			bestsym = sym;
+			if (addr == symaddr)
+				break;
+		}
+	}
+
+	if (!best) return 0;
+
+	if (DL_FDPIC && (bestsym->st_info&0xf) == STT_FUNC)
+		best = p->funcdescs + (bestsym - p->syms);
+
+	info->dli_fname = p->name;
+	info->dli_fbase = p->base;
+	info->dli_sname = strings + bestsym->st_name;
+	info->dli_saddr = best;
+
+	return 1;
+}
+
+__attribute__((__visibility__("hidden")))
+void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra)
+{
+	void *res;
+	pthread_rwlock_rdlock(&lock);
+	res = do_dlsym(p, s, ra);
+	pthread_rwlock_unlock(&lock);
+	return res;
+}
+
+int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void *data), void *data)
+{
+	struct dso *current;
+	struct dl_phdr_info info;
+	int ret = 0;
+	for(current = head; current;) {
+		info.dlpi_addr      = (uintptr_t)current->base;
+		info.dlpi_name      = current->name;
+		info.dlpi_phdr      = current->phdr;
+		info.dlpi_phnum     = current->phnum;
+		info.dlpi_adds      = gencnt;
+		info.dlpi_subs      = 0;
+		info.dlpi_tls_modid = current->tls_id;
+		info.dlpi_tls_data  = current->tls.image;
+
+		ret = (callback)(&info, sizeof (info), data);
+
+		if (ret != 0) break;
+
+		pthread_rwlock_rdlock(&lock);
+		current = current->next;
+		pthread_rwlock_unlock(&lock);
+	}
+	return ret;
+}
+
+__attribute__((__visibility__("hidden")))
+void __dl_vseterr(const char *, va_list);
+
+static void error(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	if (!runtime) {
+		vdprintf(2, fmt, ap);
+		dprintf(2, "\n");
+		ldso_fail = 1;
+		va_end(ap);
+		return;
+	}
+	__dl_vseterr(fmt, ap);
+	va_end(ap);
+}
diff --git a/system/lib/libc/musl/src/aio/aio.c b/system/lib/libc/musl/src/aio/aio.c
new file mode 100644
index 0000000..aafd8e8
--- /dev/null
+++ b/system/lib/libc/musl/src/aio/aio.c
@@ -0,0 +1,379 @@
+#include <aio.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <limits.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "syscall.h"
+#include "atomic.h"
+#include "libc.h"
+#include "pthread_impl.h"
+
+/* The following is a threads-based implementation of AIO with minimal
+ * dependence on implementation details. Most synchronization is
+ * performed with pthread primitives, but atomics and futex operations
+ * are used for notification in a couple places where the pthread
+ * primitives would be inefficient or impractical.
+ *
+ * For each fd with outstanding aio operations, an aio_queue structure
+ * is maintained. These are reference-counted and destroyed by the last
+ * aio worker thread to exit. Accessing any member of the aio_queue
+ * structure requires a lock on the aio_queue. Adding and removing aio
+ * queues themselves requires a write lock on the global map object,
+ * a 4-level table mapping file descriptor numbers to aio queues. A
+ * read lock on the map is used to obtain locks on existing queues by
+ * excluding destruction of the queue by a different thread while it is
+ * being locked.
+ *
+ * Each aio queue has a list of active threads/operations. Presently there
+ * is a one to one relationship between threads and operations. The only
+ * members of the aio_thread structure which are accessed by other threads
+ * are the linked list pointers, op (which is immutable), running (which
+ * is updated atomically), and err (which is synchronized via running),
+ * so no locking is necessary. Most of the other other members are used
+ * for sharing data between the main flow of execution and cancellation
+ * cleanup handler.
+ *
+ * Taking any aio locks requires having all signals blocked. This is
+ * necessary because aio_cancel is needed by close, and close is required
+ * to be async-signal safe. All aio worker threads run with all signals
+ * blocked permanently.
+ */
+
+struct aio_args {
+	struct aiocb *cb;
+	int op;
+	int err;
+	sem_t sem;
+};
+
+struct aio_thread {
+	pthread_t td;
+	struct aiocb *cb;
+	struct aio_thread *next, *prev;
+	struct aio_queue *q;
+	volatile int running;
+	int err, op;
+	ssize_t ret;
+};
+
+struct aio_queue {
+	int fd, seekable, append, ref, init;
+	pthread_mutex_t lock;
+	pthread_cond_t cond;
+	struct aio_thread *head;
+};
+
+static pthread_rwlock_t maplock = PTHREAD_RWLOCK_INITIALIZER;
+static struct aio_queue *****map;
+static volatile int aio_fd_cnt;
+volatile int __aio_fut;
+
+static struct aio_queue *__aio_get_queue(int fd, int need)
+{
+	if (fd < 0) return 0;
+	int a=fd>>24;
+	unsigned char b=fd>>16, c=fd>>8, d=fd;
+	struct aio_queue *q = 0;
+	pthread_rwlock_rdlock(&maplock);
+	if ((!map || !map[a] || !map[a][b] || !map[a][b][c] || !(q=map[a][b][c][d])) && need) {
+		pthread_rwlock_unlock(&maplock);
+		pthread_rwlock_wrlock(&maplock);
+		if (!map) map = calloc(sizeof *map, (-1U/2+1)>>24);
+		if (!map) goto out;
+		if (!map[a]) map[a] = calloc(sizeof **map, 256);
+		if (!map[a]) goto out;
+		if (!map[a][b]) map[a][b] = calloc(sizeof ***map, 256);
+		if (!map[a][b]) goto out;
+		if (!map[a][b][c]) map[a][b][c] = calloc(sizeof ****map, 256);
+		if (!map[a][b][c]) goto out;
+		if (!(q = map[a][b][c][d])) {
+			map[a][b][c][d] = q = calloc(sizeof *****map, 1);
+			if (q) {
+				q->fd = fd;
+				pthread_mutex_init(&q->lock, 0);
+				pthread_cond_init(&q->cond, 0);
+				a_inc(&aio_fd_cnt);
+			}
+		}
+	}
+	if (q) pthread_mutex_lock(&q->lock);
+out:
+	pthread_rwlock_unlock(&maplock);
+	return q;
+}
+
+static void __aio_unref_queue(struct aio_queue *q)
+{
+	if (q->ref > 1) {
+		q->ref--;
+		pthread_mutex_unlock(&q->lock);
+		return;
+	}
+
+	/* This is potentially the last reference, but a new reference
+	 * may arrive since we cannot free the queue object without first
+	 * taking the maplock, which requires releasing the queue lock. */
+	pthread_mutex_unlock(&q->lock);
+	pthread_rwlock_wrlock(&maplock);
+	pthread_mutex_lock(&q->lock);
+	if (q->ref == 1) {
+		int fd=q->fd;
+		int a=fd>>24;
+		unsigned char b=fd>>16, c=fd>>8, d=fd;
+		map[a][b][c][d] = 0;
+		a_dec(&aio_fd_cnt);
+		pthread_rwlock_unlock(&maplock);
+		pthread_mutex_unlock(&q->lock);
+		free(q);
+	} else {
+		q->ref--;
+		pthread_rwlock_unlock(&maplock);
+		pthread_mutex_unlock(&q->lock);
+	}
+}
+
+static void cleanup(void *ctx)
+{
+	struct aio_thread *at = ctx;
+	struct aio_queue *q = at->q;
+	struct aiocb *cb = at->cb;
+	struct sigevent sev = cb->aio_sigevent;
+
+	/* There are four potential types of waiters we could need to wake:
+	 *   1. Callers of aio_cancel/close.
+	 *   2. Callers of aio_suspend with a single aiocb.
+	 *   3. Callers of aio_suspend with a list.
+	 *   4. AIO worker threads waiting for sequenced operations.
+	 * Types 1-3 are notified via atomics/futexes, mainly for AS-safety
+	 * considerations. Type 4 is notified later via a cond var. */
+
+	cb->__ret = at->ret;
+	if (a_swap(&at->running, 0) < 0)
+		__wake(&at->running, -1, 1);
+	if (a_swap(&cb->__err, at->err) != EINPROGRESS)
+		__wake(&cb->__err, -1, 1);
+	if (a_swap(&__aio_fut, 0))
+		__wake(&__aio_fut, -1, 1);
+
+	pthread_mutex_lock(&q->lock);
+
+	if (at->next) at->next->prev = at->prev;
+	if (at->prev) at->prev->next = at->next;
+	else q->head = at->next;
+
+	/* Signal aio worker threads waiting for sequenced operations. */
+	pthread_cond_broadcast(&q->cond);
+
+	__aio_unref_queue(q);
+
+	if (sev.sigev_notify == SIGEV_SIGNAL) {
+		siginfo_t si = {
+			.si_signo = sev.sigev_signo,
+			.si_value = sev.sigev_value,
+			.si_code = SI_ASYNCIO,
+			.si_pid = getpid(),
+			.si_uid = getuid()
+		};
+		__syscall(SYS_rt_sigqueueinfo, si.si_pid, si.si_signo, &si);
+	}
+	if (sev.sigev_notify == SIGEV_THREAD) {
+		a_store(&__pthread_self()->cancel, 0);
+		sev.sigev_notify_function(sev.sigev_value);
+	}
+}
+
+static void *io_thread_func(void *ctx)
+{
+	struct aio_thread at, *p;
+
+	struct aio_args *args = ctx;
+	struct aiocb *cb = args->cb;
+	int fd = cb->aio_fildes;
+	int op = args->op;
+	void *buf = (void *)cb->aio_buf;
+	size_t len = cb->aio_nbytes;
+	off_t off = cb->aio_offset;
+
+	struct aio_queue *q = __aio_get_queue(fd, 1);
+	ssize_t ret;
+
+	args->err = q ? 0 : EAGAIN;
+	sem_post(&args->sem);
+	if (!q) return 0;
+
+	at.op = op;
+	at.running = 1;
+	at.ret = -1;
+	at.err = ECANCELED;
+	at.q = q;
+	at.td = __pthread_self();
+	at.cb = cb;
+	at.prev = 0;
+	if ((at.next = q->head)) at.next->prev = &at;
+	q->head = &at;
+	q->ref++;
+
+	if (!q->init) {
+		int seekable = lseek(fd, 0, SEEK_CUR) >= 0;
+		q->seekable = seekable;
+		q->append = !seekable || (fcntl(fd, F_GETFL) & O_APPEND);
+		q->init = 1;
+	}
+
+	pthread_cleanup_push(cleanup, &at);
+
+	/* Wait for sequenced operations. */
+	if (op!=LIO_READ && (op!=LIO_WRITE || q->append)) {
+		for (;;) {
+			for (p=at.next; p && p->op!=LIO_WRITE; p=p->next);
+			if (!p) break;
+			pthread_cond_wait(&q->cond, &q->lock);
+		}
+	}
+
+	pthread_mutex_unlock(&q->lock);
+
+	switch (op) {
+	case LIO_WRITE:
+		ret = q->append ? write(fd, buf, len) : pwrite(fd, buf, len, off);
+		break;
+	case LIO_READ:
+		ret = !q->seekable ? read(fd, buf, len) : pread(fd, buf, len, off);
+		break;
+	case O_SYNC:
+		ret = fsync(fd);
+		break;
+	case O_DSYNC:
+		ret = fdatasync(fd);
+		break;
+	}
+	at.ret = ret;
+	at.err = ret<0 ? errno : 0;
+	
+	pthread_cleanup_pop(1);
+
+	return 0;
+}
+
+static int submit(struct aiocb *cb, int op)
+{
+	int ret = 0;
+	pthread_attr_t a;
+	sigset_t allmask, origmask;
+	pthread_t td;
+	struct aio_args args = { .cb = cb, .op = op };
+	sem_init(&args.sem, 0, 0);
+
+	if (cb->aio_sigevent.sigev_notify == SIGEV_THREAD) {
+		if (cb->aio_sigevent.sigev_notify_attributes)
+			a = *cb->aio_sigevent.sigev_notify_attributes;
+		else
+			pthread_attr_init(&a);
+	} else {
+		pthread_attr_init(&a);
+		pthread_attr_setstacksize(&a, PTHREAD_STACK_MIN);
+		pthread_attr_setguardsize(&a, 0);
+	}
+	pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
+	sigfillset(&allmask);
+	pthread_sigmask(SIG_BLOCK, &allmask, &origmask);
+	cb->__err = EINPROGRESS;
+	if (pthread_create(&td, &a, io_thread_func, &args)) {
+		errno = EAGAIN;
+		ret = -1;
+	}
+	pthread_sigmask(SIG_SETMASK, &origmask, 0);
+
+	if (!ret) {
+		while (sem_wait(&args.sem));
+		if (args.err) {
+			errno = args.err;
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+int aio_read(struct aiocb *cb)
+{
+	return submit(cb, LIO_READ);
+}
+
+int aio_write(struct aiocb *cb)
+{
+	return submit(cb, LIO_WRITE);
+}
+
+int aio_fsync(int op, struct aiocb *cb)
+{
+	if (op != O_SYNC && op != O_DSYNC) {
+		errno = EINVAL;
+		return -1;
+	}
+	return submit(cb, op);
+}
+
+ssize_t aio_return(struct aiocb *cb)
+{
+	return cb->__ret;
+}
+
+int aio_error(const struct aiocb *cb)
+{
+	a_barrier();
+	return cb->__err & 0x7fffffff;
+}
+
+int aio_cancel(int fd, struct aiocb *cb)
+{
+	sigset_t allmask, origmask;
+	int ret = AIO_ALLDONE;
+	struct aio_thread *p;
+	struct aio_queue *q;
+
+	/* Unspecified behavior case. Report an error. */
+	if (cb && fd != cb->aio_fildes) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	sigfillset(&allmask);
+	pthread_sigmask(SIG_BLOCK, &allmask, &origmask);
+
+	if (!(q = __aio_get_queue(fd, 0))) {
+		if (fcntl(fd, F_GETFD) < 0) ret = -1;
+		goto done;
+	}
+
+	for (p = q->head; p; p = p->next) {
+		if (cb && cb != p->cb) continue;
+		/* Transition target from running to running-with-waiters */
+		if (a_cas(&p->running, 1, -1)) {
+			pthread_cancel(p->td);
+			__wait(&p->running, 0, -1, 1);
+			if (p->err == ECANCELED) ret = AIO_CANCELED;
+		}
+	}
+
+	pthread_mutex_unlock(&q->lock);
+done:
+	pthread_sigmask(SIG_SETMASK, &origmask, 0);
+	return ret;
+}
+
+int __aio_close(int fd)
+{
+	a_barrier();
+	if (aio_fd_cnt) aio_cancel(fd, 0);
+	return fd;
+}
+
+LFS64(aio_cancel);
+LFS64(aio_error);
+LFS64(aio_fsync);
+LFS64(aio_read);
+LFS64(aio_write);
+LFS64(aio_return);
diff --git a/system/lib/libc/musl/src/aio/aio_suspend.c b/system/lib/libc/musl/src/aio/aio_suspend.c
index 39a1d3a..08fb5dd 100644
--- a/system/lib/libc/musl/src/aio/aio_suspend.c
+++ b/system/lib/libc/musl/src/aio/aio_suspend.c
@@ -1,57 +1,79 @@
 #include <aio.h>
 #include <errno.h>
+#include <time.h>
+#include "atomic.h"
+#include "libc.h"
 #include "pthread_impl.h"
 
-/* Due to the requirement that aio_suspend be async-signal-safe, we cannot
- * use any locks, wait queues, etc. that would make it more efficient. The
- * only obviously-correct algorithm is to generate a wakeup every time any
- * aio operation finishes and have aio_suspend re-evaluate the completion
- * status of each aiocb it was waiting on. */
-
-static volatile int seq;
-
-void __aio_wake(void)
-{
-	a_inc(&seq);
-	__wake(&seq, -1, 1);
-}
+extern volatile int __aio_fut;
 
 int aio_suspend(const struct aiocb *const cbs[], int cnt, const struct timespec *ts)
 {
-	int i, last, first=1, ret=0;
+	int i, tid = 0, ret, expect = 0;
 	struct timespec at;
+	volatile int dummy_fut, *pfut;
+	int nzcnt = 0;
+	const struct aiocb *cb = 0;
+
+	pthread_testcancel();
 
 	if (cnt<0) {
 		errno = EINVAL;
 		return -1;
 	}
 
+	for (i=0; i<cnt; i++) if (cbs[i]) {
+		if (aio_error(cbs[i]) != EINPROGRESS) return 0;
+		nzcnt++;
+		cb = cbs[i];
+	}
+
+	if (ts) {
+		clock_gettime(CLOCK_MONOTONIC, &at);
+		at.tv_sec += ts->tv_sec;
+		if ((at.tv_nsec += ts->tv_nsec) >= 1000000000) {
+			at.tv_nsec -= 1000000000;
+			at.tv_sec++;
+		}
+	}
+
 	for (;;) {
-		last = seq;
-
-		for (i=0; i<cnt; i++) {
-			if (cbs[i] && cbs[i]->__err != EINPROGRESS)
+		for (i=0; i<cnt; i++)
+			if (cbs[i] && aio_error(cbs[i]) != EINPROGRESS)
 				return 0;
+
+		switch (nzcnt) {
+		case 0:
+			pfut = &dummy_fut;
+			break;
+		case 1:
+			pfut = (void *)&cb->__err;
+			expect = EINPROGRESS | 0x80000000;
+			a_cas(pfut, EINPROGRESS, expect);
+			break;
+		default:
+			pfut = &__aio_fut;
+			if (!tid) tid = __pthread_self()->tid;
+			expect = a_cas(pfut, 0, tid);
+			if (!expect) expect = tid;
+			/* Need to recheck the predicate before waiting. */
+			for (i=0; i<cnt; i++)
+				if (cbs[i] && aio_error(cbs[i]) != EINPROGRESS)
+					return 0;
+			break;
 		}
 
-		if (first && ts) {
-			clock_gettime(CLOCK_MONOTONIC, &at);
-			at.tv_sec += ts->tv_sec;
-			if ((at.tv_nsec += ts->tv_nsec) >= 1000000000) {
-				at.tv_nsec -= 1000000000;
-				at.tv_sec++;
-			}
-			first = 0;
-		}
+		ret = __timedwait_cp(pfut, expect, CLOCK_MONOTONIC, ts?&at:0, 1);
 
-		ret = __timedwait(&seq, last, CLOCK_MONOTONIC,
-			ts ? &at : 0, 0, 0, 1);
-
-		if (ret == ETIMEDOUT) ret = EAGAIN;
-
-		if (ret) {
+		switch (ret) {
+		case ETIMEDOUT:
+			ret = EAGAIN;
+		case ECANCELED:
+		case EINTR:
 			errno = ret;
 			return -1;
 		}
 	}
 }
+
+LFS64(aio_suspend);
diff --git a/system/lib/libc/musl/src/aio/lio_listio.c b/system/lib/libc/musl/src/aio/lio_listio.c
index 61d7f20..bd37767 100644
--- a/system/lib/libc/musl/src/aio/lio_listio.c
+++ b/system/lib/libc/musl/src/aio/lio_listio.c
@@ -44,7 +44,7 @@
 		.si_signo = sev->sigev_signo,
 		.si_value = sev->sigev_value,
 		.si_code = SI_ASYNCIO,
-		.si_pid = __pthread_self()->pid,
+		.si_pid = getpid(),
 		.si_uid = getuid()
 	};
 	__syscall(SYS_rt_sigqueueinfo, si.si_pid, si.si_signo, &si);
@@ -141,3 +141,4 @@
 	return 0;
 }
 
+LFS64(lio_listio);
diff --git a/system/lib/libc/musl/src/complex/ctanh.c b/system/lib/libc/musl/src/complex/ctanh.c
index 0461050..3ba3a89 100644
--- a/system/lib/libc/musl/src/complex/ctanh.c
+++ b/system/lib/libc/musl/src/complex/ctanh.c
@@ -101,11 +101,13 @@
 	}
 
 	/*
+	 * ctanh(+-0 + i NAN) = +-0 + i NaN
+	 * ctanh(+-0 +- i Inf) = +-0 + i NaN
 	 * ctanh(x + i NAN) = NaN + i NaN
 	 * ctanh(x +- i Inf) = NaN + i NaN
 	 */
 	if (!isfinite(y))
-		return CMPLX(y - y, y - y);
+		return CMPLX(x ? y - y : x, y - y);
 
 	/*
 	 * ctanh(+-huge + i +-y) ~= +-1 +- i 2sin(2y)/exp(2x), using the
diff --git a/system/lib/libc/musl/src/complex/ctanhf.c b/system/lib/libc/musl/src/complex/ctanhf.c
index a7e1a5f..72b76da 100644
--- a/system/lib/libc/musl/src/complex/ctanhf.c
+++ b/system/lib/libc/musl/src/complex/ctanhf.c
@@ -50,7 +50,7 @@
 	}
 
 	if (!isfinite(y))
-		return CMPLXF(y - y, y - y);
+		return CMPLXF(ix ? y - y : x, y - y);
 
 	if (ix >= 0x41300000) { /* x >= 11 */
 		float exp_mx = expf(-fabsf(x));
diff --git a/system/lib/libc/musl/src/conf/legacy.c b/system/lib/libc/musl/src/conf/legacy.c
new file mode 100644
index 0000000..f1d9e32
--- /dev/null
+++ b/system/lib/libc/musl/src/conf/legacy.c
@@ -0,0 +1,22 @@
+#include <sys/sysinfo.h>
+#include <unistd.h>
+
+int get_nprocs_conf()
+{
+	return sysconf(_SC_NPROCESSORS_CONF);
+}
+
+int get_nprocs()
+{
+	return sysconf(_SC_NPROCESSORS_ONLN);
+}
+
+long get_phys_pages()
+{
+	return sysconf(_SC_PHYS_PAGES);	
+}
+
+long get_avphys_pages()
+{
+	return sysconf(_SC_AVPHYS_PAGES);	
+}
diff --git a/system/lib/libc/musl/src/conf/sysconf.c b/system/lib/libc/musl/src/conf/sysconf.c
index 12e8136..b8b761d 100644
--- a/system/lib/libc/musl/src/conf/sysconf.c
+++ b/system/lib/libc/musl/src/conf/sysconf.c
@@ -3,18 +3,28 @@
 #include <errno.h>
 #include <sys/resource.h>
 #include <signal.h>
+#include <sys/sysinfo.h>
 #include "syscall.h"
 #include "libc.h"
 
-#define VER (-2)
-#define OFLOW (-3)
-#define CPUCNT (-4)
+#define JT(x) (-256|(x))
+#define VER JT(1)
+#define JT_ARG_MAX JT(2)
+#define JT_MQ_PRIO_MAX JT(3)
+#define JT_PAGE_SIZE JT(4)
+#define JT_SEM_VALUE_MAX JT(5)
+#define JT_NPROCESSORS_CONF JT(6)
+#define JT_NPROCESSORS_ONLN JT(7)
+#define JT_PHYS_PAGES JT(8)
+#define JT_AVPHYS_PAGES JT(9)
+#define JT_ZERO JT(10)
+
 #define RLIM(x) (-32768|(RLIMIT_ ## x))
 
 long sysconf(int name)
 {
 	static const short values[] = {
-		[_SC_ARG_MAX] = OFLOW,
+		[_SC_ARG_MAX] = JT_ARG_MAX,
 		[_SC_CHILD_MAX] = RLIM(NPROC),
 		[_SC_CLK_TCK] = 100,
 		[_SC_NGROUPS_MAX] = 32,
@@ -39,15 +49,15 @@
 		[_SC_SHARED_MEMORY_OBJECTS] = VER,
 		[_SC_AIO_LISTIO_MAX] = -1,
 		[_SC_AIO_MAX] = -1,
-		[_SC_AIO_PRIO_DELTA_MAX] = 0, /* ?? */
+		[_SC_AIO_PRIO_DELTA_MAX] = JT_ZERO, /* ?? */
 		[_SC_DELAYTIMER_MAX] = _POSIX_DELAYTIMER_MAX,
 		[_SC_MQ_OPEN_MAX] = -1,
-		[_SC_MQ_PRIO_MAX] = OFLOW,
+		[_SC_MQ_PRIO_MAX] = JT_MQ_PRIO_MAX,
 		[_SC_VERSION] = VER,
-		[_SC_PAGE_SIZE] = OFLOW,
+		[_SC_PAGE_SIZE] = JT_PAGE_SIZE,
 		[_SC_RTSIG_MAX] = _NSIG - 1 - 31 - 3,
 		[_SC_SEM_NSEMS_MAX] = SEM_NSEMS_MAX,
-		[_SC_SEM_VALUE_MAX] = OFLOW,
+		[_SC_SEM_VALUE_MAX] = JT_SEM_VALUE_MAX,
 		[_SC_SIGQUEUE_MAX] = -1,
 		[_SC_TIMER_MAX] = -1,
 		[_SC_BC_BASE_MAX] = _POSIX2_BC_BASE_MAX,
@@ -55,11 +65,9 @@
 		[_SC_BC_SCALE_MAX] = _POSIX2_BC_SCALE_MAX,
 		[_SC_BC_STRING_MAX] = _POSIX2_BC_STRING_MAX,
 		[_SC_COLL_WEIGHTS_MAX] = COLL_WEIGHTS_MAX,
-		[_SC_EQUIV_CLASS_MAX] = -1, /* ?? */
 		[_SC_EXPR_NEST_MAX] = -1,
 		[_SC_LINE_MAX] = -1,
 		[_SC_RE_DUP_MAX] = RE_DUP_MAX,
-		[_SC_CHARCLASS_NAME_MAX] = -1, /* ?? */
 		[_SC_2_VERSION] = VER,
 		[_SC_2_C_BIND] = VER,
 		[_SC_2_C_DEV] = -1,
@@ -67,20 +75,7 @@
 		[_SC_2_FORT_RUN] = -1,
 		[_SC_2_SW_DEV] = -1,
 		[_SC_2_LOCALEDEF] = -1,
-		[_SC_PII] = -1, /* ????????? */
-		[_SC_PII_XTI] = -1,
-		[_SC_PII_SOCKET] = -1,
-		[_SC_PII_INTERNET] = -1,
-		[_SC_PII_OSI] = -1,
-		[_SC_POLL] = 1,
-		[_SC_SELECT] = 1,
 		[_SC_IOV_MAX] = IOV_MAX,
-		[_SC_PII_INTERNET_STREAM] = -1,
-		[_SC_PII_INTERNET_DGRAM] = -1,
-		[_SC_PII_OSI_COTS] = -1,
-		[_SC_PII_OSI_CLTS] = -1,
-		[_SC_PII_OSI_M] = -1,
-		[_SC_T_IOV_MAX] = -1,
 		[_SC_THREADS] = VER,
 		[_SC_THREAD_SAFE_FUNCTIONS] = VER,
 		[_SC_GETGR_R_SIZE_MAX] = -1,
@@ -97,10 +92,10 @@
 		[_SC_THREAD_PRIO_INHERIT] = -1,
 		[_SC_THREAD_PRIO_PROTECT] = -1,
 		[_SC_THREAD_PROCESS_SHARED] = VER,
-		[_SC_NPROCESSORS_CONF] = CPUCNT,
-		[_SC_NPROCESSORS_ONLN] = CPUCNT,
-		[_SC_PHYS_PAGES] = -1,
-		[_SC_AVPHYS_PAGES] = -1,
+		[_SC_NPROCESSORS_CONF] = JT_NPROCESSORS_CONF,
+		[_SC_NPROCESSORS_ONLN] = JT_NPROCESSORS_ONLN,
+		[_SC_PHYS_PAGES] = JT_PHYS_PAGES,
+		[_SC_AVPHYS_PAGES] = JT_AVPHYS_PAGES,
 		[_SC_ATEXIT_MAX] = -1,
 		[_SC_PASS_MAX] = -1,
 		[_SC_XOPEN_VERSION] = _XOPEN_VERSION,
@@ -110,89 +105,44 @@
 		[_SC_XOPEN_ENH_I18N] = 1,
 		[_SC_XOPEN_SHM] = 1,
 		[_SC_2_CHAR_TERM] = -1,
-		[_SC_2_C_VERSION] = -1,
 		[_SC_2_UPE] = -1,
 		[_SC_XOPEN_XPG2] = -1,
 		[_SC_XOPEN_XPG3] = -1,
 		[_SC_XOPEN_XPG4] = -1,
-		[_SC_CHAR_BIT] = -1,
-		[_SC_CHAR_MAX] = -1,
-		[_SC_CHAR_MIN] = -1,
-		[_SC_INT_MAX] = -1,
-		[_SC_INT_MIN] = -1,
-		[_SC_LONG_BIT] = -1,
-		[_SC_WORD_BIT] = -1,
-		[_SC_MB_LEN_MAX] = -1,
 		[_SC_NZERO] = NZERO,
-		[_SC_SSIZE_MAX] = -1,
-		[_SC_SCHAR_MAX] = -1,
-		[_SC_SCHAR_MIN] = -1,
-		[_SC_SHRT_MAX] = -1,
-		[_SC_SHRT_MIN] = -1,
-		[_SC_UCHAR_MAX] = -1,
-		[_SC_UINT_MAX] = -1,
-		[_SC_ULONG_MAX] = -1,
-		[_SC_USHRT_MAX] = -1,
-		[_SC_NL_ARGMAX] = -1,
-		[_SC_NL_LANGMAX] = -1,
-		[_SC_NL_MSGMAX] = -1,
-		[_SC_NL_NMAX] = -1,
-		[_SC_NL_SETMAX] = -1,
-		[_SC_NL_TEXTMAX] = -1,
 		[_SC_XBS5_ILP32_OFF32] = -1,
-		[_SC_XBS5_ILP32_OFFBIG] = 2*(sizeof(long)==4)-1,
-		[_SC_XBS5_LP64_OFF64] = 2*(sizeof(long)==8)-1,
+		[_SC_XBS5_ILP32_OFFBIG] = sizeof(long)==4 ? 1 : JT_ZERO,
+		[_SC_XBS5_LP64_OFF64] = sizeof(long)==8 ? 1 : JT_ZERO,
 		[_SC_XBS5_LPBIG_OFFBIG] = -1,
 		[_SC_XOPEN_LEGACY] = -1,
 		[_SC_XOPEN_REALTIME] = -1,
 		[_SC_XOPEN_REALTIME_THREADS] = -1,
 		[_SC_ADVISORY_INFO] = VER,
 		[_SC_BARRIERS] = VER,
-		[_SC_BASE] = -1,
-		[_SC_C_LANG_SUPPORT] = -1,
-		[_SC_C_LANG_SUPPORT_R] = -1,
 		[_SC_CLOCK_SELECTION] = VER,
 		[_SC_CPUTIME] = VER,
 		[_SC_THREAD_CPUTIME] = VER,
-		[_SC_DEVICE_IO] = -1,
-		[_SC_DEVICE_SPECIFIC] = -1,
-		[_SC_DEVICE_SPECIFIC_R] = -1,
-		[_SC_FD_MGMT] = -1,
-		[_SC_FIFO] = -1,
-		[_SC_PIPE] = -1,
-		[_SC_FILE_ATTRIBUTES] = -1,
-		[_SC_FILE_LOCKING] = -1,
-		[_SC_FILE_SYSTEM] = -1,
 		[_SC_MONOTONIC_CLOCK] = VER,
-		[_SC_MULTI_PROCESS] = -1,
-		[_SC_SINGLE_PROCESS] = -1,
-		[_SC_NETWORKING] = -1,
 		[_SC_READER_WRITER_LOCKS] = VER,
 		[_SC_SPIN_LOCKS] = VER,
 		[_SC_REGEXP] = 1,
-		[_SC_REGEX_VERSION] = -1,
 		[_SC_SHELL] = 1,
-		[_SC_SIGNALS] = -1,
 		[_SC_SPAWN] = VER,
 		[_SC_SPORADIC_SERVER] = -1,
 		[_SC_THREAD_SPORADIC_SERVER] = -1,
-		[_SC_SYSTEM_DATABASE] = -1,
-		[_SC_SYSTEM_DATABASE_R] = -1,
 		[_SC_TIMEOUTS] = VER,
 		[_SC_TYPED_MEMORY_OBJECTS] = -1,
-		[_SC_USER_GROUPS] = -1,
-		[_SC_USER_GROUPS_R] = -1,
 		[_SC_2_PBS] = -1,
 		[_SC_2_PBS_ACCOUNTING] = -1,
 		[_SC_2_PBS_LOCATE] = -1,
 		[_SC_2_PBS_MESSAGE] = -1,
 		[_SC_2_PBS_TRACK] = -1,
 		[_SC_SYMLOOP_MAX] = SYMLOOP_MAX,
-		[_SC_STREAMS] = 0,
+		[_SC_STREAMS] = JT_ZERO,
 		[_SC_2_PBS_CHECKPOINT] = -1,
 		[_SC_V6_ILP32_OFF32] = -1,
-		[_SC_V6_ILP32_OFFBIG] = 2*(sizeof(long)==4)-1,
-		[_SC_V6_LP64_OFF64] = 2*(sizeof(long)==8)-1,
+		[_SC_V6_ILP32_OFFBIG] = sizeof(long)==4 ? 1 : JT_ZERO,
+		[_SC_V6_LP64_OFF64] = sizeof(long)==8 ? 1 : JT_ZERO,
 		[_SC_V6_LPBIG_OFFBIG] = -1,
 		[_SC_HOST_NAME_MAX] = HOST_NAME_MAX,
 		[_SC_TRACE] = -1,
@@ -203,40 +153,63 @@
 		[_SC_IPV6] = VER,
 		[_SC_RAW_SOCKETS] = VER,
 		[_SC_V7_ILP32_OFF32] = -1,
-		[_SC_V7_ILP32_OFFBIG] = 2*(sizeof(long)==4)-1,
-		[_SC_V7_LP64_OFF64] = 2*(sizeof(long)==8)-1,
+		[_SC_V7_ILP32_OFFBIG] = sizeof(long)==4 ? 1 : JT_ZERO,
+		[_SC_V7_LP64_OFF64] = sizeof(long)==8 ? 1 : JT_ZERO,
 		[_SC_V7_LPBIG_OFFBIG] = -1,
 		[_SC_SS_REPL_MAX] = -1,
 		[_SC_TRACE_EVENT_NAME_MAX] = -1,
 		[_SC_TRACE_NAME_MAX] = -1,
 		[_SC_TRACE_SYS_MAX] = -1,
 		[_SC_TRACE_USER_EVENT_MAX] = -1,
-		[_SC_XOPEN_STREAMS] = 0,
+		[_SC_XOPEN_STREAMS] = JT_ZERO,
 		[_SC_THREAD_ROBUST_PRIO_INHERIT] = -1,
 		[_SC_THREAD_ROBUST_PRIO_PROTECT] = -1,
 	};
-	if (name > sizeof(values)/sizeof(values[0])) {
+
+	if (name >= sizeof(values)/sizeof(values[0]) || !values[name]) {
 		errno = EINVAL;
 		return -1;
-	} else if (values[name] == VER) {
+	} else if (values[name] >= -1) {
+		return values[name];
+	} else if (values[name] < -256) {
+		struct rlimit lim;
+		getrlimit(values[name]&16383, &lim);
+		return lim.rlim_cur > LONG_MAX ? LONG_MAX : lim.rlim_cur;
+	}
+
+	switch ((unsigned char)values[name]) {
+	case VER & 255:
 		return _POSIX_VERSION;
-	} else if (values[name] == OFLOW) {
-		if (name == _SC_ARG_MAX) return ARG_MAX;
-		if (name == _SC_SEM_VALUE_MAX) return SEM_VALUE_MAX;
-		if (name == _SC_MQ_PRIO_MAX) return MQ_PRIO_MAX;
-		/* name == _SC_PAGE_SIZE */
+	case JT_ARG_MAX & 255:
+		return ARG_MAX;
+	case JT_MQ_PRIO_MAX & 255:
+		return MQ_PRIO_MAX;
+	case JT_PAGE_SIZE & 255:
 		return PAGE_SIZE;
-	} else if (values[name] == CPUCNT) {
+	case JT_SEM_VALUE_MAX & 255:
+		return SEM_VALUE_MAX;
+	case JT_NPROCESSORS_CONF & 255:
+	case JT_NPROCESSORS_ONLN & 255: ;
 		unsigned char set[128] = {1};
 		int i, cnt;
 		__syscall(SYS_sched_getaffinity, 0, sizeof set, set);
 		for (i=cnt=0; i<sizeof set; i++)
 			for (; set[i]; set[i]&=set[i]-1, cnt++);
 		return cnt;
-	} else if (values[name] < OFLOW) {
-		long lim[2];
-		__syscall(SYS_getrlimit, values[name]&16383, lim);
-		return lim[0] < 0 ? LONG_MAX : lim[0];
+	case JT_PHYS_PAGES & 255:
+	case JT_AVPHYS_PAGES & 255: ;
+		unsigned long long mem;
+		int __lsysinfo(struct sysinfo *);
+		struct sysinfo si;
+		__lsysinfo(&si);
+		if (!si.mem_unit) si.mem_unit = 1;
+		if (name==_SC_PHYS_PAGES) mem = si.totalram;
+		else mem = si.freeram + si.bufferram;
+		mem *= si.mem_unit;
+		mem /= PAGE_SIZE;
+		return (mem > LONG_MAX) ? LONG_MAX : mem;
+	case JT_ZERO & 255:
+		return 0;
 	}
 	return values[name];
 }
diff --git a/system/lib/libc/musl/src/crypt/crypt_sha256.c b/system/lib/libc/musl/src/crypt/crypt_sha256.c
index d5f0b78..e885dc6 100644
--- a/system/lib/libc/musl/src/crypt/crypt_sha256.c
+++ b/system/lib/libc/musl/src/crypt/crypt_sha256.c
@@ -230,7 +230,7 @@
 		if (u < ROUNDS_MIN)
 			r = ROUNDS_MIN;
 		else if (u > ROUNDS_MAX)
-			r = ROUNDS_MAX;
+			return 0;
 		else
 			r = u;
 		/* needed when rounds is zero prefixed or out of bounds */
diff --git a/system/lib/libc/musl/src/crypt/crypt_sha512.c b/system/lib/libc/musl/src/crypt/crypt_sha512.c
index 1294e98..39970ca 100644
--- a/system/lib/libc/musl/src/crypt/crypt_sha512.c
+++ b/system/lib/libc/musl/src/crypt/crypt_sha512.c
@@ -252,7 +252,7 @@
 		if (u < ROUNDS_MIN)
 			r = ROUNDS_MIN;
 		else if (u > ROUNDS_MAX)
-			r = ROUNDS_MAX;
+			return 0;
 		else
 			r = u;
 		/* needed when rounds is zero prefixed or out of bounds */
diff --git a/system/lib/libc/musl/src/ctype/__ctype_get_mb_cur_max.c b/system/lib/libc/musl/src/ctype/__ctype_get_mb_cur_max.c
index d235f4d..8e946fc 100644
--- a/system/lib/libc/musl/src/ctype/__ctype_get_mb_cur_max.c
+++ b/system/lib/libc/musl/src/ctype/__ctype_get_mb_cur_max.c
@@ -1,6 +1,7 @@
-#include <stddef.h>
+#include <stdlib.h>
+#include "locale_impl.h"
 
 size_t __ctype_get_mb_cur_max()
 {
-	return 4;
+	return MB_CUR_MAX;
 }
diff --git a/system/lib/libc/musl/src/ctype/isalnum.c b/system/lib/libc/musl/src/ctype/isalnum.c
index e3d2cf0..2214936 100644
--- a/system/lib/libc/musl/src/ctype/isalnum.c
+++ b/system/lib/libc/musl/src/ctype/isalnum.c
@@ -1,6 +1,14 @@
 #include <ctype.h>
+#include "libc.h"
 
 int isalnum(int c)
 {
 	return isalpha(c) || isdigit(c);
 }
+
+int __isalnum_l(int c, locale_t l)
+{
+	return isalnum(c);
+}
+
+weak_alias(__isalnum_l, isalnum_l);
diff --git a/system/lib/libc/musl/src/ctype/isalpha.c b/system/lib/libc/musl/src/ctype/isalpha.c
index 53e115c..f155d3a 100644
--- a/system/lib/libc/musl/src/ctype/isalpha.c
+++ b/system/lib/libc/musl/src/ctype/isalpha.c
@@ -1,7 +1,15 @@
 #include <ctype.h>
+#include "libc.h"
 #undef isalpha
 
 int isalpha(int c)
 {
 	return ((unsigned)c|32)-'a' < 26;
 }
+
+int __isalpha_l(int c, locale_t l)
+{
+	return isalpha(c);
+}
+
+weak_alias(__isalpha_l, isalpha_l);
diff --git a/system/lib/libc/musl/src/ctype/isascii.c b/system/lib/libc/musl/src/ctype/isascii.c
index 3af0a10..54ad3bf 100644
--- a/system/lib/libc/musl/src/ctype/isascii.c
+++ b/system/lib/libc/musl/src/ctype/isascii.c
@@ -1,4 +1,5 @@
 #include <ctype.h>
+#undef isascii
 
 int isascii(int c)
 {
diff --git a/system/lib/libc/musl/src/ctype/isblank.c b/system/lib/libc/musl/src/ctype/isblank.c
index 957400b..299120e 100644
--- a/system/lib/libc/musl/src/ctype/isblank.c
+++ b/system/lib/libc/musl/src/ctype/isblank.c
@@ -1,6 +1,14 @@
 #include <ctype.h>
+#include "libc.h"
 
 int isblank(int c)
 {
 	return (c == ' ' || c == '\t');
 }
+
+int __isblank_l(int c, locale_t l)
+{
+	return isblank(c);
+}
+
+weak_alias(__isblank_l, isblank_l);
diff --git a/system/lib/libc/musl/src/ctype/iscntrl.c b/system/lib/libc/musl/src/ctype/iscntrl.c
index 92ed7f0..cb4114a 100644
--- a/system/lib/libc/musl/src/ctype/iscntrl.c
+++ b/system/lib/libc/musl/src/ctype/iscntrl.c
@@ -1,6 +1,14 @@
 #include <ctype.h>
+#include "libc.h"
 
 int iscntrl(int c)
 {
 	return (unsigned)c < 0x20 || c == 0x7f;
 }
+
+int __iscntrl_l(int c, locale_t l)
+{
+	return iscntrl(c);
+}
+
+weak_alias(__iscntrl_l, iscntrl_l);
diff --git a/system/lib/libc/musl/src/ctype/isdigit.c b/system/lib/libc/musl/src/ctype/isdigit.c
index 0bc82a6..4d8a103 100644
--- a/system/lib/libc/musl/src/ctype/isdigit.c
+++ b/system/lib/libc/musl/src/ctype/isdigit.c
@@ -1,7 +1,15 @@
 #include <ctype.h>
+#include "libc.h"
 #undef isdigit
 
 int isdigit(int c)
 {
 	return (unsigned)c-'0' < 10;
 }
+
+int __isdigit_l(int c, locale_t l)
+{
+	return isdigit(c);
+}
+
+weak_alias(__isdigit_l, isdigit_l);
diff --git a/system/lib/libc/musl/src/ctype/isgraph.c b/system/lib/libc/musl/src/ctype/isgraph.c
index 98979d1..a0aae08 100644
--- a/system/lib/libc/musl/src/ctype/isgraph.c
+++ b/system/lib/libc/musl/src/ctype/isgraph.c
@@ -1,4 +1,15 @@
+#include <ctype.h>
+#include "libc.h"
+#undef isgraph
+
 int isgraph(int c)
 {
 	return (unsigned)c-0x21 < 0x5e;
 }
+
+int __isgraph_l(int c, locale_t l)
+{
+	return isgraph(c);
+}
+
+weak_alias(__isgraph_l, isgraph_l);
diff --git a/system/lib/libc/musl/src/ctype/islower.c b/system/lib/libc/musl/src/ctype/islower.c
index d72fb21..0264021 100644
--- a/system/lib/libc/musl/src/ctype/islower.c
+++ b/system/lib/libc/musl/src/ctype/islower.c
@@ -1,7 +1,15 @@
 #include <ctype.h>
+#include "libc.h"
 #undef islower
 
 int islower(int c)
 {
 	return (unsigned)c-'a' < 26;
 }
+
+int __islower_l(int c, locale_t l)
+{
+	return islower(c);
+}
+
+weak_alias(__islower_l, islower_l);
diff --git a/system/lib/libc/musl/src/ctype/isprint.c b/system/lib/libc/musl/src/ctype/isprint.c
index 504e66e..067275f 100644
--- a/system/lib/libc/musl/src/ctype/isprint.c
+++ b/system/lib/libc/musl/src/ctype/isprint.c
@@ -1,4 +1,15 @@
+#include <ctype.h>
+#include "libc.h"
+#undef isprint
+
 int isprint(int c)
 {
 	return (unsigned)c-0x20 < 0x5f;
 }
+
+int __isprint_l(int c, locale_t l)
+{
+	return isprint(c);
+}
+
+weak_alias(__isprint_l, isprint_l);
diff --git a/system/lib/libc/musl/src/ctype/ispunct.c b/system/lib/libc/musl/src/ctype/ispunct.c
index fc45535..e772d76 100644
--- a/system/lib/libc/musl/src/ctype/ispunct.c
+++ b/system/lib/libc/musl/src/ctype/ispunct.c
@@ -1,6 +1,14 @@
 #include <ctype.h>
+#include "libc.h"
 
 int ispunct(int c)
 {
 	return isgraph(c) && !isalnum(c);
 }
+
+int __ispunct_l(int c, locale_t l)
+{
+	return ispunct(c);
+}
+
+weak_alias(__ispunct_l, ispunct_l);
diff --git a/system/lib/libc/musl/src/ctype/isspace.c b/system/lib/libc/musl/src/ctype/isspace.c
index 8e535aa..231e907 100644
--- a/system/lib/libc/musl/src/ctype/isspace.c
+++ b/system/lib/libc/musl/src/ctype/isspace.c
@@ -1,6 +1,15 @@
 #include <ctype.h>
+#include "libc.h"
+#undef isspace
 
 int isspace(int c)
 {
 	return c == ' ' || (unsigned)c-'\t' < 5;
 }
+
+int __isspace_l(int c, locale_t l)
+{
+	return isspace(c);
+}
+
+weak_alias(__isspace_l, isspace_l);
diff --git a/system/lib/libc/musl/src/ctype/isupper.c b/system/lib/libc/musl/src/ctype/isupper.c
index f09d88c..68c36f4 100644
--- a/system/lib/libc/musl/src/ctype/isupper.c
+++ b/system/lib/libc/musl/src/ctype/isupper.c
@@ -1,7 +1,15 @@
 #include <ctype.h>
+#include "libc.h"
 #undef isupper
 
 int isupper(int c)
 {
 	return (unsigned)c-'A' < 26;
 }
+
+int __isupper_l(int c, locale_t l)
+{
+	return isupper(c);
+}
+
+weak_alias(__isupper_l, isupper_l);
diff --git a/system/lib/libc/musl/src/ctype/iswalnum.c b/system/lib/libc/musl/src/ctype/iswalnum.c
index 35dbe02..a6082da 100644
--- a/system/lib/libc/musl/src/ctype/iswalnum.c
+++ b/system/lib/libc/musl/src/ctype/iswalnum.c
@@ -1,7 +1,14 @@
-#include <wchar.h>
 #include <wctype.h>
+#include "libc.h"
 
 int iswalnum(wint_t wc)
 {
 	return iswdigit(wc) || iswalpha(wc);
 }
+
+int __iswalnum_l(wint_t c, locale_t l)
+{
+	return iswalnum(c);
+}
+
+weak_alias(__iswalnum_l, iswalnum_l);
diff --git a/system/lib/libc/musl/src/ctype/iswalpha.c b/system/lib/libc/musl/src/ctype/iswalpha.c
index d558fae..00f9d81 100644
--- a/system/lib/libc/musl/src/ctype/iswalpha.c
+++ b/system/lib/libc/musl/src/ctype/iswalpha.c
@@ -1,4 +1,5 @@
 #include <wctype.h>
+#include "libc.h"
 
 static const unsigned char table[] = {
 #include "alpha.h"
@@ -12,3 +13,10 @@
 		return 1;
 	return 0;
 }
+
+int __iswalpha_l(wint_t c, locale_t l)
+{
+	return iswalpha(c);
+}
+
+weak_alias(__iswalpha_l, iswalpha_l);
diff --git a/system/lib/libc/musl/src/ctype/iswblank.c b/system/lib/libc/musl/src/ctype/iswblank.c
index bc6196f..d9b33ef 100644
--- a/system/lib/libc/musl/src/ctype/iswblank.c
+++ b/system/lib/libc/musl/src/ctype/iswblank.c
@@ -1,8 +1,15 @@
-#include <wchar.h>
 #include <wctype.h>
 #include <ctype.h>
+#include "libc.h"
 
 int iswblank(wint_t wc)
 {
 	return isblank(wc);
 }
+
+int __iswblank_l(wint_t c, locale_t l)
+{
+	return iswblank(c);
+}
+
+weak_alias(__iswblank_l, iswblank_l);
diff --git a/system/lib/libc/musl/src/ctype/iswcntrl.c b/system/lib/libc/musl/src/ctype/iswcntrl.c
index 93942b0..daace82 100644
--- a/system/lib/libc/musl/src/ctype/iswcntrl.c
+++ b/system/lib/libc/musl/src/ctype/iswcntrl.c
@@ -1,5 +1,5 @@
-#include <wchar.h>
 #include <wctype.h>
+#include "libc.h"
 
 int iswcntrl(wint_t wc)
 {
@@ -8,3 +8,10 @@
 	    || (unsigned)(wc-0x2028) < 2
 	    || (unsigned)(wc-0xfff9) < 3;
 }
+
+int __iswcntrl_l(wint_t c, locale_t l)
+{
+	return iswcntrl(c);
+}
+
+weak_alias(__iswcntrl_l, iswcntrl_l);
diff --git a/system/lib/libc/musl/src/ctype/iswctype.c b/system/lib/libc/musl/src/ctype/iswctype.c
index d917975..3d9c2cc 100644
--- a/system/lib/libc/musl/src/ctype/iswctype.c
+++ b/system/lib/libc/musl/src/ctype/iswctype.c
@@ -1,6 +1,6 @@
-#include <wchar.h>
 #include <wctype.h>
 #include <string.h>
+#include "libc.h"
 
 #define WCTYPE_ALNUM  1
 #define WCTYPE_ALPHA  2
@@ -61,3 +61,16 @@
 			return i;
 	return 0;
 }
+
+int __iswctype_l(wint_t c, wctype_t t, locale_t l)
+{
+	return iswctype(c, t);
+}
+
+wctype_t __wctype_l(const char *s, locale_t l)
+{
+	return wctype(s);
+}
+
+weak_alias(__iswctype_l, iswctype_l);
+weak_alias(__wctype_l, wctype_l);
diff --git a/system/lib/libc/musl/src/ctype/iswdigit.c b/system/lib/libc/musl/src/ctype/iswdigit.c
index 0487145..ed9a88e 100644
--- a/system/lib/libc/musl/src/ctype/iswdigit.c
+++ b/system/lib/libc/musl/src/ctype/iswdigit.c
@@ -1,5 +1,5 @@
-#include <wchar.h>
 #include <wctype.h>
+#include "libc.h"
 
 #undef iswdigit
 
@@ -7,3 +7,10 @@
 {
 	return (unsigned)wc-'0' < 10;
 }
+
+int __iswdigit_l(wint_t c, locale_t l)
+{
+	return iswdigit(c);
+}
+
+weak_alias(__iswdigit_l, iswdigit_l);
diff --git a/system/lib/libc/musl/src/ctype/iswgraph.c b/system/lib/libc/musl/src/ctype/iswgraph.c
index fdc9785..0ea5ca3 100644
--- a/system/lib/libc/musl/src/ctype/iswgraph.c
+++ b/system/lib/libc/musl/src/ctype/iswgraph.c
@@ -1,7 +1,15 @@
 #include <wctype.h>
+#include "libc.h"
 
 int iswgraph(wint_t wc)
 {
 	/* ISO C defines this function as: */
 	return !iswspace(wc) && iswprint(wc);
 }
+
+int __iswgraph_l(wint_t c, locale_t l)
+{
+	return iswgraph(c);
+}
+
+weak_alias(__iswgraph_l, iswgraph_l);
diff --git a/system/lib/libc/musl/src/ctype/iswlower.c b/system/lib/libc/musl/src/ctype/iswlower.c
index 438fe26..79df44a 100644
--- a/system/lib/libc/musl/src/ctype/iswlower.c
+++ b/system/lib/libc/musl/src/ctype/iswlower.c
@@ -1,6 +1,14 @@
 #include <wctype.h>
+#include "libc.h"
 
 int iswlower(wint_t wc)
 {
-	return towupper(wc) != wc || wc == 0xdf;
+	return towupper(wc) != wc;
 }
+
+int __iswlower_l(wint_t c, locale_t l)
+{
+	return iswlower(c);
+}
+
+weak_alias(__iswlower_l, iswlower_l);
diff --git a/system/lib/libc/musl/src/ctype/iswprint.c b/system/lib/libc/musl/src/ctype/iswprint.c
index 333f19c..69856e0 100644
--- a/system/lib/libc/musl/src/ctype/iswprint.c
+++ b/system/lib/libc/musl/src/ctype/iswprint.c
@@ -1,4 +1,5 @@
 #include <wctype.h>
+#include "libc.h"
 
 /* Consider all legal codepoints as printable except for:
  * - C0 and C1 control characters
@@ -17,3 +18,10 @@
 		return 0;
 	return 1;
 }
+
+int __iswprint_l(wint_t c, locale_t l)
+{
+	return iswprint(c);
+}
+
+weak_alias(__iswprint_l, iswprint_l);
diff --git a/system/lib/libc/musl/src/ctype/iswpunct.c b/system/lib/libc/musl/src/ctype/iswpunct.c
index 16e8703..d880104 100644
--- a/system/lib/libc/musl/src/ctype/iswpunct.c
+++ b/system/lib/libc/musl/src/ctype/iswpunct.c
@@ -1,4 +1,5 @@
 #include <wctype.h>
+#include "libc.h"
 
 static const unsigned char table[] = {
 #include "punct.h"
@@ -10,3 +11,10 @@
 		return (table[table[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1;
 	return 0;
 }
+
+int __iswpunct_l(wint_t c, locale_t l)
+{
+	return iswpunct(c);
+}
+
+weak_alias(__iswpunct_l, iswpunct_l);
diff --git a/system/lib/libc/musl/src/ctype/iswspace.c b/system/lib/libc/musl/src/ctype/iswspace.c
index b0c0ae1..75ae7e8 100644
--- a/system/lib/libc/musl/src/ctype/iswspace.c
+++ b/system/lib/libc/musl/src/ctype/iswspace.c
@@ -1,6 +1,6 @@
 #include <wchar.h>
 #include <wctype.h>
-#include <ctype.h>
+#include "libc.h"
 
 /* Our definition of whitespace is the Unicode White_Space property,
  * minus non-breaking spaces (U+00A0, U+2007, and U+202F) and script-
@@ -16,3 +16,10 @@
 	};
 	return wc && wcschr(spaces, wc);
 }
+
+int __iswspace_l(wint_t c, locale_t l)
+{
+	return iswspace(c);
+}
+
+weak_alias(__iswspace_l, iswspace_l);
diff --git a/system/lib/libc/musl/src/ctype/iswupper.c b/system/lib/libc/musl/src/ctype/iswupper.c
index eae59a7..6e1e029 100644
--- a/system/lib/libc/musl/src/ctype/iswupper.c
+++ b/system/lib/libc/musl/src/ctype/iswupper.c
@@ -1,6 +1,14 @@
 #include <wctype.h>
+#include "libc.h"
 
 int iswupper(wint_t wc)
 {
 	return towlower(wc) != wc;
 }
+
+int __iswupper_l(wint_t c, locale_t l)
+{
+	return iswupper(c);
+}
+
+weak_alias(__iswupper_l, iswupper_l);
diff --git a/system/lib/libc/musl/src/ctype/iswxdigit.c b/system/lib/libc/musl/src/ctype/iswxdigit.c
index 229a469..1e27f1f 100644
--- a/system/lib/libc/musl/src/ctype/iswxdigit.c
+++ b/system/lib/libc/musl/src/ctype/iswxdigit.c
@@ -1,7 +1,14 @@
-#include <wchar.h>
 #include <wctype.h>
+#include "libc.h"
 
 int iswxdigit(wint_t wc)
 {
 	return (unsigned)(wc-'0') < 10 || (unsigned)((wc|32)-'a') < 6;
 }
+
+int __iswxdigit_l(wint_t c, locale_t l)
+{
+	return iswxdigit(c);
+}
+
+weak_alias(__iswxdigit_l, iswxdigit_l);
diff --git a/system/lib/libc/musl/src/ctype/isxdigit.c b/system/lib/libc/musl/src/ctype/isxdigit.c
index ae68a3d..0e9152a 100644
--- a/system/lib/libc/musl/src/ctype/isxdigit.c
+++ b/system/lib/libc/musl/src/ctype/isxdigit.c
@@ -1,6 +1,14 @@
 #include <ctype.h>
+#include "libc.h"
 
 int isxdigit(int c)
 {
 	return isdigit(c) || ((unsigned)c|32)-'a' < 6;
 }
+
+int __isxdigit_l(int c, locale_t l)
+{
+	return isxdigit(c);
+}
+
+weak_alias(__isxdigit_l, isxdigit_l);
diff --git a/system/lib/libc/musl/src/ctype/tolower.c b/system/lib/libc/musl/src/ctype/tolower.c
index b56f3c5..362d6b2 100644
--- a/system/lib/libc/musl/src/ctype/tolower.c
+++ b/system/lib/libc/musl/src/ctype/tolower.c
@@ -1,7 +1,15 @@
 #include <ctype.h>
+#include "libc.h"
 
 int tolower(int c)
 {
 	if (isupper(c)) return c | 32;
 	return c;
 }
+
+int __tolower_l(int c, locale_t l)
+{
+	return tolower(c);
+}
+
+weak_alias(__tolower_l, tolower_l);
diff --git a/system/lib/libc/musl/src/ctype/toupper.c b/system/lib/libc/musl/src/ctype/toupper.c
index 1799f03..bbf4e06 100644
--- a/system/lib/libc/musl/src/ctype/toupper.c
+++ b/system/lib/libc/musl/src/ctype/toupper.c
@@ -1,7 +1,15 @@
 #include <ctype.h>
+#include "libc.h"
 
 int toupper(int c)
 {
 	if (islower(c)) return c & 0x5f;
 	return c;
 }
+
+int __toupper_l(int c, locale_t l)
+{
+	return toupper(c);
+}
+
+weak_alias(__toupper_l, toupper_l);
diff --git a/system/lib/libc/musl/src/ctype/towctrans.c b/system/lib/libc/musl/src/ctype/towctrans.c
index 2842d69..6af6187 100644
--- a/system/lib/libc/musl/src/ctype/towctrans.c
+++ b/system/lib/libc/musl/src/ctype/towctrans.c
@@ -1,6 +1,5 @@
-#include <wchar.h>
 #include <wctype.h>
-#include <stdio.h>
+#include "libc.h"
 
 #define CASEMAP(u1,u2,l) { (u1), (l)-(u1), (u2)-(u1)+1 }
 #define CASELACE(u1,u2) CASEMAP((u1),(u2),(u1)+1)
@@ -152,7 +151,6 @@
 	{ 0x03f7, 0x03f8 },
 	{ 0x03fa, 0x03fb },
 	{ 0x1e60, 0x1e9b },
-	{ 0xdf, 0xdf },
 	{ 0x1e9e, 0xdf },
 
 	{ 0x1f59, 0x1f51 },
@@ -266,3 +264,16 @@
 {
 	return __towcase(wc, 1);
 }
+
+wint_t __towupper_l(wint_t c, locale_t l)
+{
+	return towupper(c);
+}
+
+wint_t __towlower_l(wint_t c, locale_t l)
+{
+	return towlower(c);
+}
+
+weak_alias(__towupper_l, towupper_l);
+weak_alias(__towlower_l, towlower_l);
diff --git a/system/lib/libc/musl/src/ctype/wctrans.c b/system/lib/libc/musl/src/ctype/wctrans.c
index 739869d..b1b1265 100644
--- a/system/lib/libc/musl/src/ctype/wctrans.c
+++ b/system/lib/libc/musl/src/ctype/wctrans.c
@@ -1,5 +1,6 @@
 #include <wctype.h>
 #include <string.h>
+#include "libc.h"
 
 wctrans_t wctrans(const char *class)
 {
@@ -14,3 +15,16 @@
 	if (trans == (wctrans_t)2) return towlower(wc);
 	return wc;
 }
+
+wctrans_t __wctrans_l(const char *s, locale_t l)
+{
+	return wctrans(s);
+}
+
+wint_t __towctrans_l(wint_t c, wctrans_t t, locale_t l)
+{
+	return towctrans(c, t);
+}
+
+weak_alias(__wctrans_l, wctrans_l);
+weak_alias(__towctrans_l, towctrans_l);
diff --git a/system/lib/libc/musl/src/dirent/__dirent.h b/system/lib/libc/musl/src/dirent/__dirent.h
index 45509e1..32871ba 100644
--- a/system/lib/libc/musl/src/dirent/__dirent.h
+++ b/system/lib/libc/musl/src/dirent/__dirent.h
@@ -4,6 +4,6 @@
 	off_t tell;
 	int buf_pos;
 	int buf_end;
-	int lock[2];
+	volatile int lock[2];
 	char buf[2048];
 };
diff --git a/system/lib/libc/musl/src/dirent/versionsort.c b/system/lib/libc/musl/src/dirent/versionsort.c
index 9769610..410cb70 100644
--- a/system/lib/libc/musl/src/dirent/versionsort.c
+++ b/system/lib/libc/musl/src/dirent/versionsort.c
@@ -1,8 +1,12 @@
 #define _GNU_SOURCE
 #include <string.h>
 #include <dirent.h>
+#include "libc.h"
 
 int versionsort(const struct dirent **a, const struct dirent **b)
 {
 	return strverscmp((*a)->d_name, (*b)->d_name);
 }
+
+#undef versionsort64
+LFS64(versionsort);
diff --git a/system/lib/libc/musl/src/env/__init_tls.c b/system/lib/libc/musl/src/env/__init_tls.c
index 5c17681..0107a54 100644
--- a/system/lib/libc/musl/src/env/__init_tls.c
+++ b/system/lib/libc/musl/src/env/__init_tls.c
@@ -2,71 +2,82 @@
 #include <limits.h>
 #include <sys/mman.h>
 #include <string.h>
+#include <stddef.h>
 #include "pthread_impl.h"
 #include "libc.h"
 #include "atomic.h"
+#include "syscall.h"
 
-#ifndef SHARED
+int __init_tp(void *p)
+{
+	pthread_t td = p;
+	td->self = td;
+	int r = __set_thread_area(TP_ADJ(p));
+	if (r < 0) return -1;
+	if (!r) libc.can_do_threads = 1;
+	td->tid = __syscall(SYS_set_tid_address, &td->tid);
+	td->locale = &libc.global_locale;
+	td->robust_list.head = &td->robust_list.head;
+	return 0;
+}
 
-struct tls_image {
-	void *image;
-	size_t len, size, align;
-} __static_tls ATTR_LIBC_VISIBILITY;
+static struct builtin_tls {
+	char c;
+	struct pthread pt;
+	void *space[16];
+} builtin_tls[1];
+#define MIN_TLS_ALIGN offsetof(struct builtin_tls, pt)
 
-#define T __static_tls
+static struct tls_module main_tls;
 
 void *__copy_tls(unsigned char *mem)
 {
 	pthread_t td;
-	if (!T.image) return mem;
-	void **dtv = (void *)mem;
-	dtv[0] = (void *)1;
+	struct tls_module *p;
+	size_t i;
+	void **dtv;
+
 #ifdef TLS_ABOVE_TP
-	mem += sizeof(void *) * 2;
-	mem += -((uintptr_t)mem + sizeof(struct pthread)) & (T.align-1);
+	dtv = (void **)(mem + libc.tls_size) - (libc.tls_cnt + 1);
+
+	mem += -((uintptr_t)mem + sizeof(struct pthread)) & (libc.tls_align-1);
 	td = (pthread_t)mem;
 	mem += sizeof(struct pthread);
+
+	for (i=1, p=libc.tls_head; p; i++, p=p->next) {
+		dtv[i] = mem + p->offset;
+		memcpy(dtv[i], p->image, p->len);
+	}
 #else
+	dtv = (void **)mem;
+
 	mem += libc.tls_size - sizeof(struct pthread);
-	mem -= (uintptr_t)mem & (T.align-1);
+	mem -= (uintptr_t)mem & (libc.tls_align-1);
 	td = (pthread_t)mem;
-	mem -= T.size;
+
+	for (i=1, p=libc.tls_head; p; i++, p=p->next) {
+		dtv[i] = mem - p->offset;
+		memcpy(dtv[i], p->image, p->len);
+	}
 #endif
-	td->dtv = dtv;
-	dtv[1] = mem;
-	memcpy(mem, T.image, T.len);
+	dtv[0] = (void *)libc.tls_cnt;
+	td->dtv = td->dtv_copy = dtv;
 	return td;
 }
 
-void *__tls_get_addr(size_t *v)
-{
-	return (char *)__pthread_self()->dtv[1]+v[1];
-}
-
-static void *simple(void *p)
-{
-	*(void **)p = p;
-	return __set_thread_area(TP_ADJ(p)) ? 0 : p;
-}
-
-weak_alias(simple, __install_initial_tls);
-
-void *__mmap(void *, size_t, int, int, int, off_t);
-
 #if ULONG_MAX == 0xffffffff
 typedef Elf32_Phdr Phdr;
 #else
 typedef Elf64_Phdr Phdr;
 #endif
 
-void __init_tls(size_t *aux)
+static void static_init_tls(size_t *aux)
 {
-	unsigned char *p, *mem;
+	unsigned char *p;
 	size_t n;
 	Phdr *phdr, *tls_phdr=0;
 	size_t base = 0;
-
-	libc.tls_size = sizeof(struct pthread);
+	void *mem;
 
 	for (p=(void *)aux[AT_PHDR],n=aux[AT_PHNUM]; n; n--,p+=aux[AT_PHENT]) {
 		phdr = (void *)p;
@@ -75,22 +86,46 @@
 		if (phdr->p_type == PT_TLS)
 			tls_phdr = phdr;
 	}
-	if (!tls_phdr) return;
 
-	T.image = (void *)(base + tls_phdr->p_vaddr);
-	T.len = tls_phdr->p_filesz;
-	T.size = tls_phdr->p_memsz;
-	T.align = tls_phdr->p_align;
+	if (tls_phdr) {
+		main_tls.image = (void *)(base + tls_phdr->p_vaddr);
+		main_tls.len = tls_phdr->p_filesz;
+		main_tls.size = tls_phdr->p_memsz;
+		main_tls.align = tls_phdr->p_align;
+		libc.tls_cnt = 1;
+		libc.tls_head = &main_tls;
+	}
 
-	T.size += (-T.size - (uintptr_t)T.image) & (T.align-1);
-	if (T.align < 4*sizeof(size_t)) T.align = 4*sizeof(size_t);
-
-	libc.tls_size = 2*sizeof(void *)+T.size+T.align+sizeof(struct pthread);
-
-	mem = __mmap(0, libc.tls_size, PROT_READ|PROT_WRITE,
-		MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
-	if (!__install_initial_tls(__copy_tls(mem))) a_crash();
-}
-#else
-void __init_tls(size_t *auxv) { }
+	main_tls.size += (-main_tls.size - (uintptr_t)main_tls.image)
+		& (main_tls.align-1);
+	if (main_tls.align < MIN_TLS_ALIGN) main_tls.align = MIN_TLS_ALIGN;
+#ifndef TLS_ABOVE_TP
+	main_tls.offset = main_tls.size;
 #endif
+
+	libc.tls_align = main_tls.align;
+	libc.tls_size = 2*sizeof(void *) + sizeof(struct pthread)
+		+ main_tls.size + main_tls.align
+		+ MIN_TLS_ALIGN-1 & -MIN_TLS_ALIGN;
+
+	if (libc.tls_size > sizeof builtin_tls) {
+#ifndef SYS_mmap2
+#define SYS_mmap2 SYS_mmap
+#endif
+		mem = (void *)__syscall(
+			SYS_mmap2,
+			0, libc.tls_size, PROT_READ|PROT_WRITE,
+			MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+		/* -4095...-1 cast to void * will crash on dereference anyway,
+		 * so don't bloat the init code checking for error codes and
+		 * explicitly calling a_crash(). */
+	} else {
+		mem = builtin_tls;
+	}
+
+	/* Failure to initialize thread pointer is always fatal. */
+	if (__init_tp(__copy_tls(mem)) < 0)
+		a_crash();
+}
+
+weak_alias(static_init_tls, __init_tls);
diff --git a/system/lib/libc/musl/src/env/__libc_start_main.c b/system/lib/libc/musl/src/env/__libc_start_main.c
index 73d4932..5c79be2 100644
--- a/system/lib/libc/musl/src/env/__libc_start_main.c
+++ b/system/lib/libc/musl/src/env/__libc_start_main.c
@@ -1,22 +1,24 @@
 #include <elf.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <signal.h>
+#include "syscall.h"
+#include "atomic.h"
 #include "libc.h"
 
 void __init_tls(size_t *);
-void __init_security(size_t *);
-void __init_ldso_ctors(void);
 
-#ifndef SHARED
-static void dummy() {}
+static void dummy(void) {}
 weak_alias(dummy, _init);
-extern void (*const __init_array_start)() __attribute__((weak));
-extern void (*const __init_array_end)() __attribute__((weak));
-#endif
+
+__attribute__((__weak__, __visibility__("hidden")))
+extern void (*const __init_array_start)(void), (*const __init_array_end)(void);
+
+static void dummy1(void *p) {}
+weak_alias(dummy1, __init_ssp);
 
 #define AUX_CNT 38
 
-extern size_t __hwcap, __sysinfo;
-extern char *__progname, *__progname_full;
-
 void __init_libc(char **envp, char *pn)
 {
 	size_t i, *auxv, aux[AUX_CNT] = { 0 };
@@ -34,22 +36,41 @@
 	}
 
 	__init_tls(aux);
-	__init_security(aux);
+	__init_ssp((void *)aux[AT_RANDOM]);
+
+	if (aux[AT_UID]==aux[AT_EUID] && aux[AT_GID]==aux[AT_EGID]
+		&& !aux[AT_SECURE]) return;
+
+	struct pollfd pfd[3] = { {.fd=0}, {.fd=1}, {.fd=2} };
+#ifdef SYS_poll
+	__syscall(SYS_poll, pfd, 3, 0);
+#else
+	__syscall(SYS_ppoll, pfd, 3, &(struct timespec){0}, 0, _NSIG/8);
+#endif
+	for (i=0; i<3; i++) if (pfd[i].revents&POLLNVAL)
+		if (__sys_open("/dev/null", O_RDWR)<0)
+			a_crash();
+	libc.secure = 1;
 }
 
+static void libc_start_init(void)
+{
+	_init();
+	uintptr_t a = (uintptr_t)&__init_array_start;
+	for (; a<(uintptr_t)&__init_array_end; a+=sizeof(void(*)()))
+		(*(void (**)())a)();
+}
+
+weak_alias(libc_start_init, __libc_start_init);
+
 int __libc_start_main(int (*main)(int,char **,char **), int argc, char **argv)
 {
 	char **envp = argv+argc+1;
 
-#ifndef SHARED
 	__init_libc(envp, argv[0]);
-	_init();
-	uintptr_t a = (uintptr_t)&__init_array_start;
-	for (; a<(uintptr_t)&__init_array_end; a+=sizeof(void(*)()))
-		(*(void (**)())a)();
-#endif
+	__libc_start_init();
 
-	/* Pass control to to application */
+	/* Pass control to the application */
 	exit(main(argc, argv, envp));
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/env/__reset_tls.c b/system/lib/libc/musl/src/env/__reset_tls.c
index 28c4405..677e57f 100644
--- a/system/lib/libc/musl/src/env/__reset_tls.c
+++ b/system/lib/libc/musl/src/env/__reset_tls.c
@@ -1,22 +1,16 @@
-#ifndef SHARED
-
 #include <string.h>
 #include "pthread_impl.h"
 #include "libc.h"
 
-extern struct tls_image {
-	void *image;
-	size_t len, size, align;
-} __static_tls ATTR_LIBC_VISIBILITY;
-
-#define T __static_tls
-
 void __reset_tls()
 {
-	if (!T.size) return;
 	pthread_t self = __pthread_self();
-	memcpy(self->dtv[1], T.image, T.len);
-	memset((char *)self->dtv[1]+T.len, 0, T.size-T.len);
+	struct tls_module *p;
+	size_t i, n = (size_t)self->dtv[0];
+	if (n) for (p=libc.tls_head, i=1; i<=n; i++, p=p->next) {
+		if (!self->dtv[i]) continue;
+		memcpy(self->dtv[i], p->image, p->len);
+		memset((char *)self->dtv[i]+p->len, 0,
+			p->size - p->len);
+	}
 }
-
-#endif
diff --git a/system/lib/libc/musl/src/env/__stack_chk_fail.c b/system/lib/libc/musl/src/env/__stack_chk_fail.c
index daa1b07..4de82fd 100644
--- a/system/lib/libc/musl/src/env/__stack_chk_fail.c
+++ b/system/lib/libc/musl/src/env/__stack_chk_fail.c
@@ -1,21 +1,23 @@
 #include <string.h>
 #include <stdint.h>
 #include "pthread_impl.h"
-#include "atomic.h"
 
 uintptr_t __stack_chk_guard;
 
 void __init_ssp(void *entropy)
 {
-	pthread_t self = __pthread_self_init();
-	uintptr_t canary;
-	if (entropy) memcpy(&canary, entropy, sizeof canary);
-	else canary = (uintptr_t)&canary * 1103515245;
-	a_cas_l(&__stack_chk_guard, 0, canary);
-	self->canary = __stack_chk_guard;
+	if (entropy) memcpy(&__stack_chk_guard, entropy, sizeof(uintptr_t));
+	else __stack_chk_guard = (uintptr_t)&__stack_chk_guard * 1103515245;
+
+	__pthread_self()->CANARY = __stack_chk_guard;
 }
 
 void __stack_chk_fail(void)
 {
 	a_crash();
 }
+
+__attribute__((__visibility__("hidden")))
+void __stack_chk_fail_local(void);
+
+weak_alias(__stack_chk_fail, __stack_chk_fail_local);
diff --git a/system/lib/libc/musl/src/env/putenv.c b/system/lib/libc/musl/src/env/putenv.c
index 4042869..7153042 100644
--- a/system/lib/libc/musl/src/env/putenv.c
+++ b/system/lib/libc/musl/src/env/putenv.c
@@ -30,6 +30,7 @@
 				}
 			} else {
 				free(__env_map[j]);
+				__env_map[j] = s;
 			}
 		}
 	}
diff --git a/system/lib/libc/musl/src/errno/__errno_location.c b/system/lib/libc/musl/src/errno/__errno_location.c
index 3e92d7c..7172a1b 100644
--- a/system/lib/libc/musl/src/errno/__errno_location.c
+++ b/system/lib/libc/musl/src/errno/__errno_location.c
@@ -2,7 +2,5 @@
 
 int *__errno_location(void)
 {
-	static int e;
-	if (libc.main_thread) return __pthread_self()->errno_ptr;
-	return &e;
+	return &__pthread_self()->errno_val;
 }
diff --git a/system/lib/libc/musl/src/errno/strerror.c b/system/lib/libc/musl/src/errno/strerror.c
index b5559cb..24c94d3 100644
--- a/system/lib/libc/musl/src/errno/strerror.c
+++ b/system/lib/libc/musl/src/errno/strerror.c
@@ -1,5 +1,7 @@
 #include <errno.h>
 #include <string.h>
+#include "locale_impl.h"
+#include "libc.h"
 
 #define E(a,b) ((unsigned char)a),
 static const unsigned char errid[] = {
@@ -12,7 +14,7 @@
 #include "__strerror.h"
 ;
 
-char *strerror(int e)
+char *__strerror_l(int e, locale_t loc)
 {
 	const char *s;
 	int i;
@@ -24,5 +26,12 @@
 	}
 	for (i=0; errid[i] && errid[i] != e; i++);
 	for (s=errmsg; i; s++, i--) for (; *s; s++);
-	return (char *)s;
+	return (char *)LCTRANS(s, LC_MESSAGES, loc);
 }
+
+char *strerror(int e)
+{
+	return __strerror_l(e, CURRENT_LOCALE);
+}
+
+weak_alias(__strerror_l, strerror_l);
diff --git a/system/lib/libc/musl/src/exit/abort.c b/system/lib/libc/musl/src/exit/abort.c
index 203dd35..ecc0f73 100644
--- a/system/lib/libc/musl/src/exit/abort.c
+++ b/system/lib/libc/musl/src/exit/abort.c
@@ -1,10 +1,14 @@
 #include <stdlib.h>
 #include <signal.h>
 #include "syscall.h"
+#include "pthread_impl.h"
+#include "atomic.h"
 
 _Noreturn void abort(void)
 {
 	raise(SIGABRT);
+	__block_all_sigs(0);
+	a_crash();
 	raise(SIGKILL);
-	for (;;);
+	_Exit(127);
 }
diff --git a/system/lib/libc/musl/src/exit/at_quick_exit.c b/system/lib/libc/musl/src/exit/at_quick_exit.c
index 85c3d26..34541ba 100644
--- a/system/lib/libc/musl/src/exit/at_quick_exit.c
+++ b/system/lib/libc/musl/src/exit/at_quick_exit.c
@@ -5,7 +5,7 @@
 
 static void (*funcs[COUNT])(void);
 static int count;
-static int lock[2];
+static volatile int lock[2];
 
 void __funcs_on_quick_exit()
 {
diff --git a/system/lib/libc/musl/src/exit/atexit.c b/system/lib/libc/musl/src/exit/atexit.c
index 89ff4ff..2b58b8b 100644
--- a/system/lib/libc/musl/src/exit/atexit.c
+++ b/system/lib/libc/musl/src/exit/atexit.c
@@ -12,18 +12,16 @@
 	void *a[COUNT];
 } builtin, *head;
 
-static int lock[2];
+static int slot;
+static volatile int lock[2];
 
 void __funcs_on_exit()
 {
-	int i;
 	void (*func)(void *), *arg;
 	LOCK(lock);
-	for (; head; head=head->next) for (i=COUNT-1; i>=0; i--) {
-		if (!head->f[i]) continue;
-		func = head->f[i];
-		arg = head->a[i];
-		head->f[i] = 0;
+	for (; head; head=head->next, slot=COUNT) while(slot-->0) {
+		func = head->f[slot];
+		arg = head->a[slot];
 		UNLOCK(lock);
 		func(arg);
 		LOCK(lock);
@@ -36,15 +34,13 @@
 
 int __cxa_atexit(void (*func)(void *), void *arg, void *dso)
 {
-	int i;
-
 	LOCK(lock);
 
 	/* Defer initialization of head so it can be in BSS */
 	if (!head) head = &builtin;
 
 	/* If the current function list is full, add a new one */
-	if (head->f[COUNT-1]) {
+	if (slot==COUNT) {
 		struct fl *new_fl = calloc(sizeof(struct fl), 1);
 		if (!new_fl) {
 			UNLOCK(lock);
@@ -52,12 +48,13 @@
 		}
 		new_fl->next = head;
 		head = new_fl;
+		slot = 0;
 	}
 
 	/* Append function to the list. */
-	for (i=0; i<COUNT && head->f[i]; i++);
-	head->f[i] = func;
-	head->a[i] = arg;
+	head->f[slot] = func;
+	head->a[slot] = arg;
+	slot++;
 
 	UNLOCK(lock);
 	return 0;
diff --git a/system/lib/libc/musl/src/exit/exit.c b/system/lib/libc/musl/src/exit/exit.c
index f8e7668..bf7835a 100644
--- a/system/lib/libc/musl/src/exit/exit.c
+++ b/system/lib/libc/musl/src/exit/exit.c
@@ -1,8 +1,6 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include "libc.h"
-#include "atomic.h"
-#include "syscall.h"
 
 static void dummy()
 {
@@ -12,31 +10,25 @@
  * as a consequence of linking either __toread.c or __towrite.c. */
 weak_alias(dummy, __funcs_on_exit);
 weak_alias(dummy, __stdio_exit);
-
-#ifndef SHARED
 weak_alias(dummy, _fini);
-extern void (*const __fini_array_start)() __attribute__((weak));
-extern void (*const __fini_array_end)() __attribute__((weak));
-#endif
 
-_Noreturn void exit(int code)
+__attribute__((__weak__, __visibility__("hidden")))
+extern void (*const __fini_array_start)(void), (*const __fini_array_end)(void);
+
+static void libc_exit_fini(void)
 {
-	static int lock;
-
-	/* If more than one thread calls exit, hang until _Exit ends it all */
-	while (a_swap(&lock, 1)) __syscall(SYS_pause);
-
-	__funcs_on_exit();
-
-#ifndef SHARED
 	uintptr_t a = (uintptr_t)&__fini_array_end;
 	for (; a>(uintptr_t)&__fini_array_start; a-=sizeof(void(*)()))
 		(*(void (**)())(a-sizeof(void(*)())))();
 	_fini();
-#endif
+}
 
+weak_alias(libc_exit_fini, __libc_exit_fini);
+
+_Noreturn void exit(int code)
+{
+	__funcs_on_exit();
+	__libc_exit_fini();
 	__stdio_exit();
-
 	_Exit(code);
-	for(;;);
 }
diff --git a/system/lib/libc/musl/src/exit/quick_exit.c b/system/lib/libc/musl/src/exit/quick_exit.c
index 1175d80..ada9134 100644
--- a/system/lib/libc/musl/src/exit/quick_exit.c
+++ b/system/lib/libc/musl/src/exit/quick_exit.c
@@ -1,6 +1,4 @@
 #include <stdlib.h>
-#include "syscall.h"
-#include "atomic.h"
 #include "libc.h"
 
 static void dummy() { }
@@ -8,8 +6,6 @@
 
 _Noreturn void quick_exit(int code)
 {
-	static int lock;
-	while (a_swap(&lock, 1)) __syscall(SYS_pause);
 	__funcs_on_quick_exit();
 	_Exit(code);
 }
diff --git a/system/lib/libc/musl/src/fcntl/fcntl.c b/system/lib/libc/musl/src/fcntl/fcntl.c
index 2c4f535..ce615d0 100644
--- a/system/lib/libc/musl/src/fcntl/fcntl.c
+++ b/system/lib/libc/musl/src/fcntl/fcntl.c
@@ -39,7 +39,6 @@
 	}
 	switch (cmd) {
 	case F_SETLK:
-	case F_SETLKW:
 	case F_GETLK:
 	case F_GETOWN_EX:
 	case F_SETOWN_EX:
diff --git a/system/lib/libc/musl/src/fcntl/open.c b/system/lib/libc/musl/src/fcntl/open.c
index be44208..3928a6e 100644
--- a/system/lib/libc/musl/src/fcntl/open.c
+++ b/system/lib/libc/musl/src/fcntl/open.c
@@ -5,12 +5,20 @@
 
 int open(const char *filename, int flags, ...)
 {
-	mode_t mode;
-	va_list ap;
-	va_start(ap, flags);
-	mode = va_arg(ap, mode_t);
-	va_end(ap);
-	return syscall_cp(SYS_open, filename, flags|O_LARGEFILE, mode);
+	mode_t mode = 0;
+
+	if ((flags & O_CREAT) || (flags & O_TMPFILE) == O_TMPFILE) {
+		va_list ap;
+		va_start(ap, flags);
+		mode = va_arg(ap, mode_t);
+		va_end(ap);
+	}
+
+	int fd = __sys_open_cp(filename, flags, mode);
+	if (fd>=0 && (flags & O_CLOEXEC))
+		__syscall(SYS_fcntl, fd, F_SETFD, FD_CLOEXEC);
+
+	return __syscall_ret(fd);
 }
 
 LFS64(open);
diff --git a/system/lib/libc/musl/src/fcntl/posix_fadvise.c b/system/lib/libc/musl/src/fcntl/posix_fadvise.c
index 2aa4d51..c1a0ef5 100644
--- a/system/lib/libc/musl/src/fcntl/posix_fadvise.c
+++ b/system/lib/libc/musl/src/fcntl/posix_fadvise.c
@@ -4,9 +4,12 @@
 
 int posix_fadvise(int fd, off_t base, off_t len, int advice)
 {
-#ifndef __EMSCRIPTEN__
-	return -(__syscall)(SYS_fadvise, fd, __SYSCALL_LL_O(base),
-		__SYSCALL_LL_E(len), advice);
+#if defined(SYSCALL_FADVISE_6_ARG)
+	/* Some archs, at least arm and powerpc, have the syscall
+	 * arguments reordered to avoid needing 7 argument registers
+	 * due to 64-bit argument alignment. */
+	return -__syscall(SYS_fadvise, fd, advice,
+		__SYSCALL_LL_E(base), __SYSCALL_LL_E(len));
 #else
 	return -__syscall(SYS_fadvise, fd, __SYSCALL_LL_O(base),
 		__SYSCALL_LL_E(len), advice);
diff --git a/system/lib/libc/musl/src/fenv/__flt_rounds.c b/system/lib/libc/musl/src/fenv/__flt_rounds.c
new file mode 100644
index 0000000..ec0b368
--- /dev/null
+++ b/system/lib/libc/musl/src/fenv/__flt_rounds.c
@@ -0,0 +1,19 @@
+#include <float.h>
+#include <fenv.h>
+
+int __flt_rounds()
+{
+	switch (fegetround()) {
+#ifdef FE_TOWARDZERO
+	case FE_TOWARDZERO: return 0;
+#endif
+	case FE_TONEAREST: return 1;
+#ifdef FE_UPWARD
+	case FE_UPWARD: return 2;
+#endif
+#ifdef FE_DOWNWARD
+	case FE_DOWNWARD: return 3;
+#endif
+	}
+	return -1;
+}
diff --git a/system/lib/libc/musl/src/internal/atomic.h b/system/lib/libc/musl/src/internal/atomic.h
new file mode 100644
index 0000000..6f37d25
--- /dev/null
+++ b/system/lib/libc/musl/src/internal/atomic.h
@@ -0,0 +1,293 @@
+#ifndef _ATOMIC_H
+#define _ATOMIC_H
+
+#include <stdint.h>
+
+#include "atomic_arch.h"
+
+#ifdef a_ll
+
+#ifndef a_pre_llsc
+#define a_pre_llsc()
+#endif
+
+#ifndef a_post_llsc
+#define a_post_llsc()
+#endif
+
+#ifndef a_cas
+#define a_cas a_cas
+static inline int a_cas(volatile int *p, int t, int s)
+{
+	int old;
+	a_pre_llsc();
+	do old = a_ll(p);
+	while (old==t && !a_sc(p, s));
+	a_post_llsc();
+	return old;
+}
+#endif
+
+#ifndef a_swap
+#define a_swap a_swap
+static inline int a_swap(volatile int *p, int v)
+{
+	int old;
+	a_pre_llsc();
+	do old = a_ll(p);
+	while (!a_sc(p, v));
+	a_post_llsc();
+	return old;
+}
+#endif
+
+#ifndef a_fetch_add
+#define a_fetch_add a_fetch_add
+static inline int a_fetch_add(volatile int *p, int v)
+{
+	int old;
+	a_pre_llsc();
+	do old = a_ll(p);
+	while (!a_sc(p, (unsigned)old + v));
+	a_post_llsc();
+	return old;
+}
+#endif
+
+#ifndef a_fetch_and
+#define a_fetch_and a_fetch_and
+static inline int a_fetch_and(volatile int *p, int v)
+{
+	int old;
+	a_pre_llsc();
+	do old = a_ll(p);
+	while (!a_sc(p, old & v));
+	a_post_llsc();
+	return old;
+}
+#endif
+
+#ifndef a_fetch_or
+#define a_fetch_or a_fetch_or
+static inline int a_fetch_or(volatile int *p, int v)
+{
+	int old;
+	a_pre_llsc();
+	do old = a_ll(p);
+	while (!a_sc(p, old | v));
+	a_post_llsc();
+	return old;
+}
+#endif
+
+#endif
+
+#ifdef a_ll_p
+
+#ifndef a_cas_p
+#define a_cas_p a_cas_p
+static inline void *a_cas_p(volatile void *p, void *t, void *s)
+{
+	void *old;
+	a_pre_llsc();
+	do old = a_ll_p(p);
+	while (old==t && !a_sc_p(p, s));
+	a_post_llsc();
+	return old;
+}
+#endif
+
+#endif
+
+#ifndef a_cas
+#error missing definition of a_cas
+#endif
+
+#ifndef a_swap
+#define a_swap a_swap
+static inline int a_swap(volatile int *p, int v)
+{
+	int old;
+	do old = *p;
+	while (a_cas(p, old, v) != old);
+	return old;
+}
+#endif
+
+#ifndef a_fetch_add
+#define a_fetch_add a_fetch_add
+static inline int a_fetch_add(volatile int *p, int v)
+{
+	int old;
+	do old = *p;
+	while (a_cas(p, old, (unsigned)old+v) != old);
+	return old;
+}
+#endif
+
+#ifndef a_fetch_and
+#define a_fetch_and a_fetch_and
+static inline int a_fetch_and(volatile int *p, int v)
+{
+	int old;
+	do old = *p;
+	while (a_cas(p, old, old&v) != old);
+	return old;
+}
+#endif
+#ifndef a_fetch_or
+#define a_fetch_or a_fetch_or
+static inline int a_fetch_or(volatile int *p, int v)
+{
+	int old;
+	do old = *p;
+	while (a_cas(p, old, old|v) != old);
+	return old;
+}
+#endif
+
+#ifndef a_and
+#define a_and a_and
+static inline void a_and(volatile int *p, int v)
+{
+	a_fetch_and(p, v);
+}
+#endif
+
+#ifndef a_or
+#define a_or a_or
+static inline void a_or(volatile int *p, int v)
+{
+	a_fetch_or(p, v);
+}
+#endif
+
+#ifndef a_inc
+#define a_inc a_inc
+static inline void a_inc(volatile int *p)
+{
+	a_fetch_add(p, 1);
+}
+#endif
+
+#ifndef a_dec
+#define a_dec a_dec
+static inline void a_dec(volatile int *p)
+{
+	a_fetch_add(p, -1);
+}
+#endif
+
+#ifndef a_store
+#define a_store a_store
+static inline void a_store(volatile int *p, int v)
+{
+#ifdef a_barrier
+	a_barrier();
+	*p = v;
+	a_barrier();
+#else
+	a_swap(p, v);
+#endif
+}
+#endif
+
+#ifndef a_barrier
+#define a_barrier a_barrier
+static void a_barrier()
+{
+	volatile int tmp = 0;
+	a_cas(&tmp, 0, 0);
+}
+#endif
+
+#ifndef a_spin
+#define a_spin a_barrier
+#endif
+
+#ifndef a_and_64
+#define a_and_64 a_and_64
+static inline void a_and_64(volatile uint64_t *p, uint64_t v)
+{
+	union { uint64_t v; uint32_t r[2]; } u = { v };
+	if (u.r[0]+1) a_and((int *)p, u.r[0]);
+	if (u.r[1]+1) a_and((int *)p+1, u.r[1]);
+}
+#endif
+
+#ifndef a_or_64
+#define a_or_64 a_or_64
+static inline void a_or_64(volatile uint64_t *p, uint64_t v)
+{
+	union { uint64_t v; uint32_t r[2]; } u = { v };
+	if (u.r[0]) a_or((int *)p, u.r[0]);
+	if (u.r[1]) a_or((int *)p+1, u.r[1]);
+}
+#endif
+
+#ifndef a_cas_p
+typedef char a_cas_p_undefined_but_pointer_not_32bit[-sizeof(char) == 0xffffffff ? 1 : -1];
+#define a_cas_p a_cas_p
+static inline void *a_cas_p(volatile void *p, void *t, void *s)
+{
+	return (void *)a_cas((volatile int *)p, (int)t, (int)s);
+}
+#endif
+
+#ifndef a_or_l
+#define a_or_l a_or_l
+static inline void a_or_l(volatile void *p, long v)
+{
+	if (sizeof(long) == sizeof(int)) a_or(p, v);
+	else a_or_64(p, v);
+}
+#endif
+
+#ifndef a_crash
+#define a_crash a_crash
+static inline void a_crash()
+{
+	*(volatile char *)0=0;
+}
+#endif
+
+#ifndef a_ctz_64
+#define a_ctz_64 a_ctz_64
+static inline int a_ctz_64(uint64_t x)
+{
+	static const char debruijn64[64] = {
+		0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28,
+		62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11,
+		63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10,
+		51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12
+	};
+	static const char debruijn32[32] = {
+		0, 1, 23, 2, 29, 24, 19, 3, 30, 27, 25, 11, 20, 8, 4, 13,
+		31, 22, 28, 18, 26, 10, 7, 12, 21, 17, 9, 6, 16, 5, 15, 14
+	};
+	if (sizeof(long) < 8) {
+		uint32_t y = x;
+		if (!y) {
+			y = x>>32;
+			return 32 + debruijn32[(y&-y)*0x076be629 >> 27];
+		}
+		return debruijn32[(y&-y)*0x076be629 >> 27];
+	}
+	return debruijn64[(x&-x)*0x022fdd63cc95386dull >> 58];
+}
+#endif
+
+#ifndef a_ctz_l
+#define a_ctz_l a_ctz_l
+static inline int a_ctz_l(unsigned long x)
+{
+	static const char debruijn32[32] = {
+		0, 1, 23, 2, 29, 24, 19, 3, 30, 27, 25, 11, 20, 8, 4, 13,
+		31, 22, 28, 18, 26, 10, 7, 12, 21, 17, 9, 6, 16, 5, 15, 14
+	};
+	if (sizeof(long) == 8) return a_ctz_64(x);
+	return debruijn32[(x&-x)*0x076be629 >> 27];
+}
+#endif
+
+#endif
diff --git a/system/lib/libc/musl/src/internal/dynlink.h b/system/lib/libc/musl/src/internal/dynlink.h
new file mode 100644
index 0000000..5717627
--- /dev/null
+++ b/system/lib/libc/musl/src/internal/dynlink.h
@@ -0,0 +1,98 @@
+#ifndef _INTERNAL_RELOC_H
+#define _INTERNAL_RELOC_H
+
+#include <features.h>
+#include <elf.h>
+#include <stdint.h>
+
+#if UINTPTR_MAX == 0xffffffff
+typedef Elf32_Ehdr Ehdr;
+typedef Elf32_Phdr Phdr;
+typedef Elf32_Sym Sym;
+#define R_TYPE(x) ((x)&255)
+#define R_SYM(x) ((x)>>8)
+#define R_INFO ELF32_R_INFO
+#else
+typedef Elf64_Ehdr Ehdr;
+typedef Elf64_Phdr Phdr;
+typedef Elf64_Sym Sym;
+#define R_TYPE(x) ((x)&0x7fffffff)
+#define R_SYM(x) ((x)>>32)
+#define R_INFO ELF64_R_INFO
+#endif
+
+/* These enum constants provide unmatchable default values for
+ * any relocation type the arch does not use. */
+enum {
+	REL_NONE = 0,
+	REL_SYMBOLIC = -100,
+	REL_GOT,
+	REL_PLT,
+	REL_RELATIVE,
+	REL_OFFSET,
+	REL_OFFSET32,
+	REL_COPY,
+	REL_SYM_OR_REL,
+	REL_DTPMOD,
+	REL_DTPOFF,
+	REL_TPOFF,
+	REL_TPOFF_NEG,
+	REL_TLSDESC,
+	REL_FUNCDESC,
+	REL_FUNCDESC_VAL,
+};
+
+struct fdpic_loadseg {
+	uintptr_t addr, p_vaddr, p_memsz;
+};
+
+struct fdpic_loadmap {
+	unsigned short version, nsegs;
+	struct fdpic_loadseg segs[];
+};
+
+struct fdpic_dummy_loadmap {
+	unsigned short version, nsegs;
+	struct fdpic_loadseg segs[1];
+};
+
+#include "reloc.h"
+
+#ifndef FDPIC_CONSTDISP_FLAG
+#define FDPIC_CONSTDISP_FLAG 0
+#endif
+
+#ifndef DL_FDPIC
+#define DL_FDPIC 0
+#endif
+
+#ifndef DL_NOMMU_SUPPORT
+#define DL_NOMMU_SUPPORT 0
+#endif
+
+#if !DL_FDPIC
+#define IS_RELATIVE(x,s) ( \
+	(R_TYPE(x) == REL_RELATIVE) || \
+	(R_TYPE(x) == REL_SYM_OR_REL && !R_SYM(x)) )
+#else
+#define IS_RELATIVE(x,s) ( ( \
+	(R_TYPE(x) == REL_FUNCDESC_VAL) || \
+	(R_TYPE(x) == REL_SYMBOLIC) ) \
+	&& (((s)[R_SYM(x)].st_info & 0xf) == STT_SECTION) )
+#endif
+
+#ifndef NEED_MIPS_GOT_RELOCS
+#define NEED_MIPS_GOT_RELOCS 0
+#endif
+
+#ifndef DT_DEBUG_INDIRECT
+#define DT_DEBUG_INDIRECT 0
+#endif
+
+#define AUX_CNT 32
+#define DYN_CNT 32
+
+typedef void (*stage2_func)(unsigned char *, size_t *);
+typedef _Noreturn void (*stage3_func)(size_t *);
+
+#endif
diff --git a/system/lib/libc/musl/src/internal/fdpic_crt.h b/system/lib/libc/musl/src/internal/fdpic_crt.h
new file mode 100644
index 0000000..7eb50c6
--- /dev/null
+++ b/system/lib/libc/musl/src/internal/fdpic_crt.h
@@ -0,0 +1,28 @@
+#include <stdint.h>
+
+__attribute__((__visibility__("hidden")))
+void *__fdpic_fixup(void *map, uintptr_t *a, uintptr_t *z)
+{
+	/* If map is a null pointer, the program was loaded by a
+	 * non-FDPIC-aware ELF loader, and fixups are not needed,
+	 * but the value for the GOT pointer is. */
+	if (!map) return (void *)z[-1];
+
+	struct {
+		unsigned short version, nsegs;
+		struct fdpic_loadseg {
+			uintptr_t addr, p_vaddr, p_memsz;
+		} segs[];
+	} *lm = map;
+	int nsegs = lm->nsegs, rseg = 0, vseg = 0;
+	for (;;) {
+		while (*a-lm->segs[rseg].p_vaddr >= lm->segs[rseg].p_memsz)
+			if (++rseg == nsegs) rseg = 0;
+		uintptr_t *r = (uintptr_t *)
+			(*a + lm->segs[rseg].addr - lm->segs[rseg].p_vaddr);
+		if (++a == z) return r;
+		while (*r-lm->segs[vseg].p_vaddr >= lm->segs[vseg].p_memsz)
+			if (++vseg == nsegs) vseg = 0;
+		*r += lm->segs[vseg].addr - lm->segs[vseg].p_vaddr;
+	}
+}
diff --git a/system/lib/libc/musl/src/internal/floatscan.c b/system/lib/libc/musl/src/internal/floatscan.c
index f6e331d..eef70df 100644
--- a/system/lib/libc/musl/src/internal/floatscan.c
+++ b/system/lib/libc/musl/src/internal/floatscan.c
@@ -15,12 +15,20 @@
 #define LD_B1B_MAX 9007199, 254740991
 #define KMAX 128
 
-#else /* LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 */
+#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
 
 #define LD_B1B_DIG 3
 #define LD_B1B_MAX 18, 446744073, 709551615
 #define KMAX 2048
 
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+
+#define LD_B1B_DIG 4
+#define LD_B1B_MAX 10384593, 717069655, 257060992, 658440191
+#define KMAX 2048
+
+#else
+#error Unsupported long double representation
 #endif
 
 #define MASK (KMAX-1)
diff --git a/system/lib/libc/musl/src/internal/futex.h b/system/lib/libc/musl/src/internal/futex.h
index d7bf2b7..cf4c795 100644
--- a/system/lib/libc/musl/src/internal/futex.h
+++ b/system/lib/libc/musl/src/internal/futex.h
@@ -12,6 +12,8 @@
 #define FUTEX_TRYLOCK_PI	8
 #define FUTEX_WAIT_BITSET	9
 
+#define FUTEX_PRIVATE 128
+
 #define FUTEX_CLOCK_REALTIME 256
 
 int __futex(volatile int *, int, int, void *);
diff --git a/system/lib/libc/musl/src/internal/ksigaction.h b/system/lib/libc/musl/src/internal/ksigaction.h
index 2eacabf..1d8d964 100644
--- a/system/lib/libc/musl/src/internal/ksigaction.h
+++ b/system/lib/libc/musl/src/internal/ksigaction.h
@@ -7,3 +7,5 @@
 	void (*restorer)(void);
 	unsigned mask[2];
 };
+
+void __restore(), __restore_rt();
diff --git a/system/lib/libc/musl/src/internal/libc.c b/system/lib/libc/musl/src/internal/libc.c
index 942f6b4..8c4139c 100644
--- a/system/lib/libc/musl/src/internal/libc.c
+++ b/system/lib/libc/musl/src/internal/libc.c
@@ -1,18 +1,6 @@
 #include "libc.h"
 
-#ifdef USE_LIBC_ACCESSOR
-struct __libc *__libc_loc()
-{
-	static struct __libc __libc;
-	return &__libc;
-}
-#else
 struct __libc __libc;
-#endif
-
-#ifdef BROKEN_VISIBILITY
-__asm__(".hidden __libc");
-#endif
 
 size_t __hwcap;
 size_t __sysinfo;
@@ -20,3 +8,10 @@
 
 weak_alias(__progname, program_invocation_short_name);
 weak_alias(__progname_full, program_invocation_name);
+
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+struct __libc* EMSCRIPTEN_KEEPALIVE emscripten_get_global_libc() {
+    return &libc;
+}
+#endif
diff --git a/system/lib/libc/musl/src/internal/libc.h b/system/lib/libc/musl/src/internal/libc.h
index d625b56..5e14518 100644
--- a/system/lib/libc/musl/src/internal/libc.h
+++ b/system/lib/libc/musl/src/internal/libc.h
@@ -5,31 +5,35 @@
 #include <stdio.h>
 #include <limits.h>
 
-struct __libc {
-	void *main_thread;
-	int threaded;
-	int secure;
-	size_t *auxv;
-	volatile int threads_minus_1;
-	int canceldisable;
-	FILE *ofl_head;
-	int ofl_lock[2];
-	size_t tls_size;
-	size_t page_size;
+struct __locale_map;
+
+struct __locale_struct {
+	const struct __locale_map *volatile cat[6];
 };
 
-extern size_t __hwcap;
+struct tls_module {
+	struct tls_module *next;
+	void *image;
+	size_t len, size, align, offset;
+};
+
+struct __libc {
+	int can_do_threads;
+	int threaded;
+	int secure;
+	volatile int threads_minus_1;
+	size_t *auxv;
+	struct tls_module *tls_head;
+	size_t tls_size, tls_align, tls_cnt;
+	size_t page_size;
+	struct __locale_struct global_locale;
+};
 
 #ifndef PAGE_SIZE
 #define PAGE_SIZE libc.page_size
 #endif
 
-#if !defined(__PIC__) || (100*__GNUC__+__GNUC_MINOR__ >= 303 && !defined(__PCC__))
-
 #ifdef __PIC__
-#if __GNUC__ < 4
-#define BROKEN_VISIBILITY 1
-#endif
 #define ATTR_LIBC_VISIBILITY __attribute__((visibility("hidden")))
 #else
 #define ATTR_LIBC_VISIBILITY
@@ -38,15 +42,9 @@
 extern struct __libc __libc ATTR_LIBC_VISIBILITY;
 #define libc __libc
 
-#else
-
-#define USE_LIBC_ACCESSOR
-#define ATTR_LIBC_VISIBILITY
-extern struct __libc *__libc_loc(void) __attribute__((const));
-#define libc (*__libc_loc())
-
-#endif
-
+extern size_t __hwcap ATTR_LIBC_VISIBILITY;
+extern size_t __sysinfo ATTR_LIBC_VISIBILITY;
+extern char *__progname, *__progname_full;
 
 /* Designed to avoid any overhead in non-threaded processes */
 void __lock(volatile int *) ATTR_LIBC_VISIBILITY;
diff --git a/system/lib/libc/musl/src/internal/libm.h b/system/lib/libc/musl/src/internal/libm.h
index d9ca4dd..6a3aaed 100644
--- a/system/lib/libc/musl/src/internal/libm.h
+++ b/system/lib/libc/musl/src/internal/libm.h
@@ -42,6 +42,20 @@
 		uint64_t hi;
 	} i2;
 };
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN
+union ldshape {
+	long double f;
+	struct {
+		uint16_t se;
+		uint16_t top;
+		uint32_t mid;
+		uint64_t lo;
+	} i;
+	struct {
+		uint64_t hi;
+		uint64_t lo;
+	} i2;
+};
 #else
 #error Unsupported long double representation
 #endif
@@ -136,6 +150,18 @@
   (d) = __u.f;                                    \
 } while (0)
 
+#undef __CMPLX
+#undef CMPLX
+#undef CMPLXF
+#undef CMPLXL
+
+#define __CMPLX(x, y, t) \
+	((union { _Complex t __z; t __xy[2]; }){.__xy = {(x),(y)}}.__z)
+
+#define CMPLX(x, y) __CMPLX(x, y, double)
+#define CMPLXF(x, y) __CMPLX(x, y, float)
+#define CMPLXL(x, y) __CMPLX(x, y, long double)
+
 /* fdlibm kernel functions */
 
 int    __rem_pio2_large(double*,double*,int,int,int);
diff --git a/system/lib/libc/musl/src/internal/locale_impl.h b/system/lib/libc/musl/src/internal/locale_impl.h
index f41c6f2..f5e4d9b 100644
--- a/system/lib/libc/musl/src/internal/locale_impl.h
+++ b/system/lib/libc/musl/src/internal/locale_impl.h
@@ -1,5 +1,40 @@
-#include <locale.h>
+#ifndef _LOCALE_IMPL_H
+#define _LOCALE_IMPL_H
 
-struct __locale_struct {
-	int dummy;
+#include <locale.h>
+#include <stdlib.h>
+#include "libc.h"
+#include "pthread_impl.h"
+
+#define LOCALE_NAME_MAX 15
+
+struct __locale_map {
+	const void *map;
+	size_t map_size;
+	char name[LOCALE_NAME_MAX+1];
+	const struct __locale_map *next;
 };
+
+extern const struct __locale_map __c_dot_utf8;
+extern const struct __locale_struct __c_locale;
+extern const struct __locale_struct __c_dot_utf8_locale;
+
+const struct __locale_map *__get_locale(int, const char *);
+const char *__mo_lookup(const void *, size_t, const char *);
+const char *__lctrans(const char *, const struct __locale_map *);
+const char *__lctrans_cur(const char *);
+
+#define LCTRANS(msg, lc, loc) __lctrans(msg, (loc)->cat[(lc)])
+#define LCTRANS_CUR(msg) __lctrans_cur(msg)
+
+#define C_LOCALE ((locale_t)&__c_locale)
+#define UTF8_LOCALE ((locale_t)&__c_dot_utf8_locale)
+
+#define CURRENT_LOCALE (__pthread_self()->locale)
+
+#define CURRENT_UTF8 (!!__pthread_self()->locale->cat[LC_CTYPE])
+
+#undef MB_CUR_MAX
+#define MB_CUR_MAX (CURRENT_UTF8 ? 4 : 1)
+
+#endif
diff --git a/system/lib/libc/musl/src/internal/pthread_impl.h b/system/lib/libc/musl/src/internal/pthread_impl.h
index c516958..059206b 100644
--- a/system/lib/libc/musl/src/internal/pthread_impl.h
+++ b/system/lib/libc/musl/src/internal/pthread_impl.h
@@ -10,9 +10,8 @@
 #include "atomic.h"
 #ifdef __EMSCRIPTEN__
 #include <emscripten/threading.h>
-#else
-#include "futex.h"
 #endif
+#include "futex.h"
 
 #define pthread __pthread
 
@@ -30,9 +29,9 @@
 	struct pthread *self;
 	void **dtv, *unused1, *unused2;
 	uintptr_t sysinfo;
-	uintptr_t canary;
+	uintptr_t canary, canary2;
 	pid_t tid, pid;
-	int tsd_used, errno_val, *errno_ptr;
+	int tsd_used, errno_val;
 	volatile int cancel, canceldisable, cancelasync;
 	int detached;
 	unsigned char *map_base;
@@ -47,17 +46,22 @@
 	pthread_attr_t attr;
 	volatile int dead;
 	struct {
-		void **head;
+		volatile void *volatile head;
 		long off;
-		void *pending;
+		volatile void *volatile pending;
 	} robust_list;
 	int unblock_cancel;
-	int timer_id;
+	volatile int timer_id;
 	locale_t locale;
-	int killlock[2];
-	int exitlock[2];
-	int startlock[2];
+	volatile int killlock[2];
+	volatile int exitlock[2];
+	volatile int startlock[2];
 	unsigned long sigmask[_NSIG/8/sizeof(long)];
+	char *dlerror_buf;
+	int dlerror_flag;
+	void *stdio_locks;
+	uintptr_t canary_at_end;
+	void **dtv_copy;
 };
 
 struct __timer {
@@ -75,39 +79,47 @@
 #define _a_policy __u.__i[3*__SU+2]
 #define _a_prio __u.__i[3*__SU+3]
 #define _m_type __u.__i[0]
-#define _m_lock __u.__i[1]
-#define _m_waiters __u.__i[2]
+#define _m_lock __u.__vi[1]
+#define _m_waiters __u.__vi[2]
 #define _m_prev __u.__p[3]
 #define _m_next __u.__p[4]
 #define _m_count __u.__i[5]
 #ifdef __EMSCRIPTEN__
-#define _m_addr __u.__i[6]
+#define _m_addr __u.__i[7]
 #endif
-#define _c_mutex __u.__p[0]
-#define _c_seq __u.__i[2]
-#define _c_waiters __u.__i[3]
+#define _c_shared __u.__p[0]
+#define _c_seq __u.__vi[2]
+#define _c_waiters __u.__vi[3]
 #define _c_clock __u.__i[4]
-#define _c_lock __u.__i[5]
-#define _c_lockwait __u.__i[6]
-#define _c_waiters2 __u.__i[7]
-#define _c_destroy __u.__i[8]
-#define _rw_lock __u.__i[0]
-#define _rw_waiters __u.__i[1]
+#define _c_lock __u.__vi[8]
+#define _c_head __u.__p[1]
+#define _c_tail __u.__p[5]
+#define _rw_lock __u.__vi[0]
+#define _rw_waiters __u.__vi[1]
+#define _rw_shared __u.__i[2]
 #ifdef __EMSCRIPTEN__
 // XXX Emscripten: The spec allows detecting when multiple write locks would deadlock, so use an extra field
 // _rw_wr_owner to record which thread owns the write lock in order to avoid hangs.
 // Points to the pthread that currently has the write lock.
-#define _rw_wr_owner __u.__i[2]
+#define _rw_wr_owner __u.__vi[3]
 #endif
-#define _b_lock __u.__i[0]
-#define _b_waiters __u.__i[1]
+#define _b_lock __u.__vi[0]
+#define _b_waiters __u.__vi[1]
 #define _b_limit __u.__i[2]
-#define _b_count __u.__i[3]
-#define _b_waiters2 __u.__i[4]
+#define _b_count __u.__vi[3]
+#define _b_waiters2 __u.__vi[4]
 #define _b_inst __u.__p[3]
 
 #include "pthread_arch.h"
 
+#ifndef CANARY
+#define CANARY canary
+#endif
+
+#ifndef DTP_OFFSET
+#define DTP_OFFSET 0
+#endif
+
 #define SIGTIMER 32
 #define SIGCANCEL 33
 #define SIGSYNCCALL 34
@@ -129,19 +141,28 @@
 void __lock(volatile int *);
 void __unmapself(void *, size_t);
 
-int __timedwait(volatile int *, int, clockid_t, const struct timespec *, void (*)(void *), void *, int);
+void __vm_wait(void);
+void __vm_lock(void);
+void __vm_unlock(void);
+
+int __timedwait(volatile int *, int, clockid_t, const struct timespec *, int);
+int __timedwait_cp(volatile int *, int, clockid_t, const struct timespec *, int);
 void __wait(volatile int *, volatile int *, int, int);
-
+static inline void __wake(volatile void *addr, int cnt, int priv)
+{
+	if (priv) priv = 128;
+	if (cnt<0) cnt = INT_MAX;
 #ifdef __EMSCRIPTEN__
-#define __wake(addr, cnt, priv) emscripten_futex_wake((void*)addr, (cnt)<0?INT_MAX:(cnt))
+	emscripten_futex_wake(addr, (cnt)<0?INT_MAX:(cnt));
 #else
-#define __wake(addr, cnt, priv) \
-	__syscall(SYS_futex, addr, FUTEX_WAKE, (cnt)<0?INT_MAX:(cnt))
+	__syscall(SYS_futex, addr, FUTEX_WAKE|priv, cnt) != -ENOSYS ||
+	__syscall(SYS_futex, addr, FUTEX_WAKE, cnt);
 #endif
+}
 
-void __acquire_ptc();
-void __release_ptc();
-void __inhibit_ptc();
+void __acquire_ptc(void);
+void __release_ptc(void);
+void __inhibit_ptc(void);
 
 void __block_all_sigs(void *);
 void __block_app_sigs(void *);
@@ -150,4 +171,12 @@
 #define DEFAULT_STACK_SIZE 81920
 #define DEFAULT_GUARD_SIZE PAGE_SIZE
 
+#define __ATTRP_C11_THREAD ((void*)(uintptr_t)-1)
+
+#ifdef __EMSCRIPTEN__
+void __emscripten_init_pthread(pthread_t thread);
+#if !__EMSCRIPTEN_PTHREADS__
+pthread_t __emscripten_pthread_stub(void);
+#endif
+#endif
 #endif
diff --git a/system/lib/libc/musl/src/internal/stdio_impl.h b/system/lib/libc/musl/src/internal/stdio_impl.h
index 79be9fd..7cdf729 100644
--- a/system/lib/libc/musl/src/internal/stdio_impl.h
+++ b/system/lib/libc/musl/src/internal/stdio_impl.h
@@ -38,14 +38,16 @@
 	short dummy3;
 	signed char mode;
 	signed char lbf;
-	int lock;
-	int waiters;
+	volatile int lock;
+	volatile int waiters;
 	void *cookie;
 	off_t off;
 	char *getln_buf;
 	void *mustbezero_2;
 	unsigned char *shend;
 	off_t shlim, shcnt;
+	FILE *prev_locked, *next_locked;
+	struct __locale_struct *locale;
 };
 
 size_t __stdio_read(FILE *, unsigned char *, size_t);
@@ -74,8 +76,9 @@
 FILE *__fdopen(int, const char *);
 int __fmodeflags(const char *);
 
-#define OFLLOCK() LOCK(libc.ofl_lock)
-#define OFLUNLOCK() UNLOCK(libc.ofl_lock)
+FILE *__ofl_add(FILE *f);
+FILE **__ofl_lock(void);
+void __ofl_unlock(void);
 
 #define feof(f) ((f)->flags & F_EOF)
 #define ferror(f) ((f)->flags & F_ERR)
@@ -83,7 +86,8 @@
 #define getc_unlocked(f) \
 	( ((f)->rpos < (f)->rend) ? *(f)->rpos++ : __uflow((f)) )
 
-#define putc_unlocked(c, f) ( ((c)!=(f)->lbf && (f)->wpos<(f)->wend) \
+#define putc_unlocked(c, f) \
+	( ((unsigned char)(c)!=(f)->lbf && (f)->wpos<(f)->wend) \
 	? *(f)->wpos++ = (c) : __overflow((f),(c)) )
 
 /* Caller-allocated FILE * operations */
diff --git a/system/lib/libc/musl/src/internal/syscall.h b/system/lib/libc/musl/src/internal/syscall.h
index 6f16a06..4ff4ae0 100644
--- a/system/lib/libc/musl/src/internal/syscall.h
+++ b/system/lib/libc/musl/src/internal/syscall.h
@@ -8,26 +8,49 @@
 #define SYSCALL_RLIM_INFINITY (~0ULL)
 #endif
 
+#ifndef SYSCALL_MMAP2_UNIT
+#define SYSCALL_MMAP2_UNIT 4096ULL
+#endif
+
 #ifndef __scc
 #define __scc(X) ((long) (X))
 typedef long syscall_arg_t;
 #endif
 
-#if defined(__PIC__) && (100*__GNUC__+__GNUC_MINOR__ >= 303)
 __attribute__((visibility("hidden")))
-#endif
 long __syscall_ret(unsigned long), __syscall(syscall_arg_t, ...),
 	__syscall_cp(syscall_arg_t, syscall_arg_t, syscall_arg_t, syscall_arg_t,
 	             syscall_arg_t, syscall_arg_t, syscall_arg_t);
 
 #ifndef __EMSCRIPTEN__
+#ifdef SYSCALL_NO_INLINE
+#define __syscall0(n) (__syscall)(n)
+#define __syscall1(n,a) (__syscall)(n,__scc(a))
+#define __syscall2(n,a,b) (__syscall)(n,__scc(a),__scc(b))
+#define __syscall3(n,a,b,c) (__syscall)(n,__scc(a),__scc(b),__scc(c))
+#define __syscall4(n,a,b,c,d) (__syscall)(n,__scc(a),__scc(b),__scc(c),__scc(d))
+#define __syscall5(n,a,b,c,d,e) (__syscall)(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e))
+#define __syscall6(n,a,b,c,d,e,f) (__syscall)(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),__scc(f))
+#else
 #define __syscall1(n,a) __syscall1(n,__scc(a))
 #define __syscall2(n,a,b) __syscall2(n,__scc(a),__scc(b))
 #define __syscall3(n,a,b,c) __syscall3(n,__scc(a),__scc(b),__scc(c))
 #define __syscall4(n,a,b,c,d) __syscall4(n,__scc(a),__scc(b),__scc(c),__scc(d))
 #define __syscall5(n,a,b,c,d,e) __syscall5(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e))
 #define __syscall6(n,a,b,c,d,e,f) __syscall6(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),__scc(f))
+#endif
 #define __syscall7(n,a,b,c,d,e,f,g) (__syscall)(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),__scc(f),__scc(g))
+#else // __EMSCRIPTEN__
+#define __syscall_emscripten(n, ...) __syscall##n(n, ##__VA_ARGS__)
+#define __syscall_emscripten0(n) __syscall_emscripten(n)
+#define __syscall_emscripten1(n,a) __syscall_emscripten(n,__scc(a))
+#define __syscall_emscripten2(n,a,b) __syscall_emscripten(n,__scc(a),__scc(b))
+#define __syscall_emscripten3(n,a,b,c) __syscall_emscripten(n,__scc(a),__scc(b),__scc(c))
+#define __syscall_emscripten4(n,a,b,c,d) __syscall_emscripten(n,__scc(a),__scc(b),__scc(c),__scc(d))
+#define __syscall_emscripten5(n,a,b,c,d,e) __syscall_emscripten(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e))
+#define __syscall_emscripten6(n,a,b,c,d,e,f) __syscall_emscripten(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),__scc(f))
+#define __syscall_emscripten7(n,a,b,c,d,e,f,g) __syscall_emscripten(n,__scc(a),__scc(b),__scc(c),__scc(d),__scc(e),__scc(f),__scc(g))
+#endif // __EMSCRIPTEN__
 
 #define __SYSCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n
 #define __SYSCALL_NARGS(...) __SYSCALL_NARGS_X(__VA_ARGS__,7,6,5,4,3,2,1,0,)
@@ -35,11 +58,11 @@
 #define __SYSCALL_CONCAT(a,b) __SYSCALL_CONCAT_X(a,b)
 #define __SYSCALL_DISP(b,...) __SYSCALL_CONCAT(b,__SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
 
+#ifndef __EMSCRIPTEN__
 #define __syscall(...) __SYSCALL_DISP(__syscall,__VA_ARGS__)
-#else // __EMSCRIPTEN__
-#define __syscall_emscripten(n, ...) __syscall##n(n, ##__VA_ARGS__)
-#define __syscall(n, ...) __syscall_emscripten(n, ##__VA_ARGS__)
-#endif // __EMSCRIPTEN__
+#else
+#define __syscall(...) __SYSCALL_DISP(__syscall_emscripten,__VA_ARGS__)
+#endif
 
 #define syscall(...) __syscall_ret(__syscall(__VA_ARGS__))
 
@@ -58,11 +81,12 @@
 #define __syscall_cp(...) __SYSCALL_DISP(__syscall_cp,__VA_ARGS__)
 #else // __EMSCRIPTEN__
 #define __syscall_cp(...) __syscall(__VA_ARGS__)
+#define SYSCALL_USE_SOCKETCALL
 #endif // __EMSCRIPTEN__
 
 #define syscall_cp(...) __syscall_ret(__syscall_cp(__VA_ARGS__))
 
-#ifdef SYS_socket
+#ifndef SYSCALL_USE_SOCKETCALL
 #define __socketcall(nm,a,b,c,d,e,f) syscall(SYS_##nm, a, b, c, d, e, f)
 #define __socketcall_cp(nm,a,b,c,d,e,f) syscall_cp(SYS_##nm, a, b, c, d, e, f)
 #else
@@ -137,14 +161,26 @@
 
 #ifdef SYS_stat64
 #undef SYS_stat
-#undef SYS_fstat
-#undef SYS_lstat
-#undef SYS_statfs
-#undef SYS_fstatfs
 #define SYS_stat SYS_stat64
+#endif
+
+#ifdef SYS_fstat64
+#undef SYS_fstat
 #define SYS_fstat SYS_fstat64
+#endif
+
+#ifdef SYS_lstat64
+#undef SYS_lstat
 #define SYS_lstat SYS_lstat64
+#endif
+
+#ifdef SYS_statfs64
+#undef SYS_statfs
 #define SYS_statfs SYS_statfs64
+#endif
+
+#ifdef SYS_fstatfs64
+#undef SYS_fstatfs
 #define SYS_fstatfs SYS_fstatfs64
 #endif
 
@@ -176,6 +212,9 @@
 #ifdef SYS_fadvise64_64
 #undef SYS_fadvise
 #define SYS_fadvise SYS_fadvise64_64
+#elif defined(SYS_fadvise64)
+#undef SYS_fadvise
+#define SYS_fadvise SYS_fadvise64
 #endif
 
 #ifdef SYS_sendfile64
@@ -183,4 +222,51 @@
 #define SYS_sendfile SYS_sendfile64
 #endif
 
+/* socketcall calls */
+
+#define __SC_socket      1
+#define __SC_bind        2
+#define __SC_connect     3
+#define __SC_listen      4
+#define __SC_accept      5
+#define __SC_getsockname 6
+#define __SC_getpeername 7
+#define __SC_socketpair  8
+#define __SC_send        9
+#define __SC_recv        10
+#define __SC_sendto      11
+#define __SC_recvfrom    12
+#define __SC_shutdown    13
+#define __SC_setsockopt  14
+#define __SC_getsockopt  15
+#define __SC_sendmsg     16
+#define __SC_recvmsg     17
+#define __SC_accept4     18
+#define __SC_recvmmsg    19
+#define __SC_sendmmsg    20
+
+#ifndef __EMSCRIPTEN__
+#ifdef SYS_open
+#define __sys_open2(x,pn,fl) __syscall2(SYS_open, pn, (fl)|O_LARGEFILE)
+#define __sys_open3(x,pn,fl,mo) __syscall3(SYS_open, pn, (fl)|O_LARGEFILE, mo)
+#define __sys_open_cp2(x,pn,fl) __syscall_cp2(SYS_open, pn, (fl)|O_LARGEFILE)
+#define __sys_open_cp3(x,pn,fl,mo) __syscall_cp3(SYS_open, pn, (fl)|O_LARGEFILE, mo)
+#else
+#define __sys_open2(x,pn,fl) __syscall3(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE)
+#define __sys_open3(x,pn,fl,mo) __syscall4(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE, mo)
+#define __sys_open_cp2(x,pn,fl) __syscall_cp3(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE)
+#define __sys_open_cp3(x,pn,fl,mo) __syscall_cp4(SYS_openat, AT_FDCWD, pn, (fl)|O_LARGEFILE, mo)
+#endif
+#else // __EMSCRIPTEN__
+#define __sys_open2(x,pn,fl) __SYSCALL_CONCAT(__syscall, SYS_open)(SYS_open, __scc(pn), __scc((fl)|O_LARGEFILE))
+#define __sys_open3(x,pn,fl,mo) __SYSCALL_CONCAT(__syscall, SYS_open)(SYS_open, __scc(pn), __scc((fl)|O_LARGEFILE), __scc(mo))
+#define __sys_open_cp2(x,pn,fl) __SYSCALL_CONCAT(__syscall, SYS_open)(SYS_open, __scc(pn), __scc((fl)|O_LARGEFILE))
+#define __sys_open_cp3(x,pn,fl,mo) __SYSCALL_CONCAT(__syscall, SYS_open)(SYS_open, __scc(pn), __scc((fl)|O_LARGEFILE), __scc(mo))
+#endif
+#define __sys_open(...) __SYSCALL_DISP(__sys_open,,__VA_ARGS__)
+#define sys_open(...) __syscall_ret(__sys_open(__VA_ARGS__))
+
+#define __sys_open_cp(...) __SYSCALL_DISP(__sys_open_cp,,__VA_ARGS__)
+#define sys_open_cp(...) __syscall_ret(__sys_open_cp(__VA_ARGS__))
+
 #endif
diff --git a/system/lib/libc/musl/src/internal/vdso.c b/system/lib/libc/musl/src/internal/vdso.c
new file mode 100644
index 0000000..6ae0212
--- /dev/null
+++ b/system/lib/libc/musl/src/internal/vdso.c
@@ -0,0 +1,91 @@
+#include <elf.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include "libc.h"
+#include "syscall.h"
+
+#ifdef VDSO_USEFUL
+
+#if ULONG_MAX == 0xffffffff
+typedef Elf32_Ehdr Ehdr;
+typedef Elf32_Phdr Phdr;
+typedef Elf32_Sym Sym;
+typedef Elf32_Verdef Verdef;
+typedef Elf32_Verdaux Verdaux;
+#else
+typedef Elf64_Ehdr Ehdr;
+typedef Elf64_Phdr Phdr;
+typedef Elf64_Sym Sym;
+typedef Elf64_Verdef Verdef;
+typedef Elf64_Verdaux Verdaux;
+#endif
+
+static int checkver(Verdef *def, int vsym, const char *vername, char *strings)
+{
+	vsym &= 0x7fff;
+	for (;;) {
+		if (!(def->vd_flags & VER_FLG_BASE)
+		  && (def->vd_ndx & 0x7fff) == vsym)
+			break;
+		if (def->vd_next == 0)
+			return 0;
+		def = (Verdef *)((char *)def + def->vd_next);
+	}
+	Verdaux *aux = (Verdaux *)((char *)def + def->vd_aux);
+	return !strcmp(vername, strings + aux->vda_name);
+}
+
+#define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON)
+#define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE)
+
+void *__vdsosym(const char *vername, const char *name)
+{
+	size_t i;
+	for (i=0; libc.auxv[i] != AT_SYSINFO_EHDR; i+=2)
+		if (!libc.auxv[i]) return 0;
+	Ehdr *eh = (void *)libc.auxv[i+1];
+	Phdr *ph = (void *)((char *)eh + eh->e_phoff);
+	size_t *dynv=0, base=-1;
+	for (i=0; i<eh->e_phnum; i++, ph=(void *)((char *)ph+eh->e_phentsize)) {
+		if (ph->p_type == PT_LOAD)
+			base = (size_t)eh + ph->p_offset - ph->p_vaddr;
+		else if (ph->p_type == PT_DYNAMIC)
+			dynv = (void *)((char *)eh + ph->p_offset);
+	}
+	if (!dynv || base==(size_t)-1) return 0;
+
+	char *strings = 0;
+	Sym *syms = 0;
+	uint32_t *hashtab = 0;
+	uint16_t *versym = 0;
+	Verdef *verdef = 0;
+	
+	for (i=0; dynv[i]; i+=2) {
+		void *p = (void *)(base + dynv[i+1]);
+		switch(dynv[i]) {
+		case DT_STRTAB: strings = p; break;
+		case DT_SYMTAB: syms = p; break;
+		case DT_HASH: hashtab = p; break;
+		case DT_VERSYM: versym = p; break;
+		case DT_VERDEF: verdef = p; break;
+		}
+	}	
+
+	if (!strings || !syms || !hashtab) return 0;
+	if (!verdef) versym = 0;
+
+	for (i=0; i<hashtab[1]; i++) {
+		if (!(1<<(syms[i].st_info&0xf) & OK_TYPES)) continue;
+		if (!(1<<(syms[i].st_info>>4) & OK_BINDS)) continue;
+		if (!syms[i].st_shndx) continue;
+		if (strcmp(name, strings+syms[i].st_name)) continue;
+		if (versym && !checkver(verdef, versym[i], vername, strings))
+			continue;
+		return (void *)(base + syms[i].st_value);
+	}
+
+	return 0;
+}
+
+#endif
diff --git a/system/lib/libc/musl/src/internal/version.c b/system/lib/libc/musl/src/internal/version.c
index 16554ba..dc044ec 100644
--- a/system/lib/libc/musl/src/internal/version.c
+++ b/system/lib/libc/musl/src/internal/version.c
@@ -1,12 +1,9 @@
-#ifdef SHARED
-
 #include "version.h"
 
 static const char version[] = VERSION;
 
+__attribute__((__visibility__("hidden")))
 const char *__libc_get_version()
 {
 	return version;
 }
-
-#endif
diff --git a/system/lib/libc/musl/src/internal/version.h b/system/lib/libc/musl/src/internal/version.h
new file mode 100644
index 0000000..adb0653
--- /dev/null
+++ b/system/lib/libc/musl/src/internal/version.h
@@ -0,0 +1 @@
+#define VERSION "1.1.15"
diff --git a/system/lib/libc/musl/src/internal/vis.h b/system/lib/libc/musl/src/internal/vis.h
new file mode 100644
index 0000000..35855fc
--- /dev/null
+++ b/system/lib/libc/musl/src/internal/vis.h
@@ -0,0 +1,27 @@
+/* This file is only used if enabled in the build system, in which case it is
+ * included automatically via command line options. It is not included
+ * explicitly by any source files or other headers. Its purpose is to
+ * override default visibilities to reduce the size and performance costs
+ * of position-independent code. */
+
+#if !defined(CRT) && !defined(__ASSEMBLER__)
+
+/* Conceptually, all symbols should be protected, but some toolchains
+ * fail to support copy relocations for protected data, so exclude all
+ * exported data symbols. */
+
+__attribute__((__visibility__("default")))
+extern struct _IO_FILE *const stdin, *const stdout, *const stderr;
+
+__attribute__((__visibility__("default")))
+extern int optind, opterr, optopt, optreset, __optreset, getdate_err, h_errno, daylight, __daylight, signgam, __signgam;
+
+__attribute__((__visibility__("default")))
+extern long timezone, __timezone;
+
+__attribute__((__visibility__("default")))
+extern char *optarg, **environ, **__environ, *tzname[2], *__tzname[2], *__progname, *__progname_full;
+
+#pragma GCC visibility push(protected)
+
+#endif
diff --git a/system/lib/libc/musl/src/ldso/__dlsym.c b/system/lib/libc/musl/src/ldso/__dlsym.c
new file mode 100644
index 0000000..99aafdf
--- /dev/null
+++ b/system/lib/libc/musl/src/ldso/__dlsym.c
@@ -0,0 +1,13 @@
+#include <dlfcn.h>
+#include "libc.h"
+
+__attribute__((__visibility__("hidden")))
+void __dl_seterr(const char *, ...);
+
+static void *stub_dlsym(void *restrict p, const char *restrict s, void *restrict ra)
+{
+	__dl_seterr("Symbol not found: %s", s);
+	return 0;
+}
+
+weak_alias(stub_dlsym, __dlsym);
diff --git a/system/lib/libc/musl/src/ldso/dl_iterate_phdr.c b/system/lib/libc/musl/src/ldso/dl_iterate_phdr.c
index 49b321a..c141fd9 100644
--- a/system/lib/libc/musl/src/ldso/dl_iterate_phdr.c
+++ b/system/lib/libc/musl/src/ldso/dl_iterate_phdr.c
@@ -1,12 +1,10 @@
-#ifndef SHARED
-
 #include <elf.h>
 #include <link.h>
 #include "libc.h"
 
 #define AUX_CNT 38
 
-int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void *data), void *data)
+static int static_dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void *data), void *data)
 {
 	unsigned char *p;
 	ElfW(Phdr) *phdr, *tls_phdr=0;
@@ -40,4 +38,5 @@
 	}
 	return (callback)(&info, sizeof (info), data);
 }
-#endif
+
+weak_alias(static_dl_iterate_phdr, dl_iterate_phdr);
diff --git a/system/lib/libc/musl/src/ldso/dladdr.c b/system/lib/libc/musl/src/ldso/dladdr.c
index 7ca718f..659ab91 100644
--- a/system/lib/libc/musl/src/ldso/dladdr.c
+++ b/system/lib/libc/musl/src/ldso/dladdr.c
@@ -1,9 +1,10 @@
 #define _GNU_SOURCE
 #include <dlfcn.h>
+#include "libc.h"
 
-int __dladdr(const void *, Dl_info *);
-
-int dladdr(const void *addr, Dl_info *info)
+static int stub_dladdr(const void *addr, Dl_info *info)
 {
-	return __dladdr(addr, info);
+	return 0;
 }
+
+weak_alias(stub_dladdr, dladdr);
diff --git a/system/lib/libc/musl/src/ldso/dlclose.c b/system/lib/libc/musl/src/ldso/dlclose.c
new file mode 100644
index 0000000..0ef2231
--- /dev/null
+++ b/system/lib/libc/musl/src/ldso/dlclose.c
@@ -0,0 +1,9 @@
+#include <dlfcn.h>
+
+__attribute__((__visibility__("hidden")))
+int __dl_invalid_handle(void *);
+
+int dlclose(void *p)
+{
+	return __dl_invalid_handle(p);
+}
diff --git a/system/lib/libc/musl/src/ldso/dlerror.c b/system/lib/libc/musl/src/ldso/dlerror.c
new file mode 100644
index 0000000..378f035
--- /dev/null
+++ b/system/lib/libc/musl/src/ldso/dlerror.c
@@ -0,0 +1,64 @@
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "pthread_impl.h"
+#include "libc.h"
+
+char *dlerror()
+{
+	pthread_t self = __pthread_self();
+	if (!self->dlerror_flag) return 0;
+	self->dlerror_flag = 0;
+	char *s = self->dlerror_buf;
+	if (s == (void *)-1)
+		return "Dynamic linker failed to allocate memory for error message";
+	else
+		return s;
+}
+
+void __dl_thread_cleanup(void)
+{
+	pthread_t self = __pthread_self();
+	if (self->dlerror_buf != (void *)-1)
+		free(self->dlerror_buf);
+}
+
+__attribute__((__visibility__("hidden")))
+void __dl_vseterr(const char *fmt, va_list ap)
+{
+	va_list ap2;
+	va_copy(ap2, ap);
+	pthread_t self = __pthread_self();
+	if (self->dlerror_buf != (void *)-1)
+		free(self->dlerror_buf);
+	size_t len = vsnprintf(0, 0, fmt, ap2);
+	va_end(ap2);
+	char *buf = malloc(len+1);
+	if (buf) {
+		vsnprintf(buf, len+1, fmt, ap);
+	} else {
+		buf = (void *)-1;	
+	}
+	self->dlerror_buf = buf;
+	self->dlerror_flag = 1;
+}
+
+__attribute__((__visibility__("hidden")))
+void __dl_seterr(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	__dl_vseterr(fmt, ap);
+	va_end(ap);
+}
+
+__attribute__((__visibility__("hidden")))
+int __dl_invalid_handle(void *);
+
+static int stub_invalid_handle(void *h)
+{
+	__dl_seterr("Invalid library handle %p", (void *)h);
+	return 1;
+}
+
+weak_alias(stub_invalid_handle, __dl_invalid_handle);
diff --git a/system/lib/libc/musl/src/ldso/dlinfo.c b/system/lib/libc/musl/src/ldso/dlinfo.c
index 63d276d..a173d1a 100644
--- a/system/lib/libc/musl/src/ldso/dlinfo.c
+++ b/system/lib/libc/musl/src/ldso/dlinfo.c
@@ -1,9 +1,19 @@
 #define _GNU_SOURCE
 #include <dlfcn.h>
 
-int __dlinfo(void *, int, void *);
+__attribute__((__visibility__("hidden")))
+int __dl_invalid_handle(void *);
+
+__attribute__((__visibility__("hidden")))
+void __dl_seterr(const char *, ...);
 
 int dlinfo(void *dso, int req, void *res)
 {
-	return __dlinfo(dso, req, res);
+	if (__dl_invalid_handle(dso)) return -1;
+	if (req != RTLD_DI_LINKMAP) {
+		__dl_seterr("Unsupported request %d", req);
+		return -1;
+	}
+	*(struct link_map **)res = dso;
+	return 0;
 }
diff --git a/system/lib/libc/musl/src/ldso/dlopen.c b/system/lib/libc/musl/src/ldso/dlopen.c
new file mode 100644
index 0000000..dcdb439
--- /dev/null
+++ b/system/lib/libc/musl/src/ldso/dlopen.c
@@ -0,0 +1,13 @@
+#include <dlfcn.h>
+#include "libc.h"
+
+__attribute__((__visibility__("hidden")))
+void __dl_seterr(const char *, ...);
+
+static void *stub_dlopen(const char *file, int mode)
+{
+	__dl_seterr("Dynamic loading not supported");
+	return 0;
+}
+
+weak_alias(stub_dlopen, dlopen);
diff --git a/system/lib/libc/musl/src/ldso/tlsdesc.c b/system/lib/libc/musl/src/ldso/tlsdesc.c
new file mode 100644
index 0000000..a2985cb
--- /dev/null
+++ b/system/lib/libc/musl/src/ldso/tlsdesc.c
@@ -0,0 +1,12 @@
+#include <stddef.h>
+#include "libc.h"
+
+__attribute__((__visibility__("hidden")))
+ptrdiff_t __tlsdesc_static(), __tlsdesc_dynamic();
+
+ptrdiff_t __tlsdesc_static()
+{
+	return 0;
+}
+
+weak_alias(__tlsdesc_static, __tlsdesc_dynamic);
diff --git a/system/lib/libc/musl/src/legacy/futimes.c b/system/lib/libc/musl/src/legacy/futimes.c
index d81d83a..1c19eb1 100644
--- a/system/lib/libc/musl/src/legacy/futimes.c
+++ b/system/lib/libc/musl/src/legacy/futimes.c
@@ -5,6 +5,7 @@
 int futimes(int fd, const struct timeval tv[2])
 {
 	struct timespec times[2];
+	if (!tv) return futimens(fd, 0);
 	times[0].tv_sec  = tv[0].tv_sec;
 	times[0].tv_nsec = tv[0].tv_usec * 1000;
 	times[1].tv_sec  = tv[1].tv_sec;
diff --git a/system/lib/libc/musl/src/legacy/getloadavg.c b/system/lib/libc/musl/src/legacy/getloadavg.c
index 43a8c9e..ff06de0 100644
--- a/system/lib/libc/musl/src/legacy/getloadavg.c
+++ b/system/lib/libc/musl/src/legacy/getloadavg.c
@@ -1,18 +1,14 @@
 #define _GNU_SOURCE
 #include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+#include <sys/sysinfo.h>
 
 int getloadavg(double *a, int n)
 {
-	int i;
-	double b[3];
-	FILE *f = fopen("/proc/loadavg", "rbe");
-	if (!f) return -1;
-	i = fscanf(f, "%lf %lf %lf", b, b+1, b+2);
-	fclose(f);
-	if (n > i) n = i;
-	if (n < 0) return -1;
-	memcpy(a, b, n * sizeof *a);
+	struct sysinfo si;
+	if (n <= 0) return n ? -1 : 0;
+	sysinfo(&si);
+	if (n > 3) n = 3;
+	for (int i=0; i<n; i++)
+		a[i] = 1.0/(1<<SI_LOAD_SHIFT) * si.loads[i];
 	return n;
 }
diff --git a/system/lib/libc/musl/src/legacy/utmpx.c b/system/lib/libc/musl/src/legacy/utmpx.c
index c483e4e..e2843c9 100644
--- a/system/lib/libc/musl/src/legacy/utmpx.c
+++ b/system/lib/libc/musl/src/legacy/utmpx.c
@@ -1,5 +1,6 @@
 #include <utmpx.h>
 #include <stddef.h>
+#include <errno.h>
 #include "libc.h"
 
 void endutxent(void)
@@ -34,6 +35,12 @@
 {
 }
 
+int __utmpxname(const char *f)
+{
+	errno = ENOTSUP;
+	return -1;
+}
+
 weak_alias(endutxent, endutent);
 weak_alias(setutxent, setutent);
 weak_alias(getutxent, getutent);
@@ -41,3 +48,5 @@
 weak_alias(getutxline, getutline);
 weak_alias(pututxline, pututline);
 weak_alias(updwtmpx, updwtmp);
+weak_alias(__utmpxname, utmpname);
+weak_alias(__utmpxname, utmpxname);
diff --git a/system/lib/libc/musl/src/linux/epoll.c b/system/lib/libc/musl/src/linux/epoll.c
index 030786d..deff5b1 100644
--- a/system/lib/libc/musl/src/linux/epoll.c
+++ b/system/lib/libc/musl/src/linux/epoll.c
@@ -1,15 +1,20 @@
 #include <sys/epoll.h>
 #include <signal.h>
+#include <errno.h>
 #include "syscall.h"
 
 int epoll_create(int size)
 {
-	return syscall(SYS_epoll_create, size);
+	return epoll_create1(0);
 }
 
 int epoll_create1(int flags)
 {
-	return syscall(SYS_epoll_create1, flags);
+	int r = __syscall(SYS_epoll_create1, flags);
+#ifdef SYS_epoll_create
+	if (r==-ENOSYS && !flags) r = __syscall(SYS_epoll_create, 1);
+#endif
+	return __syscall_ret(r);
 }
 
 int epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev)
@@ -19,10 +24,14 @@
 
 int epoll_pwait(int fd, struct epoll_event *ev, int cnt, int to, const sigset_t *sigs)
 {
-	return syscall(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8);
+	int r = __syscall(SYS_epoll_pwait, fd, ev, cnt, to, sigs, _NSIG/8);
+#ifdef SYS_epoll_wait
+	if (r==-ENOSYS && !sigs) r = __syscall(SYS_epoll_wait, fd, ev, cnt, to);
+#endif
+	return __syscall_ret(r);
 }
 
 int epoll_wait(int fd, struct epoll_event *ev, int cnt, int to)
 {
-	return syscall(SYS_epoll_wait, fd, ev, cnt, to);
+	return epoll_pwait(fd, ev, cnt, to, 0);
 }
diff --git a/system/lib/libc/musl/src/linux/eventfd.c b/system/lib/libc/musl/src/linux/eventfd.c
index 5306648..68e489c 100644
--- a/system/lib/libc/musl/src/linux/eventfd.c
+++ b/system/lib/libc/musl/src/linux/eventfd.c
@@ -1,10 +1,15 @@
 #include <sys/eventfd.h>
 #include <unistd.h>
+#include <errno.h>
 #include "syscall.h"
 
 int eventfd(unsigned int count, int flags)
 {
-	return syscall(flags ? SYS_eventfd2 : SYS_eventfd, count, flags);
+	int r = __syscall(SYS_eventfd2, count, flags);
+#ifdef SYS_eventfd
+	if (r==-ENOSYS && !flags) r = __syscall(SYS_eventfd, count);
+#endif
+	return __syscall_ret(r);
 }
 
 int eventfd_read(int fd, eventfd_t *value)
diff --git a/system/lib/libc/musl/src/linux/inotify.c b/system/lib/libc/musl/src/linux/inotify.c
index a417c89..df5e48b 100644
--- a/system/lib/libc/musl/src/linux/inotify.c
+++ b/system/lib/libc/musl/src/linux/inotify.c
@@ -1,13 +1,18 @@
 #include <sys/inotify.h>
+#include <errno.h>
 #include "syscall.h"
 
 int inotify_init()
 {
-	return syscall(SYS_inotify_init);
+	return inotify_init1(0);
 }
 int inotify_init1(int flags)
 {
-	return syscall(SYS_inotify_init1, flags);
+	int r = __syscall(SYS_inotify_init1, flags);
+#ifdef SYS_inotify_init
+	if (r==-ENOSYS && !flags) r = __syscall(SYS_inotify_init);
+#endif
+	return __syscall_ret(r);
 }
 
 int inotify_add_watch(int fd, const char *pathname, uint32_t mask)
diff --git a/system/lib/libc/musl/src/linux/signalfd.c b/system/lib/libc/musl/src/linux/signalfd.c
index da6bced..4bf4332 100644
--- a/system/lib/libc/musl/src/linux/signalfd.c
+++ b/system/lib/libc/musl/src/linux/signalfd.c
@@ -7,6 +7,7 @@
 int signalfd(int fd, const sigset_t *sigs, int flags)
 {
 	int ret = __syscall(SYS_signalfd4, fd, sigs, _NSIG/8, flags);
+#ifdef SYS_signalfd
 	if (ret != -ENOSYS) return __syscall_ret(ret);
 	ret = __syscall(SYS_signalfd, fd, sigs, _NSIG/8);
 	if (ret >= 0) {
@@ -15,5 +16,6 @@
 		if (flags & SFD_NONBLOCK)
 			__syscall(SYS_fcntl, ret, F_SETFL, O_NONBLOCK);
 	}
+#endif
 	return __syscall_ret(ret);
 }
diff --git a/system/lib/libc/musl/src/linux/syncfs.c b/system/lib/libc/musl/src/linux/syncfs.c
index fe2b8a7..bc7d301 100644
--- a/system/lib/libc/musl/src/linux/syncfs.c
+++ b/system/lib/libc/musl/src/linux/syncfs.c
@@ -2,7 +2,7 @@
 #include <unistd.h>
 #include "syscall.h"
 
-void syncfs(int fd)
+int syncfs(int fd)
 {
-	__syscall(SYS_syncfs, fd);
+	return syscall(SYS_syncfs, fd);
 }
diff --git a/system/lib/libc/musl/src/linux/sysinfo.c b/system/lib/libc/musl/src/linux/sysinfo.c
index 7e64f33..4b5a798 100644
--- a/system/lib/libc/musl/src/linux/sysinfo.c
+++ b/system/lib/libc/musl/src/linux/sysinfo.c
@@ -1,7 +1,10 @@
 #include <sys/sysinfo.h>
 #include "syscall.h"
+#include "libc.h"
 
-int sysinfo(struct sysinfo *info)
+int __lsysinfo(struct sysinfo *info)
 {
 	return syscall(SYS_sysinfo, info);
 }
+
+weak_alias(__lsysinfo, sysinfo);
diff --git a/system/lib/libc/musl/src/linux/utimes.c b/system/lib/libc/musl/src/linux/utimes.c
index 70c0695..b814c88 100644
--- a/system/lib/libc/musl/src/linux/utimes.c
+++ b/system/lib/libc/musl/src/linux/utimes.c
@@ -1,7 +1,10 @@
 #include <sys/time.h>
+#include "fcntl.h"
 #include "syscall.h"
 
+int __futimesat(int, const char *, const struct timeval [2]);
+
 int utimes(const char *path, const struct timeval times[2])
 {
-	return syscall(SYS_utimes, path, times);
+	return __futimesat(AT_FDCWD, path, times);
 }
diff --git a/system/lib/libc/musl/src/locale/__lctrans.c b/system/lib/libc/musl/src/locale/__lctrans.c
new file mode 100644
index 0000000..107fe14
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/__lctrans.c
@@ -0,0 +1,20 @@
+#include <locale.h>
+#include "locale_impl.h"
+#include "libc.h"
+
+static const char *dummy(const char *msg, const struct __locale_map *lm)
+{
+	return msg;
+}
+
+weak_alias(dummy, __lctrans_impl);
+
+const char *__lctrans(const char *msg, const struct __locale_map *lm)
+{
+	return __lctrans_impl(msg, lm);
+}
+
+const char *__lctrans_cur(const char *msg)
+{
+	return __lctrans_impl(msg, CURRENT_LOCALE->cat[LC_MESSAGES]);
+}
diff --git a/system/lib/libc/musl/src/locale/__mo_lookup.c b/system/lib/libc/musl/src/locale/__mo_lookup.c
new file mode 100644
index 0000000..d18ab77
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/__mo_lookup.c
@@ -0,0 +1,42 @@
+#include <stdint.h>
+#include <string.h>
+
+static inline uint32_t swapc(uint32_t x, int c)
+{
+	return c ? x>>24 | x>>8&0xff00 | x<<8&0xff0000 | x<<24 : x;
+}
+
+const char *__mo_lookup(const void *p, size_t size, const char *s)
+{
+	const uint32_t *mo = p;
+	int sw = *mo - 0x950412de;
+	uint32_t b = 0, n = swapc(mo[2], sw);
+	uint32_t o = swapc(mo[3], sw);
+	uint32_t t = swapc(mo[4], sw);
+	if (n>=size/4 || o>=size-4*n || t>=size-4*n || ((o|t)%4))
+		return 0;
+	o/=4;
+	t/=4;
+	for (;;) {
+		uint32_t ol = swapc(mo[o+2*(b+n/2)], sw);
+		uint32_t os = swapc(mo[o+2*(b+n/2)+1], sw);
+		if (os >= size || ol >= size-os || ((char *)p)[os+ol])
+			return 0;
+		int sign = strcmp(s, (char *)p + os);
+		if (!sign) {
+			uint32_t tl = swapc(mo[t+2*(b+n/2)], sw);
+			uint32_t ts = swapc(mo[t+2*(b+n/2)+1], sw);
+			if (ts >= size || tl >= size-ts || ((char *)p)[ts+tl])
+				return 0;
+			return (char *)p + ts;
+		}
+		else if (n == 1) return 0;
+		else if (sign < 0)
+			n /= 2;
+		else {
+			b += n/2;
+			n -= n/2;
+		}
+	}
+	return 0;
+}
diff --git a/system/lib/libc/musl/src/locale/bind_textdomain_codeset.c b/system/lib/libc/musl/src/locale/bind_textdomain_codeset.c
new file mode 100644
index 0000000..5ebfd5e
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/bind_textdomain_codeset.c
@@ -0,0 +1,11 @@
+#include <libintl.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+
+char *bind_textdomain_codeset(const char *domainname, const char *codeset)
+{
+	if (codeset && strcasecmp(codeset, "UTF-8"))
+		errno = EINVAL;
+	return NULL;
+}
diff --git a/system/lib/libc/musl/src/locale/c_locale.c b/system/lib/libc/musl/src/locale/c_locale.c
new file mode 100644
index 0000000..77ccf58
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/c_locale.c
@@ -0,0 +1,15 @@
+#include "locale_impl.h"
+#include <stdint.h>
+
+static const uint32_t empty_mo[] = { 0x950412de, 0, -1, -1, -1 };
+
+const struct __locale_map __c_dot_utf8 = {
+	.map = empty_mo,
+	.map_size = sizeof empty_mo,
+	.name = "C.UTF-8"
+};
+
+const struct __locale_struct __c_locale = { 0 };
+const struct __locale_struct __c_dot_utf8_locale = {
+	.cat[LC_CTYPE] = &__c_dot_utf8
+};
diff --git a/system/lib/libc/musl/src/locale/codepages.h b/system/lib/libc/musl/src/locale/codepages.h
index 35acd5a..ab146e8 100644
--- a/system/lib/libc/musl/src/locale/codepages.h
+++ b/system/lib/libc/musl/src/locale/codepages.h
@@ -4,145 +4,148 @@
 
 "iso88592\0"
 "\0\40"
-"\0\124\0\211\22\0\40\1\6\0\0\230\101\206\32\177\0\60\110\40\0\130\40\311\22"
-"\0\44\21\306\43\0\234\121\306\32\200\120\102\210\40\132\0\0\300\4\0\20\161\1\0"
-"\35\0\160\2\0\51\0\0\300\7\41\60\1\5\0\0\130\1\0\0\136\320\1\200\35"
-"\0\0\200\6\0\133\0\0\0\5\0\24\201\1\0\36\0\200\2\0\52\0\0\0\10\42\64\21\5\0"
-"\0\134\1\0\0\137\324\1\300\35\0\0\220\106\44"
+"\0\330\20\313\32\0\244\21\10\0\0\34\122\310\42\240\0\100\212\50\0\334\60\13\33"
+"\0\250\41\10\54\0\40\142\10\43\241\324\122\312\50\173\0\0\0\15\0\224\201\3\0"
+"\76\0\200\4\0\112\0\0\0\20\102\264\21\7\0\0\334\1\0\0\177\124\2\300\45"
+"\0\0\220\10\0\174\0\0\100\15\0\230\221\3\0\77\0\220\4\0\113\0\0\100\20"
+"\103\270\41\7\0\0\340\1\0\0\200\130\2\0\46\0\0\240\210\54"
 
 "iso88593\0"
 "\0\40"
-"\0\324\0\11\0\0\4\60\3\0\0\364\100\106\13\77\0\20\100\40\0\330\0\0\0"
-"\0\0\100\3\0\0\370\120\206\13\100\0\20\200\40\0\0\0\100\0\0\154\220\1\0"
-"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\274\0\0\0\53\0\0\0\0\0\310\41\6\0\0\0\0\100\0"
-"\0\160\240\1\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\300\0\0\0\54\0\0\0\0"
-"\0\314\61\106\44"
+"\0\130\21\13\0\0\4\100\5\0\0\170\121\210\23\140\0\20\200\50\0\134\1\0\0"
+"\0\0\120\5\0\0\174\141\310\23\141\0\20\300\50\0\0\0\100\0\0\360\240\3\0"
+"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\100\1\0\0\114\0\0\0\0\0\114\62\10\0"
+"\0\0\0\100\0\0\364\260\3\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\104\1\0\0"
+"\115\0\0\0\0\0\120\102\210\54"
 
 "iso88594\0"
 "\0\40"
-"\0\124\60\4\27\0\334\140\4\0\0\230\61\102\14\154\0\60\10\0\0\130\40\111\27"
-"\0\340\160\304\43\0\234\101\202\14\155\110\101\310\24\21\0\0\0\0\0\0\0\300\16"
-"\35\0\160\2\0\45\0\0\100\16\41\70\101\105\20\0\0\0\0\0\0\340\1\0\0\0\270\1\7\0"
-"\22\0\0\0\0\0\0\0\0\17\36\0\200\2\0\46\0\0\200\16\42\74\121\205\20\0\0\0\0\0"
-"\0\344\1\0\0\0\274\21\107\44"
+"\0\330\100\106\37\0\140\161\6\0\0\34\102\204\24\215\0\100\12\0\0\334\60\213\37"
+"\0\144\201\6\54\0\40\122\304\24\216\314\121\12\35\62\0\0\0\0\0\0\0\0\27"
+"\76\0\200\4\0\106\0\0\200\26\102\274\121\207\30\0\0\0\0\0\0\144\2\0\0"
+"\0\74\22\11\0\63\0\0\0\0\0\0\0\100\27\77\0\220\4\0\107\0\0\300\26"
+"\103\300\141\307\30\0\0\0\0\0\0\150\2\0\0\0\100\42\211\54"
 
 "iso88595\0"
 "\0\40"
-"\0\210\63\16\71\345\230\163\16\72\351\250\263\16\73\355\0\340\316\73"
-"\360\304\43\317\74\364\324\143\317\75\370\344\243\317\76\374\364\343\317\77"
-"\0\5\44\320\100\4\25\144\320\101\10\45\244\320\102\14\65\344\320\103"
-"\20\105\44\321\104\24\125\144\321\105\30\145\244\321\106\34\165\344\321\107"
-"\40\205\44\322\110\44\225\144\322\111\50\245\244\322\112\54\265\344\322\113"
-"\65\302\24\223\114\63\321\124\223\115\67\341\224\223\116\73\15\300\123\117"
+"\0\14\104\120\101\6\35\204\120\102\12\55\304\120\103\16\1\360\20\104"
+"\21\111\64\21\105\25\131\164\21\106\31\151\264\21\107\35\171\364\21\110"
+"\41\211\64\22\111\45\231\164\22\112\51\251\264\22\113\55\271\364\22\114"
+"\61\311\64\23\115\65\331\164\23\116\71\351\264\23\117\75\371\364\23\120"
+"\101\11\65\24\121\105\31\165\24\122\111\51\265\24\123\115\71\365\24\124"
+"\127\106\45\325\124\124\125\145\325\125\130\145\245\325\126\134\15\320\225\127"
 
 "iso88596\0"
 "\0\40"
-"\0\4\20\100\0\0\4\20\100\0\1\4\20\100\0\163\1\20\100\0\1\4\20\100\0"
-"\1\4\20\100\0\1\4\20\0\135\1\4\20\100\135\1\330\165\27\136\171\351\265\27\137"
-"\175\371\365\27\140\201\11\66\30\141\205\31\166\30\142\211\51\266\30\143"
-"\215\71\366\130\0\1\4\20\100\0\220\105\46\331\144\224\125\146\331\145"
-"\230\145\246\331\146\234\165\346\331\147\240\205\46\132\0\1\4\20\100\0"
-"\1\4\20\100\0\1\4\20\100\0"
+"\0\4\20\100\0\0\4\20\100\0\1\4\20\100\0\224\1\20\100\0\1\4\20\100\0"
+"\1\4\20\100\0\1\4\20\100\145\1\4\20\200\145\1\134\206\131\146"
+"\232\155\306\131\147\236\175\6\132\150\242\215\106\132\151\246\235\206\132\152"
+"\252\255\306\132\153\256\275\6\133\0\1\4\20\100\0\261\311\66\33\155"
+"\265\331\166\33\156\271\351\266\33\157\275\371\366\33\160\301\11\67\134\0"
+"\1\4\20\100\0\1\4\20\100\0\1\4\20\100\0"
 
 "iso88597\0"
 "\0\40"
-"\0\220\130\42\0\63\322\10\0\0\0\0\240\11\0\0\0\20\200\210\0\0\0\0\0"
-"\233\160\322\11\0\236\174\2\12\0\241\0\40\312\50\244\224\142\312\51"
-"\250\244\242\312\52\254\264\342\312\53\260\304\42\313\54\264\324\22\200\55"
-"\267\340\222\213\56\273\360\322\213\57\277\0\23\214\60\303\20\123\214\61"
-"\307\40\223\214\62\313\60\323\214\63\317\100\23\215\64\323\120\123\215\65"
-"\327\140\223\215\66\333\160\323\215\67\337\200\23\116\0"
+"\0\24\151\44\0\124\126\11\0\0\0\0\260\13\0\0\0\20\300\220\0\0\0\0\0"
+"\274\364\342\13\0\277\0\23\14\0\302\0\60\14\61\305\30\163\14\62"
+"\311\50\263\14\63\315\70\363\14\64\321\110\63\15\65\325\130\23\300\65"
+"\330\144\243\315\66\334\164\343\315\67\340\204\43\316\70\344\224\143\316\71"
+"\350\244\243\316\72\354\264\343\316\73\360\304\43\317\74\364\324\143\317\75"
+"\370\344\243\317\76\374\364\343\317\77\0\5\44\120\0"
 
 "iso88598\0"
 "\0\40"
 "\0\4\0\0\0\0\0\0\0\0\0\0\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\0\0"
 "\0\0\0\100\0\1\4\20\100\0\1\4\20\100\0\1\4\20\100\0\1\4\20\100\0\1\4\20\100\0"
-"\1\4\20\100\0\1\4\20\100\0\1\4\20\300\210\123\121\125\225\125"
-"\127\141\225\225\126\133\161\325\225\127\137\201\25\226\130"
-"\143\221\125\226\131\147\241\225\226\132\153\261\325\126\0\1\170\370\141\0"
+"\1\4\20\100\0\1\4\20\100\0\1\4\20\0\221\164\325\145\327\135"
+"\170\345\245\327\136\174\365\345\327\137\200\5\46\330\140\204\25\146\330\141"
+"\210\45\246\330\142\214\65\346\130\0\1\374\10\144\0"
 
 "iso88599\0"
 "\0\120"
-"\55\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\364\100\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\56\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\370\120\6\0"
+"\116\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\170\121\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+"\0\0\0\0\0\117\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\174\141\10\0"
 
 "iso885910\0"
 "\0\40"
-"\0\124\60\102\14\71\334\20\4\0\106\204\140\6\33\203\0\0\207\24"
-"\0\130\100\202\14\72\340\40\4\0\107\210\160\106\33\204\210\30\307\24"
-"\21\0\0\0\0\0\0\0\300\16\35\0\160\2\0\45\0\0\0\0\0\70\101\5\0\0\0\0\200\33"
-"\0\340\1\0\0\0\0\0\0\0\22\0\0\0\0\0\0\0\0\17\36\0\200\2\0\46\0\0\0\0"
-"\0\74\121\5\0\0\0\0\300\33\0\344\1\0\0\0\0\0\300\20"
+"\0\330\100\204\24\132\140\41\6\0\147\10\161\110\43\244\0\20\311\34"
+"\0\334\120\304\24\133\144\61\6\0\150\14\201\210\43\245\14\51\11\35\62\0\0\0\0"
+"\0\0\0\0\27\76\0\200\4\0\106\0\0\0\0\0\274\121\7\0\0\0\0\300\43\0\144\2\0\0"
+"\0\0\0\0\0\63\0\0\0\0\0\0\0\100\27\77\0\220\4\0\107\0\0\0\0\0\300\141\7\0"
+"\0\0\0\0\44\0\150\2\0\0\0\0\0\0\31"
 
 "iso885911\0"
 "tis620\0"
 "\0\40"
-"\0\274\6\133\154\262\315\106\133\155\266\335\206\133\156\272\355\306\133\157"
-"\276\375\6\134\160\302\15\107\134\161\306\35\207\134\162\312\55\307\134\163"
-"\316\75\7\135\164\322\115\107\135\165\326\135\207\135\166\332\155\307\135\167"
-"\336\175\7\136\170\342\215\107\136\171\346\235\207\136\0\1\4\20\100\172"
-"\352\255\307\136\173\356\275\7\137\174\362\315\107\137\175\366\335\207\137\176"
-"\372\355\307\137\177\376\375\7\140\200\2\16\110\140\201\1\4\20\100\0"
+"\0\100\27\235\164\323\121\127\235\165\327\141\227\235\166\333\161\327\235\167"
+"\337\201\27\236\170\343\221\127\236\171\347\241\227\236\172"
+"\353\261\327\236\173\357\301\27\237\174\363\321\127\237\175"
+"\367\341\227\237\176\373\361\327\237\177\377\1\30\240\200\3\22\130\240\201"
+"\7\42\230\140\0\1\4\20\200\202\13\62\330\240\203\17\102\30\241\204"
+"\23\122\130\241\205\27\142\230\241\206\33\162\330\241\207\37\202\30\242\210"
+"\43\222\130\242\211\1\4\20\100\0"
 
 "iso885913\0"
 "\0\40"
-"\0\240\10\0\0\0\244\10\0\0\15\0\300\5\0\0\0\0\300\2\0\0\0\0\0\47\2\0\0\0"
-"\20\0\320\5\0\0\0\0\200\3\25\354\20\301\5\0\0\160\302\10\35\0\360\107\11"
-"\61\4\221\203\21\146\60\341\4\0\124\0\0\0\0\170\50\1\6\34\0\4\62\10\0"
-"\26\360\40\1\6\0\0\200\2\11\36\0\0\210\11\62\10\241\303\21\147\64\361\4\0"
-"\125\0\0\0\0\171\54\21\106\34\0\10\102\110\211"
+"\0\44\11\0\0\0\50\11\0\0\15\0\320\7\0\0\0\0\300\2\0\0\0\0\0\110\2\0\0\0"
+"\20\0\340\7\0\0\0\0\200\3\66\160\41\3\16\0\0\200\4\21\76\0\0\212\21"
+"\122\210\241\305\31\207\264\361\6\0\165\0\0\0\0\231\254\21\110\44"
+"\0\210\102\12\0\67\164\61\103\16\0\0\220\104\21\77\0\20\312\21"
+"\123\214\261\5\32\210\270\1\7\0\166\0\0\0\0\232\260\41\210\44"
+"\0\214\122\212\221"
 
 "iso885914\0"
 "\0\40"
-"\0\30\170\40\0\33\160\200\40\0\24\2\140\141\202\32\2\0\200\37\12\56\370\2\14"
-"\14\66\10\200\203\25\76\170\41\204\33\142\230\141\204\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\172\0\0\0\0\0\0\0\200\204\0\0\0\0\0\0\0\300\7\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\173\0\0\0\0\0\0\0\300\204\0\0\0\0\0\0\0\320\7\0"
+"\0\234\210\42\0\74\364\220\42\0\65\2\160\243\212\73\2\0\300\47"
+"\53\262\10\105\24\55\272\10\300\213\66\302\210\143\214\74\346\250\243\214"
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\233\0\0\0\0\0\0\0\300\214\0\0\0\0\0"
+"\0\0\320\11\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\234\0\0\0\0\0\0\0\0\215"
+"\0\0\0\0\0\0\0\340\11\0"
 
 "iso885915\0"
 "latin9\0"
 "\0\44"
-"\63\2\140\6\0\147\0\0\0\0\0\0\0\0\0\0\0\0\0\0\203\0\0\0\0\204\0\0\0\0"
-"\130\144\341\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+"\124\2\160\10\0\210\0\0\0\0\0\0\0\0\0\0\0\0\0\0\244\0\0\0\0\245\0\0\0\0"
+"\171\350\361\11\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 
 "iso885916\0"
 "\0\40"
-"\0\124\140\201\22\63\246\150\6\0\147\0\240\10\0\177\0\0\110\40\0\0\320\301\22"
-"\203\240\10\0\0\204\170\260\10\0\130\144\341\207\40\0\0\0\300\4\0\134\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\41\60\1\0\0\0\130\1\0\30\166\0\0\0\0\0\234\300\10\0"
-"\0\0\0\0\5\0\140\0\0\0\0\0\0\0\0\0\0\0\0\0\42\64\1\0\0\0\134\1\100\30"
-"\167\0\0\0\0\0\240\320\10\0"
+"\0\330\160\303\32\124\52\171\10\0\210\0\260\12\0\240\0\20\212\50\0\0\340\3\33"
+"\244\44\11\0\0\245\374\300\12\0\171\350\361\311\50\0\0\0\0\15\0\340\0\0\0"
+"\0\0\0\0\0\0\0\0\0\0\102\264\1\0\0\0\334\1\100\40\227\0\0\0\0\0\40\321\12\0"
+"\0\0\0\100\15\0\344\0\0\0\0\0\0\0\0\0\0\0\0\0\103\270\1\0\0\0\340\1\200\40"
+"\230\0\0\0\0\0\44\341\12\0"
 
 "cp1250\0"
 "windows1250\0"
 "\0\0"
-"\63\6\140\142\0\51\266\250\342\212\1\270\150\306\213\140\250\61\310\37"
-"\1\220\130\342\211\50\262\10\142\210\1\330\170\6\214\141\254\101\10\40"
-"\0\74\2\211\22\0\124\0\0\0\0\0\100\6\0\0\0\0\100\40\0\0\40\311\22\0\0\0\0\0"
-"\0\130\120\6\0\110\120\222\204\40\132\0\0\300\4\0\20\161\1\0\35\0\160\2\0"
-"\51\0\0\300\7\41\60\1\5\0\0\130\1\0\0\136\320\1\200\35\0\0\200\6\0\133\0\0\0\5"
-"\0\24\201\1\0\36\0\200\2\0\52\0\0\0\10\42\64\21\5\0\0\134\1\0\0"
-"\137\324\1\300\35\0\0\220\106\44"
+"\124\6\160\144\0\112\72\271\44\223\1\74\171\10\224\201\54\102\12\50"
+"\1\24\151\44\222\111\66\31\244\220\1\140\211\110\224\202\60\122\112\50"
+"\0\300\22\313\32\0\330\0\0\0\0\0\120\10\0\0\0\0\200\50\0\0\60\13\33\0\0\0\0\0"
+"\0\334\140\10\0\151\324\242\306\50\173\0\0\0\15\0\224\201\3\0\76\0\200\4\0"
+"\112\0\0\0\20\102\264\21\7\0\0\334\1\0\0\177\124\2\300\45\0\0\220\10\0"
+"\174\0\0\100\15\0\230\221\3\0\77\0\220\4\0\113\0\0\100\20\103\270\41\7\0"
+"\0\340\1\0\0\200\130\2\0\46\0\0\240\210\54"
 
 "cp1251\0"
 "windows1251\0"
 "\0\0"
-"\343\220\143\242\114\51\266\250\342\212\63\272\250\316\213\353\264\303\316\73"
-"\61\221\130\342\211\50\262\10\142\210\1\330\210\23\214\71\355\244\123\117"
-"\0\270\303\123\72\0\370\4\0\0\342\0\120\16\0\0\0\0\0\72\0\0\160\116\115"
-"\77\1\0\0\0\60\325\70\23\0\67\231\103\223\115\360\304\43\317\74"
-"\364\324\143\317\75\370\344\243\317\76\374\364\343\317\77\0\5\44\320\100"
-"\4\25\144\320\101\10\45\244\320\102\14\65\344\320\103\20\105\44\321\104"
-"\24\125\144\321\105\30\145\244\321\106\34\165\344\321\107\40\205\44\322\110"
-"\44\225\144\322\111\50\245\244\322\112\54\265\344\322\113"
+"\4\25\164\344\124\112\72\271\44\223\124\76\271\20\224\14\71\324\20\104"
+"\122\25\151\44\222\111\66\31\244\220\1\140\231\125\224\132\161\265\225\127"
+"\0\74\324\225\102\0\174\5\0\0\3\1\140\20\0\0\0\0\100\102\0\0\200\220\125"
+"\140\1\0\0\0\121\135\111\25\0\130\35\124\325\125\21\111\64\21\105"
+"\25\131\164\21\106\31\151\264\21\107\35\171\364\21\110\41\211\64\22\111"
+"\45\231\164\22\112\51\251\264\22\113\55\271\364\22\114\61\311\64\23\115"
+"\65\331\164\23\116\71\351\264\23\117\75\371\364\23\120\101\11\65\24\121"
+"\105\31\165\24\122\111\51\265\24\123\115\71\365\24\124"
 
 "cp1252\0"
 "windows1252\0"
 "\0\0"
-"\63\6\140\142\41\51\266\250\342\212\216\270\150\306\213\130\4\60\110\0"
-"\1\220\130\342\211\50\262\10\142\210\223\330\170\6\214\131\4\100\210\37"
+"\124\6\160\244\51\112\72\271\44\223\257\74\171\10\224\171\4\100\112\0"
+"\1\24\151\44\222\111\66\31\244\220\264\140\211\110\224\172\4\120\312\47"
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
@@ -151,88 +154,112 @@
 "cp1253\0"
 "windows1253\0"
 "\0\0"
-"\63\6\140\142\41\51\266\250\342\212\1\270\30\300\213\1\4\20\100\0"
-"\1\220\130\342\211\50\262\10\142\210\1\330\30\0\214\1\4\20\100\0"
-"\0\160\322\11\0\0\0\0\0\0\0\0\20\0\0\0\0\0\200\210\0\0\0\0\0\233\0\0\0\0"
-"\236\174\2\12\0\241\0\40\312\50\244\224\142\312\51\250\244\242\312\52"
-"\254\264\342\312\53\260\304\42\313\54\264\324\22\200\55\267\340\222\213\56"
-"\273\360\322\213\57\277\0\23\214\60\303\20\123\214\61\307\40\223\214\62"
-"\313\60\323\214\63\317\100\23\215\64\323\120\123\215\65\327\140\223\215\66"
-"\333\160\323\215\67\337\200\23\116\0"
+"\124\6\160\244\51\112\72\271\44\223\1\74\31\0\224\1\4\20\100\0\1\24\151\44\222"
+"\111\66\31\244\220\1\140\31\100\224\1\4\20\100\0\0\364\342\13\0\0\0\0\0\0"
+"\0\0\20\0\0\0\0\0\300\220\0\0\0\0\0\274\0\0\0\0\277\0\23\14\0\302\0\60\14\61"
+"\305\30\163\14\62\311\50\263\14\63\315\70\363\14\64\321\110\63\15\65"
+"\325\130\23\300\65\330\144\243\315\66\334\164\343\315\67\340\204\43\316\70"
+"\344\224\143\316\71\350\244\243\316\72\354\264\343\316\73\360\304\43\317\74"
+"\364\324\143\317\75\370\344\243\317\76\374\364\343\317\77\0\5\44\120\0"
 
 "cp1254\0"
 "windows1254\0"
 "\0\0"
-"\63\6\140\142\41\51\266\250\342\212\216\270\150\306\213\130\4\20\100\0"
-"\1\220\130\342\211\50\262\10\142\210\223\330\170\6\214\131\4\20\200\37"
+"\124\6\160\244\51\112\72\271\44\223\257\74\171\10\224\171\4\20\100\0"
+"\1\24\151\44\222\111\66\31\244\220\264\140\211\110\224\172\4\20\300\47"
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\55\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\364\100\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\56\0\0\0\0"
-"\0\0\0\0\0\0\0\0\0\0\0\370\120\6\0"
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\116\0\0\0\0\0\0\0\0\0"
+"\0\0\0\0\0\0\170\121\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\117\0\0\0\0"
+"\0\0\0\0\0\0\0\0\0\0\0\174\141\10\0"
 
 "cp1255\0"
 "windows1255\0"
 "\0\0"
-"\63\6\140\142\41\51\266\250\342\212\216\270\30\300\213\1\4\20\100\0"
-"\1\220\130\342\211\50\262\10\142\210\223\330\30\0\214\1\4\20\100\0\0\0\0\0\0"
-"\61\2\0\0\0\0\0\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\0\0\0\0\0\0\0"
-"\100\5\45\324\120\104\25\145\324\121\110\45\25\200\122\113\61\325\224\123"
-"\117\101\25\225\124\156\275\5\127\134\162\5\20\100\0\1\4\20\100\0"
-"\123\121\125\225\125\127\141\225\225\126\133\161\325\225\127"
-"\137\201\25\226\130\143\221\125\226\131\147\241\225\226\132\153\261\325\126\0"
-"\1\170\370\141\0"
+"\124\6\160\244\51\112\72\271\44\223\257\74\31\0\224\1\4\20\100\0"
+"\1\24\151\44\222\111\66\31\244\220\264\140\31\100\224\1\4\20\100\0\0\0\0\0\0"
+"\122\2\0\0\0\0\0\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\0\0\0\0\0\0\0"
+"\141\211\65\26\131\145\231\165\26\132\151\251\25\300\132\154\265\345\326\133"
+"\160\305\45\327\134\217\101\26\231\144\223\5\20\100\0\1\4\20\100\0"
+"\164\325\145\327\135\170\345\245\327\136\174\365\345\327\137\200\5\46\330\140"
+"\204\25\146\330\141\210\45\246\330\142\214\65\346\130\0\1\374\10\144\0"
 
 "cp1256\0"
 "windows1256\0"
 "\0\0"
-"\63\222\146\142\41\51\266\250\342\212\216\270\70\332\213\130\224\206\232\151"
-"\252\221\130\342\211\50\262\10\142\210\251\331\170\32\214\131\160\330\341\152"
-"\0\314\5\0\0\0\0\0\0\0\0\0\300\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\100\27\0"
-"\0\0\0\100\135\255\331\165\27\136\171\351\265\27\137\175\371\365\27\140"
-"\201\11\66\30\141\205\31\166\30\142\211\51\266\30\0\214\65\346\330\143"
-"\220\105\46\331\144\0\120\6\100\145\226\135\206\31\0\0\0\0\0\0\231\151\6\0\0"
-"\233\161\326\231\147\0\174\6\32\0\241\1\40\32\0\0\170\370\241\153"
+"\124\26\167\244\51\112\72\271\44\223\257\74\111\34\224\171\30\227\334\161"
+"\313\25\151\44\222\111\66\31\244\220\312\141\211\134\224\172\364\350\43\163"
+"\0\120\6\0\0\0\0\0\0\0\0\0\320\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\120\31\0"
+"\0\0\0\200\145\316\135\206\131\146\232\155\306\131\147\236\175\6\132\150"
+"\242\215\106\132\151\246\235\206\132\152\252\255\306\32\0\255\271\366\32\154"
+"\261\311\66\33\155\0\324\6\200\155\267\341\226\33\0\0\0\0\0\0\272\355\6\0\0"
+"\274\365\346\333\157\0\0\27\34\0\302\1\60\34\0\0\374\10\344\163"
 
 "cp1257\0"
 "windows1257\0"
 "\0\0"
-"\63\6\140\142\0\51\266\250\342\212\1\270\30\300\213\1\20\360\210\2"
-"\1\220\130\342\211\50\262\10\142\210\1\330\30\0\214\1\30\40\111\0\0\4\0\0\0"
-"\0\4\0\0\0\15\0\300\5\0\0\0\0\300\2\0\0\0\0\0\0\0\0\0\0\20\0\320\5\0"
-"\0\0\0\200\3\25\354\20\301\5\0\0\160\302\10\35\0\360\107\11\61\4\221\203\21"
-"\146\60\341\4\0\124\0\0\0\0\170\50\1\6\34\0\4\62\10\0\26\360\40\1\6"
-"\0\0\200\2\11\36\0\0\210\11\62\10\241\303\21\147\64\361\4\0\125\0\0\0\0"
-"\171\54\21\106\34\0\10\102\110\44"
+"\124\6\160\144\0\112\72\271\44\223\1\74\31\0\224\1\20\0\213\2\1\24\151\44\222"
+"\111\66\31\244\220\1\140\31\100\224\1\30\60\113\0\0\4\0\0\0\0\4\0\0\0"
+"\15\0\320\7\0\0\0\0\300\2\0\0\0\0\0\0\0\0\0\0\20\0\340\7\0\0\0\0\200\3"
+"\66\160\41\3\16\0\0\200\4\21\76\0\0\212\21\122\210\241\305\31\207\264\361\6\0"
+"\165\0\0\0\0\231\254\21\110\44\0\210\102\12\0\67\164\61\103\16\0\0\220\104\21"
+"\77\0\20\312\21\123\214\261\5\32\210\270\1\7\0\166\0\0\0\0\232\260\41\210\44"
+"\0\214\122\212\54"
 
 "cp1258\0"
 "windows1258\0"
 "\0\0"
-"\63\6\140\142\41\51\266\250\342\212\216\270\30\300\213\130\4\20\100\0"
-"\1\220\130\342\211\50\262\10\142\210\223\330\30\0\214\131\4\20\200\37"
+"\124\6\160\244\51\112\72\271\44\223\257\74\31\0\224\171\4\20\100\0"
+"\1\24\151\44\222\111\66\31\244\220\264\140\31\100\224\172\4\20\300\47"
 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
-"\0\0\0\0\0\0\0\0\300\4\0\0\0\0\0\0\0\0\0\0\225\0\0\0\0\41\0\200\11\0"
-"\0\30\2\0\0\0\0\0\0\0\0\40\162\11\0\0\0\0\0\5\0\0\0\0\0\0\0\0\0\0\226\0\0\0\0"
-"\42\0\220\11\0\0\34\2\0\0\0\0\0\0\0\0\44\42\43\0"
+"\0\0\0\0\0\0\0\0\0\15\0\0\0\0\0\0\0\0\0\0\266\0\0\0\0\102\0\220\13\0"
+"\0\234\2\0\0\0\0\0\0\0\0\244\202\13\0\0\0\0\100\15\0\0\0\0\0\0\0\0\0\0"
+"\267\0\0\0\0\103\0\240\13\0\0\240\2\0\0\0\0\0\0\0\0\250\62\45\0"
 
 "koi8r\0"
 "\0\0"
-"\76\376\10\144\220\102\16\111\144\221\106\36\211\244\231\147\242\231\246\232"
-"\153\262\331\46\217\156\336\210\143\216\72\356\50\100\217\7\40\220\300\3"
-"\111\52\271\44\114\114\66\351\344\223\120\106\51\345\224\124\126\151\345\225"
-"\130\146\251\245\70\133\162\331\245\227\137\202\31\246\230\143\222\131\146\1"
-"\56\101\24\221\111\24\125\104\322\104\45\141\224\221\106\33\161\324\221\107"
-"\37\275\4\122\110\42\215\144\221\104\54\255\164\21\112\55\245\164\222\112"
-"\16\301\23\217\101\364\324\103\320\74\5\341\223\217\76\373\360\323\217\77"
-"\377\74\4\120\100\2\15\144\217\74\14\55\164\17\102\15\45\164\220\102"
+"\140\206\51\346\230\144\226\151\346\231\150\246\251\46\242\211\52\272\50\243"
+"\215\72\372\250\227\220\146\251\345\226\134\166\51\300\227\7\40\220\300\3"
+"\153\262\331\146\124\156\276\11\147\234\162\316\111\147\235"
+"\166\336\211\147\236\172\356\311\347\100\175\372\371\47\240\201\12\72\50\241"
+"\205\32\172\150\1\117\305\44\323\121\65\331\124\24\115\106\345\244\323\116"
+"\74\365\344\323\117\100\101\25\224\120\103\21\165\323\114\115\61\205\123\122"
+"\116\51\205\324\122\57\105\44\321\111\25\131\124\22\105\46\145\244\321\106"
+"\34\165\344\321\107\40\301\24\222\110\43\221\164\321\104\55\261\204\121\112"
+"\56\251\204\322\112"
 
 "koi8u\0"
 "\0\0"
-"\76\376\10\144\220\102\16\111\144\221\106\36\211\244\231\147\242\231\246\232"
-"\153\262\331\46\217\156\336\210\143\216\72\356\50\100\217\7\40\220\300\3"
-"\111\52\271\44\114\63\65\131\223\115\120\106\51\345\224\124\376\144\345\225"
-"\130\146\251\245\70\345\160\171\16\72\137\202\31\246\230\143\372\124\146\1"
-"\56\101\24\221\111\24\125\104\322\104\45\141\224\221\106\33\161\324\221\107"
-"\37\275\4\122\110\42\215\144\221\104\54\255\164\21\112\55\245\164\222\112"
-"\16\301\23\217\101\364\324\103\320\74\5\341\223\217\76\373\360\323\217\77"
-"\377\74\4\120\100\2\15\144\217\74\14\55\164\17\102\15\45\164\220\102"
+"\140\206\51\346\230\144\226\151\346\231\150\246\251\46\242\211\52\272\50\243"
+"\215\72\372\250\227\220\146\251\345\226\134\166\51\300\227\7\40\220\300\3"
+"\153\262\331\146\124\124\275\151\325\125\162\316\111\147\235"
+"\166\202\205\147\236\172\356\311\347\100\6\371\211\120\102\201\12\72\50\241"
+"\205\176\165\150\1\117\305\44\323\121\65\331\124\24\115\106\345\244\323\116"
+"\74\365\344\323\117\100\101\25\224\120\103\21\165\323\114\115\61\205\123\122"
+"\116\51\205\324\122\57\105\44\321\111\25\131\124\22\105\46\145\244\321\106"
+"\34\165\344\321\107\40\301\24\222\110\43\221\164\321\104\55\261\204\121\112"
+"\56\251\204\322\112"
+
+"cp437\0"
+"\0\0"
+"\27\300\100\202\7\37\164\0\202\10\45\230\60\102\12\50\234\100\101\5"
+"\30\70\260\300\12\54\250\360\202\13\61\144\300\101\4\22\114\140\245\51"
+"\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244"
+"\215\72\372\150\230\147\362\331\147\234\160\372\311\246\234"
+"\170\336\151\347\230\144\246\211\246\231\140\252\231\247\236"
+"\165\276\111\150\240\173\256\171\250\240\203\376\11\50\235\163\266\351\246\241"
+"\205\226\51\246\242\211\56\312\50\242\345\104\212\14\75\327\334\23\51\76"
+"\332\64\323\15\72\221\352\223\116\244\221\106\332\45\227\136\176\371\300\226"
+"\7\144\231\200\226\221\42\0\251\0"
+
+"cp850\0"
+"\0\0"
+"\27\300\100\202\7\37\164\0\202\10\45\230\60\102\12\50\234\100\101\5"
+"\30\70\260\300\12\54\250\360\202\13\61\144\300\1\4\22\64\300\200\51"
+"\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244"
+"\215\72\372\150\230\147\106\32\151\244\5\370\311\246\234\170\106\60\301\230"
+"\144\246\211\246\231\140\252\31\151\244\165\276\111\150\240"
+"\173\256\171\150\244\221\106\32\151\244\221\176\21\151\244\221\226\51\246\242"
+"\211\106\32\51\242\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244"
+"\221\106\152\100\244\221\106\112\144\244\221\16\360\200\2\7\20\220\100\244"
+"\221\42\0\251\0"
 
diff --git a/system/lib/libc/musl/src/locale/dcngettext.c b/system/lib/libc/musl/src/locale/dcngettext.c
new file mode 100644
index 0000000..a5ff847
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/dcngettext.c
@@ -0,0 +1,249 @@
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include "locale_impl.h"
+#include "libc.h"
+#include "atomic.h"
+
+struct binding {
+	struct binding *next;
+	int dirlen;
+	volatile int active;
+	char *domainname;
+	char *dirname;
+	char buf[];
+};
+
+static void *volatile bindings;
+
+static char *gettextdir(const char *domainname, size_t *dirlen)
+{
+	struct binding *p;
+	for (p=bindings; p; p=p->next) {
+		if (!strcmp(p->domainname, domainname) && p->active) {
+			*dirlen = p->dirlen;
+			return (char *)p->dirname;
+		}
+	}
+	return 0;
+}
+
+char *bindtextdomain(const char *domainname, const char *dirname)
+{
+	static volatile int lock[2];
+	struct binding *p, *q;
+
+	if (!domainname) return 0;
+	if (!dirname) return gettextdir(domainname, &(size_t){0});
+
+	size_t domlen = strlen(domainname);
+	size_t dirlen = strlen(dirname);
+	if (domlen > NAME_MAX || dirlen >= PATH_MAX) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	LOCK(lock);
+
+	for (p=bindings; p; p=p->next) {
+		if (!strcmp(p->domainname, domainname) &&
+		    !strcmp(p->dirname, dirname)) {
+			break;
+		}
+	}
+
+	if (!p) {
+		p = malloc(sizeof *p + domlen + dirlen + 2);
+		if (!p) {
+			UNLOCK(lock);
+			return 0;
+		}
+		p->next = bindings;
+		p->dirlen = dirlen;
+		p->domainname = p->buf;
+		p->dirname = p->buf + domlen + 1;
+		memcpy(p->domainname, domainname, domlen+1);
+		memcpy(p->dirname, dirname, dirlen+1);
+		a_cas_p(&bindings, bindings, p);
+	}
+
+	a_store(&p->active, 1);
+
+	for (q=bindings; q; q=q->next) {
+		if (!strcmp(p->domainname, domainname) && q != p)
+			a_store(&q->active, 0);
+	}
+
+	UNLOCK(lock);
+	
+	return (char *)p->dirname;
+}
+
+static const char catnames[][12] = {
+	"LC_CTYPE",
+	"LC_NUMERIC",
+	"LC_TIME",
+	"LC_COLLATE",
+	"LC_MONETARY",
+	"LC_MESSAGES",
+};
+
+static const char catlens[] = { 8, 10, 7, 10, 11, 11 };
+
+struct msgcat {
+	struct msgcat *next;
+	const void *map;
+	size_t map_size;
+	void *volatile plural_rule;
+	volatile int nplurals;
+	char name[];
+};
+
+static char *dummy_gettextdomain()
+{
+	return "messages";
+}
+
+weak_alias(dummy_gettextdomain, __gettextdomain);
+
+const unsigned char *__map_file(const char *, size_t *);
+int __munmap(void *, size_t);
+unsigned long __pleval(const char *, unsigned long);
+
+char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n, int category)
+{
+	static struct msgcat *volatile cats;
+	struct msgcat *p;
+	struct __locale_struct *loc = CURRENT_LOCALE;
+	const struct __locale_map *lm;
+	const char *dirname, *locname, *catname;
+	size_t dirlen, loclen, catlen, domlen;
+
+	if ((unsigned)category >= LC_ALL) goto notrans;
+
+	if (!domainname) domainname = __gettextdomain();
+
+	domlen = strlen(domainname);
+	if (domlen > NAME_MAX) goto notrans;
+
+	dirname = gettextdir(domainname, &dirlen);
+	if (!dirname) goto notrans;
+
+	lm = loc->cat[category];
+	if (!lm) {
+notrans:
+		return (char *) ((n == 1) ? msgid1 : msgid2);
+	}
+	locname = lm->name;
+
+	catname = catnames[category];
+	catlen = catlens[category];
+	loclen = strlen(locname);
+
+	size_t namelen = dirlen+1 + loclen+1 + catlen+1 + domlen+3;
+	char name[namelen+1], *s = name;
+
+	memcpy(s, dirname, dirlen);
+	s[dirlen] = '/';
+	s += dirlen + 1;
+	memcpy(s, locname, loclen);
+	s[loclen] = '/';
+	s += loclen + 1;
+	memcpy(s, catname, catlen);
+	s[catlen] = '/';
+	s += catlen + 1;
+	memcpy(s, domainname, domlen);
+	s[domlen] = '.';
+	s[domlen+1] = 'm';
+	s[domlen+2] = 'o';
+	s[domlen+3] = 0;
+
+	for (p=cats; p; p=p->next)
+		if (!strcmp(p->name, name))
+			break;
+
+	if (!p) {
+		void *old_cats;
+		size_t map_size;
+		const void *map = __map_file(name, &map_size);
+		if (!map) goto notrans;
+		p = malloc(sizeof *p + namelen + 1);
+		if (!p) {
+			__munmap((void *)map, map_size);
+			goto notrans;
+		}
+		p->map = map;
+		p->map_size = map_size;
+		memcpy(p->name, name, namelen+1);
+		do {
+			old_cats = cats;
+			p->next = old_cats;
+		} while (a_cas_p(&cats, old_cats, p) != old_cats);
+	}
+
+	const char *trans = __mo_lookup(p->map, p->map_size, msgid1);
+	if (!trans) goto notrans;
+
+	/* Non-plural-processing gettext forms pass a null pointer as
+	 * msgid2 to request that dcngettext suppress plural processing. */
+	if (!msgid2) return (char *)trans;
+
+	if (!p->plural_rule) {
+		const char *rule = "n!=1;";
+		unsigned long np = 2;
+		const char *r = __mo_lookup(p->map, p->map_size, "");
+		char *z;
+		while (r && strncmp(r, "Plural-Forms:", 13)) {
+			z = strchr(r, '\n');
+			r = z ? z+1 : 0;
+		}
+		if (r) {
+			r += 13;
+			while (isspace(*r)) r++;
+			if (!strncmp(r, "nplurals=", 9)) {
+				np = strtoul(r+9, &z, 10);
+				r = z;
+			}
+			while (*r && *r != ';') r++;
+			if (*r) {
+				r++;
+				while (isspace(*r)) r++;
+				if (!strncmp(r, "plural=", 7))
+					rule = r+7;
+			}
+		}
+		a_store(&p->nplurals, np);
+		a_cas_p(&p->plural_rule, 0, (void *)rule);
+	}
+	if (p->nplurals) {
+		unsigned long plural = __pleval(p->plural_rule, n);
+		if (plural > p->nplurals) goto notrans;
+		while (plural--) {
+			size_t rem = p->map_size - (trans - (char *)p->map);
+			size_t l = strnlen(trans, rem);
+			if (l+1 >= rem)
+				goto notrans;
+			trans += l+1;
+		}
+	}
+	return (char *)trans;
+}
+
+char *dcgettext(const char *domainname, const char *msgid, int category)
+{
+	return dcngettext(domainname, msgid, 0, 1, category);
+}
+
+char *dngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n)
+{
+	return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES);
+}
+
+char *dgettext(const char *domainname, const char *msgid)
+{
+	return dcngettext(domainname, msgid, 0, 1, LC_MESSAGES);
+}
diff --git a/system/lib/libc/musl/src/locale/duplocale.c b/system/lib/libc/musl/src/locale/duplocale.c
index f9fc1ff..030b64c 100644
--- a/system/lib/libc/musl/src/locale/duplocale.c
+++ b/system/lib/libc/musl/src/locale/duplocale.c
@@ -3,12 +3,13 @@
 #include "locale_impl.h"
 #include "libc.h"
 
-locale_t duplocale(locale_t old)
+locale_t __duplocale(locale_t old)
 {
-	locale_t new;
-	new = calloc(1, sizeof *new);
-	if (new && old != LC_GLOBAL_LOCALE) memcpy(new, old, sizeof *new);
+	locale_t new = malloc(sizeof *new);
+	if (!new) return 0;
+	if (old == LC_GLOBAL_LOCALE) old = &libc.global_locale;
+	*new = *old;
 	return new;
 }
 
-weak_alias(duplocale, __duplocale);
+weak_alias(__duplocale, duplocale);
diff --git a/system/lib/libc/musl/src/locale/freelocale.c b/system/lib/libc/musl/src/locale/freelocale.c
index ee3f029..c2ae1a3 100644
--- a/system/lib/libc/musl/src/locale/freelocale.c
+++ b/system/lib/libc/musl/src/locale/freelocale.c
@@ -2,9 +2,11 @@
 #include "locale_impl.h"
 #include "libc.h"
 
+int __loc_is_allocated(locale_t);
+
 void freelocale(locale_t l)
 {
-	free(l);
+	if (__loc_is_allocated(l)) free(l);
 }
 
 weak_alias(freelocale, __freelocale);
diff --git a/system/lib/libc/musl/src/locale/iconv.c b/system/lib/libc/musl/src/locale/iconv.c
index a0b0232..1eeea94 100644
--- a/system/lib/libc/musl/src/locale/iconv.c
+++ b/system/lib/libc/musl/src/locale/iconv.c
@@ -5,6 +5,7 @@
 #include <stdlib.h>
 #include <limits.h>
 #include <stdint.h>
+#include "locale_impl.h"
 
 #define UTF_32BE    0300
 #define UTF_16LE    0301
@@ -23,19 +24,13 @@
 #define BIG5        0340
 #define EUC_KR      0350
 
-/* FIXME: these are not implemented yet
- * EUC:   A1-FE A1-FE
- * GBK:   81-FE 40-7E,80-FE
- * Big5:  A1-FE 40-7E,A1-FE
- */
-
 /* Definitions of charmaps. Each charmap consists of:
  * 1. Empty-string-terminated list of null-terminated aliases.
  * 2. Special type code or number of elided entries.
  * 3. Character table (size determined by field 2). */
 
 static const unsigned char charmaps[] =
-"utf8\0\0\310"
+"utf8\0char\0\0\310"
 "wchart\0\0\306"
 "ucs2\0ucs2be\0\0\304"
 "ucs2le\0\0\305"
@@ -90,6 +85,7 @@
 static size_t find_charmap(const void *name)
 {
 	const unsigned char *s;
+	if (!*(char *)name) name=charmaps; /* "utf8" */
 	for (s=charmaps; *s; ) {
 		if (!fuzzycmp(name, s)) {
 			for (; *s; s+=strlen((void *)s)+1);
@@ -170,9 +166,12 @@
 	int err;
 	unsigned char type = map[-1];
 	unsigned char totype = tomap[-1];
+	locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
 
 	if (!in || !*in || !*inb) return 0;
 
+	*ploc = UTF8_LOCALE;
+
 	for (; *inb; *in+=l, *inb-=l) {
 		c = *(unsigned char *)*in;
 		l = 1;
@@ -436,6 +435,7 @@
 			break;
 		}
 	}
+	*ploc = loc;
 	return x;
 ilseq:
 	err = EILSEQ;
@@ -450,5 +450,6 @@
 	x = -1;
 end:
 	errno = err;
+	*ploc = loc;
 	return x;
 }
diff --git a/system/lib/libc/musl/src/locale/intl.c b/system/lib/libc/musl/src/locale/intl.c
deleted file mode 100644
index ad04052..0000000
--- a/system/lib/libc/musl/src/locale/intl.c
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <libintl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <errno.h>
-
-char *gettext(const char *msgid)
-{
-	return (char *) msgid;
-}
-
-char *dgettext(const char *domainname, const char *msgid)
-{
-	return (char *) msgid;
-}
-
-char *dcgettext(const char *domainname, const char *msgid, int category)
-{
-	return (char *) msgid;
-}
-
-char *ngettext(const char *msgid1, const char *msgid2, unsigned long int n)
-{
-	return (char *) ((n == 1) ? msgid1 : msgid2);
-}
-
-char *dngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n)
-{
-	return (char *) ((n == 1) ? msgid1 : msgid2);
-}
-
-char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n, int category)
-{
-	return (char *) ((n == 1) ? msgid1 : msgid2);
-}
-
-char *textdomain(const char *domainname)
-{
-	static const char default_str[] = "messages";
-
-	if (domainname && *domainname && strcmp(domainname, default_str)) {
-		errno = EINVAL;
-		return NULL;
-	}
-	return (char *) default_str;
-}
-
-char *bindtextdomain(const char *domainname, const char *dirname)
-{
-	static const char dir[] = "/";
-
-	if (!domainname || !*domainname
-		|| (dirname && ((dirname[0] != '/') || dirname[1]))
-		) {
-		errno = EINVAL;
-		return NULL;
-	}
-
-	return (char *) dir;
-}
-
-char *bind_textdomain_codeset(const char *domainname, const char *codeset)
-{
-	if (!domainname || !*domainname || (codeset && strcasecmp(codeset, "UTF-8"))) {
-		errno = EINVAL;
-	}
-	return NULL;
-}
diff --git a/system/lib/libc/musl/src/locale/langinfo.c b/system/lib/libc/musl/src/locale/langinfo.c
index 8159033..b2c8569 100644
--- a/system/lib/libc/musl/src/locale/langinfo.c
+++ b/system/lib/libc/musl/src/locale/langinfo.c
@@ -1,5 +1,6 @@
 #include <locale.h>
 #include <langinfo.h>
+#include "locale_impl.h"
 #include "libc.h"
 
 static const char c_time[] =
@@ -32,36 +33,37 @@
 	int idx = item & 65535;
 	const char *str;
 
-	if (item == CODESET) return "UTF-8";
+	if (item == CODESET) return MB_CUR_MAX==1 ? "ASCII" : "UTF-8";
 	
 	switch (cat) {
 	case LC_NUMERIC:
-		if (idx > 1) return NULL;
+		if (idx > 1) return "";
 		str = c_numeric;
 		break;
 	case LC_TIME:
-		if (idx > 0x31) return NULL;
+		if (idx > 0x31) return "";
 		str = c_time;
 		break;
 	case LC_MONETARY:
-		if (idx > 0) return NULL;
+		if (idx > 0) return "";
 		str = "";
 		break;
 	case LC_MESSAGES:
-		if (idx > 3) return NULL;
+		if (idx > 3) return "";
 		str = c_messages;
 		break;
 	default:
-		return NULL;
+		return "";
 	}
 
 	for (; idx; idx--, str++) for (; *str; str++);
+	if (cat != LC_NUMERIC && *str) str = LCTRANS(str, cat, loc);
 	return (char *)str;
 }
 
 char *__nl_langinfo(nl_item item)
 {
-	return __nl_langinfo_l(item, 0);
+	return __nl_langinfo_l(item, CURRENT_LOCALE);
 }
 
 weak_alias(__nl_langinfo, nl_langinfo);
diff --git a/system/lib/libc/musl/src/locale/legacychars.h b/system/lib/libc/musl/src/locale/legacychars.h
index 4ddbaeb..914ad0d 100644
--- a/system/lib/libc/musl/src/locale/legacychars.h
+++ b/system/lib/libc/musl/src/locale/legacychars.h
@@ -1,39 +1,41 @@
-0,1,160,167,168,169,175,176,178,183,184,198,215,216,230,247,248,256,257,258,
-259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,278,279,
-280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,
-299,302,303,304,305,308,309,310,311,312,313,314,315,316,317,318,321,322,323,
-324,325,326,327,328,330,331,332,333,336,337,338,339,340,341,342,343,344,345,
-346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,
-365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,402,
-416,417,431,432,536,537,538,539,710,711,728,729,731,732,733,768,769,771,777,
-803,890,900,901,902,904,905,906,908,910,911,912,913,914,915,916,917,918,919,
-920,921,922,923,924,925,926,927,928,929,931,932,933,934,935,936,937,938,939,
-940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,
-959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,1025,1026,
-1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,1038,1039,1040,1041,1042,
-1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,1053,1054,1055,1056,1057,
-1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,1068,1069,1070,1071,1072,
-1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085,1086,1087,
-1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,1098,1099,1100,1101,1102,
-1103,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1115,1116,1118,1119,
-1168,1169,1456,1457,1458,1459,1460,1461,1462,1463,1464,1465,1467,1468,1469,
-1470,1471,1472,1473,1474,1475,1488,1489,1490,1491,1492,1493,1494,1495,1496,
-1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,1507,1508,1509,1510,1511,
-1512,1513,1514,1520,1521,1522,1523,1524,1548,1563,1567,1569,1570,1571,1572,
-1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,1583,1584,1585,1586,1587,
-1588,1589,1590,1591,1592,1593,1594,1600,1601,1602,1603,1604,1605,1606,1607,
-1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,1618,1657,1662,1670,1672,
-1681,1688,1705,1711,1722,1726,1729,1746,3585,3586,3587,3588,3589,3590,3591,
-3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,
-3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,
-3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,
-3637,3638,3639,3640,3641,3642,3647,3648,3649,3650,3651,3652,3653,3654,3655,
-3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,
-3671,3672,3673,3674,3675,7682,7683,7690,7691,7710,7711,7744,7745,7766,7767,
-7776,7777,7786,7787,7808,7809,7810,7811,7812,7813,7922,7923,8204,8205,8206,
-8207,8211,8212,8213,8215,8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,
-8240,8249,8250,8362,8363,8364,8367,8470,8482,8729,8730,8776,8804,8805,8992,
-8993,9472,9474,9484,9488,9492,9496,9500,9508,9516,9524,9532,9552,9553,9554,
-9555,9556,9557,9558,9559,9560,9561,9562,9563,9564,9565,9566,9567,9568,9569,
-9570,9571,9572,9573,9574,9575,9576,9577,9578,9579,9580,9600,9604,9608,9612,
-9616,9617,9618,9619,9632,
+0,1,160,167,168,169,175,176,178,183,184,198,215,216,230,247,248,162,163,165,
+196,197,198,199,201,214,215,216,220,224,226,228,229,230,231,232,233,234,235,
+236,238,239,242,244,246,248,249,251,252,255,256,257,258,259,260,261,262,263,
+264,265,266,267,268,269,270,271,272,273,274,275,278,279,280,281,282,283,284,
+285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,302,303,304,305,
+308,309,310,311,312,313,314,315,316,317,318,321,322,323,324,325,326,327,328,
+330,331,332,333,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,
+351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,
+370,371,372,373,374,375,376,377,378,379,380,381,382,402,416,417,431,432,536,
+537,538,539,710,711,728,729,731,732,733,768,769,771,777,803,890,900,901,902,
+904,905,906,908,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,
+925,926,927,928,929,931,932,933,934,935,936,937,938,939,940,941,942,943,944,
+945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,
+964,965,966,967,968,969,970,
+971,972,973,974,1025,1026,1027,1028,1029,1030,1031,1032,1033,1034,1035,1036,
+1038,1039,1040,1041,1042,1043,1044,1045,1046,1047,1048,1049,1050,1051,1052,
+1053,1054,1055,1056,1057,1058,1059,1060,1061,1062,1063,1064,1065,1066,1067,
+1068,1069,1070,1071,1072,1073,1074,1075,1076,1077,1078,1079,1080,1081,1082,
+1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1093,1094,1095,1096,1097,
+1098,1099,1100,1101,1102,1103,1105,1106,1107,1108,1109,1110,1111,1112,1113,
+1114,1115,1116,1118,1119,1168,1169,1456,1457,1458,1459,1460,1461,1462,1463,
+1464,1465,1467,1468,1469,1470,1471,1472,1473,1474,1475,1488,1489,1490,1491,
+1492,1493,1494,1495,1496,1497,1498,1499,1500,1501,1502,1503,1504,1505,1506,
+1507,1508,1509,1510,1511,1512,1513,1514,1520,1521,1522,1523,1524,1548,1563,
+1567,1569,1570,1571,1572,1573,1574,1575,1576,1577,1578,1579,1580,1581,1582,
+1583,1584,1585,1586,1587,1588,1589,1590,1591,1592,1593,1594,1600,1601,1602,
+1603,1604,1605,1606,1607,1608,1609,1610,1611,1612,1613,1614,1615,1616,1617,
+1618,1657,1662,1670,1672,1681,1688,1705,1711,1722,1726,1729,1746,3585,3586,
+3587,3588,3589,3590,3591,3592,3593,3594,3595,3596,3597,3598,3599,3600,3601,
+3602,3603,3604,3605,3606,3607,3608,3609,3610,3611,3612,3613,3614,3615,3616,
+3617,3618,3619,3620,3621,3622,3623,3624,3625,3626,3627,3628,3629,3630,3631,
+3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642,3647,3648,3649,3650,
+3651,3652,3653,3654,3655,3656,3657,3658,3659,3660,3661,3662,3663,3664,3665,
+3666,3667,3668,3669,3670,3671,3672,3673,3674,3675,7682,7683,7690,7691,7710,
+7711,7744,7745,7766,7767,7776,7777,7786,7787,7808,7809,7810,7811,7812,7813,
+7922,7923,8204,8205,8206,8207,8211,8212,8213,8215,8216,8217,8218,8220,8221,
+8222,8224,8225,8226,8230,8240,8249,8250,8362,8363,8364,8367,8359,8470,8482,
+8729,8730,8776,8804,8805,8992,8993,9472,9474,9484,9488,9492,9496,9500,9508,
+9516,9524,9532,9552,9553,9554,9555,9556,9557,9558,9559,9560,9561,9562,9563,
+9564,9565,9566,9567,9568,9569,9570,9571,9572,9573,9574,9575,9576,9577,9578,
+9579,9580,9600,9604,9608,9612,9616,9617,9618,9619,9632,
diff --git a/system/lib/libc/musl/src/locale/locale_map.c b/system/lib/libc/musl/src/locale/locale_map.c
new file mode 100644
index 0000000..c3e5917
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/locale_map.c
@@ -0,0 +1,116 @@
+#include <locale.h>
+#include <string.h>
+#include "locale_impl.h"
+#include "libc.h"
+#include "atomic.h"
+
+const char *__lctrans_impl(const char *msg, const struct __locale_map *lm)
+{
+	const char *trans = 0;
+	if (lm) trans = __mo_lookup(lm->map, lm->map_size, msg);
+	return trans ? trans : msg;
+}
+
+const unsigned char *__map_file(const char *, size_t *);
+int __munmap(void *, size_t);
+char *__strchrnul(const char *, int);
+
+static const char envvars[][12] = {
+	"LC_CTYPE",
+	"LC_NUMERIC",
+	"LC_TIME",
+	"LC_COLLATE",
+	"LC_MONETARY",
+	"LC_MESSAGES",
+};
+
+const struct __locale_map *__get_locale(int cat, const char *val)
+{
+	static int lock[2];
+	static void *volatile loc_head;
+	const struct __locale_map *p;
+	struct __locale_map *new = 0;
+	const char *path = 0, *z;
+	char buf[256];
+	size_t l, n;
+
+	if (!*val) {
+		(val = getenv("LC_ALL")) && *val ||
+		(val = getenv(envvars[cat])) && *val ||
+		(val = getenv("LANG")) && *val ||
+		(val = "C.UTF-8");
+	}
+
+	/* Limit name length and forbid leading dot or any slashes. */
+	for (n=0; n<LOCALE_NAME_MAX && val[n] && val[n]!='/'; n++);
+	if (val[0]=='.' || val[n]) val = "C.UTF-8";
+	int builtin = (val[0]=='C' && !val[1])
+		|| !strcmp(val, "C.UTF-8")
+		|| !strcmp(val, "POSIX");
+
+	if (builtin) {
+		if (cat == LC_CTYPE && val[1]=='.')
+			return (void *)&__c_dot_utf8;
+		return 0;
+	}
+
+	for (p=loc_head; p; p=p->next)
+		if (!strcmp(val, p->name)) return p;
+
+	LOCK(lock);
+
+	for (p=loc_head; p; p=p->next)
+		if (!strcmp(val, p->name)) {
+			UNLOCK(lock);
+			return p;
+		}
+
+	if (!libc.secure) path = getenv("MUSL_LOCPATH");
+	/* FIXME: add a default path? */
+
+	if (path) for (; *path; path=z+!!*z) {
+		z = __strchrnul(path, ':');
+		l = z - path - !!*z;
+		if (l >= sizeof buf - n - 2) continue;
+		memcpy(buf, path, l);
+		buf[l] = '/';
+		memcpy(buf+l+1, val, n);
+		buf[l+1+n] = 0;
+		size_t map_size;
+		const void *map = __map_file(buf, &map_size);
+		if (map) {
+			new = malloc(sizeof *new);
+			if (!new) {
+				__munmap((void *)map, map_size);
+				break;
+			}
+			new->map = map;
+			new->map_size = map_size;
+			memcpy(new->name, val, n);
+			new->name[n] = 0;
+			new->next = loc_head;
+			loc_head = new;
+			break;
+		}
+	}
+
+	/* If no locale definition was found, make a locale map
+	 * object anyway to store the name, which is kept for the
+	 * sake of being able to do message translations at the
+	 * application level. */
+	if (!new && (new = malloc(sizeof *new))) {
+		new->map = __c_dot_utf8.map;
+		new->map_size = __c_dot_utf8.map_size;
+		memcpy(new->name, val, n);
+		new->name[n] = 0;
+		new->next = loc_head;
+		loc_head = new;
+	}
+
+	/* For LC_CTYPE, never return a null pointer unless the
+	 * requested name was "C" or "POSIX". */
+	if (!new && cat == LC_CTYPE) new = (void *)&__c_dot_utf8;
+
+	UNLOCK(lock);
+	return new;
+}
diff --git a/system/lib/libc/musl/src/locale/localeconv.c b/system/lib/libc/musl/src/locale/localeconv.c
index cbc75d7..4cbb9dc 100644
--- a/system/lib/libc/musl/src/locale/localeconv.c
+++ b/system/lib/libc/musl/src/locale/localeconv.c
@@ -1,4 +1,5 @@
 #include <locale.h>
+#include <limits.h>
 
 static const struct lconv posix_lconv = {
 	.decimal_point = ".",
@@ -11,20 +12,20 @@
 	.mon_grouping = "",
 	.positive_sign = "",
 	.negative_sign = "",
-	.int_frac_digits = -1,
-	.frac_digits = -1,
-	.p_cs_precedes = -1,
-	.p_sep_by_space = -1,
-	.n_cs_precedes = -1,
-	.n_sep_by_space = -1,
-	.p_sign_posn = -1,
-	.n_sign_posn = -1,
-	.int_p_cs_precedes = -1,
-	.int_p_sep_by_space = -1,
-	.int_n_cs_precedes = -1,
-	.int_n_sep_by_space = -1,
-	.int_p_sign_posn = -1,
-	.int_n_sign_posn = -1,
+	.int_frac_digits = CHAR_MAX,
+	.frac_digits = CHAR_MAX,
+	.p_cs_precedes = CHAR_MAX,
+	.p_sep_by_space = CHAR_MAX,
+	.n_cs_precedes = CHAR_MAX,
+	.n_sep_by_space = CHAR_MAX,
+	.p_sign_posn = CHAR_MAX,
+	.n_sign_posn = CHAR_MAX,
+	.int_p_cs_precedes = CHAR_MAX,
+	.int_p_sep_by_space = CHAR_MAX,
+	.int_n_cs_precedes = CHAR_MAX,
+	.int_n_sep_by_space = CHAR_MAX,
+	.int_p_sign_posn = CHAR_MAX,
+	.int_n_sign_posn = CHAR_MAX,
 };
 
 struct lconv *localeconv(void)
diff --git a/system/lib/libc/musl/src/locale/newlocale.c b/system/lib/libc/musl/src/locale/newlocale.c
index e4404cd..bbc3120 100644
--- a/system/lib/libc/musl/src/locale/newlocale.c
+++ b/system/lib/libc/musl/src/locale/newlocale.c
@@ -3,22 +3,45 @@
 #include "locale_impl.h"
 #include "libc.h"
 
-locale_t newlocale(int mask, const char *name, locale_t base)
+int __loc_is_allocated(locale_t loc)
 {
-	if (*name && strcmp(name, "C") && strcmp(name, "POSIX"))
-		return 0;
-	if (!base) {
-		// XXX EMSCRIPTEN: avoid a malloc (which does time(), sysconf(), sbrk(), etc.) during startup
-		static struct __locale_struct first;
-		static int used_first = 0;
-		if (!used_first) {
-			used_first = 1;
-			base = &first;
-		} else {
-			base = calloc(1, sizeof *base);
-		}
-	}
-	return base;
+	return loc && loc != C_LOCALE && loc != UTF8_LOCALE;
 }
 
-weak_alias(newlocale, __newlocale);
+locale_t __newlocale(int mask, const char *name, locale_t loc)
+{
+	int i, j;
+	struct __locale_struct tmp;
+	const struct __locale_map *lm;
+
+	/* For locales with allocated storage, modify in-place. */
+	if (__loc_is_allocated(loc)) {
+		for (i=0; i<LC_ALL; i++)
+			if (mask & (1<<i))
+				loc->cat[i] = __get_locale(i, name);
+		return loc;
+	}
+
+	/* Otherwise, build a temporary locale object, which will only
+	 * be instantiated in allocated storage if it does not match
+	 * one of the built-in static locales. This makes the common
+	 * usage case for newlocale, getting a C locale with predictable
+	 * behavior, very fast, and more importantly, fail-safe. */
+	for (j=i=0; i<LC_ALL; i++) {
+		if (loc && !(mask & (1<<i)))
+			lm = loc->cat[i];
+		else
+			lm = __get_locale(i, mask & (1<<i) ? name : "");
+		if (lm) j++;
+		tmp.cat[i] = lm;
+	}
+
+	if (!j)
+		return C_LOCALE;
+	if (j==1 && tmp.cat[LC_CTYPE]==&__c_dot_utf8)
+		return UTF8_LOCALE;
+
+	return loc;
+}
+
+weak_alias(__newlocale, newlocale);
diff --git a/system/lib/libc/musl/src/locale/pleval.c b/system/lib/libc/musl/src/locale/pleval.c
new file mode 100644
index 0000000..d60058d
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/pleval.c
@@ -0,0 +1,157 @@
+#include <stdlib.h>
+#include <ctype.h>
+
+/*
+grammar:
+
+Start = Expr ';'
+Expr  = Or | Or '?' Expr ':' Expr
+Or    = And | Or '||' And
+And   = Eq | And '&&' Eq
+Eq    = Rel | Eq '==' Rel | Eq '!=' Rel
+Rel   = Add | Rel '<=' Add | Rel '>=' Add | Rel '<' Add | Rel '>' Add
+Add   = Mul | Add '+' Mul | Add '-' Mul
+Mul   = Prim | Mul '*' Prim | Mul '/' Prim | Mul '%' Prim
+Prim  = '(' Expr ')' | '!' Prim | decimal | 'n'
+
+internals:
+
+recursive descent expression evaluator with stack depth limit.
+for binary operators an operator-precedence parser is used.
+eval* functions store the result of the parsed subexpression
+and return a pointer to the next non-space character.
+*/
+
+struct st {
+	unsigned long r;
+	unsigned long n;
+	int op;
+};
+
+static const char *skipspace(const char *s)
+{
+	while (isspace(*s)) s++;
+	return s;
+}
+
+static const char *evalexpr(struct st *st, const char *s, int d);
+
+static const char *evalprim(struct st *st, const char *s, int d)
+{
+	char *e;
+	if (--d < 0) return "";
+	s = skipspace(s);
+	if (isdigit(*s)) {
+		st->r = strtoul(s, &e, 10);
+		if (e == s || st->r == -1) return "";
+		return skipspace(e);
+	}
+	if (*s == 'n') {
+		st->r = st->n;
+		return skipspace(s+1);
+	}
+	if (*s == '(') {
+		s = evalexpr(st, s+1, d);
+		if (*s != ')') return "";
+		return skipspace(s+1);
+	}
+	if (*s == '!') {
+		s = evalprim(st, s+1, d);
+		st->r = !st->r;
+		return s;
+	}
+	return "";
+}
+
+static int binop(struct st *st, int op, unsigned long left)
+{
+	unsigned long a = left, b = st->r;
+	switch (op) {
+	case 0: st->r = a||b; return 0;
+	case 1: st->r = a&&b; return 0;
+	case 2: st->r = a==b; return 0;
+	case 3: st->r = a!=b; return 0;
+	case 4: st->r = a>=b; return 0;
+	case 5: st->r = a<=b; return 0;
+	case 6: st->r = a>b; return 0;
+	case 7: st->r = a<b; return 0;
+	case 8: st->r = a+b; return 0;
+	case 9: st->r = a-b; return 0;
+	case 10: st->r = a*b; return 0;
+	case 11: if (b) {st->r = a%b; return 0;} return 1;
+	case 12: if (b) {st->r = a/b; return 0;} return 1;
+	}
+	return 1;
+}
+
+static const char *parseop(struct st *st, const char *s)
+{
+	static const char opch[11] = "|&=!><+-*%/";
+	static const char opch2[6] = "|&====";
+	int i;
+	for (i=0; i<11; i++)
+		if (*s == opch[i]) {
+			/* note: >,< are accepted with or without = */
+			if (i<6 && s[1] == opch2[i]) {
+				st->op = i;
+				return s+2;
+			}
+			if (i>=4) {
+				st->op = i+2;
+				return s+1;
+			}
+			break;
+		}
+	st->op = 13;
+	return s;
+}
+
+static const char *evalbinop(struct st *st, const char *s, int minprec, int d)
+{
+	static const char prec[14] = {1,2,3,3,4,4,4,4,5,5,6,6,6,0};
+	unsigned long left;
+	int op;
+	d--;
+	s = evalprim(st, s, d);
+	s = parseop(st, s);
+	for (;;) {
+		/*
+		st->r (left hand side value) and st->op are now set,
+		get the right hand side or back out if op has low prec,
+		if op was missing then prec[op]==0
+		*/
+		op = st->op;
+		if (prec[op] <= minprec)
+			return s;
+		left = st->r;
+		s = evalbinop(st, s, prec[op], d);
+		if (binop(st, op, left))
+			return "";
+	}
+}
+
+static const char *evalexpr(struct st *st, const char *s, int d)
+{
+	unsigned long a, b;
+	if (--d < 0)
+		return "";
+	s = evalbinop(st, s, 0, d);
+	if (*s != '?')
+		return s;
+	a = st->r;
+	s = evalexpr(st, s+1, d);
+	if (*s != ':')
+		return "";
+	b = st->r;
+	s = evalexpr(st, s+1, d);
+	st->r = a ? b : st->r;
+	return s;
+}
+
+unsigned long __pleval(const char *s, unsigned long n)
+{
+	struct st st;
+	st.n = n;
+	s = evalexpr(&st, s, 100);
+	return *s == ';' ? st.r : -1;
+}
diff --git a/system/lib/libc/musl/src/locale/setlocale.c b/system/lib/libc/musl/src/locale/setlocale.c
index 28f29b8..8dae5a4 100644
--- a/system/lib/libc/musl/src/locale/setlocale.c
+++ b/system/lib/libc/musl/src/locale/setlocale.c
@@ -1,9 +1,70 @@
 #include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include "locale_impl.h"
+#include "libc.h"
+#include "atomic.h"
 
-char *setlocale(int category, const char *locale)
+static char buf[LC_ALL*(LOCALE_NAME_MAX+1)];
+
+static char *setlocale_one_unlocked(int cat, const char *name)
 {
-	/* Note: plain "C" would be better, but puts some broken
-	 * software into legacy 8-bit-codepage mode, ignoring
-	 * the standard library's multibyte encoding */
-	return "C.UTF-8";
+	const struct __locale_map *lm;
+
+	if (name) libc.global_locale.cat[cat] = lm = __get_locale(cat, name);
+	else lm = libc.global_locale.cat[cat];
+
+	return lm ? (char *)lm->name : "C";
+}
+
+char *__strchrnul(const char *, int);
+
+char *setlocale(int cat, const char *name)
+{
+	static volatile int lock[2];
+
+	if ((unsigned)cat > LC_ALL) return 0;
+
+	LOCK(lock);
+
+	/* For LC_ALL, setlocale is required to return a string which
+	 * encodes the current setting for all categories. The format of
+	 * this string is unspecified, and only the following code, which
+	 * performs both the serialization and deserialization, depends
+	 * on the format, so it can easily be changed if needed. */
+	if (cat == LC_ALL) {
+		int i;
+		if (name) {
+			char part[LOCALE_NAME_MAX+1] = "C.UTF-8";
+			const char *p = name;
+			for (i=0; i<LC_ALL; i++) {
+				const char *z = __strchrnul(p, ';');
+				if (z-p <= LOCALE_NAME_MAX) {
+					memcpy(part, p, z-p);
+					part[z-p] = 0;
+					if (*z) p = z+1;
+				}
+				setlocale_one_unlocked(i, part);
+			}
+		}
+		char *s = buf;
+		for (i=0; i<LC_ALL; i++) {
+			const struct __locale_map *lm =
+				libc.global_locale.cat[i];
+			const char *part = lm ? lm->name : "C";
+			size_t l = strlen(part);
+			memcpy(s, part, l);
+			s[l] = ';';
+			s += l+1;
+		}
+		*--s = 0;
+		UNLOCK(lock);
+		return buf;
+	}
+
+	char *ret = setlocale_one_unlocked(cat, name);
+
+	UNLOCK(lock);
+
+	return ret;
 }
diff --git a/system/lib/libc/musl/src/locale/strcoll.c b/system/lib/libc/musl/src/locale/strcoll.c
index 39ea112..84f199f 100644
--- a/system/lib/libc/musl/src/locale/strcoll.c
+++ b/system/lib/libc/musl/src/locale/strcoll.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <locale.h>
+#include "locale_impl.h"
 #include "libc.h"
 
 int __strcoll_l(const char *l, const char *r, locale_t loc)
@@ -9,7 +10,7 @@
 
 int strcoll(const char *l, const char *r)
 {
-	return __strcoll_l(l, r, 0);
+	return __strcoll_l(l, r, CURRENT_LOCALE);
 }
 
 weak_alias(__strcoll_l, strcoll_l);
diff --git a/system/lib/libc/musl/src/locale/strfmon.c b/system/lib/libc/musl/src/locale/strfmon.c
index e25db97..7cf2136 100644
--- a/system/lib/libc/musl/src/locale/strfmon.c
+++ b/system/lib/libc/musl/src/locale/strfmon.c
@@ -3,6 +3,7 @@
 #include <stdarg.h>
 #include <monetary.h>
 #include <errno.h>
+#include "locale_impl.h"
 
 static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_list ap)
 {
@@ -93,7 +94,7 @@
 	ssize_t ret;
 
 	va_start(ap, fmt);
-	ret = vstrfmon_l(s, n, 0, fmt, ap);
+	ret = vstrfmon_l(s, n, CURRENT_LOCALE, fmt, ap);
 	va_end(ap);
 
 	return ret;
diff --git a/system/lib/libc/musl/src/locale/strxfrm.c b/system/lib/libc/musl/src/locale/strxfrm.c
index 32c4619..14b76a6 100644
--- a/system/lib/libc/musl/src/locale/strxfrm.c
+++ b/system/lib/libc/musl/src/locale/strxfrm.c
@@ -1,5 +1,6 @@
 #include <string.h>
 #include <locale.h>
+#include "locale_impl.h"
 #include "libc.h"
 
 /* collate only by code points */
@@ -12,7 +13,7 @@
 
 size_t strxfrm(char *restrict dest, const char *restrict src, size_t n)
 {
-	return __strxfrm_l(dest, src, n, 0);
+	return __strxfrm_l(dest, src, n, CURRENT_LOCALE);
 }
 
 weak_alias(__strxfrm_l, strxfrm_l);
diff --git a/system/lib/libc/musl/src/locale/textdomain.c b/system/lib/libc/musl/src/locale/textdomain.c
new file mode 100644
index 0000000..c501501
--- /dev/null
+++ b/system/lib/libc/musl/src/locale/textdomain.c
@@ -0,0 +1,44 @@
+#include <libintl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include "libc.h"
+#include "atomic.h"
+
+static char *current_domain;
+
+char *__gettextdomain()
+{
+	return current_domain ? current_domain : "messages";
+}
+
+char *textdomain(const char *domainname)
+{
+	if (!domainname) return __gettextdomain();
+
+	size_t domlen = strlen(domainname);
+	if (domlen > NAME_MAX) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	if (!current_domain) {
+		current_domain = malloc(NAME_MAX+1);
+		if (!current_domain) return 0;
+	}
+
+	memcpy(current_domain, domainname, domlen+1);
+
+	return current_domain;
+}
+
+char *gettext(const char *msgid)
+{
+	return dgettext(0, msgid);
+}
+
+char *ngettext(const char *msgid1, const char *msgid2, unsigned long int n)
+{
+	return dngettext(0, msgid1, msgid2, n);
+}
diff --git a/system/lib/libc/musl/src/locale/uselocale.c b/system/lib/libc/musl/src/locale/uselocale.c
index 224ef38..0fc5ecb 100644
--- a/system/lib/libc/musl/src/locale/uselocale.c
+++ b/system/lib/libc/musl/src/locale/uselocale.c
@@ -2,12 +2,15 @@
 #include "pthread_impl.h"
 #include "libc.h"
 
-locale_t uselocale(locale_t l)
+locale_t __uselocale(locale_t new)
 {
-	pthread_t self = pthread_self();
+	pthread_t self = __pthread_self();
 	locale_t old = self->locale;
-	if (l) self->locale = l;
-	return old;
+	locale_t global = &libc.global_locale;
+
+	if (new) self->locale = new == LC_GLOBAL_LOCALE ? global : new;
+
+	return old == global ? LC_GLOBAL_LOCALE : old;
 }
 
-weak_alias(uselocale, __uselocale);
+weak_alias(__uselocale, uselocale);
diff --git a/system/lib/libc/musl/src/locale/wcscoll.c b/system/lib/libc/musl/src/locale/wcscoll.c
index 20a6090..14bb8b9 100644
--- a/system/lib/libc/musl/src/locale/wcscoll.c
+++ b/system/lib/libc/musl/src/locale/wcscoll.c
@@ -1,5 +1,6 @@
 #include <wchar.h>
 #include <locale.h>
+#include "locale_impl.h"
 #include "libc.h"
 
 /* FIXME: stub */
@@ -10,7 +11,7 @@
 
 int wcscoll(const wchar_t *l, const wchar_t *r)
 {
-	return __wcscoll_l(l, r, 0);
+	return __wcscoll_l(l, r, CURRENT_LOCALE);
 }
 
 weak_alias(__wcscoll_l, wcscoll_l);
diff --git a/system/lib/libc/musl/src/locale/wcsxfrm.c b/system/lib/libc/musl/src/locale/wcsxfrm.c
index 5d89e7d..0a0b776 100644
--- a/system/lib/libc/musl/src/locale/wcsxfrm.c
+++ b/system/lib/libc/musl/src/locale/wcsxfrm.c
@@ -1,5 +1,6 @@
 #include <wchar.h>
 #include <locale.h>
+#include "locale_impl.h"
 #include "libc.h"
 
 /* collate only by code points */
@@ -17,7 +18,7 @@
 
 size_t wcsxfrm(wchar_t *restrict dest, const wchar_t *restrict src, size_t n)
 {
-	return __wcsxfrm_l(dest, src, n, 0);
+	return __wcsxfrm_l(dest, src, n, CURRENT_LOCALE);
 }
 
 weak_alias(__wcsxfrm_l, wcsxfrm_l);
diff --git a/system/lib/libc/musl/src/math/__fpclassifyl.c b/system/lib/libc/musl/src/math/__fpclassifyl.c
index 6dc1002..481c0b9 100644
--- a/system/lib/libc/musl/src/math/__fpclassifyl.c
+++ b/system/lib/libc/musl/src/math/__fpclassifyl.c
@@ -1,7 +1,10 @@
 #include "libm.h"
 
 #if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
-
+int __fpclassifyl(long double x)
+{
+	return __fpclassify(x);
+}
 #elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
 int __fpclassifyl(long double x)
 {
@@ -21,12 +24,11 @@
 {
 	union ldshape u = {x};
 	int e = u.i.se & 0x7fff;
+	u.i.se = 0;
 	if (!e)
 		return u.i2.lo | u.i2.hi ? FP_SUBNORMAL : FP_ZERO;
-	if (e == 0x7fff) {
-		u.i.se = 0;
+	if (e == 0x7fff)
 		return u.i2.lo | u.i2.hi ? FP_NAN : FP_INFINITE;
-	}
 	return FP_NORMAL;
 }
 #endif
diff --git a/system/lib/libc/musl/src/math/__polevll.c b/system/lib/libc/musl/src/math/__polevll.c
index a272865..ce1a840 100644
--- a/system/lib/libc/musl/src/math/__polevll.c
+++ b/system/lib/libc/musl/src/math/__polevll.c
@@ -56,6 +56,8 @@
 
 #include "libm.h"
 
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+#else
 /*
  * Polynomial evaluator:
  *  P[0] x^n  +  P[1] x^(n-1)  +  ...  +  P[n]
@@ -88,3 +90,4 @@
 
 	return y;
 }
+#endif
diff --git a/system/lib/libc/musl/src/math/__rem_pio2.c b/system/lib/libc/musl/src/math/__rem_pio2.c
index 5fafc4a..d403f81 100644
--- a/system/lib/libc/musl/src/math/__rem_pio2.c
+++ b/system/lib/libc/musl/src/math/__rem_pio2.c
@@ -19,6 +19,12 @@
 
 #include "libm.h"
 
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+
 /*
  * invpio2:  53 bits of 2/pi
  * pio2_1:   first  33 bit of pi/2
@@ -29,6 +35,7 @@
  * pio2_3t:  pi/2 - (pio2_1+pio2_2+pio2_3)
  */
 static const double
+toint   = 1.5/EPS,
 invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */
 pio2_1  = 1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */
 pio2_1t = 6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */
@@ -41,8 +48,8 @@
 int __rem_pio2(double x, double *y)
 {
 	union {double f; uint64_t i;} u = {x};
-	double_t z,w,t,r;
-	double tx[3],ty[2],fn;
+	double_t z,w,t,r,fn;
+	double tx[3],ty[2];
 	uint32_t ix;
 	int sign, n, ex, ey, i;
 
@@ -111,8 +118,7 @@
 	if (ix < 0x413921fb) {  /* |x| ~< 2^20*(pi/2), medium size */
 medium:
 		/* rint(x/(pi/2)), Assume round-to-nearest. */
-		fn = x*invpio2 + 0x1.8p52;
-		fn = fn - 0x1.8p52;
+		fn = (double_t)x*invpio2 + toint - toint;
 		n = (int32_t)fn;
 		r = x - fn*pio2_1;
 		w = fn*pio2_1t;  /* 1st round, good to 85 bits */
diff --git a/system/lib/libc/musl/src/math/__rem_pio2f.c b/system/lib/libc/musl/src/math/__rem_pio2f.c
index 838e1fc..4473c1c 100644
--- a/system/lib/libc/musl/src/math/__rem_pio2f.c
+++ b/system/lib/libc/musl/src/math/__rem_pio2f.c
@@ -22,12 +22,19 @@
 
 #include "libm.h"
 
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+
 /*
  * invpio2:  53 bits of 2/pi
  * pio2_1:   first 25 bits of pi/2
  * pio2_1t:  pi/2 - pio2_1
  */
 static const double
+toint   = 1.5/EPS,
 invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */
 pio2_1  = 1.57079631090164184570e+00, /* 0x3FF921FB, 0x50000000 */
 pio2_1t = 1.58932547735281966916e-08; /* 0x3E5110b4, 0x611A6263 */
@@ -35,7 +42,8 @@
 int __rem_pio2f(float x, double *y)
 {
 	union {float f; uint32_t i;} u = {x};
-	double tx[1],ty[1],fn;
+	double tx[1],ty[1];
+	double_t fn;
 	uint32_t ix;
 	int n, sign, e0;
 
@@ -43,8 +51,7 @@
 	/* 25+53 bit pi is good enough for medium size */
 	if (ix < 0x4dc90fdb) {  /* |x| ~< 2^28*(pi/2), medium size */
 		/* Use a specialized rint() to get fn.  Assume round-to-nearest. */
-		fn = x*invpio2 + 0x1.8p52;
-		fn = fn - 0x1.8p52;
+		fn = (double_t)x*invpio2 + toint - toint;
 		n  = (int32_t)fn;
 		*y = x - fn*pio2_1 - fn*pio2_1t;
 		return n;
diff --git a/system/lib/libc/musl/src/math/__rem_pio2l.c b/system/lib/libc/musl/src/math/__rem_pio2l.c
index 8b15b7b..77255bd 100644
--- a/system/lib/libc/musl/src/math/__rem_pio2l.c
+++ b/system/lib/libc/musl/src/math/__rem_pio2l.c
@@ -20,10 +20,11 @@
  * use __rem_pio2_large() for large x
  */
 
+static const long double toint = 1.5/LDBL_EPSILON;
+
 #if LDBL_MANT_DIG == 64
 /* u ~< 0x1p25*pi/2 */
 #define SMALL(u) (((u.i.se & 0x7fffU)<<16 | u.i.m>>48) < ((0x3fff + 25)<<16 | 0x921f>>1 | 0x8000))
-#define TOINT 0x1.8p63
 #define QUOBITS(x) ((uint32_t)(int32_t)x & 0x7fffffff)
 #define ROUND1 22
 #define ROUND2 61
@@ -50,7 +51,6 @@
 #elif LDBL_MANT_DIG == 113
 /* u ~< 0x1p45*pi/2 */
 #define SMALL(u) (((u.i.se & 0x7fffU)<<16 | u.i.top) < ((0x3fff + 45)<<16 | 0x921f))
-#define TOINT 0x1.8p112
 #define QUOBITS(x) ((uint32_t)(int64_t)x & 0x7fffffff)
 #define ROUND1 51
 #define ROUND2 119
@@ -77,7 +77,7 @@
 	ex = u.i.se & 0x7fff;
 	if (SMALL(u)) {
 		/* rint(x/(pi/2)), Assume round-to-nearest. */
-		fn = x*invpio2 + TOINT - TOINT;
+		fn = x*invpio2 + toint - toint;
 		n = QUOBITS(fn);
 		r = x-fn*pio2_1;
 		w = fn*pio2_1t;  /* 1st round good to 102/180 bits (ld80/ld128) */
diff --git a/system/lib/libc/musl/src/math/__signbitl.c b/system/lib/libc/musl/src/math/__signbitl.c
index c52c87b..63b3dc5 100644
--- a/system/lib/libc/musl/src/math/__signbitl.c
+++ b/system/lib/libc/musl/src/math/__signbitl.c
@@ -6,4 +6,9 @@
 	union ldshape u = {x};
 	return u.i.se >> 15;
 }
+#elif LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+int __signbitl(long double x)
+{
+	return __signbit(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/acoshl.c b/system/lib/libc/musl/src/math/acoshl.c
index 4aa84ac..8d4b43f 100644
--- a/system/lib/libc/musl/src/math/acoshl.c
+++ b/system/lib/libc/musl/src/math/acoshl.c
@@ -20,4 +20,10 @@
 		return logl(2*x - 1/(x+sqrtl(x*x-1)));
 	return logl(x) + 0.693147180559945309417232121458176568L;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double acoshl(long double x)
+{
+	return acosh(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/asinhl.c b/system/lib/libc/musl/src/math/asinhl.c
index e5f3175..8635f52 100644
--- a/system/lib/libc/musl/src/math/asinhl.c
+++ b/system/lib/libc/musl/src/math/asinhl.c
@@ -32,4 +32,10 @@
 	}
 	return s ? -x : x;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double asinhl(long double x)
+{
+	return asinh(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/atanhl.c b/system/lib/libc/musl/src/math/atanhl.c
index f63d60b..87cd1cd 100644
--- a/system/lib/libc/musl/src/math/atanhl.c
+++ b/system/lib/libc/musl/src/math/atanhl.c
@@ -5,7 +5,7 @@
 {
 	return atanh(x);
 }
-#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
 /* atanh(x) = log((1+x)/(1-x))/2 = log1p(2x/(1-x))/2 ~= x + x^3/3 + o(x^5) */
 long double atanhl(long double x)
 {
diff --git a/system/lib/libc/musl/src/math/ceil.c b/system/lib/libc/musl/src/math/ceil.c
index 22dc224..b13e6f2 100644
--- a/system/lib/libc/musl/src/math/ceil.c
+++ b/system/lib/libc/musl/src/math/ceil.c
@@ -1,5 +1,12 @@
 #include "libm.h"
 
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const double_t toint = 1/EPS;
+
 double ceil(double x)
 {
 	union {double f; uint64_t i;} u = {x};
@@ -10,9 +17,9 @@
 		return x;
 	/* y = int(x) - x, where int(x) is an integer neighbor of x */
 	if (u.i >> 63)
-		y = (double)(x - 0x1p52) + 0x1p52 - x;
+		y = x - toint + toint - x;
 	else
-		y = (double)(x + 0x1p52) - 0x1p52 - x;
+		y = x + toint - toint - x;
 	/* special case because of non-nearest rounding modes */
 	if (e <= 0x3ff-1) {
 		FORCE_EVAL(y);
diff --git a/system/lib/libc/musl/src/math/ceill.c b/system/lib/libc/musl/src/math/ceill.c
index a2cb0a7..60a8302 100644
--- a/system/lib/libc/musl/src/math/ceill.c
+++ b/system/lib/libc/musl/src/math/ceill.c
@@ -6,11 +6,9 @@
 	return ceil(x);
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-#if LDBL_MANT_DIG == 64
-#define TOINT 0x1p63
-#elif LDBL_MANT_DIG == 113
-#define TOINT 0x1p112
-#endif
+
+static const long double toint = 1/LDBL_EPSILON;
+
 long double ceill(long double x)
 {
 	union ldshape u = {x};
@@ -21,9 +19,9 @@
 		return x;
 	/* y = int(x) - x, where int(x) is an integer neighbor of x */
 	if (u.i.se >> 15)
-		y = x - TOINT + TOINT - x;
+		y = x - toint + toint - x;
 	else
-		y = x + TOINT - TOINT - x;
+		y = x + toint - toint - x;
 	/* special case because of non-nearest rounding modes */
 	if (e <= 0x3fff-1) {
 		FORCE_EVAL(y);
diff --git a/system/lib/libc/musl/src/math/coshl.c b/system/lib/libc/musl/src/math/coshl.c
index 080e5eb..06a56fe 100644
--- a/system/lib/libc/musl/src/math/coshl.c
+++ b/system/lib/libc/musl/src/math/coshl.c
@@ -38,4 +38,10 @@
 	t = expl(0.5*x);
 	return 0.5*t*t;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double coshl(long double x)
+{
+	return cosh(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/erfl.c b/system/lib/libc/musl/src/math/erfl.c
index 96b74de..e267c23 100644
--- a/system/lib/libc/musl/src/math/erfl.c
+++ b/system/lib/libc/musl/src/math/erfl.c
@@ -340,4 +340,14 @@
 	y = 0x1p-16382L;
 	return sign ? 2 - y : y*y;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double erfl(long double x)
+{
+	return erf(x);
+}
+long double erfcl(long double x)
+{
+	return erfc(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/exp10.c b/system/lib/libc/musl/src/math/exp10.c
index 16d704a..9f5e3c2 100644
--- a/system/lib/libc/musl/src/math/exp10.c
+++ b/system/lib/libc/musl/src/math/exp10.c
@@ -1,5 +1,6 @@
 #define _GNU_SOURCE
 #include <math.h>
+#include <stdint.h>
 #include "libc.h"
 
 double exp10(double x)
@@ -11,7 +12,9 @@
 		1e10, 1e11, 1e12, 1e13, 1e14, 1e15
 	};
 	double n, y = modf(x, &n);
-	if (fabs(n) < 16) {
+	union {double f; uint64_t i;} u = {n};
+	/* fabs(n) < 16 without raising invalid on nan */
+	if ((u.i>>52 & 0x7ff) < 0x3ff+4) {
 		if (!y) return p10[(int)n+15];
 		y = exp2(3.32192809488736234787031942948939 * y);
 		return y * p10[(int)n+15];
diff --git a/system/lib/libc/musl/src/math/exp10f.c b/system/lib/libc/musl/src/math/exp10f.c
index 5fd1af9..7a8d447 100644
--- a/system/lib/libc/musl/src/math/exp10f.c
+++ b/system/lib/libc/musl/src/math/exp10f.c
@@ -1,5 +1,6 @@
 #define _GNU_SOURCE
 #include <math.h>
+#include <stdint.h>
 #include "libc.h"
 
 float exp10f(float x)
@@ -9,7 +10,9 @@
 		1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7
 	};
 	float n, y = modff(x, &n);
-	if (fabsf(n) < 8) {
+	union {float f; uint32_t i;} u = {n};
+	/* fabsf(n) < 8 without raising invalid on nan */
+	if ((u.i>>23 & 0xff) < 0x7f+3) {
 		if (!y) return p10[(int)n+7];
 		y = exp2f(3.32192809488736234787031942948939f * y);
 		return y * p10[(int)n+7];
diff --git a/system/lib/libc/musl/src/math/exp10l.c b/system/lib/libc/musl/src/math/exp10l.c
index 22a4636..b758ebf 100644
--- a/system/lib/libc/musl/src/math/exp10l.c
+++ b/system/lib/libc/musl/src/math/exp10l.c
@@ -1,7 +1,15 @@
 #define _GNU_SOURCE
+#include <float.h>
 #include <math.h>
 #include "libc.h"
+#include "libm.h"
 
+#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
+long double exp10l(long double x)
+{
+	return exp10(x);
+}
+#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
 long double exp10l(long double x)
 {
 	static const long double p10[] = {
@@ -11,12 +19,15 @@
 		1e10, 1e11, 1e12, 1e13, 1e14, 1e15
 	};
 	long double n, y = modfl(x, &n);
-	if (fabsl(n) < 16) {
+	union ldshape u = {n};
+	/* fabsl(n) < 16 without raising invalid on nan */
+	if ((u.i.se & 0x7fff) < 0x3fff+4) {
 		if (!y) return p10[(int)n+15];
 		y = exp2l(3.32192809488736234787031942948939L * y);
 		return y * p10[(int)n+15];
 	}
 	return powl(10.0, x);
 }
+#endif
 
 weak_alias(exp10l, pow10l);
diff --git a/system/lib/libc/musl/src/math/exp2f.c b/system/lib/libc/musl/src/math/exp2f.c
index cf6126e..296b634 100644
--- a/system/lib/libc/musl/src/math/exp2f.c
+++ b/system/lib/libc/musl/src/math/exp2f.c
@@ -91,6 +91,8 @@
 	/* Filter out exceptional cases. */
 	ix = u.i & 0x7fffffff;
 	if (ix > 0x42fc0000) {  /* |x| > 126 */
+		if (ix > 0x7f800000) /* NaN */
+			return x;
 		if (u.i >= 0x43000000 && u.i < 0x80000000) {  /* x >= 128 */
 			x *= 0x1p127f;
 			return x;
diff --git a/system/lib/libc/musl/src/math/exp2l.c b/system/lib/libc/musl/src/math/exp2l.c
index 8fc4037..3565c1e 100644
--- a/system/lib/libc/musl/src/math/exp2l.c
+++ b/system/lib/libc/musl/src/math/exp2l.c
@@ -1,4 +1,4 @@
-/* origin: FreeBSD /usr/src/lib/msun/ld80/s_exp2l.c */
+/* origin: FreeBSD /usr/src/lib/msun/ld80/s_exp2l.c and /usr/src/lib/msun/ld128/s_exp2l.c */
 /*-
  * Copyright (c) 2005-2008 David Schultz <das@FreeBSD.ORG>
  * All rights reserved.
@@ -251,4 +251,369 @@
 
 	return scalbnl(r, k.i);
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+#define TBLBITS 7
+#define TBLSIZE (1 << TBLBITS)
+
+static const long double
+    P1        = 0x1.62e42fefa39ef35793c7673007e6p-1L,
+    P2        = 0x1.ebfbdff82c58ea86f16b06ec9736p-3L,
+    P3        = 0x1.c6b08d704a0bf8b33a762bad3459p-5L,
+    P4        = 0x1.3b2ab6fba4e7729ccbbe0b4f3fc2p-7L,
+    P5        = 0x1.5d87fe78a67311071dee13fd11d9p-10L,
+    P6        = 0x1.430912f86c7876f4b663b23c5fe5p-13L;
+
+static const double
+    P7        = 0x1.ffcbfc588b041p-17,
+    P8        = 0x1.62c0223a5c7c7p-20,
+    P9        = 0x1.b52541ff59713p-24,
+    P10       = 0x1.e4cf56a391e22p-28,
+    redux     = 0x1.8p112 / TBLSIZE;
+
+static const long double tbl[TBLSIZE] = {
+	0x1.6a09e667f3bcc908b2fb1366dfeap-1L,
+	0x1.6c012750bdabeed76a99800f4edep-1L,
+	0x1.6dfb23c651a2ef220e2cbe1bc0d4p-1L,
+	0x1.6ff7df9519483cf87e1b4f3e1e98p-1L,
+	0x1.71f75e8ec5f73dd2370f2ef0b148p-1L,
+	0x1.73f9a48a58173bd5c9a4e68ab074p-1L,
+	0x1.75feb564267c8bf6e9aa33a489a8p-1L,
+	0x1.780694fde5d3f619ae02808592a4p-1L,
+	0x1.7a11473eb0186d7d51023f6ccb1ap-1L,
+	0x1.7c1ed0130c1327c49334459378dep-1L,
+	0x1.7e2f336cf4e62105d02ba1579756p-1L,
+	0x1.80427543e1a11b60de67649a3842p-1L,
+	0x1.82589994cce128acf88afab34928p-1L,
+	0x1.8471a4623c7acce52f6b97c6444cp-1L,
+	0x1.868d99b4492ec80e41d90ac2556ap-1L,
+	0x1.88ac7d98a669966530bcdf2d4cc0p-1L,
+	0x1.8ace5422aa0db5ba7c55a192c648p-1L,
+	0x1.8cf3216b5448bef2aa1cd161c57ap-1L,
+	0x1.8f1ae991577362b982745c72eddap-1L,
+	0x1.9145b0b91ffc588a61b469f6b6a0p-1L,
+	0x1.93737b0cdc5e4f4501c3f2540ae8p-1L,
+	0x1.95a44cbc8520ee9b483695a0e7fep-1L,
+	0x1.97d829fde4e4f8b9e920f91e8eb6p-1L,
+	0x1.9a0f170ca07b9ba3109b8c467844p-1L,
+	0x1.9c49182a3f0901c7c46b071f28dep-1L,
+	0x1.9e86319e323231824ca78e64c462p-1L,
+	0x1.a0c667b5de564b29ada8b8cabbacp-1L,
+	0x1.a309bec4a2d3358c171f770db1f4p-1L,
+	0x1.a5503b23e255c8b424491caf88ccp-1L,
+	0x1.a799e1330b3586f2dfb2b158f31ep-1L,
+	0x1.a9e6b5579fdbf43eb243bdff53a2p-1L,
+	0x1.ac36bbfd3f379c0db966a3126988p-1L,
+	0x1.ae89f995ad3ad5e8734d17731c80p-1L,
+	0x1.b0e07298db66590842acdfc6fb4ep-1L,
+	0x1.b33a2b84f15faf6bfd0e7bd941b0p-1L,
+	0x1.b59728de559398e3881111648738p-1L,
+	0x1.b7f76f2fb5e46eaa7b081ab53ff6p-1L,
+	0x1.ba5b030a10649840cb3c6af5b74cp-1L,
+	0x1.bcc1e904bc1d2247ba0f45b3d06cp-1L,
+	0x1.bf2c25bd71e088408d7025190cd0p-1L,
+	0x1.c199bdd85529c2220cb12a0916bap-1L,
+	0x1.c40ab5fffd07a6d14df820f17deap-1L,
+	0x1.c67f12e57d14b4a2137fd20f2a26p-1L,
+	0x1.c8f6d9406e7b511acbc48805c3f6p-1L,
+	0x1.cb720dcef90691503cbd1e949d0ap-1L,
+	0x1.cdf0b555dc3f9c44f8958fac4f12p-1L,
+	0x1.d072d4a07897b8d0f22f21a13792p-1L,
+	0x1.d2f87080d89f18ade123989ea50ep-1L,
+	0x1.d5818dcfba48725da05aeb66dff8p-1L,
+	0x1.d80e316c98397bb84f9d048807a0p-1L,
+	0x1.da9e603db3285708c01a5b6d480cp-1L,
+	0x1.dd321f301b4604b695de3c0630c0p-1L,
+	0x1.dfc97337b9b5eb968cac39ed284cp-1L,
+	0x1.e264614f5a128a12761fa17adc74p-1L,
+	0x1.e502ee78b3ff6273d130153992d0p-1L,
+	0x1.e7a51fbc74c834b548b2832378a4p-1L,
+	0x1.ea4afa2a490d9858f73a18f5dab4p-1L,
+	0x1.ecf482d8e67f08db0312fb949d50p-1L,
+	0x1.efa1bee615a27771fd21a92dabb6p-1L,
+	0x1.f252b376bba974e8696fc3638f24p-1L,
+	0x1.f50765b6e4540674f84b762861a6p-1L,
+	0x1.f7bfdad9cbe138913b4bfe72bd78p-1L,
+	0x1.fa7c1819e90d82e90a7e74b26360p-1L,
+	0x1.fd3c22b8f71f10975ba4b32bd006p-1L,
+	0x1.0000000000000000000000000000p+0L,
+	0x1.0163da9fb33356d84a66ae336e98p+0L,
+	0x1.02c9a3e778060ee6f7caca4f7a18p+0L,
+	0x1.04315e86e7f84bd738f9a20da442p+0L,
+	0x1.059b0d31585743ae7c548eb68c6ap+0L,
+	0x1.0706b29ddf6ddc6dc403a9d87b1ep+0L,
+	0x1.0874518759bc808c35f25d942856p+0L,
+	0x1.09e3ecac6f3834521e060c584d5cp+0L,
+	0x1.0b5586cf9890f6298b92b7184200p+0L,
+	0x1.0cc922b7247f7407b705b893dbdep+0L,
+	0x1.0e3ec32d3d1a2020742e4f8af794p+0L,
+	0x1.0fb66affed31af232091dd8a169ep+0L,
+	0x1.11301d0125b50a4ebbf1aed9321cp+0L,
+	0x1.12abdc06c31cbfb92bad324d6f84p+0L,
+	0x1.1429aaea92ddfb34101943b2588ep+0L,
+	0x1.15a98c8a58e512480d573dd562aep+0L,
+	0x1.172b83c7d517adcdf7c8c50eb162p+0L,
+	0x1.18af9388c8de9bbbf70b9a3c269cp+0L,
+	0x1.1a35beb6fcb753cb698f692d2038p+0L,
+	0x1.1bbe084045cd39ab1e72b442810ep+0L,
+	0x1.1d4873168b9aa7805b8028990be8p+0L,
+	0x1.1ed5022fcd91cb8819ff61121fbep+0L,
+	0x1.2063b88628cd63b8eeb0295093f6p+0L,
+	0x1.21f49917ddc962552fd29294bc20p+0L,
+	0x1.2387a6e75623866c1fadb1c159c0p+0L,
+	0x1.251ce4fb2a63f3582ab7de9e9562p+0L,
+	0x1.26b4565e27cdd257a673281d3068p+0L,
+	0x1.284dfe1f5638096cf15cf03c9fa0p+0L,
+	0x1.29e9df51fdee12c25d15f5a25022p+0L,
+	0x1.2b87fd0dad98ffddea46538fca24p+0L,
+	0x1.2d285a6e4030b40091d536d0733ep+0L,
+	0x1.2ecafa93e2f5611ca0f45d5239a4p+0L,
+	0x1.306fe0a31b7152de8d5a463063bep+0L,
+	0x1.32170fc4cd8313539cf1c3009330p+0L,
+	0x1.33c08b26416ff4c9c8610d96680ep+0L,
+	0x1.356c55f929ff0c94623476373be4p+0L,
+	0x1.371a7373aa9caa7145502f45452ap+0L,
+	0x1.38cae6d05d86585a9cb0d9bed530p+0L,
+	0x1.3a7db34e59ff6ea1bc9299e0a1fep+0L,
+	0x1.3c32dc313a8e484001f228b58cf0p+0L,
+	0x1.3dea64c12342235b41223e13d7eep+0L,
+	0x1.3fa4504ac801ba0bf701aa417b9cp+0L,
+	0x1.4160a21f72e29f84325b8f3dbacap+0L,
+	0x1.431f5d950a896dc704439410b628p+0L,
+	0x1.44e086061892d03136f409df0724p+0L,
+	0x1.46a41ed1d005772512f459229f0ap+0L,
+	0x1.486a2b5c13cd013c1a3b69062f26p+0L,
+	0x1.4a32af0d7d3de672d8bcf46f99b4p+0L,
+	0x1.4bfdad5362a271d4397afec42e36p+0L,
+	0x1.4dcb299fddd0d63b36ef1a9e19dep+0L,
+	0x1.4f9b2769d2ca6ad33d8b69aa0b8cp+0L,
+	0x1.516daa2cf6641c112f52c84d6066p+0L,
+	0x1.5342b569d4f81df0a83c49d86bf4p+0L,
+	0x1.551a4ca5d920ec52ec620243540cp+0L,
+	0x1.56f4736b527da66ecb004764e61ep+0L,
+	0x1.58d12d497c7fd252bc2b7343d554p+0L,
+	0x1.5ab07dd48542958c93015191e9a8p+0L,
+	0x1.5c9268a5946b701c4b1b81697ed4p+0L,
+	0x1.5e76f15ad21486e9be4c20399d12p+0L,
+	0x1.605e1b976dc08b076f592a487066p+0L,
+	0x1.6247eb03a5584b1f0fa06fd2d9eap+0L,
+	0x1.6434634ccc31fc76f8714c4ee122p+0L,
+	0x1.66238825522249127d9e29b92ea2p+0L,
+	0x1.68155d44ca973081c57227b9f69ep+0L,
+};
+
+static const float eps[TBLSIZE] = {
+	-0x1.5c50p-101,
+	-0x1.5d00p-106,
+	 0x1.8e90p-102,
+	-0x1.5340p-103,
+	 0x1.1bd0p-102,
+	-0x1.4600p-105,
+	-0x1.7a40p-104,
+	 0x1.d590p-102,
+	-0x1.d590p-101,
+	 0x1.b100p-103,
+	-0x1.0d80p-105,
+	 0x1.6b00p-103,
+	-0x1.9f00p-105,
+	 0x1.c400p-103,
+	 0x1.e120p-103,
+	-0x1.c100p-104,
+	-0x1.9d20p-103,
+	 0x1.a800p-108,
+	 0x1.4c00p-106,
+	-0x1.9500p-106,
+	 0x1.6900p-105,
+	-0x1.29d0p-100,
+	 0x1.4c60p-103,
+	 0x1.13a0p-102,
+	-0x1.5b60p-103,
+	-0x1.1c40p-103,
+	 0x1.db80p-102,
+	 0x1.91a0p-102,
+	 0x1.dc00p-105,
+	 0x1.44c0p-104,
+	 0x1.9710p-102,
+	 0x1.8760p-103,
+	-0x1.a720p-103,
+	 0x1.ed20p-103,
+	-0x1.49c0p-102,
+	-0x1.e000p-111,
+	 0x1.86a0p-103,
+	 0x1.2b40p-103,
+	-0x1.b400p-108,
+	 0x1.1280p-99,
+	-0x1.02d8p-102,
+	-0x1.e3d0p-103,
+	-0x1.b080p-105,
+	-0x1.f100p-107,
+	-0x1.16c0p-105,
+	-0x1.1190p-103,
+	-0x1.a7d2p-100,
+	 0x1.3450p-103,
+	-0x1.67c0p-105,
+	 0x1.4b80p-104,
+	-0x1.c4e0p-103,
+	 0x1.6000p-108,
+	-0x1.3f60p-105,
+	 0x1.93f0p-104,
+	 0x1.5fe0p-105,
+	 0x1.6f80p-107,
+	-0x1.7600p-106,
+	 0x1.21e0p-106,
+	-0x1.3a40p-106,
+	-0x1.40c0p-104,
+	-0x1.9860p-105,
+	-0x1.5d40p-108,
+	-0x1.1d70p-106,
+	 0x1.2760p-105,
+	 0x0.0000p+0,
+	 0x1.21e2p-104,
+	-0x1.9520p-108,
+	-0x1.5720p-106,
+	-0x1.4810p-106,
+	-0x1.be00p-109,
+	 0x1.0080p-105,
+	-0x1.5780p-108,
+	-0x1.d460p-105,
+	-0x1.6140p-105,
+	 0x1.4630p-104,
+	 0x1.ad50p-103,
+	 0x1.82e0p-105,
+	 0x1.1d3cp-101,
+	 0x1.6100p-107,
+	 0x1.ec30p-104,
+	 0x1.f200p-108,
+	 0x1.0b40p-103,
+	 0x1.3660p-102,
+	 0x1.d9d0p-103,
+	-0x1.02d0p-102,
+	 0x1.b070p-103,
+	 0x1.b9c0p-104,
+	-0x1.01c0p-103,
+	-0x1.dfe0p-103,
+	 0x1.1b60p-104,
+	-0x1.ae94p-101,
+	-0x1.3340p-104,
+	 0x1.b3d8p-102,
+	-0x1.6e40p-105,
+	-0x1.3670p-103,
+	 0x1.c140p-104,
+	 0x1.1840p-101,
+	 0x1.1ab0p-102,
+	-0x1.a400p-104,
+	 0x1.1f00p-104,
+	-0x1.7180p-103,
+	 0x1.4ce0p-102,
+	 0x1.9200p-107,
+	-0x1.54c0p-103,
+	 0x1.1b80p-105,
+	-0x1.1828p-101,
+	 0x1.5720p-102,
+	-0x1.a060p-100,
+	 0x1.9160p-102,
+	 0x1.a280p-104,
+	 0x1.3400p-107,
+	 0x1.2b20p-102,
+	 0x1.7800p-108,
+	 0x1.cfd0p-101,
+	 0x1.2ef0p-102,
+	-0x1.2760p-99,
+	 0x1.b380p-104,
+	 0x1.0048p-101,
+	-0x1.60b0p-102,
+	 0x1.a1ccp-100,
+	-0x1.a640p-104,
+	-0x1.08a0p-101,
+	 0x1.7e60p-102,
+	 0x1.22c0p-103,
+	-0x1.7200p-106,
+	 0x1.f0f0p-102,
+	 0x1.eb4ep-99,
+	 0x1.c6e0p-103,
+};
+
+/*
+ * exp2l(x): compute the base 2 exponential of x
+ *
+ * Accuracy: Peak error < 0.502 ulp.
+ *
+ * Method: (accurate tables)
+ *
+ *   Reduce x:
+ *     x = 2**k + y, for integer k and |y| <= 1/2.
+ *     Thus we have exp2(x) = 2**k * exp2(y).
+ *
+ *   Reduce y:
+ *     y = i/TBLSIZE + z - eps[i] for integer i near y * TBLSIZE.
+ *     Thus we have exp2(y) = exp2(i/TBLSIZE) * exp2(z - eps[i]),
+ *     with |z - eps[i]| <= 2**-8 + 2**-98 for the table used.
+ *
+ *   We compute exp2(i/TBLSIZE) via table lookup and exp2(z - eps[i]) via
+ *   a degree-10 minimax polynomial with maximum error under 2**-120.
+ *   The values in exp2t[] and eps[] are chosen such that
+ *   exp2t[i] = exp2(i/TBLSIZE + eps[i]), and eps[i] is a small offset such
+ *   that exp2t[i] is accurate to 2**-122.
+ *
+ *   Note that the range of i is +-TBLSIZE/2, so we actually index the tables
+ *   by i0 = i + TBLSIZE/2.
+ *
+ *   This method is due to Gal, with many details due to Gal and Bachelis:
+ *
+ *	Gal, S. and Bachelis, B.  An Accurate Elementary Mathematical Library
+ *	for the IEEE Floating Point Standard.  TOMS 17(1), 26-46 (1991).
+ */
+long double
+exp2l(long double x)
+{
+	union ldshape u = {x};
+	int e = u.i.se & 0x7fff;
+	long double r, z, t;
+	uint32_t i0;
+	union {uint32_t u; int32_t i;} k;
+
+	/* Filter out exceptional cases. */
+	if (e >= 0x3fff + 14) {  /* |x| >= 16384 or x is NaN */
+		if (u.i.se >= 0x3fff + 15 && u.i.se >> 15 == 0)
+			/* overflow */
+			return x * 0x1p16383L;
+		if (e == 0x7fff)  /* -inf or -nan */
+			return -1/x;
+		if (x < -16382) {
+			if (x <= -16495 || x - 0x1p112 + 0x1p112 != x)
+				/* underflow */
+				FORCE_EVAL((float)(-0x1p-149/x));
+			if (x <= -16446)
+				return 0;
+		}
+	} else if (e < 0x3fff - 114) {
+		return 1 + x;
+	}
+
+	/*
+	 * Reduce x, computing z, i0, and k. The low bits of x + redux
+	 * contain the 16-bit integer part of the exponent (k) followed by
+	 * TBLBITS fractional bits (i0). We use bit tricks to extract these
+	 * as integers, then set z to the remainder.
+	 *
+	 * Example: Suppose x is 0xabc.123456p0 and TBLBITS is 8.
+	 * Then the low-order word of x + redux is 0x000abc12,
+	 * We split this into k = 0xabc and i0 = 0x12 (adjusted to
+	 * index into the table), then we compute z = 0x0.003456p0.
+	 */
+	u.f = x + redux;
+	i0 = u.i2.lo + TBLSIZE / 2;
+	k.u = i0 / TBLSIZE * TBLSIZE;
+	k.i /= TBLSIZE;
+	i0 %= TBLSIZE;
+	u.f -= redux;
+	z = x - u.f;
+
+	/* Compute r = exp2(y) = exp2t[i0] * p(z - eps[i]). */
+	t = tbl[i0];
+	z -= eps[i0];
+	r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * (P5 + z * (P6
+	    + z * (P7 + z * (P8 + z * (P9 + z * P10)))))))));
+
+	return scalbnl(r, k.i);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/expf.c b/system/lib/libc/musl/src/math/expf.c
index 16e9afe..feee2b0 100644
--- a/system/lib/libc/musl/src/math/expf.c
+++ b/system/lib/libc/musl/src/math/expf.c
@@ -39,6 +39,8 @@
 
 	/* special cases */
 	if (hx >= 0x42aeac50) {  /* if |x| >= -87.33655f or NaN */
+		if (hx > 0x7f800000) /* NaN */
+			return x;
 		if (hx >= 0x42b17218 && !sign) {  /* x >= 88.722839f */
 			/* overflow */
 			x *= 0x1p127f;
diff --git a/system/lib/libc/musl/src/math/expl.c b/system/lib/libc/musl/src/math/expl.c
index b62980f..0a7f44f 100644
--- a/system/lib/libc/musl/src/math/expl.c
+++ b/system/lib/libc/musl/src/math/expl.c
@@ -119,4 +119,10 @@
 	x = 1.0 + 2.0 * x;
 	return scalbnl(x, k);
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double expl(long double x)
+{
+	return exp(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/expm1l.c b/system/lib/libc/musl/src/math/expm1l.c
index 21a86c0..d171507 100644
--- a/system/lib/libc/musl/src/math/expm1l.c
+++ b/system/lib/libc/musl/src/math/expm1l.c
@@ -114,4 +114,10 @@
 	x = px * qx + (px - 1.0);
 	return x;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double expm1l(long double x)
+{
+	return expm1(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/floor.c b/system/lib/libc/musl/src/math/floor.c
index ebc9fab..14a31cd 100644
--- a/system/lib/libc/musl/src/math/floor.c
+++ b/system/lib/libc/musl/src/math/floor.c
@@ -1,5 +1,12 @@
 #include "libm.h"
 
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const double_t toint = 1/EPS;
+
 double floor(double x)
 {
 	union {double f; uint64_t i;} u = {x};
@@ -10,9 +17,9 @@
 		return x;
 	/* y = int(x) - x, where int(x) is an integer neighbor of x */
 	if (u.i >> 63)
-		y = (double)(x - 0x1p52) + 0x1p52 - x;
+		y = x - toint + toint - x;
 	else
-		y = (double)(x + 0x1p52) - 0x1p52 - x;
+		y = x + toint - toint - x;
 	/* special case because of non-nearest rounding modes */
 	if (e <= 0x3ff-1) {
 		FORCE_EVAL(y);
diff --git a/system/lib/libc/musl/src/math/floorl.c b/system/lib/libc/musl/src/math/floorl.c
index 961f9e8..16aaec4 100644
--- a/system/lib/libc/musl/src/math/floorl.c
+++ b/system/lib/libc/musl/src/math/floorl.c
@@ -6,11 +6,9 @@
 	return floor(x);
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-#if LDBL_MANT_DIG == 64
-#define TOINT 0x1p63
-#elif LDBL_MANT_DIG == 113
-#define TOINT 0x1p112
-#endif
+
+static const long double toint = 1/LDBL_EPSILON;
+
 long double floorl(long double x)
 {
 	union ldshape u = {x};
@@ -21,9 +19,9 @@
 		return x;
 	/* y = int(x) - x, where int(x) is an integer neighbor of x */
 	if (u.i.se >> 15)
-		y = x - TOINT + TOINT - x;
+		y = x - toint + toint - x;
 	else
-		y = x + TOINT - TOINT - x;
+		y = x + toint - toint - x;
 	/* special case because of non-nearest rounding modes */
 	if (e <= 0x3fff-1) {
 		FORCE_EVAL(y);
diff --git a/system/lib/libc/musl/src/math/fmodl.c b/system/lib/libc/musl/src/math/fmodl.c
index 54af6a3..9f5b873 100644
--- a/system/lib/libc/musl/src/math/fmodl.c
+++ b/system/lib/libc/musl/src/math/fmodl.c
@@ -63,7 +63,7 @@
 	xhi = (ux.i2.hi & -1ULL>>16) | 1ULL<<48;
 	yhi = (uy.i2.hi & -1ULL>>16) | 1ULL<<48;
 	xlo = ux.i2.lo;
-	ylo = ux.i2.lo;
+	ylo = uy.i2.lo;
 	for (; ex > ey; ex--) {
 		hi = xhi - yhi;
 		lo = xlo - ylo;
diff --git a/system/lib/libc/musl/src/math/hypot.c b/system/lib/libc/musl/src/math/hypot.c
index 29ec6a4..6071bf1 100644
--- a/system/lib/libc/musl/src/math/hypot.c
+++ b/system/lib/libc/musl/src/math/hypot.c
@@ -12,10 +12,10 @@
 {
 	double_t xh, xl, xc;
 
-	xc = x*SPLIT;
+	xc = (double_t)x*SPLIT;
 	xh = x - xc + xc;
 	xl = x - xh;
-	*hi = x*x;
+	*hi = (double_t)x*x;
 	*lo = xh*xh - *hi + 2*xh*xl + xl*xl;
 }
 
diff --git a/system/lib/libc/musl/src/math/lgammal.c b/system/lib/libc/musl/src/math/lgammal.c
index 55ec532..2b354a7 100644
--- a/system/lib/libc/musl/src/math/lgammal.c
+++ b/system/lib/libc/musl/src/math/lgammal.c
@@ -340,9 +340,16 @@
 		r = nadj - r;
 	return r;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+double __lgamma_r(double x, int *sg);
+
+long double __lgammal_r(long double x, int *sg)
+{
+	return __lgamma_r(x, sg);
+}
 #endif
 
-#if (LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024) || (LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384)
 extern int __signgam;
 
 long double lgammal(long double x)
@@ -351,4 +358,3 @@
 }
 
 weak_alias(__lgammal_r, lgammal_r);
-#endif
diff --git a/system/lib/libc/musl/src/math/log10l.c b/system/lib/libc/musl/src/math/log10l.c
index c7aacf9..63dcc28 100644
--- a/system/lib/libc/musl/src/math/log10l.c
+++ b/system/lib/libc/musl/src/math/log10l.c
@@ -182,4 +182,10 @@
 	z += e * (L102A);
 	return z;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double log10l(long double x)
+{
+	return log10(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/log1pl.c b/system/lib/libc/musl/src/math/log1pl.c
index 37da46d..141b5f0 100644
--- a/system/lib/libc/musl/src/math/log1pl.c
+++ b/system/lib/libc/musl/src/math/log1pl.c
@@ -168,4 +168,10 @@
 	z = z + e * C1;
 	return z;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double log1pl(long double x)
+{
+	return log1p(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/log2l.c b/system/lib/libc/musl/src/math/log2l.c
index d00531d..722b451 100644
--- a/system/lib/libc/musl/src/math/log2l.c
+++ b/system/lib/libc/musl/src/math/log2l.c
@@ -173,4 +173,10 @@
 	z += e;
 	return z;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double log2l(long double x)
+{
+	return log2(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/logl.c b/system/lib/libc/musl/src/math/logl.c
index 03c5188..5d53659 100644
--- a/system/lib/libc/musl/src/math/logl.c
+++ b/system/lib/libc/musl/src/math/logl.c
@@ -166,4 +166,10 @@
 	z = z + e * C1; /* This sum has an error of 1/2 lsb. */
 	return z;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double logl(long double x)
+{
+	return log(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/modfl.c b/system/lib/libc/musl/src/math/modfl.c
index 4b03a4b..a47b192 100644
--- a/system/lib/libc/musl/src/math/modfl.c
+++ b/system/lib/libc/musl/src/math/modfl.c
@@ -11,11 +11,9 @@
 	return r;
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-#if LDBL_MANT_DIG == 64
-#define TOINT 0x1p63
-#elif LDBL_MANT_DIG == 113
-#define TOINT 0x1p112
-#endif
+
+static const long double toint = 1/LDBL_EPSILON;
+
 long double modfl(long double x, long double *iptr)
 {
 	union ldshape u = {x};
@@ -40,7 +38,7 @@
 
 	/* raises spurious inexact */
 	absx = s ? -x : x;
-	y = absx + TOINT - TOINT - absx;
+	y = absx + toint - toint - absx;
 	if (y == 0) {
 		*iptr = x;
 		return s ? -0.0 : 0.0;
diff --git a/system/lib/libc/musl/src/math/pow.c b/system/lib/libc/musl/src/math/pow.c
index 82f684b..b66f632 100644
--- a/system/lib/libc/musl/src/math/pow.c
+++ b/system/lib/libc/musl/src/math/pow.c
@@ -143,7 +143,7 @@
 				return 1.0;
 			else if (ix >= 0x3ff00000) /* (|x|>1)**+-inf = inf,0 */
 				return hy >= 0 ? y : 0.0;
-			else if ((ix|lx) != 0)     /* (|x|<1)**+-inf = 0,inf if x!=0 */
+			else                       /* (|x|<1)**+-inf = 0,inf */
 				return hy >= 0 ? 0.0 : -y;
 		}
 		if (iy == 0x3ff00000) {    /* y is +-1 */
diff --git a/system/lib/libc/musl/src/math/powf.c b/system/lib/libc/musl/src/math/powf.c
index 59baf6f..427c896 100644
--- a/system/lib/libc/musl/src/math/powf.c
+++ b/system/lib/libc/musl/src/math/powf.c
@@ -90,7 +90,7 @@
 			return 1.0f;
 		else if (ix > 0x3f800000)  /* (|x|>1)**+-inf = inf,0 */
 			return hy >= 0 ? y : 0.0f;
-		else if (ix != 0)          /* (|x|<1)**+-inf = 0,inf if x!=0 */
+		else                       /* (|x|<1)**+-inf = 0,inf */
 			return hy >= 0 ? 0.0f: -y;
 	}
 	if (iy == 0x3f800000)    /* y is +-1 */
diff --git a/system/lib/libc/musl/src/math/powl.c b/system/lib/libc/musl/src/math/powl.c
index ce6274c..5b6da07 100644
--- a/system/lib/libc/musl/src/math/powl.c
+++ b/system/lib/libc/musl/src/math/powl.c
@@ -227,7 +227,7 @@
 	if (y <= -LDBL_MAX) {
 		if (x > 1.0 || x < -1.0)
 			return 0.0;
-		if (x != 0.0)
+		if (x != 0.0 || y == -INFINITY)
 			return INFINITY;
 	}
 	if (x >= LDBL_MAX) {
@@ -513,5 +513,10 @@
 		y = 1.0/y;
 	return y;
 }
-
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double powl(long double x, long double y)
+{
+	return pow(x, y);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/rint.c b/system/lib/libc/musl/src/math/rint.c
index 81f4e62..fbba390 100644
--- a/system/lib/libc/musl/src/math/rint.c
+++ b/system/lib/libc/musl/src/math/rint.c
@@ -1,6 +1,14 @@
+#include <float.h>
 #include <math.h>
 #include <stdint.h>
 
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const double_t toint = 1/EPS;
+
 double rint(double x)
 {
 	union {double f; uint64_t i;} u = {x};
@@ -11,9 +19,9 @@
 	if (e >= 0x3ff+52)
 		return x;
 	if (s)
-		y = (double)(x - 0x1p52) + 0x1p52;
+		y = x - toint + toint;
 	else
-		y = (double)(x + 0x1p52) - 0x1p52;
+		y = x + toint - toint;
 	if (y == 0)
 		return s ? -0.0 : 0;
 	return y;
diff --git a/system/lib/libc/musl/src/math/rintf.c b/system/lib/libc/musl/src/math/rintf.c
index 9cfc2a2..9047688 100644
--- a/system/lib/libc/musl/src/math/rintf.c
+++ b/system/lib/libc/musl/src/math/rintf.c
@@ -1,6 +1,16 @@
+#include <float.h>
 #include <math.h>
 #include <stdint.h>
 
+#if FLT_EVAL_METHOD==0
+#define EPS FLT_EPSILON
+#elif FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const float_t toint = 1/EPS;
+
 float rintf(float x)
 {
 	union {float f; uint32_t i;} u = {x};
@@ -11,9 +21,9 @@
 	if (e >= 0x7f+23)
 		return x;
 	if (s)
-		y = (float)(x - 0x1p23f) + 0x1p23f;
+		y = x - toint + toint;
 	else
-		y = (float)(x + 0x1p23f) - 0x1p23f;
+		y = x + toint - toint;
 	if (y == 0)
 		return s ? -0.0f : 0.0f;
 	return y;
diff --git a/system/lib/libc/musl/src/math/rintl.c b/system/lib/libc/musl/src/math/rintl.c
index 2672507..374327d 100644
--- a/system/lib/libc/musl/src/math/rintl.c
+++ b/system/lib/libc/musl/src/math/rintl.c
@@ -6,11 +6,9 @@
 	return rint(x);
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-#if LDBL_MANT_DIG == 64
-#define TOINT 0x1p63
-#elif LDBL_MANT_DIG == 113
-#define TOINT 0x1p112
-#endif
+
+static const long double toint = 1/LDBL_EPSILON;
+
 long double rintl(long double x)
 {
 	union ldshape u = {x};
@@ -21,9 +19,9 @@
 	if (e >= 0x3fff+LDBL_MANT_DIG-1)
 		return x;
 	if (s)
-		y = x - TOINT + TOINT;
+		y = x - toint + toint;
 	else
-		y = x + TOINT - TOINT;
+		y = x + toint - toint;
 	if (y == 0)
 		return 0*x;
 	return y;
diff --git a/system/lib/libc/musl/src/math/round.c b/system/lib/libc/musl/src/math/round.c
index 4b38d1f..130d58d 100644
--- a/system/lib/libc/musl/src/math/round.c
+++ b/system/lib/libc/musl/src/math/round.c
@@ -1,5 +1,12 @@
 #include "libm.h"
 
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const double_t toint = 1/EPS;
+
 double round(double x)
 {
 	union {double f; uint64_t i;} u = {x};
@@ -12,10 +19,10 @@
 		x = -x;
 	if (e < 0x3ff-1) {
 		/* raise inexact if x!=0 */
-		FORCE_EVAL(x + 0x1p52);
+		FORCE_EVAL(x + toint);
 		return 0*u.f;
 	}
-	y = (double)(x + 0x1p52) - 0x1p52 - x;
+	y = x + toint - toint - x;
 	if (y > 0.5)
 		y = y + x - 1;
 	else if (y <= -0.5)
diff --git a/system/lib/libc/musl/src/math/roundf.c b/system/lib/libc/musl/src/math/roundf.c
index c6b2779..e8210af 100644
--- a/system/lib/libc/musl/src/math/roundf.c
+++ b/system/lib/libc/musl/src/math/roundf.c
@@ -1,5 +1,14 @@
 #include "libm.h"
 
+#if FLT_EVAL_METHOD==0
+#define EPS FLT_EPSILON
+#elif FLT_EVAL_METHOD==1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD==2
+#define EPS LDBL_EPSILON
+#endif
+static const float_t toint = 1/EPS;
+
 float roundf(float x)
 {
 	union {float f; uint32_t i;} u = {x};
@@ -11,10 +20,10 @@
 	if (u.i >> 31)
 		x = -x;
 	if (e < 0x7f-1) {
-		FORCE_EVAL(x + 0x1p23f);
+		FORCE_EVAL(x + toint);
 		return 0*u.f;
 	}
-	y = (float)(x + 0x1p23f) - 0x1p23f - x;
+	y = x + toint - toint - x;
 	if (y > 0.5f)
 		y = y + x - 1;
 	else if (y <= -0.5f)
diff --git a/system/lib/libc/musl/src/math/roundl.c b/system/lib/libc/musl/src/math/roundl.c
index 8f3f28b..f4ff682 100644
--- a/system/lib/libc/musl/src/math/roundl.c
+++ b/system/lib/libc/musl/src/math/roundl.c
@@ -6,11 +6,9 @@
 	return round(x);
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-#if LDBL_MANT_DIG == 64
-#define TOINT 0x1p63
-#elif LDBL_MANT_DIG == 113
-#define TOINT 0x1p112
-#endif
+
+static const long double toint = 1/LDBL_EPSILON;
+
 long double roundl(long double x)
 {
 	union ldshape u = {x};
@@ -22,10 +20,10 @@
 	if (u.i.se >> 15)
 		x = -x;
 	if (e < 0x3fff-1) {
-		FORCE_EVAL(x + TOINT);
+		FORCE_EVAL(x + toint);
 		return 0*u.f;
 	}
-	y = x + TOINT - TOINT - x;
+	y = x + toint - toint - x;
 	if (y > 0.5)
 		y = y + x - 1;
 	else if (y <= -0.5)
diff --git a/system/lib/libc/musl/src/math/sinhl.c b/system/lib/libc/musl/src/math/sinhl.c
index 4864ddf..b305d4d 100644
--- a/system/lib/libc/musl/src/math/sinhl.c
+++ b/system/lib/libc/musl/src/math/sinhl.c
@@ -34,4 +34,10 @@
 	t = expl(0.5*absx);
 	return h*t*t;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double sinhl(long double x)
+{
+	return sinh(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/tanhl.c b/system/lib/libc/musl/src/math/tanhl.c
index f594b85..4e1aa9f 100644
--- a/system/lib/libc/musl/src/math/tanhl.c
+++ b/system/lib/libc/musl/src/math/tanhl.c
@@ -39,4 +39,10 @@
 	}
 	return sign ? -t : t;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double tanhl(long double x)
+{
+	return tanh(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/tgammal.c b/system/lib/libc/musl/src/math/tgammal.c
index 5c1a02a..5336c5b 100644
--- a/system/lib/libc/musl/src/math/tgammal.c
+++ b/system/lib/libc/musl/src/math/tgammal.c
@@ -272,4 +272,10 @@
 		q = z / (x * __polevll(x, S, 8));
 	return q;
 }
+#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
+// TODO: broken implementation to make things compile
+long double tgammal(long double x)
+{
+	return tgamma(x);
+}
 #endif
diff --git a/system/lib/libc/musl/src/math/truncl.c b/system/lib/libc/musl/src/math/truncl.c
index 3eedb08..f07b193 100644
--- a/system/lib/libc/musl/src/math/truncl.c
+++ b/system/lib/libc/musl/src/math/truncl.c
@@ -6,11 +6,9 @@
 	return trunc(x);
 }
 #elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
-#if LDBL_MANT_DIG == 64
-#define TOINT 0x1p63
-#elif LDBL_MANT_DIG == 113
-#define TOINT 0x1p112
-#endif
+
+static const long double toint = 1/LDBL_EPSILON;
+
 long double truncl(long double x)
 {
 	union ldshape u = {x};
@@ -27,7 +25,7 @@
 	/* y = int(|x|) - |x|, where int(|x|) is an integer neighbor of |x| */
 	if (s)
 		x = -x;
-	y = x + TOINT - TOINT - x;
+	y = x + toint - toint - x;
 	if (y > 0)
 		y -= 1;
 	x += y;
diff --git a/system/lib/libc/musl/src/misc/a64l.c b/system/lib/libc/musl/src/misc/a64l.c
index 86aeefe..6055771 100644
--- a/system/lib/libc/musl/src/misc/a64l.c
+++ b/system/lib/libc/musl/src/misc/a64l.c
@@ -9,9 +9,12 @@
 {
 	int e;
 	uint32_t x = 0;
-	for (e=0; e<36 && *s; e+=6, s++)
-		x |= (strchr(digits, *s)-digits)<<e;
-	return x;
+	for (e=0; e<36 && *s; e+=6, s++) {
+		const char *d = strchr(digits, *s);
+		if (!d) break;
+		x |= (uint32_t)(d-digits)<<e;
+	}
+	return (int32_t)x;
 }
 
 char *l64a(long x0)
diff --git a/system/lib/libc/musl/src/misc/emscripten_pthread.c b/system/lib/libc/musl/src/misc/emscripten_pthread.c
new file mode 100644
index 0000000..862cdea
--- /dev/null
+++ b/system/lib/libc/musl/src/misc/emscripten_pthread.c
@@ -0,0 +1,15 @@
+#include <pthread.h>
+#include "libc.h"
+#include "pthread_impl.h"
+
+#if !__EMSCRIPTEN_PTHREADS__
+static struct pthread __main_pthread;
+pthread_t pthread_self(void) {
+    return &__main_pthread;
+}
+#endif
+
+__attribute__((constructor))
+void __emscripten_pthread_data_constructor(void) {
+    pthread_self()->locale = &libc.global_locale;
+}
diff --git a/system/lib/libc/musl/src/misc/ffsl.c b/system/lib/libc/musl/src/misc/ffsl.c
new file mode 100644
index 0000000..0105c66
--- /dev/null
+++ b/system/lib/libc/musl/src/misc/ffsl.c
@@ -0,0 +1,7 @@
+#include <strings.h>
+#include "atomic.h"
+
+int ffsl(long i)
+{
+	return i ? a_ctz_l(i)+1 : 0;
+}
diff --git a/system/lib/libc/musl/src/misc/ffsll.c b/system/lib/libc/musl/src/misc/ffsll.c
new file mode 100644
index 0000000..0c5ced8
--- /dev/null
+++ b/system/lib/libc/musl/src/misc/ffsll.c
@@ -0,0 +1,7 @@
+#include <strings.h>
+#include "atomic.h"
+
+int ffsll(long long i)
+{
+	return i ? a_ctz_64(i)+1 : 0;
+}
diff --git a/system/lib/libc/musl/src/misc/fmtmsg.c b/system/lib/libc/musl/src/misc/fmtmsg.c
new file mode 100644
index 0000000..69170b2
--- /dev/null
+++ b/system/lib/libc/musl/src/misc/fmtmsg.c
@@ -0,0 +1,90 @@
+/* Public domain fmtmsg()
+ * Written by Isaac Dunham, 2014
+ */
+#include <fmtmsg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+/*
+ * If lstr is the first part of bstr, check that the next char in bstr
+ * is either \0 or :
+ */
+static int _strcolcmp(const char *lstr, const char *bstr)
+{
+	size_t i = 0;
+	while (lstr[i] && bstr[i] && (bstr[i] == lstr[i])) i++;
+	if ( lstr[i] || (bstr[i] && bstr[i] != ':')) return 1;
+	return 0;
+}
+
+int fmtmsg(long classification, const char *label, int severity,
+           const char *text, const char *action, const char *tag)
+{
+	int ret = 0, i, consolefd, verb = 0;
+	char *errstring = MM_NULLSEV, *cmsg = getenv("MSGVERB");
+	char *const msgs[] = {
+		"label", "severity", "text", "action", "tag", NULL
+	};
+	int cs;
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+	if (severity == MM_HALT) errstring = "HALT: ";
+	else if (severity == MM_ERROR) errstring = "ERROR: ";
+	else if (severity == MM_WARNING) errstring = "WARNING: ";
+	else if (severity == MM_INFO) errstring = "INFO: ";
+
+	if (classification & MM_CONSOLE) {
+		consolefd = open("/dev/console", O_WRONLY);
+		if (consolefd < 0) {
+			ret = MM_NOCON;
+		} else {
+			if (dprintf(consolefd, "%s%s%s%s%s%s%s%s\n",
+			            label?label:"", label?": ":"",
+			            severity?errstring:"", text?text:"",
+			            action?"\nTO FIX: ":"",
+			            action?action:"", action?" ":"",
+			            tag?tag:"" )<1)
+				ret = MM_NOCON;
+			close(consolefd);
+		}
+	}
+
+	if (classification & MM_PRINT) {
+		while (cmsg && cmsg[0]) {
+			for(i=0; msgs[i]; i++) {
+				if (!_strcolcmp(msgs[i], cmsg)) break;
+			}
+			if (msgs[i] == NULL) {
+				//ignore MSGVERB-unrecognized component
+				verb = 0xFF;
+				break;
+			} else {
+				verb |= (1 << i);
+				cmsg = strchr(cmsg, ':');
+				if (cmsg) cmsg++;
+			}
+		}
+		if (!verb) verb = 0xFF;
+		if (dprintf(2, "%s%s%s%s%s%s%s%s\n",
+		            (verb&1 && label) ? label : "",
+		            (verb&1 && label) ? ": " : "",
+		            (verb&2 && severity) ? errstring : "",
+		            (verb&4 && text) ? text : "",
+		            (verb&8 && action) ? "\nTO FIX: " : "",
+		            (verb&8 && action) ? action : "",
+		            (verb&8 && action) ? " " : "",
+		            (verb&16 && tag) ? tag : "" ) < 1)
+			ret |= MM_NOMSG;
+	}
+	if ((ret & (MM_NOCON|MM_NOMSG)) == (MM_NOCON|MM_NOMSG))
+		ret = MM_NOTOK;
+
+	pthread_setcancelstate(cs, 0);
+
+	return ret;
+}
diff --git a/system/lib/libc/musl/src/misc/forkpty.c b/system/lib/libc/musl/src/misc/forkpty.c
index 07f8d01..caf13ad 100644
--- a/system/lib/libc/musl/src/misc/forkpty.c
+++ b/system/lib/libc/musl/src/misc/forkpty.c
@@ -1,38 +1,57 @@
 #include <pty.h>
+#include <utmp.h>
 #include <unistd.h>
-#include <sys/ioctl.h>
+#include <errno.h>
 #include <fcntl.h>
+#include <sys/wait.h>
+#include <pthread.h>
 
-int forkpty(int *m, char *name, const struct termios *tio, const struct winsize *ws)
+int forkpty(int *pm, char *name, const struct termios *tio, const struct winsize *ws)
 {
-	int s, t, i, istmp[3]={0};
-	pid_t pid;
+	int m, s, ec=0, p[2], cs;
+	pid_t pid=-1;
+	sigset_t set, oldset;
 
-	if (openpty(m, &s, name, tio, ws) < 0) return -1;
+	if (openpty(&m, &s, name, tio, ws) < 0) return -1;
 
-	/* Ensure before forking that we don't exceed fd limit */
-	for (i=0; i<3; i++) {
-		if (fcntl(i, F_GETFL) < 0) {
-			t = fcntl(s, F_DUPFD, i);
-			if (t<0) break;
-			else if (t!=i) close(t);
-			else istmp[i] = 1;
-		}
+	sigfillset(&set);
+	pthread_sigmask(SIG_BLOCK, &set, &oldset);
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+	if (pipe2(p, O_CLOEXEC)) {
+		close(s);
+		goto out;
 	}
-	pid = i==3 ? fork() : -1;
+
+	pid = fork();
 	if (!pid) {
-		close(*m);
-		setsid();
-		ioctl(s, TIOCSCTTY, (char *)0);
-		dup2(s, 0);
-		dup2(s, 1);
-		dup2(s, 2);
-		if (s>2) close(s);
+		close(m);
+		close(p[0]);
+		if (login_tty(s)) {
+			write(p[1], &errno, sizeof errno);
+			_exit(127);
+		}
+		close(p[1]);
+		pthread_setcancelstate(cs, 0);
+		pthread_sigmask(SIG_SETMASK, &oldset, 0);
 		return 0;
 	}
-	for (i=0; i<3; i++)
-		if (istmp[i]) close(i);
 	close(s);
-	if (pid < 0) close(*m);
+	close(p[1]);
+	if (read(p[0], &ec, sizeof ec) > 0) {
+		int status;
+		waitpid(pid, &status, 0);
+		pid = -1;
+		errno = ec;
+	}
+	close(p[0]);
+
+out:
+	if (pid > 0) *pm = m;
+	else close(m);
+
+	pthread_setcancelstate(cs, 0);
+	pthread_sigmask(SIG_SETMASK, &oldset, 0);
+
 	return pid;
 }
diff --git a/system/lib/libc/musl/src/misc/getauxval.c b/system/lib/libc/musl/src/misc/getauxval.c
new file mode 100644
index 0000000..b846c80
--- /dev/null
+++ b/system/lib/libc/musl/src/misc/getauxval.c
@@ -0,0 +1,13 @@
+#include <sys/auxv.h>
+#include <errno.h>
+#include "libc.h"
+
+unsigned long getauxval(unsigned long item)
+{
+	size_t *auxv = libc.auxv;
+	if (item == AT_SECURE) return libc.secure;
+	for (; *auxv; auxv+=2)
+		if (*auxv==item) return auxv[1];
+	errno = ENOENT;
+	return 0;
+}
diff --git a/system/lib/libc/musl/src/misc/getopt.c b/system/lib/libc/musl/src/misc/getopt.c
index f1a1639..8290aef 100644
--- a/system/lib/libc/musl/src/misc/getopt.c
+++ b/system/lib/libc/musl/src/misc/getopt.c
@@ -4,6 +4,7 @@
 #include <limits.h>
 #include <stdlib.h>
 #include "libc.h"
+#include "locale_impl.h"
 
 char *optarg;
 int optind=1, opterr=1, optopt, __optpos, __optreset=0;
@@ -11,6 +12,18 @@
 #define optpos __optpos
 weak_alias(__optreset, optreset);
 
+void __getopt_msg(const char *a, const char *b, const char *c, size_t l)
+{
+	FILE *f = stderr;
+	b = __lctrans_cur(b);
+	flockfile(f);
+	fputs(a, f)>=0
+	&& fwrite(b, strlen(b), 1, f)
+	&& fwrite(c, 1, l, f)==l
+	&& putc('\n', f);
+	funlockfile(f);
+}
+
 int getopt(int argc, char * const argv[], const char *optstring)
 {
 	int i;
@@ -24,8 +37,20 @@
 		optind = 1;
 	}
 
-	if (optind >= argc || !argv[optind] || argv[optind][0] != '-' || !argv[optind][1])
+	if (optind >= argc || !argv[optind])
 		return -1;
+
+	if (argv[optind][0] != '-') {
+		if (optstring[0] == '-') {
+			optarg = argv[optind++];
+			return 1;
+		}
+		return -1;
+	}
+
+	if (!argv[optind][1])
+		return -1;
+
 	if (argv[optind][1] == '-' && !argv[optind][2])
 		return optind++, -1;
 
@@ -43,30 +68,34 @@
 		optpos = 0;
 	}
 
-	for (i=0; (l = mbtowc(&d, optstring+i, MB_LEN_MAX)) && d!=c; i+=l>0?l:1);
+	if (optstring[0] == '-' || optstring[0] == '+')
+		optstring++;
+
+	i = 0;
+	d = 0;
+	do {
+		l = mbtowc(&d, optstring+i, MB_LEN_MAX);
+		if (l>0) i+=l; else i++;
+	} while (l && d != c);
 
 	if (d != c) {
-		if (optstring[0] != ':' && opterr) {
-			write(2, argv[0], strlen(argv[0]));
-			write(2, ": illegal option: ", 18);
-			write(2, optchar, k);
-			write(2, "\n", 1);
-		}
+		if (optstring[0] != ':' && opterr)
+			__getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
 		return '?';
 	}
-	if (optstring[i+1] == ':') {
-		if (optind >= argc) {
+	if (optstring[i] == ':') {
+		if (optstring[i+1] == ':') optarg = 0;
+		else if (optind >= argc) {
 			if (optstring[0] == ':') return ':';
-			if (opterr) {
-				write(2, argv[0], strlen(argv[0]));
-				write(2, ": option requires an argument: ", 31);
-				write(2, optchar, k);
-				write(2, "\n", 1);
-			}
+			if (opterr) __getopt_msg(argv[0],
+				": option requires an argument: ",
+				optchar, k);
 			return '?';
 		}
-		optarg = argv[optind++] + optpos;
-		optpos = 0;
+		if (optstring[i+1] != ':' || optpos) {
+			optarg = argv[optind++] + optpos;
+			optpos = 0;
+		}
 	}
 	return c;
 }
diff --git a/system/lib/libc/musl/src/misc/getopt_long.c b/system/lib/libc/musl/src/misc/getopt_long.c
index 4ef5a5c..480c001 100644
--- a/system/lib/libc/musl/src/misc/getopt_long.c
+++ b/system/lib/libc/musl/src/misc/getopt_long.c
@@ -2,37 +2,103 @@
 #include <stddef.h>
 #include <getopt.h>
 #include <stdio.h>
+#include <string.h>
 
 extern int __optpos, __optreset;
 
+static void permute(char *const *argv, int dest, int src)
+{
+	char **av = (char **)argv;
+	char *tmp = av[src];
+	int i;
+	for (i=src; i>dest; i--)
+		av[i] = av[i-1];
+	av[dest] = tmp;
+}
+
+void __getopt_msg(const char *, const char *, const char *, size_t);
+
+static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly);
+
 static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
 {
+	int ret, skipped, resumed;
 	if (!optind || __optreset) {
 		__optreset = 0;
 		__optpos = 0;
 		optind = 1;
 	}
-	if (optind >= argc || !argv[optind] || argv[optind][0] != '-') return -1;
-	if ((longonly && argv[optind][1]) ||
-		(argv[optind][1] == '-' && argv[optind][2]))
-	{
+	if (optind >= argc || !argv[optind]) return -1;
+	skipped = optind;
+	if (optstring[0] != '+' && optstring[0] != '-') {
 		int i;
-		for (i=0; longopts[i].name; i++) {
+		for (i=optind; ; i++) {
+			if (i >= argc || !argv[i]) return -1;
+			if (argv[i][0] == '-' && argv[i][1]) break;
+		}
+		optind = i;
+	}
+	resumed = optind;
+	ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
+	if (resumed > skipped) {
+		int i, cnt = optind-resumed;
+		for (i=0; i<cnt; i++)
+			permute(argv, skipped, optind-1);
+		optind = skipped + cnt;
+	}
+	return ret;
+}
+
+static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
+{
+	optarg = 0;
+	if (longopts && argv[optind][0] == '-' &&
+		((longonly && argv[optind][1]) ||
+		 (argv[optind][1] == '-' && argv[optind][2])))
+	{
+		int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
+		int i, cnt, match;
+		char *opt;
+		for (cnt=i=0; longopts[i].name; i++) {
 			const char *name = longopts[i].name;
-			char *opt = argv[optind]+1;
+			opt = argv[optind]+1;
 			if (*opt == '-') opt++;
 			for (; *name && *name == *opt; name++, opt++);
-			if (*name || (*opt && *opt != '=')) continue;
-			if (*opt == '=') {
-				if (!longopts[i].has_arg) continue;
-				optarg = opt+1;
-			} else {
-				if (longopts[i].has_arg == required_argument) {
-					if (!(optarg = argv[++optind]))
-						return ':';
-				} else optarg = NULL;
+			if (*opt && *opt != '=') continue;
+			match = i;
+			if (!*name) {
+				cnt = 1;
+				break;
 			}
+			cnt++;
+		}
+		if (cnt==1) {
+			i = match;
 			optind++;
+			optopt = longopts[i].val;
+			if (*opt == '=') {
+				if (!longopts[i].has_arg) {
+					if (colon || !opterr)
+						return '?';
+					__getopt_msg(argv[0],
+						": option does not take an argument: ",
+						longopts[i].name,
+						strlen(longopts[i].name));
+					return '?';
+				}
+				optarg = opt+1;
+			} else if (longopts[i].has_arg == required_argument) {
+				if (!(optarg = argv[optind])) {
+					if (colon) return ':';
+					if (!opterr) return '?';
+					__getopt_msg(argv[0],
+						": option requires an argument: ",
+						longopts[i].name,
+						strlen(longopts[i].name));
+					return '?';
+				}
+				optind++;
+			}
 			if (idx) *idx = i;
 			if (longopts[i].flag) {
 				*longopts[i].flag = longopts[i].val;
@@ -41,6 +107,12 @@
 			return longopts[i].val;
 		}
 		if (argv[optind][1] == '-') {
+			if (!colon && opterr)
+				__getopt_msg(argv[0], cnt ?
+					": option is ambiguous: " :
+					": unrecognized option: ",
+					argv[optind]+2,
+					strlen(argv[optind]+2));
 			optind++;
 			return '?';
 		}
diff --git a/system/lib/libc/musl/src/misc/getsubopt.c b/system/lib/libc/musl/src/misc/getsubopt.c
index dac9bf9..53ee957 100644
--- a/system/lib/libc/musl/src/misc/getsubopt.c
+++ b/system/lib/libc/musl/src/misc/getsubopt.c
@@ -15,7 +15,7 @@
 		size_t l = strlen(keys[i]);
 		if (strncmp(keys[i], s, l)) continue;
 		if (s[l] == '=')
-			*val = s + l;
+			*val = s + l + 1;
 		else if (s[l]) continue;
 		return i;
 	}
diff --git a/system/lib/libc/musl/src/misc/issetugid.c b/system/lib/libc/musl/src/misc/issetugid.c
new file mode 100644
index 0000000..6ffd930
--- /dev/null
+++ b/system/lib/libc/musl/src/misc/issetugid.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+#include "libc.h"
+
+int issetugid(void)
+{
+	return libc.secure;
+}
diff --git a/system/lib/libc/musl/src/misc/login_tty.c b/system/lib/libc/musl/src/misc/login_tty.c
new file mode 100644
index 0000000..f0be0a0
--- /dev/null
+++ b/system/lib/libc/musl/src/misc/login_tty.c
@@ -0,0 +1,14 @@
+#include <utmp.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+int login_tty(int fd)
+{
+	setsid();
+	if (ioctl(fd, TIOCSCTTY, (char *)0)) return -1;
+	dup2(fd, 0);
+	dup2(fd, 1);
+	dup2(fd, 2);
+	if (fd>2) close(fd);
+	return 0;
+}
diff --git a/system/lib/libc/musl/src/misc/mntent.c b/system/lib/libc/musl/src/misc/mntent.c
index 3eafba5..a16d652 100644
--- a/system/lib/libc/musl/src/misc/mntent.c
+++ b/system/lib/libc/musl/src/misc/mntent.c
@@ -10,7 +10,7 @@
 
 int endmntent(FILE *f)
 {
-	fclose(f);
+	if (f) fclose(f);
 	return 1;
 }
 
diff --git a/system/lib/libc/musl/src/misc/openpty.c b/system/lib/libc/musl/src/misc/openpty.c
index 1020247..c107406 100644
--- a/system/lib/libc/musl/src/misc/openpty.c
+++ b/system/lib/libc/musl/src/misc/openpty.c
@@ -3,31 +3,38 @@
 #include <unistd.h>
 #include <pty.h>
 #include <stdio.h>
+#include <pthread.h>
 
 /* Nonstandard, but vastly superior to the standard functions */
 
-int openpty(int *m, int *s, char *name, const struct termios *tio, const struct winsize *ws)
+int openpty(int *pm, int *ps, char *name, const struct termios *tio, const struct winsize *ws)
 {
-	int n=0;
+	int m, s, n=0, cs;
 	char buf[20];
 
-	*m = open("/dev/ptmx", O_RDWR|O_NOCTTY);
-	if (*m < 0) return -1;
+	m = open("/dev/ptmx", O_RDWR|O_NOCTTY);
+	if (m < 0) return -1;
 
-	if (ioctl(*m, TIOCSPTLCK, &n) || ioctl (*m, TIOCGPTN, &n)) {
-		close(*m);
-		return -1;
-	}
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+	if (ioctl(m, TIOCSPTLCK, &n) || ioctl (m, TIOCGPTN, &n))
+		goto fail;
 
 	if (!name) name = buf;
 	snprintf(name, sizeof buf, "/dev/pts/%d", n);
-	if ((*s = open(name, O_RDWR|O_NOCTTY)) < 0) {
-		close(*m);
-		return -1;
-	}
+	if ((s = open(name, O_RDWR|O_NOCTTY)) < 0)
+		goto fail;
 
-	if (tio) tcsetattr(*s, TCSANOW, tio);
-	if (ws) ioctl(*s, TIOCSWINSZ, ws);
+	if (tio) tcsetattr(s, TCSANOW, tio);
+	if (ws) ioctl(s, TIOCSWINSZ, ws);
 
+	*pm = m;
+	*ps = s;
+
+	pthread_setcancelstate(cs, 0);
 	return 0;
+fail:
+	close(m);
+	pthread_setcancelstate(cs, 0);
+	return -1;
 }
diff --git a/system/lib/libc/musl/src/misc/realpath.c b/system/lib/libc/musl/src/misc/realpath.c
index 4cc7e7d..88c849c 100644
--- a/system/lib/libc/musl/src/misc/realpath.c
+++ b/system/lib/libc/musl/src/misc/realpath.c
@@ -22,7 +22,7 @@
 		return 0;
 	}
 
-	fd = syscall(SYS_open, filename, O_PATH|O_NONBLOCK|O_CLOEXEC|O_LARGEFILE);
+	fd = sys_open(filename, O_PATH|O_NONBLOCK|O_CLOEXEC);
 	if (fd < 0) return 0;
 	__procfdname(buf, fd);
 
diff --git a/system/lib/libc/musl/src/misc/setrlimit.c b/system/lib/libc/musl/src/misc/setrlimit.c
index 8a1b8cc..7130d71 100644
--- a/system/lib/libc/musl/src/misc/setrlimit.c
+++ b/system/lib/libc/musl/src/misc/setrlimit.c
@@ -32,16 +32,16 @@
 static void do_setrlimit(void *p)
 {
 	struct ctx *c = p;
-	if (c->err) return;
+	if (c->err>0) return;
 	c->err = -__setrlimit(c->res, c->rlim);
 }
 
 int setrlimit(int resource, const struct rlimit *rlim)
 {
-	struct ctx c = { .res = resource, .rlim = rlim };
+	struct ctx c = { .res = resource, .rlim = rlim, .err = -1 };
 	__synccall(do_setrlimit, &c);
 	if (c.err) {
-		errno = c.err;
+		if (c.err>0) errno = c.err;
 		return -1;
 	}
 	return 0;
diff --git a/system/lib/libc/musl/src/misc/syslog.c b/system/lib/libc/musl/src/misc/syslog.c
index cc82508..9dd1ddb 100644
--- a/system/lib/libc/musl/src/misc/syslog.c
+++ b/system/lib/libc/musl/src/misc/syslog.c
@@ -8,10 +8,10 @@
 #include <string.h>
 #include <pthread.h>
 #include <errno.h>
+#include <fcntl.h>
 #include "libc.h"
-#include "atomic.h"
 
-static int lock[2];
+static volatile int lock[2];
 static char log_ident[32];
 static int log_opt;
 static int log_facility = LOG_USER;
@@ -20,8 +20,11 @@
 
 int setlogmask(int maskpri)
 {
-	if (maskpri) return a_swap(&log_mask, maskpri);
-	else return log_mask;
+	LOCK(lock);
+	int ret = log_mask;
+	if (maskpri) log_mask = maskpri;
+	UNLOCK(lock);
+	return ret;
 }
 
 static const struct {
@@ -45,12 +48,8 @@
 
 static void __openlog()
 {
-	int fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-	if (fd < 0) return;
-	if (connect(fd, (void *)&log_addr, sizeof log_addr) < 0)
-		close(fd);
-	else
-		log_fd = fd;
+	log_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+	if (log_fd >= 0) connect(log_fd, (void *)&log_addr, sizeof log_addr);
 }
 
 void openlog(const char *ident, int opt, int facility)
@@ -75,20 +74,24 @@
 	pthread_setcancelstate(cs, 0);
 }
 
+static int is_lost_conn(int e)
+{
+	return e==ECONNREFUSED || e==ECONNRESET || e==ENOTCONN || e==EPIPE;
+}
+
 static void _vsyslog(int priority, const char *message, va_list ap)
 {
 	char timebuf[16];
 	time_t now;
 	struct tm tm;
-	char buf[256];
+	char buf[1024];
 	int errno_save = errno;
 	int pid;
 	int l, l2;
+	int hlen;
+	int fd;
 
-	if (log_fd < 0) {
-		__openlog();
-		if (log_fd < 0) return;
-	}
+	if (log_fd < 0) __openlog();
 
 	if (!(priority & LOG_FACMASK)) priority |= log_facility;
 
@@ -97,15 +100,25 @@
 	strftime(timebuf, sizeof timebuf, "%b %e %T", &tm);
 
 	pid = (log_opt & LOG_PID) ? getpid() : 0;
-	l = snprintf(buf, sizeof buf, "<%d>%s %s%s%.0d%s: ",
-		priority, timebuf, log_ident, "["+!pid, pid, "]"+!pid);
+	l = snprintf(buf, sizeof buf, "<%d>%s %n%s%s%.0d%s: ",
+		priority, timebuf, &hlen, log_ident, "["+!pid, pid, "]"+!pid);
 	errno = errno_save;
 	l2 = vsnprintf(buf+l, sizeof buf - l, message, ap);
 	if (l2 >= 0) {
 		if (l2 >= sizeof buf - l) l = sizeof buf - 1;
 		else l += l2;
 		if (buf[l-1] != '\n') buf[l++] = '\n';
-		send(log_fd, buf, l, 0);
+		if (send(log_fd, buf, l, 0) < 0 && (!is_lost_conn(errno)
+		    || connect(log_fd, (void *)&log_addr, sizeof log_addr) < 0
+		    || send(log_fd, buf, l, 0) < 0)
+		    && (log_opt & LOG_CONS)) {
+			fd = open("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+			if (fd >= 0) {
+				dprintf(fd, "%.*s", l-hlen, buf+hlen);
+				close(fd);
+			}
+		}
+		if (log_opt & LOG_PERROR) dprintf(2, "%.*s", l-hlen, buf+hlen);
 	}
 }
 
diff --git a/system/lib/libc/musl/src/mman/mmap.c b/system/lib/libc/musl/src/mman/mmap.c
index b56cff8..b85f25c 100644
--- a/system/lib/libc/musl/src/mman/mmap.c
+++ b/system/lib/libc/musl/src/mman/mmap.c
@@ -6,17 +6,14 @@
 #include "syscall.h"
 #include "libc.h"
 
-static void dummy1(int x) { }
-static void dummy0(void) { }
-weak_alias(dummy1, __vm_lock);
-weak_alias(dummy0, __vm_unlock);
+static void dummy(void) { }
+weak_alias(dummy, __vm_wait);
 
-#define OFF_MASK ((-0x2000ULL << (8*sizeof(long)-1)) | 0xfff)
+#define UNIT SYSCALL_MMAP2_UNIT
+#define OFF_MASK ((-0x2000ULL << (8*sizeof(long)-1)) | (UNIT-1))
 
 void *__mmap(void *start, size_t len, int prot, int flags, int fd, off_t off)
 {
-	void *ret;
-
 	if (off & OFF_MASK) {
 		errno = EINVAL;
 		return MAP_FAILED;
@@ -25,14 +22,14 @@
 		errno = ENOMEM;
 		return MAP_FAILED;
 	}
-	if (flags & MAP_FIXED) __vm_lock(-1);
+	if (flags & MAP_FIXED) {
+		__vm_wait();
+	}
 #ifdef SYS_mmap2
-	ret = (void *)syscall(SYS_mmap2, start, len, prot, flags, fd, off>>12);
+	return (void *)syscall(SYS_mmap2, start, len, prot, flags, fd, off/UNIT);
 #else
-	ret = (void *)syscall(SYS_mmap, start, len, prot, flags, fd, off);
+	return (void *)syscall(SYS_mmap, start, len, prot, flags, fd, off);
 #endif
-	if (flags & MAP_FIXED) __vm_unlock();
-	return ret;
 }
 
 weak_alias(__mmap, mmap);
diff --git a/system/lib/libc/musl/src/mman/mprotect.c b/system/lib/libc/musl/src/mman/mprotect.c
index f488486..535787b 100644
--- a/system/lib/libc/musl/src/mman/mprotect.c
+++ b/system/lib/libc/musl/src/mman/mprotect.c
@@ -2,10 +2,12 @@
 #include "libc.h"
 #include "syscall.h"
 
-int mprotect(void *addr, size_t len, int prot)
+int __mprotect(void *addr, size_t len, int prot)
 {
 	size_t start, end;
 	start = (size_t)addr & -PAGE_SIZE;
 	end = (size_t)((char *)addr + len + PAGE_SIZE-1) & -PAGE_SIZE;
 	return syscall(SYS_mprotect, start, end-start, prot);
 }
+
+weak_alias(__mprotect, mprotect);
diff --git a/system/lib/libc/musl/src/mman/mremap.c b/system/lib/libc/musl/src/mman/mremap.c
index 596c45f..ce4e8ea 100644
--- a/system/lib/libc/musl/src/mman/mremap.c
+++ b/system/lib/libc/musl/src/mman/mremap.c
@@ -1,17 +1,31 @@
+#define _GNU_SOURCE
 #include <unistd.h>
 #include <sys/mman.h>
+#include <errno.h>
+#include <stdint.h>
 #include <stdarg.h>
 #include "syscall.h"
 #include "libc.h"
 
+static void dummy(void) { }
+weak_alias(dummy, __vm_wait);
+
 void *__mremap(void *old_addr, size_t old_len, size_t new_len, int flags, ...)
 {
 	va_list ap;
-	void *new_addr;
-	
-	va_start(ap, flags);
-	new_addr = va_arg(ap, void *);
-	va_end(ap);
+	void *new_addr = 0;
+
+	if (new_len >= PTRDIFF_MAX) {
+		errno = ENOMEM;
+		return MAP_FAILED;
+	}
+
+	if (flags & MREMAP_FIXED) {
+		__vm_wait();
+		va_start(ap, flags);
+		new_addr = va_arg(ap, void *);
+		va_end(ap);
+	}
 
 	return (void *)syscall(SYS_mremap, old_addr, old_len, new_len, flags, new_addr);
 }
diff --git a/system/lib/libc/musl/src/mman/msync.c b/system/lib/libc/musl/src/mman/msync.c
index bb20475..fcd8cdf 100644
--- a/system/lib/libc/musl/src/mman/msync.c
+++ b/system/lib/libc/musl/src/mman/msync.c
@@ -3,5 +3,5 @@
 
 int msync(void *start, size_t len, int flags)
 {
-	return syscall(SYS_msync, start, len, flags);
+	return syscall_cp(SYS_msync, start, len, flags);
 }
diff --git a/system/lib/libc/musl/src/mman/munmap.c b/system/lib/libc/musl/src/mman/munmap.c
index 8488d75..3f711ee 100644
--- a/system/lib/libc/musl/src/mman/munmap.c
+++ b/system/lib/libc/musl/src/mman/munmap.c
@@ -2,18 +2,13 @@
 #include "syscall.h"
 #include "libc.h"
 
-static void dummy1(int x) { }
-static void dummy0(void) { }
-weak_alias(dummy1, __vm_lock);
-weak_alias(dummy0, __vm_unlock);
+static void dummy(void) { }
+weak_alias(dummy, __vm_wait);
 
 int __munmap(void *start, size_t len)
 {
-	int ret;
-	__vm_lock(-1);
-	ret = syscall(SYS_munmap, start, len);
-	__vm_unlock();
-	return ret;
+	__vm_wait();
+	return syscall(SYS_munmap, start, len);
 }
 
 weak_alias(__munmap, munmap);
diff --git a/system/lib/libc/musl/src/multibyte/btowc.c b/system/lib/libc/musl/src/multibyte/btowc.c
index 9d2c3b1..8acd0a2 100644
--- a/system/lib/libc/musl/src/multibyte/btowc.c
+++ b/system/lib/libc/musl/src/multibyte/btowc.c
@@ -1,7 +1,10 @@
 #include <stdio.h>
 #include <wchar.h>
+#include <stdlib.h>
+#include "internal.h"
 
 wint_t btowc(int c)
 {
-	return c<128U ? c : EOF;
+	int b = (unsigned char)c;
+	return b<128U ? b : (MB_CUR_MAX==1 && c!=EOF) ? CODEUNIT(c) : WEOF;
 }
diff --git a/system/lib/libc/musl/src/multibyte/c16rtomb.c b/system/lib/libc/musl/src/multibyte/c16rtomb.c
new file mode 100644
index 0000000..39ca375
--- /dev/null
+++ b/system/lib/libc/musl/src/multibyte/c16rtomb.c
@@ -0,0 +1,35 @@
+#include <uchar.h>
+#include <errno.h>
+#include <wchar.h>
+
+size_t c16rtomb(char *restrict s, char16_t c16, mbstate_t *restrict ps)
+{
+	static unsigned internal_state;
+	if (!ps) ps = (void *)&internal_state;
+	unsigned *x = (unsigned *)ps;
+	wchar_t wc;
+
+	if (!s) {
+		if (*x) goto ilseq;
+		return 1;
+	}
+
+	if (!*x && c16 - 0xd800u < 0x400) {
+		*x = c16 - 0xd7c0 << 10;
+		return 0;
+	}
+
+	if (*x) {
+		if (c16 - 0xdc00u >= 0x400) goto ilseq;
+		else wc = *x + c16 - 0xdc00;
+		*x = 0;
+	} else {
+		wc = c16;
+	}
+	return wcrtomb(s, wc, 0);
+
+ilseq:
+	*x = 0;
+	errno = EILSEQ;
+	return -1;
+}
diff --git a/system/lib/libc/musl/src/multibyte/c32rtomb.c b/system/lib/libc/musl/src/multibyte/c32rtomb.c
new file mode 100644
index 0000000..6785132
--- /dev/null
+++ b/system/lib/libc/musl/src/multibyte/c32rtomb.c
@@ -0,0 +1,7 @@
+#include <uchar.h>
+#include <wchar.h>
+
+size_t c32rtomb(char *restrict s, char32_t c32, mbstate_t *restrict ps)
+{
+	return wcrtomb(s, c32, ps);
+}
diff --git a/system/lib/libc/musl/src/multibyte/internal.c b/system/lib/libc/musl/src/multibyte/internal.c
index 1813b26..7e1b1c0 100644
--- a/system/lib/libc/musl/src/multibyte/internal.c
+++ b/system/lib/libc/musl/src/multibyte/internal.c
@@ -1,9 +1,3 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #include "internal.h"
 
 #define C(x) ( x<2 ? -1 : ( R(0x80,0xc0) | x ) )
@@ -30,7 +24,3 @@
 	E(0x8),E(0x9),E(0xa),E(0xb),E(0xc),E(0xd),E(0xe),E(0xf),
 	F(0x0),F(0x1),F(0x2),F(0x3),F(0x4)
 };
-
-#ifdef BROKEN_VISIBILITY
-__asm__(".hidden __fsmu8");
-#endif
diff --git a/system/lib/libc/musl/src/multibyte/internal.h b/system/lib/libc/musl/src/multibyte/internal.h
index 82f5a07..421a3d4 100644
--- a/system/lib/libc/musl/src/multibyte/internal.h
+++ b/system/lib/libc/musl/src/multibyte/internal.h
@@ -1,23 +1,26 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #define bittab __fsmu8
 
 #include <stdint.h>
-#include "libc.h"
 
-extern const uint32_t bittab[] ATTR_LIBC_VISIBILITY;
+#ifdef __PIC__
+__attribute__((__visibility__("hidden")))
+#endif
+extern const uint32_t bittab[];
 
 /* Upper 6 state bits are a negative integer offset to bound-check next byte */
 /*    equivalent to: ( (b-0x80) | (b+offset) ) & ~0x3f      */
 #define OOB(c,b) (((((b)>>3)-0x10)|(((b)>>3)+((int32_t)(c)>>26))) & ~7)
 
 /* Interval [a,b). Either a must be 80 or b must be c0, lower 3 bits clear. */
-#define R(a,b) ((uint32_t)((a==0x80 ? 0x40-b : -a) << 23))
+#define R(a,b) ((uint32_t)((a==0x80 ? 0x40u-b : 0u-a) << 23))
 #define FAILSTATE R(0x80,0x80)
 
 #define SA 0xc2u
 #define SB 0xf4u
+
+/* Arbitrary encoding for representing code units instead of characters. */
+#define CODEUNIT(c) (0xdfff & (signed char)(c))
+#define IS_CODEUNIT(c) ((unsigned)(c)-0xdf80 < 0x80)
+
+/* Get inline definition of MB_CUR_MAX. */
+#include "locale_impl.h"
diff --git a/system/lib/libc/musl/src/multibyte/mblen.c b/system/lib/libc/musl/src/multibyte/mblen.c
index 96b47b1..a4304bf 100644
--- a/system/lib/libc/musl/src/multibyte/mblen.c
+++ b/system/lib/libc/musl/src/multibyte/mblen.c
@@ -1,9 +1,3 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #include <stdlib.h>
 
 int mblen(const char *s, size_t n)
diff --git a/system/lib/libc/musl/src/multibyte/mbrlen.c b/system/lib/libc/musl/src/multibyte/mbrlen.c
index 3a5a743..accf4b3 100644
--- a/system/lib/libc/musl/src/multibyte/mbrlen.c
+++ b/system/lib/libc/musl/src/multibyte/mbrlen.c
@@ -1,9 +1,3 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #include <wchar.h>
 
 size_t mbrlen(const char *restrict s, size_t n, mbstate_t *restrict st)
diff --git a/system/lib/libc/musl/src/multibyte/mbrtoc16.c b/system/lib/libc/musl/src/multibyte/mbrtoc16.c
new file mode 100644
index 0000000..765ff90
--- /dev/null
+++ b/system/lib/libc/musl/src/multibyte/mbrtoc16.c
@@ -0,0 +1,30 @@
+#include <uchar.h>
+#include <wchar.h>
+
+size_t mbrtoc16(char16_t *restrict pc16, const char *restrict s, size_t n, mbstate_t *restrict ps)
+{
+	static unsigned internal_state;
+	if (!ps) ps = (void *)&internal_state;
+	unsigned *pending = (unsigned *)ps;
+
+	if (!s) return mbrtoc16(0, "", 1, ps);
+
+	/* mbrtowc states for partial UTF-8 characters have the high bit set;
+	 * we use nonzero states without high bit for pending surrogates. */
+	if ((int)*pending > 0) {
+ 		if (pc16) *pc16 = *pending;
+		*pending = 0;
+		return -3;
+	}
+
+	wchar_t wc;
+	size_t ret = mbrtowc(&wc, s, n, ps);
+	if (ret <= 4) {
+		if (wc >= 0x10000) {
+			*pending = (wc & 0x3ff) + 0xdc00;
+			wc = 0xd7c0 + (wc >> 10);
+		}
+		if (pc16) *pc16 = wc;
+	}
+	return ret;
+}
diff --git a/system/lib/libc/musl/src/multibyte/mbrtoc32.c b/system/lib/libc/musl/src/multibyte/mbrtoc32.c
new file mode 100644
index 0000000..9b6b236
--- /dev/null
+++ b/system/lib/libc/musl/src/multibyte/mbrtoc32.c
@@ -0,0 +1,13 @@
+#include <uchar.h>
+#include <wchar.h>
+
+size_t mbrtoc32(char32_t *restrict pc32, const char *restrict s, size_t n, mbstate_t *restrict ps)
+{
+	static unsigned internal_state;
+	if (!ps) ps = (void *)&internal_state;
+	if (!s) return mbrtoc32(0, "", 1, ps);
+	wchar_t wc;
+	size_t ret = mbrtowc(&wc, s, n, ps);
+	if (ret <= 4 && pc32) *pc32 = wc;
+	return ret;
+}
diff --git a/system/lib/libc/musl/src/multibyte/mbrtowc.c b/system/lib/libc/musl/src/multibyte/mbrtowc.c
index e7b3654..c94819e 100644
--- a/system/lib/libc/musl/src/multibyte/mbrtowc.c
+++ b/system/lib/libc/musl/src/multibyte/mbrtowc.c
@@ -1,9 +1,4 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
+#include <stdlib.h>
 #include <wchar.h>
 #include <errno.h>
 #include "internal.h"
@@ -27,6 +22,7 @@
 	if (!n) return -2;
 	if (!c) {
 		if (*s < 0x80) return !!(*wc = *s);
+		if (MB_CUR_MAX==1) return (*wc = CODEUNIT(*s)), 1;
 		if (*s-SA > SB-SA) goto ilseq;
 		c = bittab[*s++-SA]; n--;
 	}
diff --git a/system/lib/libc/musl/src/multibyte/mbsinit.c b/system/lib/libc/musl/src/multibyte/mbsinit.c
index e001d84..c608194 100644
--- a/system/lib/libc/musl/src/multibyte/mbsinit.c
+++ b/system/lib/libc/musl/src/multibyte/mbsinit.c
@@ -1,9 +1,3 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #include <wchar.h>
 
 int mbsinit(const mbstate_t *st)
diff --git a/system/lib/libc/musl/src/multibyte/mbsnrtowcs.c b/system/lib/libc/musl/src/multibyte/mbsnrtowcs.c
index 68b9960..cae4caa 100644
--- a/system/lib/libc/musl/src/multibyte/mbsnrtowcs.c
+++ b/system/lib/libc/musl/src/multibyte/mbsnrtowcs.c
@@ -1,9 +1,3 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #include <wchar.h>
 
 size_t mbsnrtowcs(wchar_t *restrict wcs, const char **restrict src, size_t n, size_t wn, mbstate_t *restrict st)
diff --git a/system/lib/libc/musl/src/multibyte/mbsrtowcs.c b/system/lib/libc/musl/src/multibyte/mbsrtowcs.c
index 3c1343a..0ee8b69 100644
--- a/system/lib/libc/musl/src/multibyte/mbsrtowcs.c
+++ b/system/lib/libc/musl/src/multibyte/mbsrtowcs.c
@@ -1,12 +1,8 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #include <stdint.h>
 #include <wchar.h>
 #include <errno.h>
+#include <string.h>
+#include <stdlib.h>
 #include "internal.h"
 
 size_t mbsrtowcs(wchar_t *restrict ws, const char **restrict src, size_t wn, mbstate_t *restrict st)
@@ -24,6 +20,23 @@
 		}
 	}
 
+	if (MB_CUR_MAX==1) {
+		if (!ws) return strlen((const char *)s);
+		for (;;) {
+			if (!wn) {
+				*src = (const void *)s;
+				return wn0;
+			}
+			if (!*s) break;
+			c = *s++;
+			*ws++ = CODEUNIT(c);
+			wn--;
+		}
+		*ws = 0;
+		*src = 0;
+		return wn0-wn;
+	}
+
 	if (!ws) for (;;) {
 		if (*s-1u < 0x7f && (uintptr_t)s%4 == 0) {
 			while (!(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) {
diff --git a/system/lib/libc/musl/src/multibyte/mbtowc.c b/system/lib/libc/musl/src/multibyte/mbtowc.c
index 803d221..c191bb0 100644
--- a/system/lib/libc/musl/src/multibyte/mbtowc.c
+++ b/system/lib/libc/musl/src/multibyte/mbtowc.c
@@ -1,9 +1,4 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
+#include <stdlib.h>
 #include <wchar.h>
 #include <errno.h>
 #include "internal.h"
@@ -19,6 +14,7 @@
 	if (!wc) wc = &dummy;
 
 	if (*s < 0x80) return !!(*wc = *s);
+	if (MB_CUR_MAX==1) return (*wc = CODEUNIT(*s)), 1;
 	if (*s-SA > SB-SA) goto ilseq;
 	c = bittab[*s++-SA];
 
diff --git a/system/lib/libc/musl/src/multibyte/wcrtomb.c b/system/lib/libc/musl/src/multibyte/wcrtomb.c
index 59f733d..8e34926 100644
--- a/system/lib/libc/musl/src/multibyte/wcrtomb.c
+++ b/system/lib/libc/musl/src/multibyte/wcrtomb.c
@@ -1,11 +1,7 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
+#include <stdlib.h>
 #include <wchar.h>
 #include <errno.h>
+#include "internal.h"
 
 size_t wcrtomb(char *restrict s, wchar_t wc, mbstate_t *restrict st)
 {
@@ -13,6 +9,13 @@
 	if ((unsigned)wc < 0x80) {
 		*s = wc;
 		return 1;
+	} else if (MB_CUR_MAX == 1) {
+		if (!IS_CODEUNIT(wc)) {
+			errno = EILSEQ;
+			return -1;
+		}
+		*s = wc;
+		return 1;
 	} else if ((unsigned)wc < 0x800) {
 		*s++ = 0xc0 | (wc>>6);
 		*s = 0x80 | (wc&0x3f);
diff --git a/system/lib/libc/musl/src/multibyte/wcsnrtombs.c b/system/lib/libc/musl/src/multibyte/wcsnrtombs.c
index 7eb05d4..640cbbe 100644
--- a/system/lib/libc/musl/src/multibyte/wcsnrtombs.c
+++ b/system/lib/libc/musl/src/multibyte/wcsnrtombs.c
@@ -1,9 +1,3 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #include <wchar.h>
 
 size_t wcsnrtombs(char *restrict dst, const wchar_t **restrict wcs, size_t wn, size_t n, mbstate_t *restrict st)
@@ -40,7 +34,7 @@
 		ws++; wn--;
 		/* safe - this loop runs fewer than sizeof(buf) times */
 		s+=l; n-=l;
-		cnt++;
+		cnt += l;
 	}
 	if (dst) *wcs = ws;
 	return cnt;
diff --git a/system/lib/libc/musl/src/multibyte/wcsrtombs.c b/system/lib/libc/musl/src/multibyte/wcsrtombs.c
index 30be415..b5713ae 100644
--- a/system/lib/libc/musl/src/multibyte/wcsrtombs.c
+++ b/system/lib/libc/musl/src/multibyte/wcsrtombs.c
@@ -1,9 +1,3 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #include <wchar.h>
 
 size_t wcsrtombs(char *restrict s, const wchar_t **restrict ws, size_t n, mbstate_t *restrict st)
diff --git a/system/lib/libc/musl/src/multibyte/wctob.c b/system/lib/libc/musl/src/multibyte/wctob.c
index d6353ee..b484a3f 100644
--- a/system/lib/libc/musl/src/multibyte/wctob.c
+++ b/system/lib/libc/musl/src/multibyte/wctob.c
@@ -1,8 +1,11 @@
-#include <stdio.h>
 #include <wchar.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "internal.h"
 
 int wctob(wint_t c)
 {
 	if (c < 128U) return c;
+	if (MB_CUR_MAX==1 && IS_CODEUNIT(c)) return (unsigned char)c;
 	return EOF;
 }
diff --git a/system/lib/libc/musl/src/multibyte/wctomb.c b/system/lib/libc/musl/src/multibyte/wctomb.c
index de7ed84..bad41c5 100644
--- a/system/lib/libc/musl/src/multibyte/wctomb.c
+++ b/system/lib/libc/musl/src/multibyte/wctomb.c
@@ -1,9 +1,3 @@
-/* 
- * This code was written by Rich Felker in 2010; no copyright is claimed.
- * This code is in the public domain. Attribution is appreciated but
- * unnecessary.
- */
-
 #include <stdlib.h>
 #include <wchar.h>
 
diff --git a/system/lib/libc/musl/src/network/dn_comp.c b/system/lib/libc/musl/src/network/dn_comp.c
index 4f4452a..a17d434 100644
--- a/system/lib/libc/musl/src/network/dn_comp.c
+++ b/system/lib/libc/musl/src/network/dn_comp.c
@@ -1,9 +1,110 @@
+#include <string.h>
 #include <resolv.h>
 #include "libc.h"
 
+/* RFC 1035 message compression */
+
+/* label start offsets of a compressed domain name s */
+static int getoffs(short *offs, const unsigned char *base, const unsigned char *s)
+{
+	int i=0;
+	for (;;) {
+		while (*s & 0xc0) {
+			if ((*s & 0xc0) != 0xc0) return 0;
+			s = base + ((s[0]&0x3f)<<8 | s[1]);
+		}
+		if (!*s) return i;
+		if (s-base >= 0x4000) return 0;
+		offs[i++] = s-base;
+		s += *s + 1;
+	}
+}
+
+/* label lengths of an ascii domain name s */
+static int getlens(unsigned char *lens, const char *s, int l)
+{
+	int i=0,j=0,k=0;
+	for (;;) {
+		for (; j<l && s[j]!='.'; j++);
+		if (j-k-1u > 62) return 0;
+		lens[i++] = j-k;
+		if (j==l) return i;
+		k = ++j;
+	}
+}
+
+/* longest suffix match of an ascii domain with a compressed domain name dn */
+static int match(int *offset, const unsigned char *base, const unsigned char *dn,
+	const char *end, const unsigned char *lens, int nlen)
+{
+	int l, o, m=0;
+	short offs[128];
+	int noff = getoffs(offs, base, dn);
+	if (!noff) return 0;
+	for (;;) {
+		l = lens[--nlen];
+		o = offs[--noff];
+		end -= l;
+		if (l != base[o] || memcmp(base+o+1, end, l))
+			return m;
+		*offset = o;
+		m += l;
+		if (nlen) m++;
+		if (!nlen || !noff) return m;
+		end--;
+	}
+}
+
 int __dn_comp(const char *src, unsigned char *dst, int space, unsigned char **dnptrs, unsigned char **lastdnptr)
 {
-	return -1;
+	int i, j, n, m=0, offset, bestlen=0, bestoff;
+	unsigned char lens[127];
+	unsigned char **p;
+	const char *end;
+	size_t l = strnlen(src, 255);
+	if (l && src[l-1] == '.') l--;
+	if (l>253 || space<=0) return -1;
+	if (!l) {
+		*dst = 0;
+		return 1;
+	}
+	end = src+l;
+	n = getlens(lens, src, l);
+	if (!n) return -1;
+
+	p = dnptrs;
+	if (p && *p) for (p++; *p; p++) {
+		m = match(&offset, *dnptrs, *p, end, lens, n);
+		if (m > bestlen) {
+			bestlen = m;
+			bestoff = offset;
+			if (m == l)
+				break;
+		}
+	}
+
+	/* encode unmatched part */
+	if (space < l-bestlen+2+(bestlen-1 < l-1)) return -1;
+	memcpy(dst+1, src, l-bestlen);
+	for (i=j=0; i<l-bestlen; i+=lens[j++]+1)
+		dst[i] = lens[j];
+
+	/* add tail */
+	if (bestlen) {
+		dst[i++] = 0xc0 | bestoff>>8;
+		dst[i++] = bestoff;
+	} else
+		dst[i++] = 0;
+
+	/* save dst pointer */
+	if (i>2 && lastdnptr && dnptrs && *dnptrs) {
+		while (*p) p++;
+		if (p+1 < lastdnptr) {
+			*p++ = dst;
+			*p=0;
+		}
+	}
+	return i;
 }
 
 weak_alias(__dn_comp, dn_comp);
diff --git a/system/lib/libc/musl/src/network/dns_parse.c b/system/lib/libc/musl/src/network/dns_parse.c
new file mode 100644
index 0000000..0c7a601
--- /dev/null
+++ b/system/lib/libc/musl/src/network/dns_parse.c
@@ -0,0 +1,32 @@
+#include <string.h>
+
+int __dns_parse(const unsigned char *r, int rlen, int (*callback)(void *, int, const void *, int, const void *), void *ctx)
+{
+	int qdcount, ancount;
+	const unsigned char *p;
+	int len;
+
+	if (rlen<12) return -1;
+	if ((r[3]&15)) return 0;
+	p = r+12;
+	qdcount = r[4]*256 + r[5];
+	ancount = r[6]*256 + r[7];
+	if (qdcount+ancount > 64) return -1;
+	while (qdcount--) {
+		while (p-r < rlen && *p-1U < 127) p++;
+		if (*p>193 || (*p==193 && p[1]>254) || p>r+rlen-6)
+			return -1;
+		p += 5 + !!*p;
+	}
+	while (ancount--) {
+		while (p-r < rlen && *p-1U < 127) p++;
+		if (*p>193 || (*p==193 && p[1]>254) || p>r+rlen-6)
+			return -1;
+		p += 1 + !!*p;
+		len = p[8]*256 + p[9];
+		if (p+len > r+rlen) return -1;
+		if (callback(ctx, p[1], p+10, len, r) < 0) return -1;
+		p += 10 + len;
+	}
+	return 0;
+}
diff --git a/system/lib/libc/musl/src/network/gai_strerror.c b/system/lib/libc/musl/src/network/gai_strerror.c
index 0bf3e37..9596580 100644
--- a/system/lib/libc/musl/src/network/gai_strerror.c
+++ b/system/lib/libc/musl/src/network/gai_strerror.c
@@ -1,4 +1,5 @@
 #include <netdb.h>
+#include "locale_impl.h"
 
 static const char msgs[] =
 	"Invalid flags\0"
@@ -19,5 +20,6 @@
 {
 	const char *s;
 	for (s=msgs, ecode++; ecode && *s; ecode++, s++) for (; *s; s++);
-	return *s ? s : s+1;
+	if (!*s) s++;
+	return LCTRANS_CUR(s);
 }
diff --git a/system/lib/libc/musl/src/network/getaddrinfo.c b/system/lib/libc/musl/src/network/getaddrinfo.c
index 5d45be7..b9439f7 100644
--- a/system/lib/libc/musl/src/network/getaddrinfo.c
+++ b/system/lib/libc/musl/src/network/getaddrinfo.c
@@ -1,249 +1,92 @@
 #include <stdlib.h>
-#include <stdio.h>
-#include <netdb.h>
-#include <netinet/in.h>
 #include <sys/socket.h>
-#include <unistd.h>
+#include <netinet/in.h>
+#include <netdb.h>
 #include <string.h>
-#include <ctype.h>
-#include "__dns.h"
-#include "stdio_impl.h"
-
-static int is_valid(const char *host)
-{
-	const unsigned char *s;
-	if (strlen(host)-1 > 254 || mbstowcs(0, host, 0) > 255) return 0;
-	for (s=(void *)host; *s>=0x80 || *s=='.' || *s=='-' || isalnum(*s); s++);
-	return !*s;
-}
-
-#if 0
-static int have_af(int family)
-{
-	struct sockaddr_in6 sin6 = { .sin6_family = family };
-	socklen_t sl = family == AF_INET
-		? sizeof(struct sockaddr_in)
-		: sizeof(struct sockaddr_in6);
-	int sock = socket(family, SOCK_STREAM, 0);
-	int have = !bind(sock, (void *)&sin6, sl);
-	close(sock);
-	return have;
-}
-#endif
-
-union sa {
-	struct sockaddr_in sin;
-	struct sockaddr_in6 sin6;
-};
-
-struct aibuf {
-	struct addrinfo ai;
-	union sa sa;
-};
-
-/* Extra slots needed for storing canonical name */
-#define EXTRA ((256+sizeof(struct aibuf)-1)/sizeof(struct aibuf))
+#include "lookup.h"
 
 int getaddrinfo(const char *restrict host, const char *restrict serv, const struct addrinfo *restrict hint, struct addrinfo **restrict res)
 {
-	int flags = hint ? hint->ai_flags : 0;
-	int family = hint ? hint->ai_family : AF_UNSPEC;
-	int type = hint ? hint->ai_socktype : 0;
-	int proto = hint ? hint->ai_protocol : 0;
-	unsigned long port = 0;
-	struct aibuf *buf;
-	union sa sa = {{0}};
-	unsigned char reply[1024];
-	int i, j;
-	char line[512];
-	FILE *f, _f;
-	unsigned char _buf[1024];
-	char *z;
-	int result;
-	int cnt;
+	struct service ports[MAXSERVS];
+	struct address addrs[MAXADDRS];
+	char canon[256], *outcanon;
+	int nservs, naddrs, nais, canon_len, i, j, k;
+	int family = AF_UNSPEC, flags = 0, proto = 0, socktype = 0;
+	struct aibuf {
+		struct addrinfo ai;
+		union sa {
+			struct sockaddr_in sin;
+			struct sockaddr_in6 sin6;
+		} sa;
+	} *out;
 
-	if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC)
-		return EAI_FAMILY;
+	if (!host && !serv) return EAI_NONAME;
 
-	if (host && strlen(host)>255) return EAI_NONAME;
-	if (serv && strlen(serv)>32) return EAI_SERVICE;
+	if (hint) {
+		family = hint->ai_family;
+		flags = hint->ai_flags;
+		proto = hint->ai_protocol;
+		socktype = hint->ai_socktype;
 
-	if (type && !proto)
-		proto = type==SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
-	if (!type && proto)
-		type = proto==IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM;
+		const int mask = AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST |
+			AI_V4MAPPED | AI_ALL | AI_ADDRCONFIG | AI_NUMERICSERV;
+		if ((flags & mask) != flags)
+			return EAI_BADFLAGS;
 
-	if (serv) {
-		if (!*serv) return EAI_SERVICE;
-		port = strtoul(serv, &z, 10);
-		if (*z) {
-			size_t servlen = strlen(serv);
-			char *end = line;
-
-			if (flags & AI_NUMERICSERV) return EAI_SERVICE;
-
-			f = __fopen_rb_ca("/etc/services", &_f, _buf, sizeof _buf);
-			if (!f) return EAI_SERVICE;
-			while (fgets(line, sizeof line, f)) {
-				if (strncmp(line, serv, servlen) || !isspace(line[servlen]))
-					continue;
-				port = strtoul(line+servlen, &end, 10);
-				if (strncmp(end, proto==IPPROTO_UDP ? "/udp" : "/tcp", 4))
-					continue;
-				break;
-			}
-			__fclose_ca(f);
-			if (feof(f)) return EAI_SERVICE;
+		switch (family) {
+		case AF_INET:
+		case AF_INET6:
+		case AF_UNSPEC:
+			break;
+		default:
+			return EAI_FAMILY;
 		}
-		if (port > 65535) return EAI_SERVICE;
-		port = htons(port);
 	}
 
-	if (!host) {
-		if (family == AF_UNSPEC) {
-			cnt = 2; family = AF_INET;
-		} else {
-			cnt = 1;
+	nservs = __lookup_serv(ports, serv, proto, socktype, flags);
+	if (nservs < 0) return nservs;
+
+	naddrs = __lookup_name(addrs, canon, host, family, flags);
+	if (naddrs < 0) return naddrs;
+
+	nais = nservs * naddrs;
+	canon_len = strlen(canon);
+	out = calloc(1, nais * sizeof(*out) + canon_len + 1);
+	if (!out) return EAI_MEMORY;
+
+	if (canon_len) {
+		outcanon = (void *)&out[nais];
+		memcpy(outcanon, canon, canon_len+1);
+	} else {
+		outcanon = 0;
+	}
+
+	for (k=i=0; i<naddrs; i++) for (j=0; j<nservs; j++, k++) {
+		out[k].ai = (struct addrinfo){
+			.ai_family = addrs[i].family,
+			.ai_socktype = ports[j].socktype,
+			.ai_protocol = ports[j].proto,
+			.ai_addrlen = addrs[i].family == AF_INET
+				? sizeof(struct sockaddr_in)
+				: sizeof(struct sockaddr_in6),
+			.ai_addr = (void *)&out[k].sa,
+			.ai_canonname = outcanon,
+			.ai_next = &out[k+1].ai };
+		switch (addrs[i].family) {
+		case AF_INET:
+			out[k].sa.sin.sin_family = AF_INET;
+			out[k].sa.sin.sin_port = htons(ports[j].port);
+			memcpy(&out[k].sa.sin.sin_addr, &addrs[i].addr, 4);
+			break;
+		case AF_INET6:
+			out[k].sa.sin6.sin6_family = AF_INET6;
+			out[k].sa.sin6.sin6_port = htons(ports[j].port);
+			out[k].sa.sin6.sin6_scope_id = addrs[i].scopeid;
+			memcpy(&out[k].sa.sin6.sin6_addr, &addrs[i].addr, 16);
+			break;			
 		}
-		buf = calloc(sizeof *buf, cnt);
-		if (!buf) return EAI_MEMORY;
-		for (i=0; i<cnt; i++) {
-			if (i) family = AF_INET6;
-			buf[i].ai.ai_protocol = proto;
-			buf[i].ai.ai_socktype = type;
-			buf[i].ai.ai_addr = (void *)&buf[i].sa;
-			buf[i].ai.ai_addrlen = family==AF_INET6
-				? sizeof sa.sin6 : sizeof sa.sin;
-			buf[i].ai.ai_family = family;
-			buf[i].sa.sin.sin_family = family;
-			buf[i].sa.sin.sin_port = port;
-			if (i+1<cnt) buf[i].ai.ai_next = &buf[i+1].ai;
-			if (!(flags & AI_PASSIVE)) {
-				if (family == AF_INET) {
-					0[(uint8_t*)&buf[i].sa.sin.sin_addr.s_addr]=127;
-					3[(uint8_t*)&buf[i].sa.sin.sin_addr.s_addr]=1;
-				} else buf[i].sa.sin6.sin6_addr.s6_addr[15] = 1;
-			}
-		}
-		*res = &buf->ai;
-		return 0;
 	}
-
-	if (!*host) return EAI_NONAME;
-
-	/* Try as a numeric address */
-	if (__ipparse(&sa, family, host) >= 0) {
-		buf = calloc(sizeof *buf, 1+EXTRA);
-		if (!buf) return EAI_MEMORY;
-		family = sa.sin.sin_family;
-		buf->ai.ai_protocol = proto;
-		buf->ai.ai_socktype = type;
-		buf->ai.ai_addr = (void *)&buf->sa;
-		buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
-		buf->ai.ai_family = family;
-		buf->ai.ai_canonname = (char *)host;
-		buf->sa = sa;
-		buf->sa.sin.sin_port = port;
-		*res = &buf->ai;
-		return 0;
-	}
-
-	if (flags & AI_NUMERICHOST) return EAI_NONAME;
-
-	f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf);
-	if (f) while (fgets(line, sizeof line, f)) {
-		char *p;
-		size_t l = strlen(host);
-
-		if ((p=strchr(line, '#'))) *p++='\n', *p=0;
-		for(p=line+1; (p=strstr(p, host)) &&
-			(!isspace(p[-1]) || !isspace(p[l])); p++);
-		if (!p) continue;
-		__fclose_ca(f);
-
-		/* Isolate IP address to parse */
-		for (p=line; *p && !isspace(*p); p++);
-		*p++ = 0;
-		if (__ipparse(&sa, family, line) < 0) return EAI_NONAME;
-
-		/* Allocate and fill result buffer */
-		buf = calloc(sizeof *buf, 1+EXTRA);
-		if (!buf) return EAI_MEMORY;
-		family = sa.sin.sin_family;
-		buf->ai.ai_protocol = proto;
-		buf->ai.ai_socktype = type;
-		buf->ai.ai_addr = (void *)&buf->sa;
-		buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
-		buf->ai.ai_family = family;
-		buf->sa = sa;
-		buf->sa.sin.sin_port = port;
-
-		/* Extract first name as canonical name */
-		for (; *p && isspace(*p); p++);
-		buf->ai.ai_canonname = (void *)(buf+1);
-		snprintf(buf->ai.ai_canonname, 256, "%s", p);
-		for (p=buf->ai.ai_canonname; *p && !isspace(*p); p++);
-		*p = 0;
-		if (!is_valid(buf->ai.ai_canonname))
-			buf->ai.ai_canonname = 0;
-
-		*res = &buf->ai;
-		return 0;
-	}
-	if (f) __fclose_ca(f);
-
-#if 0
-	f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf);
-	if (f) while (fgets(line, sizeof line, f)) {
-		if (!isspace(line[10]) || (strncmp(line, "search", 6)
-			&& strncmp(line, "domain", 6))) continue;
-	}
-	if (f) __fclose_ca(f);
-#endif
-
-	/* Perform one or more DNS queries for host */
-	memset(reply, 0, sizeof reply);
-	result = __dns_query(reply, host, family, 0);
-	if (result < 0) return result;
-
-	cnt = __dns_count_addrs(reply, result);
-	if (cnt <= 0) return EAI_NONAME;
-
-	buf = calloc(sizeof *buf, cnt+EXTRA);
-	if (!buf) return EAI_MEMORY;
-
-	i = 0;
-	if (family != AF_INET6) {
-		j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply, RR_A, 0);
-		while (j--) buf[i++].sa.sin.sin_family = AF_INET;
-	}
-	if (family != AF_INET) {
-		j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply, RR_AAAA, 0);
-		while (j--) buf[i++].sa.sin.sin_family = AF_INET6;
-	}
-	if (result>1) {
-		j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply+512, RR_A, 0);
-		while (j--) buf[i++].sa.sin.sin_family = AF_INET;
-		j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply+512, RR_AAAA, 0);
-		while (j--) buf[i++].sa.sin.sin_family = AF_INET6;
-	}
-
-	if (__dns_get_rr((void *)&buf[cnt], 0, 256, 1, reply, RR_CNAME, 1) <= 0)
-		strcpy((void *)&buf[cnt], host);
-
-	for (i=0; i<cnt; i++) {
-		buf[i].ai.ai_protocol = proto;
-		buf[i].ai.ai_socktype = type;
-		buf[i].ai.ai_addr = (void *)&buf[i].sa;
-		buf[i].ai.ai_addrlen = buf[i].sa.sin.sin_family==AF_INET6
-			? sizeof sa.sin6 : sizeof sa.sin;
-		buf[i].ai.ai_family = buf[i].sa.sin.sin_family;
-		buf[i].sa.sin.sin_port = port;
-		buf[i].ai.ai_next = &buf[i+1].ai;
-		buf[i].ai.ai_canonname = (void *)&buf[cnt];
-	}
-	buf[cnt-1].ai.ai_next = 0;
-	*res = &buf->ai;
-
+	out[nais-1].ai.ai_next = 0;
+	*res = &out->ai;
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/network/gethostbyaddr_r.c b/system/lib/libc/musl/src/network/gethostbyaddr_r.c
index 66e0330..0f1e61a 100644
--- a/system/lib/libc/musl/src/network/gethostbyaddr_r.c
+++ b/system/lib/libc/musl/src/network/gethostbyaddr_r.c
@@ -64,6 +64,7 @@
 	}
 
 	h->h_addrtype = af;
+	h->h_length = l;
 	h->h_name = h->h_aliases[0];
 	*res = h;
 	return 0;
diff --git a/system/lib/libc/musl/src/network/gethostbyname2_r.c b/system/lib/libc/musl/src/network/gethostbyname2_r.c
index d6920b2..5c1cae9 100644
--- a/system/lib/libc/musl/src/network/gethostbyname2_r.c
+++ b/system/lib/libc/musl/src/network/gethostbyname2_r.c
@@ -6,43 +6,32 @@
 #include <netinet/in.h>
 #include <errno.h>
 #include <stdint.h>
+#include "lookup.h"
 
 int gethostbyname2_r(const char *name, int af,
 	struct hostent *h, char *buf, size_t buflen,
 	struct hostent **res, int *err)
 {
-	struct addrinfo hint = {
-		.ai_family = af==AF_INET6 ? af : AF_INET,
-		.ai_flags = AI_CANONNAME
-	};
-	struct addrinfo *ai, *p;
-	int i;
-	size_t need;
-	const char *canon;
+	struct address addrs[MAXADDRS];
+	char canon[256];
+	int i, cnt;
+	size_t align, need;
 
 	*res = 0;
-
-	af = hint.ai_family;
-
-	/* Align buffer */
-	i = (uintptr_t)buf & sizeof(char *)-1;
-	if (i) {
-		if (buflen < sizeof(char *)-i) return ERANGE;
-		buf += sizeof(char *)-i;
-		buflen -= sizeof(char *)-i;
-	}
-
-	switch (getaddrinfo(name, 0, &hint, &ai)) {
+	cnt = __lookup_name(addrs, canon, name, af, AI_CANONNAME);
+	if (cnt<0) switch (cnt) {
 	case EAI_NONAME:
 		*err = HOST_NOT_FOUND;
-		return errno;
+		return ENOENT;
 	case EAI_AGAIN:
 		*err = TRY_AGAIN;
-		return errno;
+		return EAGAIN;
 	default:
+	case EAI_FAIL:
+		*err = NO_RECOVERY;
+		return EBADMSG;
 	case EAI_MEMORY:
 	case EAI_SYSTEM:
-	case EAI_FAIL:
 		*err = NO_RECOVERY;
 		return errno;
 	case 0:
@@ -52,22 +41,29 @@
 	h->h_addrtype = af;
 	h->h_length = af==AF_INET6 ? 16 : 4;
 
-	canon = ai->ai_canonname ? ai->ai_canonname : name;
+	/* Align buffer */
+	align = -(uintptr_t)buf & sizeof(char *)-1;
+
 	need = 4*sizeof(char *);
-	for (i=0, p=ai; p; i++, p=p->ai_next)
-		need += sizeof(char *) + h->h_length;
+	need += (cnt + 1) * (sizeof(char *) + h->h_length);
 	need += strlen(name)+1;
 	need += strlen(canon)+1;
+	need += align;
 
-	if (need > buflen) {
-		freeaddrinfo(ai);
-		return ERANGE;
-	}
+	if (need > buflen) return ERANGE;
 
+	buf += align;
 	h->h_aliases = (void *)buf;
 	buf += 3*sizeof(char *);
 	h->h_addr_list = (void *)buf;
-	buf += (i+1)*sizeof(char *);
+	buf += (cnt+1)*sizeof(char *);
+
+	for (i=0; i<cnt; i++) {
+		h->h_addr_list[i] = (void *)buf;
+		buf += h->h_length;
+		memcpy(h->h_addr_list[i], addrs[i].addr, h->h_length);
+	}
+	h->h_addr_list[i] = 0;
 
 	h->h_name = h->h_aliases[0] = buf;
 	strcpy(h->h_name, canon);
@@ -81,16 +77,6 @@
 
 	h->h_aliases[2] = 0;
 
-	for (i=0, p=ai; p; i++, p=p->ai_next) {
-		h->h_addr_list[i] = (void *)buf;
-		buf += h->h_length;
-		memcpy(h->h_addr_list[i],
-			&((struct sockaddr_in *)p->ai_addr)->sin_addr,
-			h->h_length);
-	}
-	h->h_addr_list[i] = 0;
-
 	*res = h;
-	freeaddrinfo(ai);
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/network/getifaddrs.c b/system/lib/libc/musl/src/network/getifaddrs.c
index 5a94cc7..fed75bd 100644
--- a/system/lib/libc/musl/src/network/getifaddrs.c
+++ b/system/lib/libc/musl/src/network/getifaddrs.c
@@ -1,181 +1,216 @@
-/* (C) 2013 John Spencer. released under musl's standard MIT license. */
-#undef _GNU_SOURCE
 #define _GNU_SOURCE
-#include <ifaddrs.h>
-#include <stdlib.h>
-#include <net/if.h> /* IFNAMSIZ, ifreq, ifconf */
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
 #include <errno.h>
-#include <arpa/inet.h> /* inet_pton */
+#include <string.h>
+#include <stdlib.h>
 #include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <syscall.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include "netlink.h"
 
-typedef union {
-	struct sockaddr_in6 v6;
+#define IFADDRS_HASH_SIZE 64
+
+/* getifaddrs() reports hardware addresses with PF_PACKET that implies
+ * struct sockaddr_ll.  But e.g. Infiniband socket address length is
+ * longer than sockaddr_ll.ssl_addr[8] can hold. Use this hack struct
+ * to extend ssl_addr - callers should be able to still use it. */
+struct sockaddr_ll_hack {
+	unsigned short sll_family, sll_protocol;
+	int sll_ifindex;
+	unsigned short sll_hatype;
+	unsigned char sll_pkttype, sll_halen;
+	unsigned char sll_addr[24];
+};
+
+union sockany {
+	struct sockaddr sa;
+	struct sockaddr_ll_hack ll;
 	struct sockaddr_in v4;
-} soa;
+	struct sockaddr_in6 v6;
+};
 
-typedef struct ifaddrs_storage {
+struct ifaddrs_storage {
 	struct ifaddrs ifa;
-	soa addr;
-	soa netmask;
-	soa dst;
+	struct ifaddrs_storage *hash_next;
+	union sockany addr, netmask, ifu;
+	unsigned int index;
 	char name[IFNAMSIZ+1];
-} stor;
-#define next ifa.ifa_next
+};
 
-static stor* list_add(stor** list, stor** head, char* ifname)
-{
-	stor* curr = calloc(1, sizeof(stor));
-	if(curr) {
-		strcpy(curr->name, ifname);
-		curr->ifa.ifa_name = curr->name;
-		if(*head) (*head)->next = (struct ifaddrs*) curr;
-		*head = curr;
-		if(!*list) *list = curr;
-	}
-	return curr;
-}
+struct ifaddrs_ctx {
+	struct ifaddrs_storage *first;
+	struct ifaddrs_storage *last;
+	struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
+};
 
 void freeifaddrs(struct ifaddrs *ifp)
 {
-	stor *head = (stor *) ifp;
-	while(head) {
-		void *p = head;
-		head = (stor *) head->next;
-		free(p);
+	struct ifaddrs *n;
+	while (ifp) {
+		n = ifp->ifa_next;
+		free(ifp);
+		ifp = n;
 	}
 }
 
-static void ipv6netmask(unsigned prefix_length, struct sockaddr_in6 *sa)
+static void copy_addr(struct sockaddr **r, int af, union sockany *sa, void *addr, size_t addrlen, int ifindex)
 {
-	unsigned char* hb = sa->sin6_addr.s6_addr;
-	unsigned onebytes = prefix_length / 8;
-	unsigned bits = prefix_length % 8;
-	unsigned nullbytes = 16 - onebytes;
-	memset(hb, -1, onebytes);
-	memset(hb+onebytes, 0, nullbytes);
-	if(bits) {
-		unsigned char x = -1;
-		x <<= 8 - bits;
-		hb[onebytes] = x;
+	uint8_t *dst;
+	int len;
+
+	switch (af) {
+	case AF_INET:
+		dst = (uint8_t*) &sa->v4.sin_addr;
+		len = 4;
+		break;
+	case AF_INET6:
+		dst = (uint8_t*) &sa->v6.sin6_addr;
+		len = 16;
+		if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr))
+			sa->v6.sin6_scope_id = ifindex;
+		break;
+	default:
+		return;
 	}
+	if (addrlen < len) return;
+	sa->sa.sa_family = af;
+	memcpy(dst, addr, len);
+	*r = &sa->sa;
 }
 
-static void dealwithipv6(stor **list, stor** head)
+static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, int prefixlen)
 {
-	FILE* f = fopen("/proc/net/if_inet6", "rbe");
-	/* 00000000000000000000000000000001 01 80 10 80 lo
-	   A                                B  C  D  E  F
-	   all numbers in hex
-	   A = addr B=netlink device#, C=prefix length,
-	   D = scope value (ipv6.h) E = interface flags (rnetlink.h, addrconf.c)
-	   F = if name */
-	char v6conv[32 + 7 + 1], *v6;
-	char *line, linebuf[512];
-	if(!f) return;
-	while((line = fgets(linebuf, sizeof linebuf, f))) {
-		v6 = v6conv;
-		size_t i = 0;
-		for(; i < 8; i++) {
-			memcpy(v6, line, 4);
-			v6+=4;
-			*v6++=':';
-			line+=4;
+	uint8_t addr[16] = {0};
+	int i;
+
+	if (prefixlen > 8*sizeof(addr)) prefixlen = 8*sizeof(addr);
+	i = prefixlen / 8;
+	memset(addr, 0xff, i);
+	if (i < sizeof(addr)) addr[i++] = 0xff << (8 - (prefixlen % 8));
+	copy_addr(r, af, sa, addr, sizeof(addr), 0);
+}
+
+static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, size_t addrlen, int ifindex, unsigned short hatype)
+{
+	if (addrlen > sizeof(sa->ll.sll_addr)) return;
+	sa->ll.sll_family = AF_PACKET;
+	sa->ll.sll_ifindex = ifindex;
+	sa->ll.sll_hatype = hatype;
+	sa->ll.sll_halen = addrlen;
+	memcpy(sa->ll.sll_addr, addr, addrlen);
+	*r = &sa->sa;
+}
+
+static int netlink_msg_to_ifaddr(void *pctx, struct nlmsghdr *h)
+{
+	struct ifaddrs_ctx *ctx = pctx;
+	struct ifaddrs_storage *ifs, *ifs0;
+	struct ifinfomsg *ifi = NLMSG_DATA(h);
+	struct ifaddrmsg *ifa = NLMSG_DATA(h);
+	struct rtattr *rta;
+	int stats_len = 0;
+
+	if (h->nlmsg_type == RTM_NEWLINK) {
+		for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+			if (rta->rta_type != IFLA_STATS) continue;
+			stats_len = RTA_DATALEN(rta);
+			break;
 		}
-		--v6; *v6 = 0;
-		line++;
-		unsigned b, c, d, e;
-		char name[IFNAMSIZ+1];
-		if(5 == sscanf(line, "%x %x %x %x %s", &b, &c, &d, &e, name)) {
-			struct sockaddr_in6 sa = {0};
-			if(1 == inet_pton(AF_INET6, v6conv, &sa.sin6_addr)) {
-				sa.sin6_family = AF_INET6;
-				stor* curr = list_add(list, head, name);
-				if(!curr) goto out;
-				curr->addr.v6 = sa;
-				curr->ifa.ifa_addr = (struct sockaddr*) &curr->addr;
-				ipv6netmask(c, &sa);
-				curr->netmask.v6 = sa;
-				curr->ifa.ifa_netmask = (struct sockaddr*) &curr->netmask;
-				/* find ipv4 struct with the same interface name to copy flags */
-				stor* scan = *list;
-				for(;scan && strcmp(name, scan->name);scan=(stor*)scan->next);
-				if(scan) curr->ifa.ifa_flags = scan->ifa.ifa_flags;
-				else curr->ifa.ifa_flags = 0;
-			} else errno = 0;
-		}
+	} else {
+		for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; ifs0 = ifs0->hash_next)
+			if (ifs0->index == ifa->ifa_index)
+				break;
+		if (!ifs0) return 0;
 	}
-	out:
-	fclose(f);
+
+	ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
+	if (ifs == 0) return -1;
+
+	if (h->nlmsg_type == RTM_NEWLINK) {
+		ifs->index = ifi->ifi_index;
+		ifs->ifa.ifa_flags = ifi->ifi_flags;
+
+		for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+			switch (rta->rta_type) {
+			case IFLA_IFNAME:
+				if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
+					memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
+					ifs->ifa.ifa_name = ifs->name;
+				}
+				break;
+			case IFLA_ADDRESS:
+				copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
+				break;
+			case IFLA_BROADCAST:
+				copy_lladdr(&ifs->ifa.ifa_broadaddr, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
+				break;
+			case IFLA_STATS:
+				ifs->ifa.ifa_data = (void*)(ifs+1);
+				memcpy(ifs->ifa.ifa_data, RTA_DATA(rta), RTA_DATALEN(rta));
+				break;
+			}
+		}
+		if (ifs->ifa.ifa_name) {
+			unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE;
+			ifs->hash_next = ctx->hash[bucket];
+			ctx->hash[bucket] = ifs;
+		}
+	} else {
+		ifs->ifa.ifa_name = ifs0->ifa.ifa_name;
+		ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags;
+		for (rta = NLMSG_RTA(h, sizeof(*ifa)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+			switch (rta->rta_type) {
+			case IFA_ADDRESS:
+				/* If ifa_addr is already set we, received an IFA_LOCAL before
+				 * so treat this as destination address */
+				if (ifs->ifa.ifa_addr)
+					copy_addr(&ifs->ifa.ifa_dstaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
+				else
+					copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
+				break;
+			case IFA_BROADCAST:
+				copy_addr(&ifs->ifa.ifa_broadaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
+				break;
+			case IFA_LOCAL:
+				/* If ifa_addr is set and we get IFA_LOCAL, assume we have
+				 * a point-to-point network. Move address to correct field. */
+				if (ifs->ifa.ifa_addr) {
+					ifs->ifu = ifs->addr;
+					ifs->ifa.ifa_dstaddr = &ifs->ifu.sa;
+					memset(&ifs->addr, 0, sizeof(ifs->addr));
+				}
+				copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
+				break;
+			case IFA_LABEL:
+				if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
+					memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
+					ifs->ifa.ifa_name = ifs->name;
+				}
+				break;
+			}
+		}
+		if (ifs->ifa.ifa_addr)
+			gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen);
+	}
+
+	if (ifs->ifa.ifa_name) {
+		if (!ctx->first) ctx->first = ifs;
+		if (ctx->last) ctx->last->ifa.ifa_next = &ifs->ifa;
+		ctx->last = ifs;
+	} else {
+		free(ifs);
+	}
+	return 0;
 }
 
 int getifaddrs(struct ifaddrs **ifap)
 {
-	stor *list = 0, *head = 0;
-	struct if_nameindex* ii = if_nameindex();
-	if(!ii) return -1;
-	size_t i;
-	for(i = 0; ii[i].if_index || ii[i].if_name; i++) {
-		stor* curr = list_add(&list, &head, ii[i].if_name);
-		if(!curr) {
-			if_freenameindex(ii);
-			goto err2;
-		}
-	}
-	if_freenameindex(ii);
-
-	int sock = socket(PF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_IP);
-	if(sock == -1) goto err2;
-	struct ifreq reqs[32]; /* arbitrary chosen boundary */
-	struct ifconf conf = {.ifc_len = sizeof reqs, .ifc_req = reqs};
-	if(-1 == ioctl(sock, SIOCGIFCONF, &conf)) goto err;
-	size_t reqitems = conf.ifc_len / sizeof(struct ifreq);
-	for(head = list; head; head = (stor*)head->next) {
-		for(i = 0; i < reqitems; i++) {
-			// get SIOCGIFADDR of active interfaces.
-			if(!strcmp(reqs[i].ifr_name, head->name)) {
-				head->addr.v4 = *(struct sockaddr_in*)&reqs[i].ifr_addr;
-				head->ifa.ifa_addr = (struct sockaddr*) &head->addr;
-				break;
-			}
-		}
-		struct ifreq req;
-		snprintf(req.ifr_name, sizeof req.ifr_name, "%s", head->name);
-		if(-1 == ioctl(sock, SIOCGIFFLAGS, &req)) goto err;
-
-		head->ifa.ifa_flags = req.ifr_flags;
-		if(head->ifa.ifa_addr) {
-			/* or'ing flags with IFF_LOWER_UP on active interfaces to mimic glibc */
-			head->ifa.ifa_flags |= IFF_LOWER_UP; 
-			if(-1 == ioctl(sock, SIOCGIFNETMASK, &req)) goto err;
-			head->netmask.v4 = *(struct sockaddr_in*)&req.ifr_netmask;
-			head->ifa.ifa_netmask = (struct sockaddr*) &head->netmask;
-	
-			if(head->ifa.ifa_flags & IFF_POINTOPOINT) {
-				if(-1 == ioctl(sock, SIOCGIFDSTADDR, &req)) goto err;
-				head->dst.v4 = *(struct sockaddr_in*)&req.ifr_dstaddr;
-			} else {
-				if(-1 == ioctl(sock, SIOCGIFBRDADDR, &req)) goto err;
-				head->dst.v4 = *(struct sockaddr_in*)&req.ifr_broadaddr;
-			}
-			head->ifa.ifa_ifu.ifu_dstaddr = (struct sockaddr*) &head->dst;
-		}
-	}
-	close(sock);
-	void* last = 0;
-	for(head = list; head; head=(stor*)head->next) last=head;
-	head = last;
-	dealwithipv6(&list, &head);
-	*ifap = (struct ifaddrs*) list;
-	return 0;
-	err:
-	close(sock);
-	err2:
-	freeifaddrs((struct ifaddrs*) list);
-	return -1;
+	struct ifaddrs_ctx _ctx, *ctx = &_ctx;
+	int r;
+	memset(ctx, 0, sizeof *ctx);
+	r = __rtnetlink_enumerate(AF_UNSPEC, AF_UNSPEC, netlink_msg_to_ifaddr, ctx);
+	if (r == 0) *ifap = &ctx->first->ifa;
+	else freeifaddrs(&ctx->first->ifa);
+	return r;
 }
-
diff --git a/system/lib/libc/musl/src/network/getnameinfo.c b/system/lib/libc/musl/src/network/getnameinfo.c
index 33f89a3..5e6fae3 100644
--- a/system/lib/libc/musl/src/network/getnameinfo.c
+++ b/system/lib/libc/musl/src/network/getnameinfo.c
@@ -5,48 +5,197 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#include "__dns.h"
+#include <net/if.h>
+#include <ctype.h>
+#include "lookup.h"
+#include "stdio_impl.h"
+
+int __dns_parse(const unsigned char *, int, int (*)(void *, int, const void *, int, const void *), void *);
+int __dn_expand(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
+int __res_mkquery(int, const char *, int, int, const unsigned char *, int, const unsigned char*, unsigned char *, int);
+int __res_send(const unsigned char *, int, unsigned char *, int);
+
+#define PTR_MAX (64 + sizeof ".in-addr.arpa")
+#define RR_PTR 12
+
+static char *itoa(char *p, unsigned x) {
+	p += 3*sizeof(int);
+	*--p = 0;
+	do {
+		*--p = '0' + x % 10;
+		x /= 10;
+	} while (x);
+	return p;
+}
+
+static void mkptr4(char *s, const unsigned char *ip)
+{
+	sprintf(s, "%d.%d.%d.%d.in-addr.arpa",
+		ip[3], ip[2], ip[1], ip[0]);
+}
+
+static void mkptr6(char *s, const unsigned char *ip)
+{
+	static const char xdigits[] = "0123456789abcdef";
+	int i;
+	for (i=15; i>=0; i--) {
+		*s++ = xdigits[ip[i]&15]; *s++ = '.';
+		*s++ = xdigits[ip[i]>>4]; *s++ = '.';
+	}
+	strcpy(s, "ip6.arpa");
+}
+
+static void reverse_hosts(char *buf, const unsigned char *a, unsigned scopeid, int family)
+{
+	char line[512], *p, *z;
+	unsigned char _buf[1032], atmp[16];
+	struct address iplit;
+	FILE _f, *f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf);
+	if (!f) return;
+	if (family == AF_INET) {
+		memcpy(atmp+12, a, 4);
+		memcpy(atmp, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
+		a = atmp;
+	}
+	while (fgets(line, sizeof line, f)) {
+		if ((p=strchr(line, '#'))) *p++='\n', *p=0;
+
+		for (p=line; *p && !isspace(*p); p++);
+		*p++ = 0;
+		if (__lookup_ipliteral(&iplit, line, AF_UNSPEC)<=0)
+			continue;
+
+		if (iplit.family == AF_INET) {
+			memcpy(iplit.addr+12, iplit.addr, 4);
+			memcpy(iplit.addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
+			iplit.scopeid = 0;
+		}
+
+		if (memcmp(a, iplit.addr, 16) || iplit.scopeid != scopeid)
+			continue;
+			
+		for (; *p && isspace(*p); p++);
+		for (z=p; *z && !isspace(*z); z++);
+		*z = 0;
+		if (z-p < 256) {
+			memcpy(buf, p, z-p+1);
+			break;
+		}
+	}
+	__fclose_ca(f);
+}
+
+static void reverse_services(char *buf, int port, int dgram)
+{
+	unsigned long svport;
+	char line[128], *p, *z;
+	unsigned char _buf[1032];
+	FILE _f, *f = __fopen_rb_ca("/etc/services", &_f, _buf, sizeof _buf);
+	if (!f) return;
+	while (fgets(line, sizeof line, f)) {
+		if ((p=strchr(line, '#'))) *p++='\n', *p=0;
+
+		for (p=line; *p && !isspace(*p); p++);
+		if (!*p) continue;
+		*p++ = 0;
+		svport = strtoul(p, &z, 10);
+
+		if (svport != port || z==p) continue;
+		if (dgram && strncmp(z, "/udp", 4)) continue;
+		if (!dgram && strncmp(z, "/tcp", 4)) continue;
+		if (p-line > 32) continue;
+
+		memcpy(buf, line, p-line);
+		break;
+	}
+	__fclose_ca(f);
+}
+
+static int dns_parse_callback(void *c, int rr, const void *data, int len, const void *packet)
+{
+	if (rr != RR_PTR) return 0;
+	if (__dn_expand(packet, (const unsigned char *)packet + 512,
+	    data, c, 256) <= 0)
+		*(char *)c = 0;
+	return 0;
+	
+}
 
 int getnameinfo(const struct sockaddr *restrict sa, socklen_t sl,
 	char *restrict node, socklen_t nodelen,
 	char *restrict serv, socklen_t servlen,
 	int flags)
 {
-	char buf[256];
-	unsigned char reply[512];
+	char ptr[PTR_MAX];
+	char buf[256], num[3*sizeof(int)+1];
 	int af = sa->sa_family;
 	unsigned char *a;
+	unsigned scopeid;
 
 	switch (af) {
 	case AF_INET:
 		a = (void *)&((struct sockaddr_in *)sa)->sin_addr;
-		if (sl != sizeof(struct sockaddr_in)) return EAI_FAMILY;
+		if (sl < sizeof(struct sockaddr_in)) return EAI_FAMILY;
+		mkptr4(ptr, a);
+		scopeid = 0;
 		break;
 	case AF_INET6:
 		a = (void *)&((struct sockaddr_in6 *)sa)->sin6_addr;
-		if (sl != sizeof(struct sockaddr_in6)) return EAI_FAMILY;
+		if (sl < sizeof(struct sockaddr_in6)) return EAI_FAMILY;
+		if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12))
+			mkptr6(ptr, a);
+		else
+			mkptr4(ptr, a+12);
+		scopeid = ((struct sockaddr_in6 *)sa)->sin6_scope_id;
 		break;
 	default:
 		return EAI_FAMILY;
 	}
 
 	if (node && nodelen) {
-		if ((flags & NI_NUMERICHOST)
-			|| __dns_query(reply, a, af, 1) <= 0
-			|| __dns_get_rr(buf, 0, 256, 1, reply, RR_PTR, 1) <= 0)
-		{
+		buf[0] = 0;
+		if (!(flags & NI_NUMERICHOST)) {
+			reverse_hosts(buf, a, scopeid, af);
+		}
+		if (!*buf && !(flags & NI_NUMERICHOST)) {
+			unsigned char query[18+PTR_MAX], reply[512];
+			int qlen = __res_mkquery(0, ptr, 1, RR_PTR,
+				0, 0, 0, query, sizeof query);
+			int rlen = __res_send(query, qlen, reply, sizeof reply);
+			buf[0] = 0;
+			if (rlen > 0)
+				__dns_parse(reply, rlen, dns_parse_callback, buf);
+		}
+		if (!*buf) {
 			if (flags & NI_NAMEREQD) return EAI_NONAME;
 			inet_ntop(af, a, buf, sizeof buf);
+			if (scopeid) {
+				char *p = 0, tmp[IF_NAMESIZE+1];
+				if (!(flags & NI_NUMERICSCOPE) &&
+				    (IN6_IS_ADDR_LINKLOCAL(a) ||
+				     IN6_IS_ADDR_MC_LINKLOCAL(a)))
+					p = if_indextoname(scopeid, tmp+1);
+				if (!p)
+					p = itoa(num, scopeid);
+				*--p = '%';
+				strcat(buf, p);
+			}
 		}
 		if (strlen(buf) >= nodelen) return EAI_OVERFLOW;
 		strcpy(node, buf);
 	}
 
 	if (serv && servlen) {
-		if (snprintf(buf, sizeof buf, "%d",
-			ntohs(((struct sockaddr_in *)sa)->sin_port))>=servlen)
+		char *p = buf;
+		int port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+		buf[0] = 0;
+		if (!(flags & NI_NUMERICSERV))
+			reverse_services(buf, port, flags & NI_DGRAM);
+		if (!*p)
+			p = itoa(num, port);
+		if (strlen(p) >= servlen)
 			return EAI_OVERFLOW;
-		strcpy(serv, buf);
+		strcpy(serv, p);
 	}
 
 	return 0;
diff --git a/system/lib/libc/musl/src/network/getservbyname.c b/system/lib/libc/musl/src/network/getservbyname.c
index 0b00ce1..dd30376 100644
--- a/system/lib/libc/musl/src/network/getservbyname.c
+++ b/system/lib/libc/musl/src/network/getservbyname.c
@@ -4,7 +4,7 @@
 struct servent *getservbyname(const char *name, const char *prots)
 {
 	static struct servent se;
-	static long buf[32/sizeof(long)];
+	static char *buf[2];
 	struct servent *res;
 	if (getservbyname_r(name, prots, &se, (void *)buf, sizeof buf, &res))
 		return 0;
diff --git a/system/lib/libc/musl/src/network/getservbyname_r.c b/system/lib/libc/musl/src/network/getservbyname_r.c
index 811c174..056c2f3 100644
--- a/system/lib/libc/musl/src/network/getservbyname_r.c
+++ b/system/lib/libc/musl/src/network/getservbyname_r.c
@@ -5,49 +5,43 @@
 #include <inttypes.h>
 #include <errno.h>
 #include <string.h>
+#include "lookup.h"
+
+#define ALIGN (sizeof(struct { char a; char *b; }) - sizeof(char *))
 
 int getservbyname_r(const char *name, const char *prots,
 	struct servent *se, char *buf, size_t buflen, struct servent **res)
 {
-	struct addrinfo *ai, hint = { .ai_family = AF_INET };
-	int i;
-
-	if (!prots) {
-		int r = getservbyname_r(name, "tcp", se, buf, buflen, res);
-		if (r) r = getservbyname_r(name, "udp", se, buf, buflen, res);
-		return r;
-	}
+	struct service servs[MAXSERVS];
+	int cnt, proto, align;
 
 	/* Align buffer */
-	i = (uintptr_t)buf & sizeof(char *)-1;
-	if (!i) i = sizeof(char *);
-	if (buflen < 3*sizeof(char *)-i)
+	align = -(uintptr_t)buf & ALIGN-1;
+	if (buflen < 2*sizeof(char *)+align)
 		return ERANGE;
-	buf += sizeof(char *)-i;
-	buflen -= sizeof(char *)-i;
+	buf += align;
 
-	if (!strcmp(prots, "tcp")) hint.ai_protocol = IPPROTO_TCP;
-	else if (!strcmp(prots, "udp")) hint.ai_protocol = IPPROTO_UDP;
+	if (!prots) proto = 0;
+	else if (!strcmp(prots, "tcp")) proto = IPPROTO_TCP;
+	else if (!strcmp(prots, "udp")) proto = IPPROTO_UDP;
 	else return EINVAL;
 
-	switch (getaddrinfo(0, name, &hint, &ai)) {
+	cnt = __lookup_serv(servs, name, proto, 0, 0);
+	if (cnt<0) switch (cnt) {
 	case EAI_MEMORY:
 	case EAI_SYSTEM:
 		return ENOMEM;
 	default:
 		return ENOENT;
-	case 0:
-		break;
 	}
 
 	se->s_name = (char *)name;
 	se->s_aliases = (void *)buf;
 	se->s_aliases[0] = se->s_name;
 	se->s_aliases[1] = 0;
-	se->s_port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
-	se->s_proto = (char *)prots;
+	se->s_port = htons(servs[0].port);
+	se->s_proto = servs[0].proto == IPPROTO_TCP ? "tcp" : "udp";
 
-	freeaddrinfo(ai);
 	*res = se;
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/network/hstrerror.c b/system/lib/libc/musl/src/network/hstrerror.c
index b7a6ab6..a4d001c 100644
--- a/system/lib/libc/musl/src/network/hstrerror.c
+++ b/system/lib/libc/musl/src/network/hstrerror.c
@@ -1,5 +1,6 @@
 #define _GNU_SOURCE
 #include <netdb.h>
+#include "locale_impl.h"
 
 static const char msgs[] =
 	"Host not found\0"
@@ -12,5 +13,6 @@
 {
 	const char *s;
 	for (s=msgs, ecode--; ecode && *s; ecode--, s++) for (; *s; s++);
-	return *s ? s : s+1;
+	if (!*s) s++;
+	return LCTRANS_CUR(s);
 }
diff --git a/system/lib/libc/musl/src/network/if_nameindex.c b/system/lib/libc/musl/src/network/if_nameindex.c
index 53b80b2..2deaef7 100644
--- a/system/lib/libc/musl/src/network/if_nameindex.c
+++ b/system/lib/libc/musl/src/network/if_nameindex.c
@@ -1,55 +1,114 @@
 #define _GNU_SOURCE
 #include <net/if.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
 #include <errno.h>
-#include "syscall.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include "netlink.h"
 
-static void *do_nameindex(int s, size_t n)
+#define IFADDRS_HASH_SIZE 64
+
+struct ifnamemap {
+	unsigned int hash_next;
+	unsigned int index;
+	unsigned char namelen;
+	char name[IFNAMSIZ];
+};
+
+struct ifnameindexctx {
+	unsigned int num, allocated, str_bytes;
+	struct ifnamemap *list;
+	unsigned int hash[IFADDRS_HASH_SIZE];
+};
+
+static int netlink_msg_to_nameindex(void *pctx, struct nlmsghdr *h)
 {
-	size_t i, len, k;
-	struct ifconf conf;
-	struct if_nameindex *idx;
+	struct ifnameindexctx *ctx = pctx;
+	struct ifnamemap *map;
+	struct rtattr *rta;
+	unsigned int i;
+	int index, type, namelen, bucket;
 
-	idx = malloc(n * (sizeof(struct if_nameindex)+sizeof(struct ifreq)));
-	if (!idx) return 0;
+	if (h->nlmsg_type == RTM_NEWLINK) {
+		struct ifinfomsg *ifi = NLMSG_DATA(h);
+		index = ifi->ifi_index;
+		type = IFLA_IFNAME;
+		rta = NLMSG_RTA(h, sizeof(*ifi));
+	} else {
+		struct ifaddrmsg *ifa = NLMSG_DATA(h);
+		index = ifa->ifa_index;
+		type = IFA_LABEL;
+		rta = NLMSG_RTA(h, sizeof(*ifa));
+	}
+	for (; NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
+		if (rta->rta_type != type) continue;
 
-	conf.ifc_buf = (void *)&idx[n];
-	conf.ifc_len = len = n * sizeof(struct ifreq);
-	if (ioctl(s, SIOCGIFCONF, &conf) < 0) {
-		free(idx);
+		namelen = RTA_DATALEN(rta) - 1;
+		if (namelen > IFNAMSIZ) return 0;
+
+		/* suppress duplicates */
+		bucket = index % IFADDRS_HASH_SIZE;
+		i = ctx->hash[bucket];
+		while (i) {
+			map = &ctx->list[i-1];
+			if (map->index == index &&
+			    map->namelen == namelen &&
+			    memcmp(map->name, RTA_DATA(rta), namelen) == 0)
+				return 0;
+			i = map->hash_next;
+		}
+
+		if (ctx->num >= ctx->allocated) {
+			size_t a = ctx->allocated ? ctx->allocated * 2 + 1 : 8;
+			if (a > SIZE_MAX/sizeof *map) return -1;
+			map = realloc(ctx->list, a * sizeof *map);
+			if (!map) return -1;
+			ctx->list = map;
+			ctx->allocated = a;
+		}
+		map = &ctx->list[ctx->num];
+		map->index = index;
+		map->namelen = namelen;
+		memcpy(map->name, RTA_DATA(rta), namelen);
+		ctx->str_bytes += namelen + 1;
+		ctx->num++;
+		map->hash_next = ctx->hash[bucket];
+		ctx->hash[bucket] = ctx->num;
 		return 0;
 	}
-	if (conf.ifc_len == len) {
-		free(idx);
-		return (void *)-1;
-	}
-
-	n = conf.ifc_len / sizeof(struct ifreq);
-	for (i=k=0; i<n; i++) {
-		if (ioctl(s, SIOCGIFINDEX, &conf.ifc_req[i]) < 0) {
-			k++;
-			continue;
-		}
-		idx[i-k].if_index = conf.ifc_req[i].ifr_ifindex;
-		idx[i-k].if_name = conf.ifc_req[i].ifr_name;
-	}
-	idx[i-k].if_name = 0;
-	idx[i-k].if_index = 0;
-
-	return idx;
+	return 0;
 }
 
 struct if_nameindex *if_nameindex()
 {
-	size_t n;
-	void *p = 0;
-	int s = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-	if (s>=0) {
-		for (n=0; (p=do_nameindex(s, n)) == (void *)-1; n++);
-		__syscall(SYS_close, s);
+	struct ifnameindexctx _ctx, *ctx = &_ctx;
+	struct if_nameindex *ifs = 0, *d;
+	struct ifnamemap *s;
+	char *p;
+	int i;
+	int cs;
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+	memset(ctx, 0, sizeof(*ctx));
+	if (__rtnetlink_enumerate(AF_UNSPEC, AF_INET, netlink_msg_to_nameindex, ctx) < 0) goto err;
+
+	ifs = malloc(sizeof(struct if_nameindex[ctx->num+1]) + ctx->str_bytes);
+	if (!ifs) goto err;
+
+	p = (char*)(ifs + ctx->num + 1);
+	for (i = ctx->num, d = ifs, s = ctx->list; i; i--, s++, d++) {
+		d->if_index = s->index;
+		d->if_name = p;
+		memcpy(p, s->name, s->namelen);
+		p += s->namelen;
+		*p++ = 0;
 	}
+	d->if_index = 0;
+	d->if_name = 0;
+err:
+	pthread_setcancelstate(cs, 0);
+	free(ctx->list);
 	errno = ENOBUFS;
-	return p;
+	return ifs;
 }
diff --git a/system/lib/libc/musl/src/network/if_nametoindex.c b/system/lib/libc/musl/src/network/if_nametoindex.c
index cb6ec05..331413c 100644
--- a/system/lib/libc/musl/src/network/if_nametoindex.c
+++ b/system/lib/libc/musl/src/network/if_nametoindex.c
@@ -10,7 +10,7 @@
 	struct ifreq ifr;
 	int fd, r;
 
-	if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) return -1;
+	if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) return 0;
 	strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
 	r = ioctl(fd, SIOCGIFINDEX, &ifr);
 	__syscall(SYS_close, fd);
diff --git a/system/lib/libc/musl/src/network/inet_addr.c b/system/lib/libc/musl/src/network/inet_addr.c
index ea0a8f7..10b21f2 100644
--- a/system/lib/libc/musl/src/network/inet_addr.c
+++ b/system/lib/libc/musl/src/network/inet_addr.c
@@ -1,11 +1,12 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#include "__dns.h"
+
+int __inet_aton(const char *, struct in_addr *);
 
 in_addr_t inet_addr(const char *p)
 {
-	struct sockaddr_in sin;
-	if (__ipparse(&sin, AF_INET, p) < 0) return -1;
-	return sin.sin_addr.s_addr;
+	struct in_addr a;
+	if (!__inet_aton(p, &a)) return -1;
+	return a.s_addr;
 }
diff --git a/system/lib/libc/musl/src/network/inet_aton.c b/system/lib/libc/musl/src/network/inet_aton.c
new file mode 100644
index 0000000..0f9a45f
--- /dev/null
+++ b/system/lib/libc/musl/src/network/inet_aton.c
@@ -0,0 +1,41 @@
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "libc.h"
+
+int __inet_aton(const char *s0, struct in_addr *dest)
+{
+	const char *s = s0;
+	unsigned char *d = (void *)dest;
+	unsigned long a[4] = { 0 };
+	char *z;
+	int i;
+
+	for (i=0; i<4; i++) {
+		a[i] = strtoul(s, &z, 0);
+		if (z==s || (*z && *z != '.') || !isdigit(*s))
+			return 0;
+		if (!*z) break;
+		s=z+1;
+	}
+	if (i==4) return 0;
+	switch (i) {
+	case 0:
+		a[1] = a[0] & 0xffffff;
+		a[0] >>= 24;
+	case 1:
+		a[2] = a[1] & 0xffff;
+		a[1] >>= 16;
+	case 2:
+		a[3] = a[2] & 0xff;
+		a[2] >>= 8;
+	}
+	for (i=0; i<4; i++) {
+		if (a[i] > 255) return 0;
+		d[i] = a[i];
+	}
+	return 1;
+}
+
+weak_alias(__inet_aton, inet_aton);
diff --git a/system/lib/libc/musl/src/network/inet_legacy.c b/system/lib/libc/musl/src/network/inet_legacy.c
index de5b75c..621b47b 100644
--- a/system/lib/libc/musl/src/network/inet_legacy.c
+++ b/system/lib/libc/musl/src/network/inet_legacy.c
@@ -1,21 +1,12 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
-#include "__dns.h"
 
 in_addr_t inet_network(const char *p)
 {
 	return ntohl(inet_addr(p));
 }
 
-int inet_aton(const char *cp, struct in_addr *inp)
-{
-	struct sockaddr_in sin;
-	if (__ipparse(&sin, AF_INET, cp) < 0) return 0;
-	*inp = sin.sin_addr;
-	return 1;
-}
-
 struct in_addr inet_makeaddr(in_addr_t n, in_addr_t h)
 {
 	if (n < 256) h |= n<<24;
diff --git a/system/lib/libc/musl/src/network/lookup.h b/system/lib/libc/musl/src/network/lookup.h
new file mode 100644
index 0000000..0468edb
--- /dev/null
+++ b/system/lib/libc/musl/src/network/lookup.h
@@ -0,0 +1,39 @@
+#ifndef LOOKUP_H
+#define LOOKUP_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+struct address {
+	int family;
+	unsigned scopeid;
+	uint8_t addr[16];
+	int sortkey;
+};
+
+struct service {
+	uint16_t port;
+	unsigned char proto, socktype;
+};
+
+#define MAXNS 3
+
+struct resolvconf {
+	struct address ns[MAXNS];
+	unsigned nns, attempts, ndots;
+	unsigned timeout;
+};
+
+/* The limit of 48 results is a non-sharp bound on the number of addresses
+ * that can fit in one 512-byte DNS packet full of v4 results and a second
+ * packet full of v6 results. Due to headers, the actual limit is lower. */
+#define MAXADDRS 48
+#define MAXSERVS 2
+
+int __lookup_serv(struct service buf[static MAXSERVS], const char *name, int proto, int socktype, int flags);
+int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, int flags);
+int __lookup_ipliteral(struct address buf[static 1], const char *name, int family);
+
+int __get_resolv_conf(struct resolvconf *, char *, size_t);
+
+#endif
diff --git a/system/lib/libc/musl/src/network/lookup_ipliteral.c b/system/lib/libc/musl/src/network/lookup_ipliteral.c
new file mode 100644
index 0000000..8ed1460
--- /dev/null
+++ b/system/lib/libc/musl/src/network/lookup_ipliteral.c
@@ -0,0 +1,57 @@
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "lookup.h"
+
+int __inet_aton(const char *, struct in_addr *);
+
+int __lookup_ipliteral(struct address buf[static 1], const char *name, int family)
+{
+	struct in_addr a4;
+	struct in6_addr a6;
+	if (__inet_aton(name, &a4) > 0) {
+		if (family == AF_INET6) /* wrong family */
+			return EAI_NONAME;
+		memcpy(&buf[0].addr, &a4, sizeof a4);
+		buf[0].family = AF_INET;
+		buf[0].scopeid = 0;
+		return 1;
+	}
+
+	char tmp[64];
+	char *p = strchr(name, '%'), *z;
+	unsigned long long scopeid = 0;
+	if (p && p-name < 64) {
+		memcpy(tmp, name, p-name);
+		tmp[p-name] = 0;
+		name = tmp;
+	}
+
+	if (inet_pton(AF_INET6, name, &a6) <= 0)
+		return 0;
+	if (family == AF_INET) /* wrong family */
+		return EAI_NONAME;
+
+	memcpy(&buf[0].addr, &a6, sizeof a6);
+	buf[0].family = AF_INET6;
+	if (p) {
+		if (isdigit(*++p)) scopeid = strtoull(p, &z, 10);
+		else z = p-1;
+		if (*z) {
+			if (!IN6_IS_ADDR_LINKLOCAL(&a6) &&
+			    !IN6_IS_ADDR_MC_LINKLOCAL(&a6))
+				return EAI_NONAME;
+			scopeid = if_nametoindex(p);
+			if (!scopeid) return EAI_NONAME;
+		}
+		if (scopeid > UINT_MAX) return EAI_NONAME;
+	}
+	buf[0].scopeid = scopeid;
+	return 1;
+}
diff --git a/system/lib/libc/musl/src/network/lookup_name.c b/system/lib/libc/musl/src/network/lookup_name.c
new file mode 100644
index 0000000..fb7303a
--- /dev/null
+++ b/system/lib/libc/musl/src/network/lookup_name.c
@@ -0,0 +1,398 @@
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+#include "lookup.h"
+#include "stdio_impl.h"
+#include "syscall.h"
+
+static int is_valid_hostname(const char *host)
+{
+	const unsigned char *s;
+	if (strnlen(host, 255)-1 >= 254 || mbstowcs(0, host, 0) == -1) return 0;
+	for (s=(void *)host; *s>=0x80 || *s=='.' || *s=='-' || isalnum(*s); s++);
+	return !*s;
+}
+
+static int name_from_null(struct address buf[static 2], const char *name, int family, int flags)
+{
+	int cnt = 0;
+	if (name) return 0;
+	if (flags & AI_PASSIVE) {
+		if (family != AF_INET6)
+			buf[cnt++] = (struct address){ .family = AF_INET };
+		if (family != AF_INET)
+			buf[cnt++] = (struct address){ .family = AF_INET6 };
+	} else {
+		if (family != AF_INET6)
+			buf[cnt++] = (struct address){ .family = AF_INET, .addr = { 127,0,0,1 } };
+		if (family != AF_INET)
+			buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } };
+	}
+	return cnt;
+}
+
+static int name_from_numeric(struct address buf[static 1], const char *name, int family)
+{
+	return __lookup_ipliteral(buf, name, family);
+}
+
+static int name_from_hosts(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family)
+{
+	char line[512];
+	size_t l = strlen(name);
+	int cnt = 0, badfam = 0;
+	unsigned char _buf[1032];
+	FILE _f, *f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf);
+	if (!f) switch (errno) {
+	case ENOENT:
+	case ENOTDIR:
+	case EACCES:
+		return 0;
+	default:
+		return EAI_SYSTEM;
+	}
+	while (fgets(line, sizeof line, f) && cnt < MAXADDRS) {
+		char *p, *z;
+
+		if ((p=strchr(line, '#'))) *p++='\n', *p=0;
+		for(p=line+1; (p=strstr(p, name)) &&
+			(!isspace(p[-1]) || !isspace(p[l])); p++);
+		if (!p) continue;
+
+		/* Isolate IP address to parse */
+		for (p=line; *p && !isspace(*p); p++);
+		*p++ = 0;
+		switch (name_from_numeric(buf+cnt, line, family)) {
+		case 1:
+			cnt++;
+			break;
+		case 0:
+			continue;
+		default:
+			badfam = EAI_NONAME;
+			continue;
+		}
+
+		/* Extract first name as canonical name */
+		for (; *p && isspace(*p); p++);
+		for (z=p; *z && !isspace(*z); z++);
+		*z = 0;
+		if (is_valid_hostname(p)) memcpy(canon, p, z-p+1);
+	}
+	__fclose_ca(f);
+	return cnt ? cnt : badfam;
+}
+
+struct dpc_ctx {
+	struct address *addrs;
+	char *canon;
+	int cnt;
+};
+
+int __dns_parse(const unsigned char *, int, int (*)(void *, int, const void *, int, const void *), void *);
+int __dn_expand(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
+int __res_mkquery(int, const char *, int, int, const unsigned char *, int, const unsigned char*, unsigned char *, int);
+int __res_msend_rc(int, const unsigned char *const *, const int *, unsigned char *const *, int *, int, const struct resolvconf *);
+
+#define RR_A 1
+#define RR_CNAME 5
+#define RR_AAAA 28
+
+static int dns_parse_callback(void *c, int rr, const void *data, int len, const void *packet)
+{
+	char tmp[256];
+	struct dpc_ctx *ctx = c;
+	switch (rr) {
+	case RR_A:
+		if (len != 4) return -1;
+		ctx->addrs[ctx->cnt].family = AF_INET;
+		ctx->addrs[ctx->cnt].scopeid = 0;
+		memcpy(ctx->addrs[ctx->cnt++].addr, data, 4);
+		break;
+	case RR_AAAA:
+		if (len != 16) return -1;
+		ctx->addrs[ctx->cnt].family = AF_INET6;
+		ctx->addrs[ctx->cnt].scopeid = 0;
+		memcpy(ctx->addrs[ctx->cnt++].addr, data, 16);
+		break;
+	case RR_CNAME:
+		if (__dn_expand(packet, (const unsigned char *)packet + 512,
+		    data, tmp, sizeof tmp) > 0 && is_valid_hostname(tmp))
+			strcpy(ctx->canon, tmp);
+		break;
+	}
+	return 0;
+}
+
+static int name_from_dns(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, const struct resolvconf *conf)
+{
+	unsigned char qbuf[2][280], abuf[2][512];
+	const unsigned char *qp[2] = { qbuf[0], qbuf[1] };
+	unsigned char *ap[2] = { abuf[0], abuf[1] };
+	int qlens[2], alens[2];
+	int i, nq = 0;
+	struct dpc_ctx ctx = { .addrs = buf, .canon = canon };
+	static const struct { int af; int rr; } afrr[2] = {
+		{ .af = AF_INET6, .rr = RR_A },
+		{ .af = AF_INET, .rr = RR_AAAA },
+	};
+
+	for (i=0; i<2; i++) {
+		if (family != afrr[i].af) {
+			qlens[nq] = __res_mkquery(0, name, 1, afrr[i].rr,
+				0, 0, 0, qbuf[nq], sizeof *qbuf);
+			if (qlens[nq] == -1)
+				return EAI_NONAME;
+			nq++;
+		}
+	}
+
+	if (__res_msend_rc(nq, qp, qlens, ap, alens, sizeof *abuf, conf) < 0)
+		return EAI_SYSTEM;
+
+	for (i=0; i<nq; i++)
+		__dns_parse(abuf[i], alens[i], dns_parse_callback, &ctx);
+
+	if (ctx.cnt) return ctx.cnt;
+	if (alens[0] < 4 || (abuf[0][3] & 15) == 2) return EAI_AGAIN;
+	if ((abuf[0][3] & 15) == 0) return EAI_NONAME;
+	if ((abuf[0][3] & 15) == 3) return 0;
+	return EAI_FAIL;
+}
+
+static int name_from_dns_search(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family)
+{
+	char search[256];
+	struct resolvconf conf;
+	size_t l, dots;
+	char *p, *z;
+
+	if (__get_resolv_conf(&conf, search, sizeof search) < 0) return -1;
+
+	/* Count dots, suppress search when >=ndots or name ends in
+	 * a dot, which is an explicit request for global scope. */
+	for (dots=l=0; name[l]; l++) if (name[l]=='.') dots++;
+	if (dots >= conf.ndots || name[l-1]=='.') *search = 0;
+
+	/* This can never happen; the caller already checked length. */
+	if (l >= 256) return EAI_NONAME;
+
+	/* Name with search domain appended is setup in canon[]. This both
+	 * provides the desired default canonical name (if the requested
+	 * name is not a CNAME record) and serves as a buffer for passing
+	 * the full requested name to name_from_dns. */
+	memcpy(canon, name, l);
+	canon[l] = '.';
+
+	for (p=search; *p; p=z) {
+		for (; isspace(*p); p++);
+		for (z=p; *z && !isspace(*z); z++);
+		if (z==p) break;
+		if (z-p < 256 - l - 1) {
+			memcpy(canon+l+1, p, z-p);
+			canon[z-p+1+l] = 0;
+			int cnt = name_from_dns(buf, canon, canon, family, &conf);
+			if (cnt) return cnt;
+		}
+	}
+
+	canon[l] = 0;
+	return name_from_dns(buf, canon, name, family, &conf);
+}
+
+static const struct policy {
+	unsigned char addr[16];
+	unsigned char len, mask;
+	unsigned char prec, label;
+} defpolicy[] = {
+	{ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1", 15, 0xff, 50, 0 },
+	{ "\0\0\0\0\0\0\0\0\0\0\xff\xff", 11, 0xff, 35, 4 },
+	{ "\x20\2", 1, 0xff, 30, 2 },
+	{ "\x20\1", 3, 0xff, 5, 5 },
+	{ "\xfc", 0, 0xfe, 3, 13 },
+#if 0
+	/* These are deprecated and/or returned to the address
+	 * pool, so despite the RFC, treating them as special
+	 * is probably wrong. */
+	{ "", 11, 0xff, 1, 3 },
+	{ "\xfe\xc0", 1, 0xc0, 1, 11 },
+	{ "\x3f\xfe", 1, 0xff, 1, 12 },
+#endif
+	/* Last rule must match all addresses to stop loop. */
+	{ "", 0, 0, 40, 1 },
+};
+
+static const struct policy *policyof(const struct in6_addr *a)
+{
+	int i;
+	for (i=0; ; i++) {
+		if (memcmp(a->s6_addr, defpolicy[i].addr, defpolicy[i].len))
+			continue;
+		if ((a->s6_addr[defpolicy[i].len] & defpolicy[i].mask)
+		    != defpolicy[i].addr[defpolicy[i].len])
+			continue;
+		return defpolicy+i;
+	}
+}
+
+static int labelof(const struct in6_addr *a)
+{
+	return policyof(a)->label;
+}
+
+static int scopeof(const struct in6_addr *a)
+{
+	if (IN6_IS_ADDR_MULTICAST(a)) return a->s6_addr[1] & 15;
+	if (IN6_IS_ADDR_LINKLOCAL(a)) return 2;
+	if (IN6_IS_ADDR_LOOPBACK(a)) return 2;
+	if (IN6_IS_ADDR_SITELOCAL(a)) return 5;
+	return 14;
+}
+
+static int prefixmatch(const struct in6_addr *s, const struct in6_addr *d)
+{
+	/* FIXME: The common prefix length should be limited to no greater
+	 * than the nominal length of the prefix portion of the source
+	 * address. However the definition of the source prefix length is
+	 * not clear and thus this limiting is not yet implemented. */
+	unsigned i;
+	for (i=0; i<128 && !((s->s6_addr[i/8]^d->s6_addr[i/8])&(128>>(i%8))); i++);
+	return i;
+}
+
+#define DAS_USABLE              0x40000000
+#define DAS_MATCHINGSCOPE       0x20000000
+#define DAS_MATCHINGLABEL       0x10000000
+#define DAS_PREC_SHIFT          20
+#define DAS_SCOPE_SHIFT         16
+#define DAS_PREFIX_SHIFT        8
+#define DAS_ORDER_SHIFT         0
+
+static int addrcmp(const void *_a, const void *_b)
+{
+	const struct address *a = _a, *b = _b;
+	return b->sortkey - a->sortkey;
+}
+
+int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, int flags)
+{
+	int cnt = 0, i, j;
+
+	*canon = 0;
+	if (name) {
+		/* reject empty name and check len so it fits into temp bufs */
+		size_t l = strnlen(name, 255);
+		if (l-1 >= 254)
+			return EAI_NONAME;
+		memcpy(canon, name, l+1);
+	}
+
+	/* Procedurally, a request for v6 addresses with the v4-mapped
+	 * flag set is like a request for unspecified family, followed
+	 * by filtering of the results. */
+	if (flags & AI_V4MAPPED) {
+		if (family == AF_INET6) family = AF_UNSPEC;
+		else flags -= AI_V4MAPPED;
+	}
+
+	/* Try each backend until there's at least one result. */
+	cnt = name_from_null(buf, name, family, flags);
+	if (!cnt) cnt = name_from_numeric(buf, name, family);
+	if (!cnt && !(flags & AI_NUMERICHOST)) {
+		cnt = name_from_hosts(buf, canon, name, family);
+		if (!cnt) cnt = name_from_dns_search(buf, canon, name, family);
+	}
+	if (cnt<=0) return cnt ? cnt : EAI_NONAME;
+
+	/* Filter/transform results for v4-mapped lookup, if requested. */
+	if (flags & AI_V4MAPPED) {
+		if (!(flags & AI_ALL)) {
+			/* If any v6 results exist, remove v4 results. */
+			for (i=0; i<cnt && buf[i].family != AF_INET6; i++);
+			if (i<cnt) {
+				for (j=0; i<cnt; i++) {
+					if (buf[i].family == AF_INET6)
+						buf[j++] = buf[i];
+				}
+				cnt = i = j;
+			}
+		}
+		/* Translate any remaining v4 results to v6 */
+		for (i=0; i<cnt; i++) {
+			if (buf[i].family != AF_INET) continue;
+			memcpy(buf[i].addr+12, buf[i].addr, 4);
+			memcpy(buf[i].addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
+			buf[i].family = AF_INET6;
+		}
+	}
+
+	/* No further processing is needed if there are fewer than 2
+	 * results or if there are only IPv4 results. */
+	if (cnt<2 || family==AF_INET) return cnt;
+	for (i=0; buf[i].family == AF_INET; i++)
+		if (i==cnt) return cnt;
+
+	int cs;
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+	/* The following implements a subset of RFC 3484/6724 destination
+	 * address selection by generating a single 31-bit sort key for
+	 * each address. Rules 3, 4, and 7 are omitted for having
+	 * excessive runtime and code size cost and dubious benefit.
+	 * So far the label/precedence table cannot be customized. */
+	for (i=0; i<cnt; i++) {
+		int key = 0;
+		struct sockaddr_in6 sa, da = {
+			.sin6_family = AF_INET6,
+			.sin6_scope_id = buf[i].scopeid,
+			.sin6_port = 65535
+		};
+		if (buf[i].family == AF_INET6) {
+			memcpy(da.sin6_addr.s6_addr, buf[i].addr, 16);
+		} else {
+			memcpy(da.sin6_addr.s6_addr,
+				"\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
+			memcpy(da.sin6_addr.s6_addr+12, buf[i].addr, 4);
+		}
+		const struct policy *dpolicy = policyof(&da.sin6_addr);
+		int dscope = scopeof(&da.sin6_addr);
+		int dlabel = dpolicy->label;
+		int dprec = dpolicy->prec;
+		int prefixlen = 0;
+		int fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP);
+		if (fd >= 0) {
+			if (!connect(fd, (void *)&da, sizeof da)) {
+				key |= DAS_USABLE;
+				if (!getsockname(fd, (void *)&sa,
+				    &(socklen_t){sizeof sa})) {
+					if (dscope == scopeof(&sa.sin6_addr))
+						key |= DAS_MATCHINGSCOPE;
+					if (dlabel == labelof(&sa.sin6_addr))
+						key |= DAS_MATCHINGLABEL;
+					prefixlen = prefixmatch(&sa.sin6_addr,
+						&da.sin6_addr);
+				}
+			}
+			close(fd);
+		}
+		key |= dprec << DAS_PREC_SHIFT;
+		key |= (15-dscope) << DAS_SCOPE_SHIFT;
+		key |= prefixlen << DAS_PREFIX_SHIFT;
+		key |= (MAXADDRS-i) << DAS_ORDER_SHIFT;
+		buf[i].sortkey = key;
+	}
+	qsort(buf, cnt, sizeof *buf, addrcmp);
+
+	pthread_setcancelstate(cs, 0);
+
+	return cnt;
+}
diff --git a/system/lib/libc/musl/src/network/lookup_serv.c b/system/lib/libc/musl/src/network/lookup_serv.c
new file mode 100644
index 0000000..66ebaea
--- /dev/null
+++ b/system/lib/libc/musl/src/network/lookup_serv.c
@@ -0,0 +1,113 @@
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "lookup.h"
+#include "stdio_impl.h"
+
+int __lookup_serv(struct service buf[static MAXSERVS], const char *name, int proto, int socktype, int flags)
+{
+	char line[128];
+	int cnt = 0;
+	char *p, *z = "";
+	unsigned long port = 0;
+
+	switch (socktype) {
+	case SOCK_STREAM:
+		switch (proto) {
+		case 0:
+			proto = IPPROTO_TCP;
+		case IPPROTO_TCP:
+			break;
+		default:
+			return EAI_SERVICE;
+		}
+		break;
+	case SOCK_DGRAM:
+		switch (proto) {
+		case 0:
+			proto = IPPROTO_UDP;
+		case IPPROTO_UDP:
+			break;
+		default:
+			return EAI_SERVICE;
+		}
+	case 0:
+		break;
+	default:
+		if (name) return EAI_SERVICE;
+		buf[0].port = 0;
+		buf[0].proto = proto;
+		buf[0].socktype = socktype;
+		return 1;
+	}
+
+	if (name) {
+		if (!*name) return EAI_SERVICE;
+		port = strtoul(name, &z, 10);
+	}
+	if (!*z) {
+		if (port > 65535) return EAI_SERVICE;
+		if (proto != IPPROTO_UDP) {
+			buf[cnt].port = port;
+			buf[cnt].socktype = SOCK_STREAM;
+			buf[cnt++].proto = IPPROTO_TCP;
+		}
+		if (proto != IPPROTO_TCP) {
+			buf[cnt].port = port;
+			buf[cnt].socktype = SOCK_DGRAM;
+			buf[cnt++].proto = IPPROTO_UDP;
+		}
+		return cnt;
+	}
+
+	if (flags & AI_NUMERICSERV) return EAI_SERVICE;
+
+	size_t l = strlen(name);
+
+	unsigned char _buf[1032];
+	FILE _f, *f = __fopen_rb_ca("/etc/services", &_f, _buf, sizeof _buf);
+	if (!f) switch (errno) {
+	case ENOENT:
+	case ENOTDIR:
+	case EACCES:
+		return EAI_SERVICE;
+	default:
+		return EAI_SYSTEM;
+	}
+
+	while (fgets(line, sizeof line, f) && cnt < MAXSERVS) {
+		if ((p=strchr(line, '#'))) *p++='\n', *p=0;
+
+		/* Find service name */
+		for(p=line; (p=strstr(p, name)); p++) {
+			if (p>line && !isspace(p[-1])) continue;
+			if (p[l] && !isspace(p[l])) continue;
+			break;
+		}
+		if (!p) continue;
+
+		/* Skip past canonical name at beginning of line */
+		for (p=line; *p && !isspace(*p); p++);
+
+		port = strtoul(p, &z, 10);
+		if (port > 65535 || z==p) continue;
+		if (!strncmp(z, "/udp", 4)) {
+			if (proto == IPPROTO_TCP) continue;
+			buf[cnt].port = port;
+			buf[cnt].socktype = SOCK_DGRAM;
+			buf[cnt++].proto = IPPROTO_UDP;
+		}
+		if (!strncmp(z, "/tcp", 4)) {
+			if (proto == IPPROTO_UDP) continue;
+			buf[cnt].port = port;
+			buf[cnt].socktype = SOCK_STREAM;
+			buf[cnt++].proto = IPPROTO_TCP;
+		}
+	}
+	__fclose_ca(f);
+	return cnt > 0 ? cnt : EAI_SERVICE;
+}
diff --git a/system/lib/libc/musl/src/network/netlink.c b/system/lib/libc/musl/src/network/netlink.c
new file mode 100644
index 0000000..94dba7f
--- /dev/null
+++ b/system/lib/libc/musl/src/network/netlink.c
@@ -0,0 +1,52 @@
+#include <errno.h>
+#include <string.h>
+#include <syscall.h>
+#include <sys/socket.h>
+#include "netlink.h"
+
+static int __netlink_enumerate(int fd, unsigned int seq, int type, int af,
+	int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx)
+{
+	struct nlmsghdr *h;
+	union {
+		uint8_t buf[8192];
+		struct {
+			struct nlmsghdr nlh;
+			struct rtgenmsg g;
+		} req;
+		struct nlmsghdr reply;
+	} u;
+	int r, ret;
+
+	memset(&u.req, 0, sizeof(u.req));
+	u.req.nlh.nlmsg_len = sizeof(u.req);
+	u.req.nlh.nlmsg_type = type;
+	u.req.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+	u.req.nlh.nlmsg_seq = seq;
+	u.req.g.rtgen_family = af;
+	r = send(fd, &u.req, sizeof(u.req), 0);
+	if (r < 0) return r;
+
+	while (1) {
+		r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT);
+		if (r <= 0) return -1;
+		for (h = &u.reply; NLMSG_OK(h, (void*)&u.buf[r]); h = NLMSG_NEXT(h)) {
+			if (h->nlmsg_type == NLMSG_DONE) return 0;
+			if (h->nlmsg_type == NLMSG_ERROR) return -1;
+			ret = cb(ctx, h);
+			if (ret) return ret;
+		}
+	}
+}
+
+int __rtnetlink_enumerate(int link_af, int addr_af, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx)
+{
+	int fd, r;
+
+	fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
+	if (fd < 0) return -1;
+	r = __netlink_enumerate(fd, 1, RTM_GETLINK, link_af, cb, ctx);
+	if (!r) r = __netlink_enumerate(fd, 2, RTM_GETADDR, addr_af, cb, ctx);
+	__syscall(SYS_close,fd);
+	return r;
+}
diff --git a/system/lib/libc/musl/src/network/netlink.h b/system/lib/libc/musl/src/network/netlink.h
new file mode 100644
index 0000000..20700ac
--- /dev/null
+++ b/system/lib/libc/musl/src/network/netlink.h
@@ -0,0 +1,94 @@
+#include <stdint.h>
+
+/* linux/netlink.h */
+
+#define NETLINK_ROUTE 0
+
+struct nlmsghdr {
+	uint32_t	nlmsg_len;
+	uint16_t	nlmsg_type;
+	uint16_t	nlmsg_flags;
+	uint32_t	nlmsg_seq;
+	uint32_t	nlmsg_pid;
+};
+
+#define NLM_F_REQUEST	1
+#define NLM_F_MULTI	2
+#define NLM_F_ACK	4
+
+#define NLM_F_ROOT	0x100
+#define NLM_F_MATCH	0x200
+#define NLM_F_ATOMIC	0x400
+#define NLM_F_DUMP	(NLM_F_ROOT|NLM_F_MATCH)
+
+#define NLMSG_NOOP	0x1
+#define NLMSG_ERROR	0x2
+#define NLMSG_DONE	0x3
+#define NLMSG_OVERRUN	0x4
+
+/* linux/rtnetlink.h */
+
+#define RTM_NEWLINK	16
+#define RTM_GETLINK	18
+#define RTM_NEWADDR	20
+#define RTM_GETADDR	22
+
+struct rtattr {
+	unsigned short	rta_len;
+	unsigned short	rta_type;
+};
+
+struct rtgenmsg {
+	unsigned char	rtgen_family;
+};
+
+struct ifinfomsg {
+	unsigned char	ifi_family;
+	unsigned char	__ifi_pad;
+	unsigned short	ifi_type;
+	int		ifi_index;
+	unsigned	ifi_flags;
+	unsigned	ifi_change;
+};
+
+/* linux/if_link.h */
+
+#define IFLA_ADDRESS	1
+#define IFLA_BROADCAST	2
+#define IFLA_IFNAME	3
+#define IFLA_STATS	7
+
+/* linux/if_addr.h */
+
+struct ifaddrmsg {
+	uint8_t		ifa_family;
+	uint8_t		ifa_prefixlen;
+	uint8_t		ifa_flags;
+	uint8_t		ifa_scope;
+	uint32_t	ifa_index;
+};
+
+#define IFA_ADDRESS	1
+#define IFA_LOCAL	2
+#define IFA_LABEL	3
+#define IFA_BROADCAST	4
+
+/* musl */
+
+#define NETLINK_ALIGN(len)	(((len)+3) & ~3)
+#define NLMSG_DATA(nlh)		((void*)((char*)(nlh)+sizeof(struct nlmsghdr)))
+#define NLMSG_DATALEN(nlh)	((nlh)->nlmsg_len-sizeof(struct nlmsghdr))
+#define NLMSG_DATAEND(nlh)	((char*)(nlh)+(nlh)->nlmsg_len)
+#define NLMSG_NEXT(nlh)		(struct nlmsghdr*)((char*)(nlh)+NETLINK_ALIGN((nlh)->nlmsg_len))
+#define NLMSG_OK(nlh,end)	((char*)(end)-(char*)(nlh) >= sizeof(struct nlmsghdr))
+
+#define RTA_DATA(rta)		((void*)((char*)(rta)+sizeof(struct rtattr)))
+#define RTA_DATALEN(rta)	((rta)->rta_len-sizeof(struct rtattr))
+#define RTA_DATAEND(rta)	((char*)(rta)+(rta)->rta_len)
+#define RTA_NEXT(rta)		(struct rtattr*)((char*)(rta)+NETLINK_ALIGN((rta)->rta_len))
+#define RTA_OK(nlh,end)		((char*)(end)-(char*)(rta) >= sizeof(struct rtattr))
+
+#define NLMSG_RTA(nlh,len)	((void*)((char*)(nlh)+sizeof(struct nlmsghdr)+NETLINK_ALIGN(len)))
+#define NLMSG_RTAOK(rta,nlh)	RTA_OK(rta,NLMSG_DATAEND(nlh))
+
+int __rtnetlink_enumerate(int link_af, int addr_af, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx);
diff --git a/system/lib/libc/musl/src/network/ns_parse.c b/system/lib/libc/musl/src/network/ns_parse.c
new file mode 100644
index 0000000..71bd309
--- /dev/null
+++ b/system/lib/libc/musl/src/network/ns_parse.c
@@ -0,0 +1,171 @@
+#define _BSD_SOURCE
+#include <errno.h>
+#include <stddef.h>
+#include <resolv.h>
+#include <arpa/nameser.h>
+
+const struct _ns_flagdata _ns_flagdata[16] = {
+	{ 0x8000, 15 },
+	{ 0x7800, 11 },
+	{ 0x0400, 10 },
+	{ 0x0200, 9 },
+	{ 0x0100, 8 },
+	{ 0x0080, 7 },
+	{ 0x0040, 6 },
+	{ 0x0020, 5 },
+	{ 0x0010, 4 },
+	{ 0x000f, 0 },
+	{ 0x0000, 0 },
+	{ 0x0000, 0 },
+	{ 0x0000, 0 },
+	{ 0x0000, 0 },
+	{ 0x0000, 0 },
+	{ 0x0000, 0 },
+};
+
+unsigned ns_get16(const unsigned char *cp)
+{
+	return cp[0]<<8 | cp[1];
+}
+
+unsigned long ns_get32(const unsigned char *cp)
+{
+	return (unsigned)cp[0]<<24 | cp[1]<<16 | cp[2]<<8 | cp[3];
+}
+
+void ns_put16(unsigned s, unsigned char *cp)
+{
+	*cp++ = s>>8;
+	*cp++ = s;
+}
+
+void ns_put32(unsigned long l, unsigned char *cp)
+{
+	*cp++ = l>>24;
+	*cp++ = l>>16;
+	*cp++ = l>>8;
+	*cp++ = l;
+}
+
+int ns_skiprr(const unsigned char *ptr, const unsigned char *eom, ns_sect section, int count)
+{
+	const unsigned char *p = ptr;
+	int r;
+
+	while (count--) {
+		r = dn_skipname(p, eom);
+		if (r < 0) goto bad;
+		if (r + 2 * NS_INT16SZ > eom - p) goto bad;
+		p += r + 2 * NS_INT16SZ;
+		if (section != ns_s_qd) {
+			if (NS_INT32SZ + NS_INT16SZ > eom - p) goto bad;
+			p += NS_INT32SZ;
+			NS_GET16(r, p);
+			if (r > eom - p) goto bad;
+			p += r;
+		}
+	}
+	return p - ptr;
+bad:
+	errno = EMSGSIZE;
+	return -1;
+}
+
+int ns_initparse(const unsigned char *msg, int msglen, ns_msg *handle)
+{
+	int i, r;
+
+	handle->_msg = msg;
+	handle->_eom = msg + msglen;
+	if (msglen < (2 + ns_s_max) * NS_INT16SZ) goto bad;
+	NS_GET16(handle->_id, msg);
+	NS_GET16(handle->_flags, msg);
+	for (i = 0; i < ns_s_max; i++) NS_GET16(handle->_counts[i], msg);
+	for (i = 0; i < ns_s_max; i++) {
+		if (handle->_counts[i]) {
+			handle->_sections[i] = msg;
+			r = ns_skiprr(msg, handle->_eom, i, handle->_counts[i]);
+			if (r < 0) return -1;
+			msg += r;
+		} else {
+			handle->_sections[i] = NULL;
+		}
+	}
+	if (msg != handle->_eom) goto bad;
+	handle->_sect = ns_s_max;
+	handle->_rrnum = -1;
+	handle->_msg_ptr = NULL;
+	return 0;
+bad:
+	errno = EMSGSIZE;
+	return -1;
+}
+
+int ns_name_uncompress(const unsigned char *msg, const unsigned char *eom,
+                       const unsigned char *src, char *dst, size_t dstsiz)
+{
+	int r;
+	r = dn_expand(msg, eom, src, dst, dstsiz);
+	if (r < 0) errno = EMSGSIZE;
+	return r;
+}
+
+int ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr)
+{
+	int r;
+
+	if (section >= ns_s_max) goto bad;
+	if (section != handle->_sect) {
+		handle->_sect = section;
+		handle->_rrnum = 0;
+		handle->_msg_ptr = handle->_sections[section];
+	}
+	if (rrnum == -1) rrnum = handle->_rrnum;
+	if (rrnum < 0 || rrnum >= handle->_counts[section]) goto bad;
+	if (rrnum < handle->_rrnum) {
+		handle->_rrnum = 0;
+		handle->_msg_ptr = handle->_sections[section];
+	}
+	if (rrnum > handle->_rrnum) {
+		r = ns_skiprr(handle->_msg_ptr, handle->_eom, section, rrnum - handle->_rrnum);
+		if (r < 0) return -1;
+		handle->_msg_ptr += r;
+		handle->_rrnum = rrnum;
+	}
+	r = ns_name_uncompress(handle->_msg, handle->_eom, handle->_msg_ptr, rr->name, NS_MAXDNAME);
+	if (r < 0) return -1;
+	handle->_msg_ptr += r;
+	if (2 * NS_INT16SZ > handle->_eom - handle->_msg_ptr) goto size;
+	NS_GET16(rr->type, handle->_msg_ptr);
+	NS_GET16(rr->rr_class, handle->_msg_ptr);
+	if (section != ns_s_qd) {
+		if (NS_INT32SZ + NS_INT16SZ > handle->_eom - handle->_msg_ptr) goto size;
+		NS_GET32(rr->ttl, handle->_msg_ptr);
+		NS_GET16(rr->rdlength, handle->_msg_ptr);
+		if (rr->rdlength > handle->_eom - handle->_msg_ptr) goto size;
+		rr->rdata = handle->_msg_ptr;
+		handle->_msg_ptr += rr->rdlength;
+	} else {
+		rr->ttl = 0;
+		rr->rdlength = 0;
+		rr->rdata = NULL;
+	}
+	handle->_rrnum++;
+	if (handle->_rrnum > handle->_counts[section]) {
+		handle->_sect = section + 1;
+		if (handle->_sect == ns_s_max) {
+			handle->_rrnum = -1;
+			handle->_msg_ptr = NULL;
+		} else {
+			handle->_rrnum = 0;
+		}
+	}
+	return 0;
+bad:
+	errno = ENODEV;
+	return -1;
+size:
+	errno = EMSGSIZE;
+	return -1;
+}
+
diff --git a/system/lib/libc/musl/src/network/proto.c b/system/lib/libc/musl/src/network/proto.c
index 3d0f584..c4fd34e 100644
--- a/system/lib/libc/musl/src/network/proto.c
+++ b/system/lib/libc/musl/src/network/proto.c
@@ -4,19 +4,43 @@
 /* do we really need all these?? */
 
 static int idx;
-static const unsigned char protos[][8] = {
-	"\000ip",
-	"\001icmp",
-	"\002igmp",
-	"\003ggp",
-	"\006tcp",
-	"\014pup",
-	"\021udp",
-	"\026idp",
-	"\051ipv6",
-	"\072icmpv6",
-	"\377raw",
-	"\0\0"
+static const unsigned char protos[] = {
+	"\000ip\0"
+	"\001icmp\0"
+	"\002igmp\0"
+	"\003ggp\0"
+	"\004ipencap\0"
+	"\005st\0"
+	"\006tcp\0"
+	"\010egp\0"
+	"\014pup\0"
+	"\021udp\0"
+	"\024hmp\0"
+	"\026xns-idp\0"
+	"\033rdp\0"
+	"\035iso-tp4\0"
+	"\044xtp\0"
+	"\045ddp\0"
+	"\046idpr-cmtp\0"
+	"\051ipv6\0"
+	"\053ipv6-route\0"
+	"\054ipv6-frag\0"
+	"\055idrp\0"
+	"\056rsvp\0"
+	"\057gre\0"
+	"\062esp\0"
+	"\063ah\0"
+	"\071skip\0"
+	"\072ipv6-icmp\0"
+	"\073ipv6-nonxt\0"
+	"\074ipv6-opts\0"
+	"\111rspf\0"
+	"\121vmtp\0"
+	"\131ospf\0"
+	"\136ipip\0"
+	"\142encap\0"
+	"\147pim\0"
+	"\377raw"
 };
 
 void endprotoent(void)
@@ -33,10 +57,11 @@
 {
 	static struct protoent p;
 	static const char *aliases;
-	if (!protos[idx][1]) return NULL;
-	p.p_proto = protos[idx][0];
-	p.p_name = (char *)protos[idx++]+1;
+	if (idx >= sizeof protos) return NULL;
+	p.p_proto = protos[idx];
+	p.p_name = (char *)&protos[idx+1];
 	p.p_aliases = (char **)&aliases;
+	idx += strlen(p.p_name) + 2;
 	return &p;
 }
 
diff --git a/system/lib/libc/musl/src/network/recvmmsg.c b/system/lib/libc/musl/src/network/recvmmsg.c
new file mode 100644
index 0000000..58b1b2f
--- /dev/null
+++ b/system/lib/libc/musl/src/network/recvmmsg.c
@@ -0,0 +1,15 @@
+#define _GNU_SOURCE
+#include <sys/socket.h>
+#include <limits.h>
+#include "syscall.h"
+
+int recvmmsg(int fd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int flags, struct timespec *timeout)
+{
+#if LONG_MAX > INT_MAX
+	struct mmsghdr *mh = msgvec;
+	unsigned int i;
+	for (i = vlen; i; i--, mh++)
+		mh->msg_hdr.__pad1 = mh->msg_hdr.__pad2 = 0;
+#endif
+	return syscall_cp(SYS_recvmmsg, fd, msgvec, vlen, flags, timeout);
+}
diff --git a/system/lib/libc/musl/src/network/res_init.c b/system/lib/libc/musl/src/network/res_init.c
index cbd5b15..5dba9df 100644
--- a/system/lib/libc/musl/src/network/res_init.c
+++ b/system/lib/libc/musl/src/network/res_init.c
@@ -1,3 +1,5 @@
+#include <resolv.h>
+
 int res_init()
 {
 	return 0;
diff --git a/system/lib/libc/musl/src/network/res_mkquery.c b/system/lib/libc/musl/src/network/res_mkquery.c
new file mode 100644
index 0000000..ec4568a
--- /dev/null
+++ b/system/lib/libc/musl/src/network/res_mkquery.c
@@ -0,0 +1,44 @@
+#include <resolv.h>
+#include <string.h>
+#include <time.h>
+#include "libc.h"
+
+int __res_mkquery(int op, const char *dname, int class, int type,
+	const unsigned char *data, int datalen,
+	const unsigned char *newrr, unsigned char *buf, int buflen)
+{
+	int id, i, j;
+	unsigned char q[280];
+	struct timespec ts;
+	size_t l = strnlen(dname, 255);
+	int n;
+
+	if (l && dname[l-1]=='.') l--;
+	n = 17+l+!!l;
+	if (l>253 || buflen<n || op>15u || class>255u || type>255u)
+		return -1;
+
+	/* Construct query template - ID will be filled later */
+	memset(q, 0, n);
+	q[2] = op*8 + 1;
+	q[5] = 1;
+	memcpy((char *)q+13, dname, l);
+	for (i=13; q[i]; i=j+1) {
+		for (j=i; q[j] && q[j] != '.'; j++);
+		if (j-i-1u > 62u) return -1;
+		q[i-1] = j-i;
+	}
+	q[i+1] = type;
+	q[i+3] = class;
+
+	/* Make a reasonably unpredictable id */
+	clock_gettime(CLOCK_REALTIME, &ts);
+	id = ts.tv_nsec + ts.tv_nsec/65536UL & 0xffff;
+	q[0] = id/256;
+	q[1] = id;
+
+	memcpy(buf, q, n);
+	return n;
+}
+
+weak_alias(__res_mkquery, res_mkquery);
diff --git a/system/lib/libc/musl/src/network/res_msend.c b/system/lib/libc/musl/src/network/res_msend.c
new file mode 100644
index 0000000..de7f615
--- /dev/null
+++ b/system/lib/libc/musl/src/network/res_msend.c
@@ -0,0 +1,184 @@
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <stdint.h>
+#include <string.h>
+#include <poll.h>
+#include <time.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include "stdio_impl.h"
+#include "syscall.h"
+#include "lookup.h"
+
+static void cleanup(void *p)
+{
+	__syscall(SYS_close, (intptr_t)p);
+}
+
+static unsigned long mtime()
+{
+	struct timespec ts;
+	clock_gettime(CLOCK_REALTIME, &ts);
+	return (unsigned long)ts.tv_sec * 1000
+		+ ts.tv_nsec / 1000000;
+}
+
+int __res_msend_rc(int nqueries, const unsigned char *const *queries,
+	const int *qlens, unsigned char *const *answers, int *alens, int asize,
+	const struct resolvconf *conf)
+{
+	int fd;
+	int timeout, attempts, retry_interval, servfail_retry;
+	union {
+		struct sockaddr_in sin;
+		struct sockaddr_in6 sin6;
+	} sa = {0}, ns[MAXNS] = {{0}};
+	socklen_t sl = sizeof sa.sin;
+	int nns = 0;
+	int family = AF_INET;
+	int rlen;
+	int next;
+	int i, j;
+	int cs;
+	struct pollfd pfd;
+	unsigned long t0, t1, t2;
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+	timeout = 1000*conf->timeout;
+	attempts = conf->attempts;
+
+	for (nns=0; nns<conf->nns; nns++) {
+		const struct address *iplit = &conf->ns[nns];
+		if (iplit->family == AF_INET) {
+			memcpy(&ns[nns].sin.sin_addr, iplit->addr, 4);
+			ns[nns].sin.sin_port = htons(53);
+			ns[nns].sin.sin_family = AF_INET;
+		} else {
+			sl = sizeof sa.sin6;
+			memcpy(&ns[nns].sin6.sin6_addr, iplit->addr, 16);
+			ns[nns].sin6.sin6_port = htons(53);
+			ns[nns].sin6.sin6_scope_id = iplit->scopeid;
+			ns[nns].sin6.sin6_family = family = AF_INET6;
+		}
+	}
+
+	/* Get local address and open/bind a socket */
+	sa.sin.sin_family = family;
+	fd = socket(family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+
+	/* Handle case where system lacks IPv6 support */
+	if (fd < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) {
+		fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+		family = AF_INET;
+	}
+	if (fd < 0 || bind(fd, (void *)&sa, sl) < 0) return -1;
+
+	/* Past this point, there are no errors. Each individual query will
+	 * yield either no reply (indicated by zero length) or an answer
+	 * packet which is up to the caller to interpret. */
+
+	pthread_cleanup_push(cleanup, (void *)(intptr_t)fd);
+	pthread_setcancelstate(cs, 0);
+
+	/* Convert any IPv4 addresses in a mixed environment to v4-mapped */
+	if (family == AF_INET6) {
+		setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0);
+		for (i=0; i<nns; i++) {
+			if (ns[i].sin.sin_family != AF_INET) continue;
+			memcpy(ns[i].sin6.sin6_addr.s6_addr+12,
+				&ns[i].sin.sin_addr, 4);
+			memcpy(ns[i].sin6.sin6_addr.s6_addr,
+				"\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
+			ns[i].sin6.sin6_family = AF_INET6;
+			ns[i].sin6.sin6_flowinfo = 0;
+			ns[i].sin6.sin6_scope_id = 0;
+		}
+	}
+
+	memset(alens, 0, sizeof *alens * nqueries);
+
+	pfd.fd = fd;
+	pfd.events = POLLIN;
+	retry_interval = timeout / attempts;
+	next = 0;
+	t0 = t2 = mtime();
+	t1 = t2 - retry_interval;
+
+	for (; t2-t0 < timeout; t2=mtime()) {
+		if (t2-t1 >= retry_interval) {
+			/* Query all configured namservers in parallel */
+			for (i=0; i<nqueries; i++)
+				if (!alens[i])
+					for (j=0; j<nns; j++)
+						sendto(fd, queries[i],
+							qlens[i], MSG_NOSIGNAL,
+							(void *)&ns[j], sl);
+			t1 = t2;
+			servfail_retry = 2 * nqueries;
+		}
+
+		/* Wait for a response, or until time to retry */
+		if (poll(&pfd, 1, t1+retry_interval-t2) <= 0) continue;
+
+		while ((rlen = recvfrom(fd, answers[next], asize, 0,
+		  (void *)&sa, (socklen_t[1]){sl})) >= 0) {
+
+			/* Ignore non-identifiable packets */
+			if (rlen < 4) continue;
+
+			/* Ignore replies from addresses we didn't send to */
+			for (j=0; j<nns && memcmp(ns+j, &sa, sl); j++);
+			if (j==nns) continue;
+
+			/* Find which query this answer goes with, if any */
+			for (i=next; i<nqueries && (
+				answers[next][0] != queries[i][0] ||
+				answers[next][1] != queries[i][1] ); i++);
+			if (i==nqueries) continue;
+			if (alens[i]) continue;
+
+			/* Only accept positive or negative responses;
+			 * retry immediately on server failure, and ignore
+			 * all other codes such as refusal. */
+			switch (answers[next][3] & 15) {
+			case 0:
+			case 3:
+				break;
+			case 2:
+				if (servfail_retry && servfail_retry--)
+					sendto(fd, queries[i],
+						qlens[i], MSG_NOSIGNAL,
+						(void *)&ns[j], sl);
+			default:
+				continue;
+			}
+
+			/* Store answer in the right slot, or update next
+			 * available temp slot if it's already in place. */
+			alens[i] = rlen;
+			if (i == next)
+				for (; next<nqueries && alens[next]; next++);
+			else
+				memcpy(answers[i], answers[next], rlen);
+
+			if (next == nqueries) goto out;
+		}
+	}
+out:
+	pthread_cleanup_pop(1);
+
+	return 0;
+}
+
+int __res_msend(int nqueries, const unsigned char *const *queries,
+	const int *qlens, unsigned char *const *answers, int *alens, int asize)
+{
+	struct resolvconf conf;
+	if (__get_resolv_conf(&conf, 0, 0) < 0) return -1;
+	return __res_msend_rc(nqueries, queries, qlens, answers, alens, asize, &conf);
+}
diff --git a/system/lib/libc/musl/src/network/res_query.c b/system/lib/libc/musl/src/network/res_query.c
index 3847da3..2b4e4bb 100644
--- a/system/lib/libc/musl/src/network/res_query.c
+++ b/system/lib/libc/musl/src/network/res_query.c
@@ -1,25 +1,17 @@
-#define _GNU_SOURCE
 #include <resolv.h>
 #include <netdb.h>
-#include "__dns.h"
 #include "libc.h"
 
-int res_query(const char *name, int class, int type, unsigned char *dest, int len)
+int __res_mkquery(int, const char *, int, int, const unsigned char *, int, const unsigned char*, unsigned char *, int);
+int __res_send(const unsigned char *, int, unsigned char *, int);
+
+int __res_query(const char *name, int class, int type, unsigned char *dest, int len)
 {
-	if (class != 1 || len < 512)
-		return -1;
-	switch(__dns_doqueries(dest, name, &type, 1)) {
-	case EAI_NONAME:
-		h_errno = HOST_NOT_FOUND;
-		return -1;
-	case EAI_AGAIN:
-		h_errno = TRY_AGAIN;
-		return -1;
-	case EAI_FAIL:
-		h_errno = NO_RECOVERY;
-		return -1;
-	}
-	return 512;
+	unsigned char q[280];
+	int ql = __res_mkquery(0, name, class, type, 0, 0, 0, q, sizeof q);
+	if (ql < 0) return ql;
+	return __res_send(q, ql, dest, len);
 }
 
-weak_alias(res_query, res_search);
+weak_alias(__res_query, res_query);
+weak_alias(__res_query, res_search);
diff --git a/system/lib/libc/musl/src/network/res_querydomain.c b/system/lib/libc/musl/src/network/res_querydomain.c
new file mode 100644
index 0000000..727e6f6
--- /dev/null
+++ b/system/lib/libc/musl/src/network/res_querydomain.c
@@ -0,0 +1,14 @@
+#include <resolv.h>
+#include <string.h>
+
+int res_querydomain(const char *name, const char *domain, int class, int type, unsigned char *dest, int len)
+{
+	char tmp[255];
+	size_t nl = strnlen(name, 255);
+	size_t dl = strnlen(domain, 255);
+	if (nl+dl+1 > 254) return -1;
+	memcpy(tmp, name, nl);
+	tmp[nl] = '.';
+	memcpy(tmp+nl+1, domain, dl+1);
+	return res_query(tmp, class, type, dest, len);
+}
diff --git a/system/lib/libc/musl/src/network/res_send.c b/system/lib/libc/musl/src/network/res_send.c
new file mode 100644
index 0000000..19cfe0f
--- /dev/null
+++ b/system/lib/libc/musl/src/network/res_send.c
@@ -0,0 +1,12 @@
+#include <resolv.h>
+#include "libc.h"
+
+int __res_msend(int, const unsigned char *const *, const int *, unsigned char *const *, int *, int);
+
+int __res_send(const unsigned char *msg, int msglen, unsigned char *answer, int anslen)
+{
+	int r = __res_msend(1, &msg, &msglen, &answer, &anslen, anslen);
+	return r<0 ? r : anslen;
+}
+
+weak_alias(__res_send, res_send);
diff --git a/system/lib/libc/musl/src/network/resolvconf.c b/system/lib/libc/musl/src/network/resolvconf.c
new file mode 100644
index 0000000..2cf1f47
--- /dev/null
+++ b/system/lib/libc/musl/src/network/resolvconf.c
@@ -0,0 +1,93 @@
+#include "lookup.h"
+#include "stdio_impl.h"
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+
+int __get_resolv_conf(struct resolvconf *conf, char *search, size_t search_sz)
+{
+	char line[256];
+	unsigned char _buf[256];
+	FILE *f, _f;
+	int nns = 0;
+
+	conf->ndots = 1;
+	conf->timeout = 5;
+	conf->attempts = 2;
+	if (search) *search = 0;
+
+	f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf);
+	if (!f) switch (errno) {
+	case ENOENT:
+	case ENOTDIR:
+	case EACCES:
+		goto no_resolv_conf;
+	default:
+		return -1;
+	}
+
+	while (fgets(line, sizeof line, f)) {
+		char *p, *z;
+		if (!strchr(line, '\n') && !feof(f)) {
+			/* Ignore lines that get truncated rather than
+			 * potentially misinterpreting them. */
+			int c;
+			do c = getc(f);
+			while (c != '\n' && c != EOF);
+			continue;
+		}
+		if (!strncmp(line, "options", 7) && isspace(line[7])) {
+			p = strstr(line, "ndots:");
+			if (p && isdigit(p[6])) {
+				p += 6;
+				unsigned long x = strtoul(p, &z, 10);
+				if (z != p) conf->ndots = x > 15 ? 15 : x;
+			}
+			p = strstr(line, "attempts:");
+			if (p && isdigit(p[6])) {
+				p += 6;
+				unsigned long x = strtoul(p, &z, 10);
+				if (z != p) conf->attempts = x > 10 ? 10 : x;
+			}
+			p = strstr(line, "timeout:");
+			if (p && (isdigit(p[8]) || p[8]=='.')) {
+				p += 8;
+				unsigned long x = strtoul(p, &z, 10);
+				if (z != p) conf->timeout = x > 60 ? 60 : x;
+			}
+			continue;
+		}
+		if (!strncmp(line, "nameserver", 10) && isspace(line[10])) {
+			if (nns >= MAXNS) continue;
+			for (p=line+11; isspace(*p); p++);
+			for (z=p; *z && !isspace(*z); z++);
+			*z=0;
+			if (__lookup_ipliteral(conf->ns+nns, p, AF_UNSPEC) > 0)
+				nns++;
+			continue;
+		}
+
+		if (!search) continue;
+		if ((strncmp(line, "domain", 6) && strncmp(line, "search", 6))
+		    || !isspace(line[6]))
+			continue;
+		for (p=line+7; isspace(*p); p++);
+		size_t l = strlen(p);
+		/* This can never happen anyway with chosen buffer sizes. */
+		if (l >= search_sz) continue;
+		memcpy(search, p, l+1);
+	}
+
+	__fclose_ca(f);
+
+no_resolv_conf:
+	if (!nns) {
+		__lookup_ipliteral(conf->ns, "127.0.0.1", AF_UNSPEC);
+		nns = 1;
+	}
+
+	conf->nns = nns;
+
+	return 0;
+}
diff --git a/system/lib/libc/musl/src/network/sendmmsg.c b/system/lib/libc/musl/src/network/sendmmsg.c
new file mode 100644
index 0000000..eeae1d0
--- /dev/null
+++ b/system/lib/libc/musl/src/network/sendmmsg.c
@@ -0,0 +1,30 @@
+#define _GNU_SOURCE
+#include <sys/socket.h>
+#include <limits.h>
+#include <errno.h>
+#include "syscall.h"
+
+int sendmmsg(int fd, struct mmsghdr *msgvec, unsigned int vlen, unsigned int flags)
+{
+#if LONG_MAX > INT_MAX
+	/* Can't use the syscall directly because the kernel has the wrong
+	 * idea for the types of msg_iovlen, msg_controllen, and cmsg_len,
+	 * and the cmsg blocks cannot be modified in-place. */
+	int i;
+	if (vlen > IOV_MAX) vlen = IOV_MAX; /* This matches the kernel. */
+	if (!vlen) return 0;
+	for (i=0; i<vlen; i++) {
+		/* As an unfortunate inconsistency, the sendmmsg API uses
+		 * unsigned int for the resulting msg_len, despite sendmsg
+		 * returning ssize_t. However Linux limits the total bytes
+		 * sent by sendmsg to INT_MAX, so the assignment is safe. */
+		ssize_t r = sendmsg(fd, &msgvec[i].msg_hdr, flags);
+		if (r < 0) goto error;
+		msgvec[i].msg_len = r;
+	}
+error:
+	return i ? i : -1;
+#else
+	return syscall_cp(SYS_sendmmsg, fd, msgvec, vlen, flags);
+#endif
+}
diff --git a/system/lib/libc/musl/src/network/socket.c b/system/lib/libc/musl/src/network/socket.c
index 51be30e..a2e92d9 100644
--- a/system/lib/libc/musl/src/network/socket.c
+++ b/system/lib/libc/musl/src/network/socket.c
@@ -13,9 +13,9 @@
 			protocol, 0, 0, 0);
 		if (s < 0) return s;
 		if (type & SOCK_CLOEXEC)
-			fcntl(s, F_SETFD, FD_CLOEXEC);
+			__syscall(SYS_fcntl, s, F_SETFD, FD_CLOEXEC);
 		if (type & SOCK_NONBLOCK)
-			fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK);
+			__syscall(SYS_fcntl, s, F_SETFL, O_NONBLOCK);
 	}
 	return s;
 }
diff --git a/system/lib/libc/musl/src/network/socketpair.c b/system/lib/libc/musl/src/network/socketpair.c
index b15f846..f348962 100644
--- a/system/lib/libc/musl/src/network/socketpair.c
+++ b/system/lib/libc/musl/src/network/socketpair.c
@@ -1,7 +1,25 @@
 #include <sys/socket.h>
+#include <fcntl.h>
+#include <errno.h>
 #include "syscall.h"
 
 int socketpair(int domain, int type, int protocol, int fd[2])
 {
-	return socketcall(socketpair, domain, type, protocol, fd, 0, 0);
+	int r = socketcall(socketpair, domain, type, protocol, fd, 0, 0);
+	if (r<0 && (errno==EINVAL || errno==EPROTONOSUPPORT)
+	    && (type&(SOCK_CLOEXEC|SOCK_NONBLOCK))) {
+		r = socketcall(socketpair, domain,
+			type & ~(SOCK_CLOEXEC|SOCK_NONBLOCK),
+			protocol, fd, 0, 0);
+		if (r < 0) return r;
+		if (type & SOCK_CLOEXEC) {
+			__syscall(SYS_fcntl, fd[0], F_SETFD, FD_CLOEXEC);
+			__syscall(SYS_fcntl, fd[1], F_SETFD, FD_CLOEXEC);
+		}
+		if (type & SOCK_NONBLOCK) {
+			__syscall(SYS_fcntl, fd[0], F_SETFL, O_NONBLOCK);
+			__syscall(SYS_fcntl, fd[1], F_SETFL, O_NONBLOCK);
+		}
+	}
+	return r;
 }
diff --git a/system/lib/libc/musl/src/passwd/fgetgrent.c b/system/lib/libc/musl/src/passwd/fgetgrent.c
index d8d1c77..7d045fd 100644
--- a/system/lib/libc/musl/src/passwd/fgetgrent.c
+++ b/system/lib/libc/musl/src/passwd/fgetgrent.c
@@ -5,6 +5,8 @@
 {
 	static char *line, **mem;
 	static struct group gr;
+	struct group *res;
 	size_t size=0, nmem=0;
-	return __getgrent_a(f, &gr, &line, &size, &mem, &nmem);
+	__getgrent_a(f, &gr, &line, &size, &mem, &nmem, &res);
+	return res;
 }
diff --git a/system/lib/libc/musl/src/passwd/fgetpwent.c b/system/lib/libc/musl/src/passwd/fgetpwent.c
index eb47b2a..fd472a0 100644
--- a/system/lib/libc/musl/src/passwd/fgetpwent.c
+++ b/system/lib/libc/musl/src/passwd/fgetpwent.c
@@ -6,5 +6,7 @@
 	static char *line;
 	static struct passwd pw;
 	size_t size=0;
-	return __getpwent_a(f, &pw, &line, &size);
+	struct passwd *res;
+	__getpwent_a(f, &pw, &line, &size, &res);
+	return res;
 }
diff --git a/system/lib/libc/musl/src/passwd/getgr_a.c b/system/lib/libc/musl/src/passwd/getgr_a.c
new file mode 100644
index 0000000..afeb1ec
--- /dev/null
+++ b/system/lib/libc/musl/src/passwd/getgr_a.c
@@ -0,0 +1,169 @@
+#include <pthread.h>
+#include <byteswap.h>
+#include <string.h>
+#include <unistd.h>
+#include "pwf.h"
+#include "nscd.h"
+
+static char *itoa(char *p, uint32_t x)
+{
+	// number of digits in a uint32_t + NUL
+	p += 11;
+	*--p = 0;
+	do {
+		*--p = '0' + x % 10;
+		x /= 10;
+	} while (x);
+	return p;
+}
+
+int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t *size, char ***mem, size_t *nmem, struct group **res)
+{
+	FILE *f;
+	int rv = 0;
+	int cs;
+
+	*res = 0;
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+	f = fopen("/etc/group", "rbe");
+	if (!f) {
+		rv = errno;
+		goto done;
+	}
+
+	while (!(rv = __getgrent_a(f, gr, buf, size, mem, nmem, res)) && *res) {
+		if (name && !strcmp(name, (*res)->gr_name)
+		|| !name && (*res)->gr_gid == gid) {
+			break;
+		}
+	}
+	fclose(f);
+
+	if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) {
+		int32_t req = name ? GETGRBYNAME : GETGRBYGID;
+		int32_t i;
+		const char *key;
+		int32_t groupbuf[GR_LEN] = {0};
+		size_t len = 0;
+		size_t grlist_len = 0;
+		char gidbuf[11] = {0};
+		int swap = 0;
+		char *ptr;
+
+		if (name) {
+			key = name;
+		} else {
+			if (gid < 0 || gid > UINT32_MAX) {
+				rv = 0;
+				goto done;
+			}
+			key = itoa(gidbuf, gid);
+		}
+
+		f = __nscd_query(req, key, groupbuf, sizeof groupbuf, &swap);
+		if (!f) { rv = errno; goto done; }
+
+		if (!groupbuf[GRFOUND]) { rv = 0; goto cleanup_f; }
+
+		if (!groupbuf[GRNAMELEN] || !groupbuf[GRPASSWDLEN]) {
+			rv = EIO;
+			goto cleanup_f;
+		}
+
+		if (groupbuf[GRNAMELEN] > SIZE_MAX - groupbuf[GRPASSWDLEN]) {
+			rv = ENOMEM;
+			goto cleanup_f;
+		}
+		len = groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
+
+		for (i = 0; i < groupbuf[GRMEMCNT]; i++) {
+			uint32_t name_len;
+			if (fread(&name_len, sizeof name_len, 1, f) < 1) {
+				rv = ferror(f) ? errno : EIO;
+				goto cleanup_f;
+			}
+			if (swap) {
+				name_len = bswap_32(name_len);
+			}
+			if (name_len > SIZE_MAX - grlist_len
+			|| name_len > SIZE_MAX - len) {
+				rv = ENOMEM;
+				goto cleanup_f;
+			}
+			len += name_len;
+			grlist_len += name_len;
+		}
+
+		if (len > *size || !*buf) {
+			char *tmp = realloc(*buf, len);
+			if (!tmp) {
+				rv = errno;
+				goto cleanup_f;
+			}
+			*buf = tmp;
+			*size = len;
+		}
+
+		if (!fread(*buf, len, 1, f)) {
+			rv = ferror(f) ? errno : EIO;
+			goto cleanup_f;
+		}
+
+		if (groupbuf[GRMEMCNT] + 1 > *nmem) {
+			if (groupbuf[GRMEMCNT] + 1 > SIZE_MAX/sizeof(char*)) {
+				rv = ENOMEM;
+				goto cleanup_f;
+			}
+			char **tmp = realloc(*mem, (groupbuf[GRMEMCNT]+1)*sizeof(char*));
+			if (!tmp) {
+				rv = errno;
+				goto cleanup_f;
+			}
+			*mem = tmp;
+			*nmem = groupbuf[GRMEMCNT] + 1;
+		}
+
+		if (groupbuf[GRMEMCNT]) {
+			mem[0][0] = *buf + groupbuf[GRNAMELEN] + groupbuf[GRPASSWDLEN];
+			for (ptr = mem[0][0], i = 0; ptr != mem[0][0]+grlist_len; ptr++)
+				if (!*ptr) mem[0][++i] = ptr+1;
+			mem[0][i] = 0;
+
+			if (i != groupbuf[GRMEMCNT]) {
+				rv = EIO;
+				goto cleanup_f;
+			}
+		} else {
+			mem[0][0] = 0;
+		}
+
+		gr->gr_name = *buf;
+		gr->gr_passwd = gr->gr_name + groupbuf[GRNAMELEN];
+		gr->gr_gid = groupbuf[GRGID];
+		gr->gr_mem = *mem;
+
+		if (gr->gr_passwd[-1]
+		|| gr->gr_passwd[groupbuf[GRPASSWDLEN]-1]) {
+			rv = EIO;
+			goto cleanup_f;
+		}
+
+		if (name && strcmp(name, gr->gr_name)
+		|| !name && gid != gr->gr_gid) {
+			rv = EIO;
+			goto cleanup_f;
+		}
+
+		*res = gr;
+
+cleanup_f:
+		fclose(f);
+		goto done;
+	}
+
+done:
+	pthread_setcancelstate(cs, 0);
+	if (rv) errno = rv;
+	return rv;
+}
diff --git a/system/lib/libc/musl/src/passwd/getgr_r.c b/system/lib/libc/musl/src/passwd/getgr_r.c
index 3fe2e2b..7246e8a 100644
--- a/system/lib/libc/musl/src/passwd/getgr_r.c
+++ b/system/lib/libc/musl/src/passwd/getgr_r.c
@@ -5,7 +5,6 @@
 
 static int getgr_r(const char *name, gid_t gid, struct group *gr, char *buf, size_t size, struct group **res)
 {
-	FILE *f;
 	char *line = 0;
 	size_t len = 0;
 	char **mem = 0;
@@ -16,37 +15,24 @@
 
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
 
-	f = fopen("/etc/group", "rbe");
-	if (!f) {
-		rv = errno;
-		goto done;
+	rv = __getgr_a(name, gid, gr, &line, &len, &mem, &nmem, res);
+	if (*res && size < len + (nmem+1)*sizeof(char *) + 32) {
+		*res = 0;
+		rv = ERANGE;
 	}
-
-	*res = 0;
-	while (__getgrent_a(f, gr, &line, &len, &mem, &nmem)) {
-		if (name && !strcmp(name, gr->gr_name)
-		|| !name && gr->gr_gid == gid) {
-			if (size < len + (nmem+1)*sizeof(char *) + 32) {
-				rv = ERANGE;
-				break;
-			}
-			*res = gr;
-			buf += (16-(uintptr_t)buf)%16;
-			gr->gr_mem = (void *)buf;
-			buf += (nmem+1)*sizeof(char *);
-			memcpy(buf, line, len);
-			FIX(name);
-			FIX(passwd);
-			for (i=0; mem[i]; i++)
-				gr->gr_mem[i] = mem[i]-line+buf;
-			gr->gr_mem[i] = 0;
-			break;
-		}
+	if (*res) {
+		buf += (16-(uintptr_t)buf)%16;
+		gr->gr_mem = (void *)buf;
+		buf += (nmem+1)*sizeof(char *);
+		memcpy(buf, line, len);
+		FIX(name);
+		FIX(passwd);
+		for (i=0; mem[i]; i++)
+			gr->gr_mem[i] = mem[i]-line+buf;
+		gr->gr_mem[i] = 0;
 	}
  	free(mem);
  	free(line);
-	fclose(f);
-done:
 	pthread_setcancelstate(cs, 0);
 	return rv;
 }
diff --git a/system/lib/libc/musl/src/passwd/getgrent.c b/system/lib/libc/musl/src/passwd/getgrent.c
index 429a3e5..835b9ab 100644
--- a/system/lib/libc/musl/src/passwd/getgrent.c
+++ b/system/lib/libc/musl/src/passwd/getgrent.c
@@ -1,6 +1,8 @@
 #include "pwf.h"
 
 static FILE *f;
+static char *line, **mem;
+static struct group gr;
 
 void setgrent()
 {
@@ -12,34 +14,26 @@
 
 struct group *getgrent()
 {
-	static char *line, **mem;
-	static struct group gr;
+	struct group *res;
 	size_t size=0, nmem=0;
 	if (!f) f = fopen("/etc/group", "rbe");
 	if (!f) return 0;
-	return __getgrent_a(f, &gr, &line, &size, &mem, &nmem);
+	__getgrent_a(f, &gr, &line, &size, &mem, &nmem, &res);
+	return res;
 }
 
 struct group *getgrgid(gid_t gid)
 {
-	struct group *gr;
-	int errno_saved;
-	setgrent();
-	while ((gr=getgrent()) && gr->gr_gid != gid);
-	errno_saved = errno;
-	endgrent();
-	errno = errno_saved;
-	return gr;
+	struct group *res;
+	size_t size=0, nmem=0;
+	__getgr_a(0, gid, &gr, &line, &size, &mem, &nmem, &res);
+	return res;
 }
 
 struct group *getgrnam(const char *name)
 {
-	struct group *gr;
-	int errno_saved;
-	setgrent();
-	while ((gr=getgrent()) && strcmp(gr->gr_name, name));
-	errno_saved = errno;
-	endgrent();
-	errno = errno_saved;
-	return gr;
+	struct group *res;
+	size_t size=0, nmem=0;
+	__getgr_a(name, 0, &gr, &line, &size, &mem, &nmem, &res);
+	return res;
 }
diff --git a/system/lib/libc/musl/src/passwd/getgrent_a.c b/system/lib/libc/musl/src/passwd/getgrent_a.c
index 2cb8521..7fc389d 100644
--- a/system/lib/libc/musl/src/passwd/getgrent_a.c
+++ b/system/lib/libc/musl/src/passwd/getgrent_a.c
@@ -8,15 +8,17 @@
 	return x;
 }
 
-struct group *__getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem)
+int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem, struct group **res)
 {
 	ssize_t l;
 	char *s, *mems;
 	size_t i;
+	int rv = 0;
 	int cs;
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
 	for (;;) {
 		if ((l=getline(line, size, f)) < 0) {
+			rv = ferror(f) ? errno : 0;
 			free(*line);
 			*line = 0;
 			gr = 0;
@@ -43,9 +45,11 @@
 	free(*mem);
 	*mem = calloc(sizeof(char *), *nmem+1);
 	if (!*mem) {
+		rv = errno;
 		free(*line);
 		*line = 0;
-		return 0;
+		gr = 0;
+		goto end;
 	}
 	if (*mems) {
 		mem[0][0] = mems;
@@ -58,5 +62,7 @@
 	gr->gr_mem = *mem;
 end:
 	pthread_setcancelstate(cs, 0);
-	return gr;
+	*res = gr;
+	if(rv) errno = rv;
+	return rv;
 }
diff --git a/system/lib/libc/musl/src/passwd/getgrouplist.c b/system/lib/libc/musl/src/passwd/getgrouplist.c
new file mode 100644
index 0000000..43e5182
--- /dev/null
+++ b/system/lib/libc/musl/src/passwd/getgrouplist.c
@@ -0,0 +1,80 @@
+#define _GNU_SOURCE
+#include "pwf.h"
+#include <grp.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <byteswap.h>
+#include <errno.h>
+#include "nscd.h"
+
+int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
+{
+	int rv, nlim, ret = -1;
+	ssize_t i, n = 1;
+	struct group gr;
+	struct group *res;
+	FILE *f;
+	int swap = 0;
+	int32_t resp[INITGR_LEN];
+	uint32_t *nscdbuf = 0;
+	char *buf = 0;
+	char **mem = 0;
+	size_t nmem = 0;
+	size_t size;
+	nlim = *ngroups;
+	if (nlim >= 1) *groups++ = gid;
+
+	f = __nscd_query(GETINITGR, user, resp, sizeof resp, &swap);
+	if (!f) goto cleanup;
+	if (resp[INITGRFOUND]) {
+		nscdbuf = calloc(resp[INITGRNGRPS], sizeof(uint32_t));
+		if (!nscdbuf) goto cleanup;
+		if (!fread(nscdbuf, sizeof(*nscdbuf)*resp[INITGRNGRPS], 1, f)) {
+			if (!ferror(f)) errno = EIO;
+			goto cleanup;
+		}
+		if (swap) {
+			for (i = 0; i < resp[INITGRNGRPS]; i++)
+				nscdbuf[i] = bswap_32(nscdbuf[i]);
+		}
+	}
+	fclose(f);
+
+	f = fopen("/etc/group", "rbe");
+	if (!f && errno != ENOENT && errno != ENOTDIR)
+		goto cleanup;
+
+	if (f) {
+		while (!(rv = __getgrent_a(f, &gr, &buf, &size, &mem, &nmem, &res)) && res) {
+			if (nscdbuf)
+				for (i=0; i < resp[INITGRNGRPS]; i++) {
+					if (nscdbuf[i] == gr.gr_gid) nscdbuf[i] = gid;
+				}
+			for (i=0; gr.gr_mem[i] && strcmp(user, gr.gr_mem[i]); i++);
+			if (!gr.gr_mem[i]) continue;
+			if (++n <= nlim) *groups++ = gr.gr_gid;
+		}
+		if (rv) {
+			errno = rv;
+			goto cleanup;
+		}
+	}
+	if (nscdbuf) {
+		for(i=0; i < resp[INITGRNGRPS]; i++) {
+			if (nscdbuf[i] != gid)
+				if(++n <= nlim) *groups++ = nscdbuf[i];
+		}
+	}
+
+	ret = n > nlim ? -1 : n;
+	*ngroups = n;
+
+cleanup:
+	if (f) fclose(f);
+	free(nscdbuf);
+	free(buf);
+	free(mem);
+	return ret;
+}
diff --git a/system/lib/libc/musl/src/passwd/getpw_a.c b/system/lib/libc/musl/src/passwd/getpw_a.c
new file mode 100644
index 0000000..15a70c0
--- /dev/null
+++ b/system/lib/libc/musl/src/passwd/getpw_a.c
@@ -0,0 +1,142 @@
+#include <pthread.h>
+#include <byteswap.h>
+#include <string.h>
+#include <unistd.h>
+#include "pwf.h"
+#include "nscd.h"
+
+static char *itoa(char *p, uint32_t x)
+{
+	// number of digits in a uint32_t + NUL
+	p += 11;
+	*--p = 0;
+	do {
+		*--p = '0' + x % 10;
+		x /= 10;
+	} while (x);
+	return p;
+}
+
+int __getpw_a(const char *name, uid_t uid, struct passwd *pw, char **buf, size_t *size, struct passwd **res)
+{
+	FILE *f;
+	int cs;
+	int rv = 0;
+
+	*res = 0;
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+
+	f = fopen("/etc/passwd", "rbe");
+	if (!f) {
+		rv = errno;
+		goto done;
+	}
+
+	while (!(rv = __getpwent_a(f, pw, buf, size, res)) && *res) {
+		if (name && !strcmp(name, (*res)->pw_name)
+		|| !name && (*res)->pw_uid == uid)
+			break;
+	}
+	fclose(f);
+
+	if (!*res && (rv == 0 || rv == ENOENT || rv == ENOTDIR)) {
+		int32_t req = name ? GETPWBYNAME : GETPWBYUID;
+		const char *key;
+		int32_t passwdbuf[PW_LEN] = {0};
+		size_t len = 0;
+		char uidbuf[11] = {0};
+
+		if (name) {
+			key = name;
+		} else {
+			/* uid outside of this range can't be queried with the
+			 * nscd interface, but might happen if uid_t ever
+			 * happens to be a larger type (this is not true as of
+			 * now)
+			 */
+			if(uid < 0 || uid > UINT32_MAX) {
+				rv = 0;
+				goto done;
+			}
+			key = itoa(uidbuf, uid);
+		}
+
+		f = __nscd_query(req, key, passwdbuf, sizeof passwdbuf, (int[]){0});
+		if (!f) { rv = errno; goto done; }
+
+		if(!passwdbuf[PWFOUND]) { rv = 0; goto cleanup_f; }
+
+		/* A zero length response from nscd is invalid. We ignore
+		 * invalid responses and just report an error, rather than
+		 * trying to do something with them.
+		 */
+		if (!passwdbuf[PWNAMELEN] || !passwdbuf[PWPASSWDLEN]
+		|| !passwdbuf[PWGECOSLEN] || !passwdbuf[PWDIRLEN]
+		|| !passwdbuf[PWSHELLLEN]) {
+			rv = EIO;
+			goto cleanup_f;
+		}
+
+		if ((passwdbuf[PWNAMELEN]|passwdbuf[PWPASSWDLEN]
+		     |passwdbuf[PWGECOSLEN]|passwdbuf[PWDIRLEN]
+		     |passwdbuf[PWSHELLLEN]) >= SIZE_MAX/8) {
+			rv = ENOMEM;
+			goto cleanup_f;
+		}
+
+		len = passwdbuf[PWNAMELEN] + passwdbuf[PWPASSWDLEN]
+		    + passwdbuf[PWGECOSLEN] + passwdbuf[PWDIRLEN]
+		    + passwdbuf[PWSHELLLEN];
+
+		if (len > *size || !*buf) {
+			char *tmp = realloc(*buf, len);
+			if (!tmp) {
+				rv = errno;
+				goto cleanup_f;
+			}
+			*buf = tmp;
+			*size = len;
+		}
+
+		if (!fread(*buf, len, 1, f)) {
+			rv = ferror(f) ? errno : EIO;
+			goto cleanup_f;
+		}
+
+		pw->pw_name = *buf;
+		pw->pw_passwd = pw->pw_name + passwdbuf[PWNAMELEN];
+		pw->pw_gecos = pw->pw_passwd + passwdbuf[PWPASSWDLEN];
+		pw->pw_dir = pw->pw_gecos + passwdbuf[PWGECOSLEN];
+		pw->pw_shell = pw->pw_dir + passwdbuf[PWDIRLEN];
+		pw->pw_uid = passwdbuf[PWUID];
+		pw->pw_gid = passwdbuf[PWGID];
+
+		/* Don't assume that nscd made sure to null terminate strings.
+		 * It's supposed to, but malicious nscd should be ignored
+		 * rather than causing a crash.
+		 */
+		if (pw->pw_passwd[-1] || pw->pw_gecos[-1] || pw->pw_dir[-1]
+		|| pw->pw_shell[passwdbuf[PWSHELLLEN]-1]) {
+			rv = EIO;
+			goto cleanup_f;
+		}
+
+		if (name && strcmp(name, pw->pw_name)
+		|| !name && uid != pw->pw_uid) {
+			rv = EIO;
+			goto cleanup_f;
+		}
+
+
+		*res = pw;
+cleanup_f:
+		fclose(f);
+		goto done;
+	}
+
+done:
+	pthread_setcancelstate(cs, 0);
+	if (rv) errno = rv;
+	return rv;
+}
diff --git a/system/lib/libc/musl/src/passwd/getpw_r.c b/system/lib/libc/musl/src/passwd/getpw_r.c
index 2855257..e8cc811 100644
--- a/system/lib/libc/musl/src/passwd/getpw_r.c
+++ b/system/lib/libc/musl/src/passwd/getpw_r.c
@@ -5,7 +5,6 @@
 
 static int getpw_r(const char *name, uid_t uid, struct passwd *pw, char *buf, size_t size, struct passwd **res)
 {
-	FILE *f;
 	char *line = 0;
 	size_t len = 0;
 	int rv = 0;
@@ -13,33 +12,20 @@
 
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
 
-	f = fopen("/etc/passwd", "rbe");
-	if (!f) {
-		rv = errno;
-		goto done;
+	rv = __getpw_a(name, uid, pw, &line, &len, res);
+	if (*res && size < len) {
+		*res = 0;
+		rv = ERANGE;
 	}
-
-	*res = 0;
-	while (__getpwent_a(f, pw, &line, &len)) {
-		if (name && !strcmp(name, pw->pw_name)
-		|| !name && pw->pw_uid == uid) {
-			if (size < len) {
-				rv = ERANGE;
-				break;
-			}
-			*res = pw;
-			memcpy(buf, line, len);
-			FIX(name);
-			FIX(passwd);
-			FIX(gecos);
-			FIX(dir);
-			FIX(shell);
-			break;
-		}
+	if (*res) {
+		memcpy(buf, line, len);
+		FIX(name);
+		FIX(passwd);
+		FIX(gecos);
+		FIX(dir);
+		FIX(shell);
 	}
  	free(line);
-	fclose(f);
-done:
 	pthread_setcancelstate(cs, 0);
 	return rv;
 }
diff --git a/system/lib/libc/musl/src/passwd/getpwent.c b/system/lib/libc/musl/src/passwd/getpwent.c
index c655135..f2bd516 100644
--- a/system/lib/libc/musl/src/passwd/getpwent.c
+++ b/system/lib/libc/musl/src/passwd/getpwent.c
@@ -1,6 +1,9 @@
 #include "pwf.h"
 
 static FILE *f;
+static char *line;
+static struct passwd pw;
+static size_t size;
 
 void setpwent()
 {
@@ -12,34 +15,23 @@
 
 struct passwd *getpwent()
 {
-	static char *line;
-	static struct passwd pw;
-	size_t size=0;
+	struct passwd *res;
 	if (!f) f = fopen("/etc/passwd", "rbe");
 	if (!f) return 0;
-	return __getpwent_a(f, &pw, &line, &size);
+	__getpwent_a(f, &pw, &line, &size, &res);
+	return res;
 }
 
 struct passwd *getpwuid(uid_t uid)
 {
-	struct passwd *pw;
-	int errno_saved;
-	setpwent();
-	while ((pw=getpwent()) && pw->pw_uid != uid);
-	errno_saved = errno;
-	endpwent();
-	errno = errno_saved;
-	return pw;
+	struct passwd *res;
+	__getpw_a(0, uid, &pw, &line, &size, &res);
+	return res;
 }
 
 struct passwd *getpwnam(const char *name)
 {
-	struct passwd *pw;
-	int errno_saved;
-	setpwent();
-	while ((pw=getpwent()) && strcmp(pw->pw_name, name));
-	errno_saved = errno;
-	endpwent();
-	errno = errno_saved;
-	return pw;
+	struct passwd *res;
+	__getpw_a(name, 0, &pw, &line, &size, &res);
+	return res;
 }
diff --git a/system/lib/libc/musl/src/passwd/getpwent_a.c b/system/lib/libc/musl/src/passwd/getpwent_a.c
index 34842a0..d1b4b53 100644
--- a/system/lib/libc/musl/src/passwd/getpwent_a.c
+++ b/system/lib/libc/musl/src/passwd/getpwent_a.c
@@ -8,14 +8,16 @@
 	return x;
 }
 
-struct passwd *__getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size)
+int __getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size, struct passwd **res)
 {
 	ssize_t l;
 	char *s;
+	int rv = 0;
 	int cs;
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
 	for (;;) {
 		if ((l=getline(line, size, f)) < 0) {
+			rv = ferror(f) ? errno : 0;
 			free(*line);
 			*line = 0;
 			pw = 0;
@@ -46,5 +48,7 @@
 		break;
 	}
 	pthread_setcancelstate(cs, 0);
-	return pw;
+	*res = pw;
+	if (rv) errno = rv;
+	return rv;
 }
diff --git a/system/lib/libc/musl/src/passwd/nscd.h b/system/lib/libc/musl/src/passwd/nscd.h
new file mode 100644
index 0000000..9a53c32
--- /dev/null
+++ b/system/lib/libc/musl/src/passwd/nscd.h
@@ -0,0 +1,44 @@
+#ifndef NSCD_H
+#define NSCD_H
+
+#include <stdint.h>
+
+#define NSCDVERSION 2
+#define GETPWBYNAME 0
+#define GETPWBYUID 1
+#define GETGRBYNAME 2
+#define GETGRBYGID 3
+#define GETINITGR 15
+
+#define REQVERSION 0
+#define REQTYPE 1
+#define REQKEYLEN 2
+#define REQ_LEN 3
+
+#define PWVERSION 0
+#define PWFOUND 1
+#define PWNAMELEN 2
+#define PWPASSWDLEN 3
+#define PWUID 4
+#define PWGID 5
+#define PWGECOSLEN 6
+#define PWDIRLEN 7
+#define PWSHELLLEN 8
+#define PW_LEN 9
+
+#define GRVERSION 0
+#define GRFOUND 1
+#define GRNAMELEN 2
+#define GRPASSWDLEN 3
+#define GRGID 4
+#define GRMEMCNT 5
+#define GR_LEN 6
+
+#define INITGRVERSION 0
+#define INITGRFOUND 1
+#define INITGRNGRPS 2
+#define INITGR_LEN 3
+
+FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap);
+
+#endif
diff --git a/system/lib/libc/musl/src/passwd/nscd_query.c b/system/lib/libc/musl/src/passwd/nscd_query.c
new file mode 100644
index 0000000..d38e371
--- /dev/null
+++ b/system/lib/libc/musl/src/passwd/nscd_query.c
@@ -0,0 +1,107 @@
+#include <sys/socket.h>
+#include <byteswap.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include "nscd.h"
+
+static const struct {
+	short sun_family;
+	char sun_path[21];
+} addr = {
+	AF_UNIX,
+	"/var/run/nscd/socket"
+};
+
+FILE *__nscd_query(int32_t req, const char *key, int32_t *buf, size_t len, int *swap)
+{
+	size_t i;
+	int fd;
+	FILE *f = 0;
+	int32_t req_buf[REQ_LEN] = {
+		NSCDVERSION,
+		req,
+		strnlen(key,LOGIN_NAME_MAX)+1
+	};
+	struct msghdr msg = {
+		.msg_iov = (struct iovec[]){
+			{&req_buf, sizeof(req_buf)},
+			{(char*)key, strlen(key)+1}
+		},
+		.msg_iovlen = 2
+	};
+	int errno_save = errno;
+
+	*swap = 0;
+retry:
+	memset(buf, 0, len);
+	buf[0] = NSCDVERSION;
+
+	fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	if (fd < 0) return NULL;
+
+	if(!(f = fdopen(fd, "r"))) {
+		close(fd);
+		return 0;
+	}
+
+	if (req_buf[2] > LOGIN_NAME_MAX)
+		return f;
+
+	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+		/* If there isn't a running nscd we simulate a "not found"
+		 * result and the caller is responsible for calling
+		 * fclose on the (unconnected) socket. The value of
+		 * errno must be left unchanged in this case.  */
+		if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT) {
+			errno = errno_save;
+			return f;
+		}
+		goto error;
+	}
+
+	if (sendmsg(fd, &msg, MSG_NOSIGNAL) < 0)
+		goto error;
+
+	if (!fread(buf, len, 1, f)) {
+		/* If the VERSION entry mismatches nscd will disconnect. The
+		 * most likely cause is that the endianness mismatched. So, we
+		 * byteswap and try once more. (if we already swapped, just
+		 * fail out)
+		 */
+		if (ferror(f)) goto error;
+		if (!*swap) {
+			fclose(f);
+			for (i = 0; i < sizeof(req_buf)/sizeof(req_buf[0]); i++) {
+				req_buf[i] = bswap_32(req_buf[i]);
+			}
+			*swap = 1;
+			goto retry;
+		} else {
+			errno = EIO;
+			goto error;
+		}
+	}
+
+	if (*swap) {
+		for (i = 0; i < len/sizeof(buf[0]); i++) {
+			buf[i] = bswap_32(buf[i]);
+		}
+	}
+
+	/* The first entry in every nscd response is the version number. This
+	 * really shouldn't happen, and is evidence of some form of malformed
+	 * response.
+	 */
+	if(buf[0] != NSCDVERSION) {
+		errno = EIO;
+		goto error;
+	}
+
+	return f;
+error:
+	fclose(f);
+	return 0;
+}
diff --git a/system/lib/libc/musl/src/passwd/pwf.h b/system/lib/libc/musl/src/passwd/pwf.h
index 2d813ad..fc63db2 100644
--- a/system/lib/libc/musl/src/passwd/pwf.h
+++ b/system/lib/libc/musl/src/passwd/pwf.h
@@ -8,6 +8,8 @@
 #include <limits.h>
 #include "libc.h"
 
-struct passwd *__getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size);
-struct group *__getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem);
+int __getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size, struct passwd **res);
+int __getpw_a(const char *name, uid_t uid, struct passwd *pw, char **buf, size_t *size, struct passwd **res);
+int __getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem, struct group **res);
+int __getgr_a(const char *name, gid_t gid, struct group *gr, char **buf, size_t *size, char ***mem, size_t *nmem, struct group **res);
 int __parsespent(char *s, struct spwd *sp);
diff --git a/system/lib/libc/musl/src/prng/random.c b/system/lib/libc/musl/src/prng/random.c
index e250e28..7d557d7 100644
--- a/system/lib/libc/musl/src/prng/random.c
+++ b/system/lib/libc/musl/src/prng/random.c
@@ -22,7 +22,7 @@
 static int i = 3;
 static int j = 0;
 static uint32_t *x = init+1;
-static int lock[2];
+static volatile int lock[2];
 
 static uint32_t lcg31(uint32_t x) {
 	return (1103515245*x + 12345) & 0x7fffffff;
diff --git a/system/lib/libc/musl/src/process/execvp.c b/system/lib/libc/musl/src/process/execvp.c
index 0a33e42..3a8bbe8 100644
--- a/system/lib/libc/musl/src/process/execvp.c
+++ b/system/lib/libc/musl/src/process/execvp.c
@@ -3,6 +3,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <limits.h>
+#include "libc.h"
 
 extern char **__environ;
 
@@ -10,6 +11,7 @@
 {
 	const char *p, *z, *path = getenv("PATH");
 	size_t l, k;
+	int seen_eacces = 0;
 
 	errno = ENOENT;
 	if (!*file) return -1;
@@ -37,9 +39,11 @@
 		b[z-p] = '/';
 		memcpy(b+(z-p)+(z>p), file, k+1);
 		execve(b, argv, envp);
-		if (errno != ENOENT) return -1;
+		if (errno == EACCES) seen_eacces = 1;
+		else if (errno != ENOENT) return -1;
 		if (!*z++) break;
 	}
+	if (seen_eacces) errno = EACCES;
 	return -1;
 }
 
@@ -47,3 +51,5 @@
 {
 	return __execvpe(file, argv, __environ);
 }
+
+weak_alias(__execvpe, execvpe);
diff --git a/system/lib/libc/musl/src/process/fork.c b/system/lib/libc/musl/src/process/fork.c
index 1a82f42..b96f002 100644
--- a/system/lib/libc/musl/src/process/fork.c
+++ b/system/lib/libc/musl/src/process/fork.c
@@ -1,5 +1,6 @@
 #include <unistd.h>
 #include <string.h>
+#include <signal.h>
 #include "syscall.h"
 #include "libc.h"
 #include "pthread_impl.h"
@@ -16,13 +17,17 @@
 	sigset_t set;
 	__fork_handler(-1);
 	__block_all_sigs(&set);
+#ifdef SYS_fork
 	ret = syscall(SYS_fork);
-	if (libc.main_thread && !ret) {
+#else
+	ret = syscall(SYS_clone, SIGCHLD, 0);
+#endif
+	if (!ret) {
 		pthread_t self = __pthread_self();
-		self->tid = self->pid = syscall(SYS_getpid);
-		memset(&self->robust_list, 0, sizeof self->robust_list);
+		self->tid = __syscall(SYS_gettid);
+		self->robust_list.off = 0;
+		self->robust_list.pending = 0;
 		libc.threads_minus_1 = 0;
-		libc.main_thread = self;
 	}
 	__restore_sigs(&set);
 	__fork_handler(!ret);
diff --git a/system/lib/libc/musl/src/process/posix_spawn.c b/system/lib/libc/musl/src/process/posix_spawn.c
index f675a13..0bdf71c 100644
--- a/system/lib/libc/musl/src/process/posix_spawn.c
+++ b/system/lib/libc/musl/src/process/posix_spawn.c
@@ -22,6 +22,20 @@
 
 void __get_handler_set(sigset_t *);
 
+static int __sys_dup2(int old, int new)
+{
+#ifdef SYS_dup2
+	return __syscall(SYS_dup2, old, new);
+#else
+	if (old==new) {
+		int r = __syscall(SYS_fcntl, old, F_GETFD);
+		return r<0 ? r : old;
+	} else {
+		return __syscall(SYS_dup3, old, new, 0);
+	}
+#endif
+}
+
 static int child(void *args_vp)
 {
 	int i, ret;
@@ -63,9 +77,9 @@
 		if ((ret=__syscall(SYS_setpgid, 0, attr->__pgrp)))
 			goto fail;
 
-	/* Use syscalls directly because pthread state because the
-	 * library functions attempt to do a multi-threaded synchronized
-	 * id-change, which would trash the parent's state. */
+	/* Use syscalls directly because the library functions attempt
+	 * to do a multi-threaded synchronized id-change, which would
+	 * trash the parent's state. */
 	if (attr->__flags & POSIX_SPAWN_RESETIDS)
 		if ((ret=__syscall(SYS_setgid, __syscall(SYS_getgid))) ||
 		    (ret=__syscall(SYS_setuid, __syscall(SYS_getuid))) )
@@ -88,19 +102,17 @@
 			}
 			switch(op->cmd) {
 			case FDOP_CLOSE:
-				if ((ret=__syscall(SYS_close, op->fd)))
-					goto fail;
+				__syscall(SYS_close, op->fd);
 				break;
 			case FDOP_DUP2:
-				if ((ret=__syscall(SYS_dup2, op->srcfd, op->fd))<0)
+				if ((ret=__sys_dup2(op->srcfd, op->fd))<0)
 					goto fail;
 				break;
 			case FDOP_OPEN:
-				fd = __syscall(SYS_open, op->path,
-					op->oflag | O_LARGEFILE, op->mode);
+				fd = __sys_open(op->path, op->oflag, op->mode);
 				if ((ret=fd) < 0) goto fail;
 				if (fd != op->fd) {
-					if ((ret=__syscall(SYS_dup2, fd, op->fd))<0)
+					if ((ret=__sys_dup2(fd, op->fd))<0)
 						goto fail;
 					__syscall(SYS_close, fd);
 				}
@@ -124,7 +136,7 @@
 fail:
 	/* Since sizeof errno < PIPE_BUF, the write is atomic. */
 	ret = -ret;
-	if (ret) while (write(p, &ret, sizeof ret) < 0);
+	if (ret) while (__syscall(SYS_write, p, &ret, sizeof ret) < 0);
 	_exit(127);
 }
 
diff --git a/system/lib/libc/musl/src/process/vfork.c b/system/lib/libc/musl/src/process/vfork.c
index fc4adb4..ac95465 100644
--- a/system/lib/libc/musl/src/process/vfork.c
+++ b/system/lib/libc/musl/src/process/vfork.c
@@ -1,12 +1,17 @@
 #define _GNU_SOURCE
 #include <unistd.h>
+#include <signal.h>
 #include "syscall.h"
 #include "libc.h"
 
 pid_t __vfork(void)
 {
 	/* vfork syscall cannot be made from C code */
+#ifdef SYS_fork
 	return syscall(SYS_fork);
+#else
+	return syscall(SYS_clone, SIGCHLD, 0);
+#endif
 }
 
 weak_alias(__vfork, vfork);
diff --git a/system/lib/libc/musl/src/regex/fnmatch.c b/system/lib/libc/musl/src/regex/fnmatch.c
index 4df10a3..978fff8 100644
--- a/system/lib/libc/musl/src/regex/fnmatch.c
+++ b/system/lib/libc/musl/src/regex/fnmatch.c
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <wchar.h>
 #include <wctype.h>
+#include "locale_impl.h"
 
 #define END 0
 #define UNMATCHABLE -2
@@ -97,7 +98,13 @@
 	return pat[0];
 }
 
-static int match_bracket(const char *p, int k)
+static int casefold(int k)
+{
+	int c = towupper(k);
+	return c == k ? towlower(k) : c;
+}
+
+static int match_bracket(const char *p, int k, int kfold)
 {
 	wchar_t wc;
 	int inv = 0;
@@ -119,7 +126,10 @@
 			wchar_t wc2;
 			int l = mbtowc(&wc2, p+1, 4);
 			if (l < 0) return 0;
-			if (wc<=wc2 && (unsigned)k-wc <= wc2-wc) return !inv;
+			if (wc <= wc2)
+				if ((unsigned)k-wc <= wc2-wc ||
+				    (unsigned)kfold-wc <= wc2-wc)
+					return !inv;
 			p += l-1;
 			continue;
 		}
@@ -132,7 +142,9 @@
 				char buf[16];
 				memcpy(buf, p0, p-1-p0);
 				buf[p-1-p0] = 0;
-				if (iswctype(k, wctype(buf))) return !inv;
+				if (iswctype(k, wctype(buf)) ||
+				    iswctype(kfold, wctype(buf)))
+					return !inv;
 			}
 			continue;
 		}
@@ -143,7 +155,7 @@
 			if (l < 0) return 0;
 			p += l-1;
 		}
-		if (wc==k) return !inv;
+		if (wc==k || wc==kfold) return !inv;
 	}
 	return inv;
 }
@@ -153,7 +165,7 @@
 	const char *p, *ptail, *endpat;
 	const char *s, *stail, *endstr;
 	size_t pinc, sinc, tailcnt=0;
-	int c, k;
+	int c, k, kfold;
 
 	if (flags & FNM_PERIOD) {
 		if (*str == '.' && *pat != '.')
@@ -173,10 +185,11 @@
 				return (c==END) ? 0 : FNM_NOMATCH;
 			str += sinc;
 			n -= sinc;
+			kfold = flags & FNM_CASEFOLD ? casefold(k) : k;
 			if (c == BRACKET) {
-				if (!match_bracket(pat, k))
+				if (!match_bracket(pat, k, kfold))
 					return FNM_NOMATCH;
-			} else if (c != QUESTION && k != c) {
+			} else if (c != QUESTION && k != c && kfold != c) {
 				return FNM_NOMATCH;
 			}
 			pat+=pinc;
@@ -217,7 +230,7 @@
 	 * On illegal sequences we may get it wrong, but in that case
 	 * we necessarily have a matching failure anyway. */
 	for (s=endstr; s>str && tailcnt; tailcnt--) {
-		if (s[-1] < 128U) s--;
+		if (s[-1] < 128U || MB_CUR_MAX==1) s--;
 		else while ((unsigned char)*--s-0x80U<0x40 && s>str);
 	}
 	if (tailcnt) return FNM_NOMATCH;
@@ -233,10 +246,11 @@
 			break;
 		}
 		s += sinc;
+		kfold = flags & FNM_CASEFOLD ? casefold(k) : k;
 		if (c == BRACKET) {
-			if (!match_bracket(p-pinc, k))
+			if (!match_bracket(p-pinc, k, kfold))
 				return FNM_NOMATCH;
-		} else if (c != QUESTION && k != c) {
+		} else if (c != QUESTION && k != c && kfold != c) {
 			return FNM_NOMATCH;
 		}
 	}
@@ -261,10 +275,11 @@
 			k = str_next(s, endstr-s, &sinc);
 			if (!k)
 				return FNM_NOMATCH;
+			kfold = flags & FNM_CASEFOLD ? casefold(k) : k;
 			if (c == BRACKET) {
-				if (!match_bracket(p-pinc, k))
+				if (!match_bracket(p-pinc, k, kfold))
 					break;
-			} else if (c != QUESTION && k != c) {
+			} else if (c != QUESTION && k != c && kfold != c) {
 				break;
 			}
 			s += sinc;
diff --git a/system/lib/libc/musl/src/regex/regcomp.c b/system/lib/libc/musl/src/regex/regcomp.c
index 3321325..65f2fd0 100644
--- a/system/lib/libc/musl/src/regex/regcomp.c
+++ b/system/lib/libc/musl/src/regex/regcomp.c
@@ -34,6 +34,7 @@
 #include <regex.h>
 #include <limits.h>
 #include <stdint.h>
+#include <ctype.h>
 
 #include "tre.h"
 
@@ -135,108 +136,88 @@
   tre_ast_node_t *right;
 } tre_union_t;
 
-static tre_ast_node_t *
-tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size);
 
 static tre_ast_node_t *
-tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position);
-
-static tre_ast_node_t *
-tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
-		 int minimal);
-
-static tre_ast_node_t *
-tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right);
-
-static tre_ast_node_t *
-tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
-		       tre_ast_node_t *right);
-
-
-static tre_ast_node_t *
-tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size)
+tre_ast_new_node(tre_mem_t mem, int type, void *obj)
 {
-  tre_ast_node_t *node;
-
-  node = tre_mem_calloc(mem, sizeof(*node));
-  if (!node)
-    return NULL;
-  node->obj = tre_mem_calloc(mem, size);
-  if (!node->obj)
-    return NULL;
-  node->type = type;
-  node->nullable = -1;
-  node->submatch_id = -1;
-
-  return node;
+	tre_ast_node_t *node = tre_mem_calloc(mem, sizeof *node);
+	if (!node || !obj)
+		return 0;
+	node->obj = obj;
+	node->type = type;
+	node->nullable = -1;
+	node->submatch_id = -1;
+	return node;
 }
 
 static tre_ast_node_t *
 tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position)
 {
-  tre_ast_node_t *node;
-  tre_literal_t *lit;
+	tre_ast_node_t *node;
+	tre_literal_t *lit;
 
-  node = tre_ast_new_node(mem, LITERAL, sizeof(tre_literal_t));
-  if (!node)
-    return NULL;
-  lit = node->obj;
-  lit->code_min = code_min;
-  lit->code_max = code_max;
-  lit->position = position;
-
-  return node;
+	lit = tre_mem_calloc(mem, sizeof *lit);
+	node = tre_ast_new_node(mem, LITERAL, lit);
+	if (!node)
+		return 0;
+	lit->code_min = code_min;
+	lit->code_max = code_max;
+	lit->position = position;
+	return node;
 }
 
 static tre_ast_node_t *
-tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
-		 int minimal)
+tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max, int minimal)
 {
-  tre_ast_node_t *node;
-  tre_iteration_t *iter;
+	tre_ast_node_t *node;
+	tre_iteration_t *iter;
 
-  node = tre_ast_new_node(mem, ITERATION, sizeof(tre_iteration_t));
-  if (!node)
-    return NULL;
-  iter = node->obj;
-  iter->arg = arg;
-  iter->min = min;
-  iter->max = max;
-  iter->minimal = minimal;
-  node->num_submatches = arg->num_submatches;
-
-  return node;
+	iter = tre_mem_calloc(mem, sizeof *iter);
+	node = tre_ast_new_node(mem, ITERATION, iter);
+	if (!node)
+		return 0;
+	iter->arg = arg;
+	iter->min = min;
+	iter->max = max;
+	iter->minimal = minimal;
+	node->num_submatches = arg->num_submatches;
+	return node;
 }
 
 static tre_ast_node_t *
 tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right)
 {
-  tre_ast_node_t *node;
+	tre_ast_node_t *node;
+	tre_union_t *un;
 
-  node = tre_ast_new_node(mem, UNION, sizeof(tre_union_t));
-  if (node == NULL)
-    return NULL;
-  ((tre_union_t *)node->obj)->left = left;
-  ((tre_union_t *)node->obj)->right = right;
-  node->num_submatches = left->num_submatches + right->num_submatches;
-
-  return node;
+	if (!left)
+		return right;
+	un = tre_mem_calloc(mem, sizeof *un);
+	node = tre_ast_new_node(mem, UNION, un);
+	if (!node || !right)
+		return 0;
+	un->left = left;
+	un->right = right;
+	node->num_submatches = left->num_submatches + right->num_submatches;
+	return node;
 }
 
 static tre_ast_node_t *
-tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
-		       tre_ast_node_t *right)
+tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right)
 {
-  tre_ast_node_t *node;
+	tre_ast_node_t *node;
+	tre_catenation_t *cat;
 
-  node = tre_ast_new_node(mem, CATENATION, sizeof(tre_catenation_t));
-  if (node == NULL)
-    return NULL;
-  ((tre_catenation_t *)node->obj)->left = left;
-  ((tre_catenation_t *)node->obj)->right = right;
-  node->num_submatches = left->num_submatches + right->num_submatches;
-
-  return node;
+	if (!left)
+		return right;
+	cat = tre_mem_calloc(mem, sizeof *cat);
+	node = tre_ast_new_node(mem, CATENATION, cat);
+	if (!node)
+		return 0;
+	cat->left = left;
+	cat->right = right;
+	node->num_submatches = left->num_submatches + right->num_submatches;
+	return node;
 }
 
 
@@ -412,1072 +393,676 @@
 
 /* Parse context. */
 typedef struct {
-  /* Memory allocator.	The AST is allocated using this. */
-  tre_mem_t mem;
-  /* Stack used for keeping track of regexp syntax. */
-  tre_stack_t *stack;
-  /* The parse result. */
-  tre_ast_node_t *result;
-  /* The regexp to parse and its length. */
-  const char *re;
-  /* The first character of the entire regexp. */
-  const char *re_start;
-  /* Current submatch ID. */
-  int submatch_id;
-  /* Current position (number of literal). */
-  int position;
-  /* The highest back reference or -1 if none seen so far. */
-  int max_backref;
-  /* This flag is set if the regexp uses approximate matching. */
-  int have_approx;
-  /* Compilation flags. */
-  int cflags;
-  /* If this flag is set the top-level submatch is not captured. */
-  int nofirstsub;
+	/* Memory allocator. The AST is allocated using this. */
+	tre_mem_t mem;
+	/* Stack used for keeping track of regexp syntax. */
+	tre_stack_t *stack;
+	/* The parsed node after a parse function returns. */
+	tre_ast_node_t *n;
+	/* Position in the regexp pattern after a parse function returns. */
+	const char *s;
+	/* The first character of the regexp. */
+	const char *re;
+	/* Current submatch ID. */
+	int submatch_id;
+	/* Current position (number of literal). */
+	int position;
+	/* The highest back reference or -1 if none seen so far. */
+	int max_backref;
+	/* Compilation flags. */
+	int cflags;
 } tre_parse_ctx_t;
 
-/* Parses a wide character regexp pattern into a syntax tree.  This parser
-   handles both syntaxes (BRE and ERE), including the TRE extensions. */
-static reg_errcode_t
-tre_parse(tre_parse_ctx_t *ctx);
-
-
-/*
-  This parser is just a simple recursive descent parser for POSIX.2
-  regexps.  The parser supports both the obsolete default syntax and
-  the "extended" syntax, and some nonstandard extensions.
-*/
-
-/* Characters with special meanings in regexp syntax. */
-#define CHAR_PIPE	   '|'
-#define CHAR_LPAREN	   '('
-#define CHAR_RPAREN	   ')'
-#define CHAR_LBRACE	   '{'
-#define CHAR_RBRACE	   '}'
-#define CHAR_LBRACKET	   '['
-#define CHAR_RBRACKET	   ']'
-#define CHAR_MINUS	   '-'
-#define CHAR_STAR	   '*'
-#define CHAR_QUESTIONMARK  '?'
-#define CHAR_PLUS	   '+'
-#define CHAR_PERIOD	   '.'
-#define CHAR_COLON	   ':'
-#define CHAR_EQUAL	   '='
-#define CHAR_COMMA	   ','
-#define CHAR_CARET	   '^'
-#define CHAR_DOLLAR	   '$'
-#define CHAR_BACKSLASH	   '\\'
-#define CHAR_HASH	   '#'
-#define CHAR_TILDE	   '~'
-
-
 /* Some macros for expanding \w, \s, etc. */
-static const struct tre_macro_struct {
-  const char c;
-  const char *expansion;
-} tre_macros[] =
-  { {'t', "\t"},	   {'n', "\n"},		   {'r', "\r"},
-    {'f', "\f"},	   {'a', "\a"},		   {'e', "\033"},
-    {'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"},
-    {'S', "[^[:space:]]"}, {'d', "[[:digit:]]"},   {'D', "[^[:digit:]]"},
-    { 0, NULL }
-  };
-
+static const struct {
+	char c;
+	const char *expansion;
+} tre_macros[] = {
+	{'t', "\t"}, {'n', "\n"}, {'r', "\r"},
+	{'f', "\f"}, {'a', "\a"}, {'e', "\033"},
+	{'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"},
+	{'S', "[^[:space:]]"}, {'d', "[[:digit:]]"}, {'D', "[^[:digit:]]"},
+	{ 0, 0 }
+};
 
 /* Expands a macro delimited by `regex' and `regex_end' to `buf', which
    must have at least `len' items.  Sets buf[0] to zero if the there
    is no match in `tre_macros'. */
-static const char *
-tre_expand_macro(const char *regex)
+static const char *tre_expand_macro(const char *s)
 {
-  int i;
-
-  if (!*regex)
-    return 0;
-
-  for (i = 0; tre_macros[i].expansion && tre_macros[i].c != *regex; i++);
-  return tre_macros[i].expansion;
+	int i;
+	for (i = 0; tre_macros[i].c && tre_macros[i].c != *s; i++);
+	return tre_macros[i].expansion;
 }
 
-static reg_errcode_t
-tre_new_item(tre_mem_t mem, int min, int max, int *i, int *max_i,
-	 tre_ast_node_t ***items)
-{
-  reg_errcode_t status;
-  tre_ast_node_t **array = *items;
-  /* Allocate more space if necessary. */
-  if (*i >= *max_i)
-    {
-      tre_ast_node_t **new_items;
-      /* If the array is already 1024 items large, give up -- there's
-	 probably an error in the regexp (e.g. not a '\0' terminated
-	 string and missing ']') */
-      if (*max_i > 1024)
-	return REG_ESPACE;
-      *max_i *= 2;
-      new_items = xrealloc(array, sizeof(*array) * *max_i);
-      if (new_items == NULL)
-	return REG_ESPACE;
-      *items = array = new_items;
-    }
-  array[*i] = tre_ast_new_literal(mem, min, max, -1);
-  status = array[*i] == NULL ? REG_ESPACE : REG_OK;
-  (*i)++;
-  return status;
-}
-
-
 static int
-tre_compare_items(const void *a, const void *b)
+tre_compare_lit(const void *a, const void *b)
 {
-  const tre_ast_node_t *node_a = *(tre_ast_node_t * const *)a;
-  const tre_ast_node_t *node_b = *(tre_ast_node_t * const *)b;
-  tre_literal_t *l_a = node_a->obj, *l_b = node_b->obj;
-  int a_min = l_a->code_min, b_min = l_b->code_min;
-
-  if (a_min < b_min)
-    return -1;
-  else if (a_min > b_min)
-    return 1;
-  else
-    return 0;
+	const tre_literal_t *const *la = a;
+	const tre_literal_t *const *lb = b;
+	/* assumes the range of valid code_min is < INT_MAX */
+	return la[0]->code_min - lb[0]->code_min;
 }
 
-/* Maximum number of character classes that can occur in a negated bracket
-   expression.	*/
+struct literals {
+	tre_mem_t mem;
+	tre_literal_t **a;
+	int len;
+	int cap;
+};
+
+static tre_literal_t *tre_new_lit(struct literals *p)
+{
+	tre_literal_t **a;
+	if (p->len >= p->cap) {
+		if (p->cap >= 1<<15)
+			return 0;
+		p->cap *= 2;
+		a = xrealloc(p->a, p->cap * sizeof *p->a);
+		if (!a)
+			return 0;
+		p->a = a;
+	}
+	a = p->a + p->len++;
+	*a = tre_mem_calloc(p->mem, sizeof **a);
+	return *a;
+}
+
+static int add_icase_literals(struct literals *ls, int min, int max)
+{
+	tre_literal_t *lit;
+	int b, e, c;
+	for (c=min; c<=max; ) {
+		/* assumes islower(c) and isupper(c) are exclusive
+		   and toupper(c)!=c if islower(c).
+		   multiple opposite case characters are not supported */
+		if (tre_islower(c)) {
+			b = e = tre_toupper(c);
+			for (c++, e++; c<=max; c++, e++)
+				if (tre_toupper(c) != e) break;
+		} else if (tre_isupper(c)) {
+			b = e = tre_tolower(c);
+			for (c++, e++; c<=max; c++, e++)
+				if (tre_tolower(c) != e) break;
+		} else {
+			c++;
+			continue;
+		}
+		lit = tre_new_lit(ls);
+		if (!lit)
+			return -1;
+		lit->code_min = b;
+		lit->code_max = e-1;
+		lit->position = -1;
+	}
+	return 0;
+}
+
+
+/* Maximum number of character classes in a negated bracket expression. */
 #define MAX_NEG_CLASSES 64
 
-/* Maximum length of character class names. */
-#define MAX_CLASS_NAME
+struct neg {
+	int negate;
+	int len;
+	tre_ctype_t a[MAX_NEG_CLASSES];
+};
 
-static reg_errcode_t
-tre_parse_bracket_items(tre_parse_ctx_t *ctx, int negate,
-			tre_ctype_t neg_classes[], int *num_neg_classes,
-			tre_ast_node_t ***items, int *num_items,
-			int *items_size)
+// TODO: parse bracket into a set of non-overlapping [lo,hi] ranges
+
+/*
+bracket grammar:
+Bracket  =  '[' List ']'  |  '[^' List ']'
+List     =  Term  |  List Term
+Term     =  Char  |  Range  |  Chclass  |  Eqclass
+Range    =  Char '-' Char  |  Char '-' '-'
+Char     =  Coll  |  coll_single
+Meta     =  ']'  |  '-'
+Coll     =  '[.' coll_single '.]'  |  '[.' coll_multi '.]'  |  '[.' Meta '.]'
+Eqclass  =  '[=' coll_single '=]'  |  '[=' coll_multi '=]'
+Chclass  =  '[:' class ':]'
+
+coll_single is a single char collating element but it can be
+ '-' only at the beginning or end of a List and
+ ']' only at the beginning of a List and
+ '^' anywhere except after the openning '['
+*/
+
+static reg_errcode_t parse_bracket_terms(tre_parse_ctx_t *ctx, const char *s, struct literals *ls, struct neg *neg)
 {
-  const char *re = ctx->re;
-  reg_errcode_t status = REG_OK;
-  tre_ctype_t class = (tre_ctype_t)0;
-  int i = *num_items;
-  int max_i = *items_size;
-  int skip;
+	const char *start = s;
+	tre_ctype_t class;
+	int min, max;
+	wchar_t wc;
+	int len;
 
-  /* Build an array of the items in the bracket expression. */
-  while (status == REG_OK)
-    {
-      skip = 0;
-      if (!*re)
-	{
-	  status = REG_EBRACK;
-	}
-      else if (*re == CHAR_RBRACKET && re > ctx->re)
-	{
-	  re++;
-	  break;
-	}
-      else
-	{
-	  tre_cint_t min = 0, max = 0;
-	  wchar_t wc;
-	  int clen = mbtowc(&wc, re, -1);
-
-	  if (clen<0) clen=1, wc=WEOF;
-
-	  class = (tre_ctype_t)0;
-	  if (*(re + clen) == CHAR_MINUS && *(re + clen + 1) != CHAR_RBRACKET)
-	    {
-	      min = wc;
-	      re += clen+1;
-	      clen = mbtowc(&wc, re, -1);
-	      if (clen<0) clen=1, wc=WEOF;
-	      max = wc;
-	      re += clen;
-	      /* XXX - Should use collation order instead of encoding values
-		 in character ranges. */
-	      if (min > max)
-		status = REG_ERANGE;
-	    }
-	  else if (*re == CHAR_LBRACKET && *(re + 1) == CHAR_PERIOD)
-	    status = REG_ECOLLATE;
-	  else if (*re == CHAR_LBRACKET && *(re + 1) == CHAR_EQUAL)
-	    status = REG_ECOLLATE;
-	  else if (*re == CHAR_LBRACKET && *(re + 1) == CHAR_COLON)
-	    {
-	      char tmp_str[64];
-	      const char *endptr = re + 2;
-	      int len;
-	      while (*endptr && *endptr != CHAR_COLON)
-		endptr++;
-	      if (*endptr)
-		{
-		  len = MIN(endptr - re - 2, 63);
-		  strncpy(tmp_str, re + 2, len);
-		  tmp_str[len] = '\0';
-		  class = tre_ctype(tmp_str);
-		  if (!class)
-		    status = REG_ECTYPE;
-		  re = endptr + 2;
+	for (;;) {
+		class = 0;
+		len = mbtowc(&wc, s, -1);
+		if (len <= 0)
+			return *s ? REG_BADPAT : REG_EBRACK;
+		if (*s == ']' && s != start) {
+			ctx->s = s+1;
+			return REG_OK;
 		}
-	      else
-		status = REG_ECTYPE;
-	      min = 0;
-	      max = TRE_CHAR_MAX;
-	    }
-	  else
-	    {
-	      if (*re == CHAR_MINUS && *(re + 1) != CHAR_RBRACKET
-		  && ctx->re != re)
-		/* Two ranges are not allowed to share and endpoint. */
-		status = REG_ERANGE;
-	      min = max = wc;
-	      re += clen;
-	    }
-
-	  if (status != REG_OK)
-	    break;
-
-	  if (class && negate)
-	    if (*num_neg_classes >= MAX_NEG_CLASSES)
-	      status = REG_ESPACE;
-	    else
-	      neg_classes[(*num_neg_classes)++] = class;
-	  else if (!skip)
-	    {
-	      status = tre_new_item(ctx->mem, min, max, &i, &max_i, items);
-	      if (status != REG_OK)
-		break;
-	      ((tre_literal_t*)((*items)[i-1])->obj)->class = class;
-	    }
-
-	  /* Add opposite-case counterpoints if REG_ICASE is present.
-	     This is broken if there are more than two "same" characters. */
-	  if (ctx->cflags & REG_ICASE && !class && status == REG_OK && !skip)
-	    {
-	      tre_cint_t cmin, ccurr;
-
-	      while (min <= max)
-		{
-		  if (tre_islower(min))
-		    {
-		      cmin = ccurr = tre_toupper(min++);
-		      while (tre_islower(min) && tre_toupper(min) == ccurr + 1
-			     && min <= max)
-			ccurr = tre_toupper(min++);
-		      status = tre_new_item(ctx->mem, cmin, ccurr,
-					    &i, &max_i, items);
-		    }
-		  else if (tre_isupper(min))
-		    {
-		      cmin = ccurr = tre_tolower(min++);
-		      while (tre_isupper(min) && tre_tolower(min) == ccurr + 1
-			     && min <= max)
-			ccurr = tre_tolower(min++);
-		      status = tre_new_item(ctx->mem, cmin, ccurr,
-					    &i, &max_i, items);
-		    }
-		  else min++;
-		  if (status != REG_OK)
-		    break;
-		}
-	      if (status != REG_OK)
-		break;
-	    }
-	}
-    }
-  *num_items = i;
-  *items_size = max_i;
-  ctx->re = re;
-  return status;
-}
-
-static reg_errcode_t
-tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
-{
-  tre_ast_node_t *node = NULL;
-  int negate = 0;
-  reg_errcode_t status = REG_OK;
-  tre_ast_node_t **items, *u, *n;
-  int i = 0, j, max_i = 32, curr_max, curr_min;
-  tre_ctype_t neg_classes[MAX_NEG_CLASSES];
-  int num_neg_classes = 0;
-
-  /* Start off with an array of `max_i' elements. */
-  items = xmalloc(sizeof(*items) * max_i);
-  if (items == NULL)
-    return REG_ESPACE;
-
-  if (*ctx->re == CHAR_CARET)
-    {
-      negate = 1;
-      ctx->re++;
-    }
-
-  status = tre_parse_bracket_items(ctx, negate, neg_classes, &num_neg_classes,
-				   &items, &i, &max_i);
-
-  if (status != REG_OK)
-    goto parse_bracket_done;
-
-  /* Sort the array if we need to negate it. */
-  if (negate)
-    qsort(items, (unsigned)i, sizeof(*items), tre_compare_items);
-
-  curr_max = curr_min = 0;
-  /* Build a union of the items in the array, negated if necessary. */
-  for (j = 0; j < i && status == REG_OK; j++)
-    {
-      int min, max;
-      tre_literal_t *l = items[j]->obj;
-      min = l->code_min;
-      max = l->code_max;
-
-      if (negate)
-	{
-	  if (min < curr_max)
-	    {
-	      /* Overlap. */
-	      curr_max = MAX(max + 1, curr_max);
-	      l = NULL;
-	    }
-	  else
-	    {
-	      /* No overlap. */
-	      curr_max = min - 1;
-	      if (curr_max >= curr_min)
-		{
-		  l->code_min = curr_min;
-		  l->code_max = curr_max;
-		}
-	      else
-		{
-		  l = NULL;
-		}
-	      curr_min = curr_max = max + 1;
-	    }
-	}
-
-      if (l != NULL)
-	{
-	  int k;
-	  l->position = ctx->position;
-	  if (num_neg_classes > 0)
-	    {
-	      l->neg_classes = tre_mem_alloc(ctx->mem,
-					     (sizeof(*l->neg_classes)
-					      * (num_neg_classes + 1)));
-	      if (l->neg_classes == NULL)
-		{
-		  status = REG_ESPACE;
-		  break;
-		}
-	      for (k = 0; k < num_neg_classes; k++)
-		l->neg_classes[k] = neg_classes[k];
-	      l->neg_classes[k] = (tre_ctype_t)0;
-	    }
-	  else
-	    l->neg_classes = NULL;
-	  if (node == NULL)
-	    node = items[j];
-	  else
-	    {
-	      u = tre_ast_new_union(ctx->mem, node, items[j]);
-	      if (u == NULL)
-		status = REG_ESPACE;
-	      node = u;
-	    }
-	}
-    }
-
-  if (status != REG_OK)
-    goto parse_bracket_done;
-
-  if (negate)
-    {
-      int k;
-      n = tre_ast_new_literal(ctx->mem, curr_min, TRE_CHAR_MAX, ctx->position);
-      if (n == NULL)
-	status = REG_ESPACE;
-      else
-	{
-	  tre_literal_t *l = n->obj;
-	  if (num_neg_classes > 0)
-	    {
-	      l->neg_classes = tre_mem_alloc(ctx->mem,
-					     (sizeof(*l->neg_classes)
-					      * (num_neg_classes + 1)));
-	      if (l->neg_classes == NULL)
-		{
-		  status = REG_ESPACE;
-		  goto parse_bracket_done;
-		}
-	      for (k = 0; k < num_neg_classes; k++)
-		l->neg_classes[k] = neg_classes[k];
-	      l->neg_classes[k] = (tre_ctype_t)0;
-	    }
-	  else
-	    l->neg_classes = NULL;
-	  if (node == NULL)
-	    node = n;
-	  else
-	    {
-	      u = tre_ast_new_union(ctx->mem, node, n);
-	      if (u == NULL)
-		status = REG_ESPACE;
-	      node = u;
-	    }
-	}
-    }
-
-  if (status != REG_OK)
-    goto parse_bracket_done;
-
-#ifdef TRE_DEBUG
-  tre_ast_print(node);
-#endif /* TRE_DEBUG */
-
- parse_bracket_done:
-  xfree(items);
-  ctx->position++;
-  *result = node;
-  return status;
-}
-
-
-/* Parses a positive decimal integer.  Returns -1 if the string does not
-   contain a valid number. */
-static int
-tre_parse_int(const char **regex)
-{
-  int num = -1;
-  const char *r = *regex;
-  while (*r-'0'<10U)
-    {
-      if (num < 0)
-	num = 0;
-      num = num * 10 + *r - '0';
-      r++;
-    }
-  *regex = r;
-  return num;
-}
-
-
-static reg_errcode_t
-tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
-{
-  int min, max;
-  const char *r = ctx->re;
-  int minimal = 0;
-
-  /* Parse number (minimum repetition count). */
-  min = -1;
-  if (*r >= '0' && *r <= '9') {
-    min = tre_parse_int(&r);
-  }
-
-  /* Parse comma and second number (maximum repetition count). */
-  max = min;
-  if (*r == CHAR_COMMA)
-    {
-      r++;
-      max = tre_parse_int(&r);
-    }
-
-  /* Check that the repeat counts are sane. */
-  if ((max >= 0 && min > max) || max > RE_DUP_MAX)
-    return REG_BADBR;
-
-  /* Missing }. */
-  if (!*r)
-    return REG_EBRACE;
-
-  /* Empty contents of {}. */
-  if (r == ctx->re)
-    return REG_BADBR;
-
-  /* Parse the ending '}' or '\}'.*/
-  if (ctx->cflags & REG_EXTENDED)
-    {
-      if (*r != CHAR_RBRACE)
-	return REG_BADBR;
-      r++;
-    }
-  else
-    {
-      if (*r != CHAR_BACKSLASH || *(r + 1) != CHAR_RBRACE)
-	return REG_BADBR;
-      r += 2;
-    }
-
-  /* Create the AST node(s). */
-  if (min == 0 && max == 0)
-    {
-      *result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
-      if (*result == NULL)
-	return REG_ESPACE;
-    }
-  else
-    {
-      if (min < 0 && max < 0)
-	/* Only approximate parameters set, no repetitions. */
-	min = max = 1;
-
-      *result = tre_ast_new_iter(ctx->mem, *result, min, max, minimal);
-      if (!*result)
-	return REG_ESPACE;
-    }
-
-  ctx->re = r;
-  return REG_OK;
-}
-
-typedef enum {
-  PARSE_RE = 0,
-  PARSE_ATOM,
-  PARSE_MARK_FOR_SUBMATCH,
-  PARSE_BRANCH,
-  PARSE_PIECE,
-  PARSE_CATENATION,
-  PARSE_POST_CATENATION,
-  PARSE_UNION,
-  PARSE_POST_UNION,
-  PARSE_POSTFIX,
-  PARSE_RESTORE_CFLAGS
-} tre_parse_re_stack_symbol_t;
-
-
-static reg_errcode_t
-tre_parse(tre_parse_ctx_t *ctx)
-{
-  tre_ast_node_t *result = NULL;
-  tre_parse_re_stack_symbol_t symbol;
-  reg_errcode_t status = REG_OK;
-  tre_stack_t *stack = ctx->stack;
-  int bottom = tre_stack_num_objects(stack);
-  int depth = 0;
-  wchar_t wc;
-  int clen;
-
-  if (!ctx->nofirstsub)
-    {
-      STACK_PUSH(stack, int, ctx->submatch_id);
-      STACK_PUSH(stack, int, PARSE_MARK_FOR_SUBMATCH);
-      ctx->submatch_id++;
-    }
-  STACK_PUSH(stack, int, PARSE_RE);
-  ctx->re_start = ctx->re;
-
-
-  /* The following is basically just a recursive descent parser.  I use
-     an explicit stack instead of recursive functions mostly because of
-     two reasons: compatibility with systems which have an overflowable
-     call stack, and efficiency (both in lines of code and speed).  */
-  while (tre_stack_num_objects(stack) > bottom && status == REG_OK)
-    {
-      if (status != REG_OK)
-	break;
-      symbol = tre_stack_pop_int(stack);
-      switch (symbol)
-	{
-	case PARSE_RE:
-	  /* Parse a full regexp.  A regexp is one or more branches,
-	     separated by the union operator `|'. */
-	  if (ctx->cflags & REG_EXTENDED)
-	    STACK_PUSHX(stack, int, PARSE_UNION);
-	  STACK_PUSHX(stack, int, PARSE_BRANCH);
-	  break;
-
-	case PARSE_BRANCH:
-	  /* Parse a branch.  A branch is one or more pieces, concatenated.
-	     A piece is an atom possibly followed by a postfix operator. */
-	  STACK_PUSHX(stack, int, PARSE_CATENATION);
-	  STACK_PUSHX(stack, int, PARSE_PIECE);
-	  break;
-
-	case PARSE_PIECE:
-	  /* Parse a piece.  A piece is an atom possibly followed by one
-	     or more postfix operators. */
-	    STACK_PUSHX(stack, int, PARSE_POSTFIX);
-	  STACK_PUSHX(stack, int, PARSE_ATOM);
-	  break;
-
-	case PARSE_CATENATION:
-	  /* If the expression has not ended, parse another piece. */
-	  {
-	    tre_char_t c;
-	    if (!*ctx->re)
-	      break;
-	    c = *ctx->re;
-		if (ctx->cflags & REG_EXTENDED && c == CHAR_PIPE)
-		  break;
-		if ((ctx->cflags & REG_EXTENDED
-		     && c == CHAR_RPAREN && depth > 0)
-		    || (!(ctx->cflags & REG_EXTENDED)
-			&& (c == CHAR_BACKSLASH
-			    && *(ctx->re + 1) == CHAR_RPAREN)))
-		  {
-		    if (!(ctx->cflags & REG_EXTENDED) && depth == 0)
-		      status = REG_EPAREN;
-		    depth--;
-		    if (!(ctx->cflags & REG_EXTENDED))
-		      ctx->re += 2;
-		    break;
-		  }
-
-	      {
-		/* Default case, left associative concatenation. */
-		STACK_PUSHX(stack, int, PARSE_CATENATION);
-		STACK_PUSHX(stack, voidptr, result);
-		STACK_PUSHX(stack, int, PARSE_POST_CATENATION);
-		STACK_PUSHX(stack, int, PARSE_PIECE);
-	      }
-	    break;
-	  }
-
-	case PARSE_POST_CATENATION:
-	  {
-	    tre_ast_node_t *tree = tre_stack_pop_voidptr(stack);
-	    tre_ast_node_t *tmp_node;
-	    tmp_node = tre_ast_new_catenation(ctx->mem, tree, result);
-	    if (!tmp_node)
-	      return REG_ESPACE;
-	    result = tmp_node;
-	    break;
-	  }
-
-	case PARSE_UNION:
-	  switch (*ctx->re)
-	    {
-	    case CHAR_PIPE:
-	      STACK_PUSHX(stack, int, PARSE_UNION);
-	      STACK_PUSHX(stack, voidptr, result);
-	      STACK_PUSHX(stack, int, PARSE_POST_UNION);
-	      STACK_PUSHX(stack, int, PARSE_BRANCH);
-	      ctx->re++;
-	      break;
-
-	    case CHAR_RPAREN:
-	      ctx->re++;
-	      break;
-
-	    default:
-	      break;
-	    }
-	  break;
-
-	case PARSE_POST_UNION:
-	  {
-	    tre_ast_node_t *tmp_node;
-	    tre_ast_node_t *tree = tre_stack_pop_voidptr(stack);
-	    tmp_node = tre_ast_new_union(ctx->mem, tree, result);
-	    if (!tmp_node)
-	      return REG_ESPACE;
-	    result = tmp_node;
-	    break;
-	  }
-
-	case PARSE_POSTFIX:
-	  /* Parse postfix operators. */
-	  switch (*ctx->re)
-	    {
-	    case CHAR_PLUS:
-	    case CHAR_QUESTIONMARK:
-	      if (!(ctx->cflags & REG_EXTENDED))
-		break;
-		/*FALLTHROUGH*/
-	    case CHAR_STAR:
-	      {
-		tre_ast_node_t *tmp_node;
-		int minimal = 0;
-		int rep_min = 0;
-		int rep_max = -1;
-
-		if (*ctx->re == CHAR_PLUS)
-		  rep_min = 1;
-		if (*ctx->re == CHAR_QUESTIONMARK)
-		  rep_max = 1;
-
-		ctx->re++;
-		tmp_node = tre_ast_new_iter(ctx->mem, result, rep_min, rep_max,
-					    minimal);
-		if (tmp_node == NULL)
-		  return REG_ESPACE;
-		result = tmp_node;
-		STACK_PUSHX(stack, int, PARSE_POSTFIX);
-	      }
-	      break;
-
-	    case CHAR_BACKSLASH:
-	      /* "\{" is special without REG_EXTENDED */
-	      if (!(ctx->cflags & REG_EXTENDED)
-		  && *(ctx->re + 1) == CHAR_LBRACE)
-		{
-		  ctx->re++;
-		  goto parse_brace;
-		}
-	      else
-		break;
-
-	    case CHAR_LBRACE:
-	      /* "{" is literal without REG_EXTENDED */
-	      if (!(ctx->cflags & REG_EXTENDED))
-		break;
-
-	    parse_brace:
-	      ctx->re++;
-
-	      status = tre_parse_bound(ctx, &result);
-	      if (status != REG_OK)
-		return status;
-	      STACK_PUSHX(stack, int, PARSE_POSTFIX);
-	      break;
-	    }
-	  break;
-
-	case PARSE_ATOM:
-	  /* Parse an atom.  An atom is a regular expression enclosed in `()',
-	     an empty set of `()', a bracket expression, `.', `^', `$',
-	     a `\' followed by a character, or a single character. */
-
-	  switch (*ctx->re)
-	    {
-	    case CHAR_LPAREN:  /* parenthesized subexpression */
-
-	      if (ctx->cflags & REG_EXTENDED)
-		{
-		lparen:
-		  depth++;
-		    {
-		      ctx->re++;
-		      /* First parse a whole RE, then mark the resulting tree
-			 for submatching. */
-		      STACK_PUSHX(stack, int, ctx->submatch_id);
-		      STACK_PUSHX(stack, int, PARSE_MARK_FOR_SUBMATCH);
-		      STACK_PUSHX(stack, int, PARSE_RE);
-		      ctx->submatch_id++;
-		    }
-		}
-	      else
-		goto parse_literal;
-	      break;
-
-	    case CHAR_LBRACKET: /* bracket expression */
-	      ctx->re++;
-	      status = tre_parse_bracket(ctx, &result);
-	      if (status != REG_OK)
-		return status;
-	      break;
-
-	    case CHAR_BACKSLASH:
-	      /* If this is "\(" or "\)" chew off the backslash and
-		 try again. */
-	      if (!(ctx->cflags & REG_EXTENDED) && *(ctx->re + 1) == CHAR_LPAREN)
-		{
-		  ctx->re++;
-		  goto lparen;
-		}
-	      if (!(ctx->cflags & REG_EXTENDED) && *(ctx->re + 1) == CHAR_RPAREN)
-		{
-		  goto empty_atom;
-		}
-
-	      /* If a macro is used, parse the expanded macro recursively. */
-	      {
-		const char *buf = tre_expand_macro(ctx->re + 1);
-		if (buf)
-		  {
-		    tre_parse_ctx_t subctx;
-		    memcpy(&subctx, ctx, sizeof(subctx));
-		    subctx.re = buf;
-		    subctx.nofirstsub = 1;
-		    status = tre_parse(&subctx);
-		    if (status != REG_OK)
-		      return status;
-		    ctx->re += 2;
-		    ctx->position = subctx.position;
-		    result = subctx.result;
-		    break;
-		  }
-	      }
-
-	      if (!ctx->re[1])
-		/* Trailing backslash. */
-		return REG_EESCAPE;
-
-	      ctx->re++;
-	      switch (*ctx->re)
-		{
-		case 'b':
-		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
-					       ASSERT_AT_WB, -1);
-		  ctx->re++;
-		  break;
-		case 'B':
-		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
-					       ASSERT_AT_WB_NEG, -1);
-		  ctx->re++;
-		  break;
-		case '<':
-		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
-					       ASSERT_AT_BOW, -1);
-		  ctx->re++;
-		  break;
-		case '>':
-		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
-					       ASSERT_AT_EOW, -1);
-		  ctx->re++;
-		  break;
-		case 'x':
-		  ctx->re++;
-		  if (ctx->re[0] != CHAR_LBRACE)
-		    {
-		      /* 8 bit hex char. */
-		      char tmp[3] = {0, 0, 0};
-		      long val;
-
-		      if (tre_isxdigit(ctx->re[0]))
-			{
-			  tmp[0] = (char)ctx->re[0];
-			  ctx->re++;
+		if (*s == '-' && s != start && s[1] != ']' &&
+		    /* extension: [a-z--@] is accepted as [a-z]|[--@] */
+		    (s[1] != '-' || s[2] == ']'))
+			return REG_ERANGE;
+		if (*s == '[' && (s[1] == '.' || s[1] == '='))
+			/* collating symbols and equivalence classes are not supported */
+			return REG_ECOLLATE;
+		if (*s == '[' && s[1] == ':') {
+			char tmp[CHARCLASS_NAME_MAX+1];
+			s += 2;
+			for (len=0; len < CHARCLASS_NAME_MAX && s[len]; len++) {
+				if (s[len] == ':') {
+					memcpy(tmp, s, len);
+					tmp[len] = 0;
+					class = tre_ctype(tmp);
+					break;
+				}
 			}
-		      if (tre_isxdigit(ctx->re[0]))
-			{
-			  tmp[1] = (char)ctx->re[0];
-			  ctx->re++;
+			if (!class || s[len+1] != ']')
+				return REG_ECTYPE;
+			min = 0;
+			max = TRE_CHAR_MAX;
+			s += len+2;
+		} else {
+			min = max = wc;
+			s += len;
+			if (*s == '-' && s[1] != ']') {
+				s++;
+				len = mbtowc(&wc, s, -1);
+				max = wc;
+				/* XXX - Should use collation order instead of
+				   encoding values in character ranges. */
+				if (len <= 0 || min > max)
+					return REG_ERANGE;
+				s += len;
 			}
-		      val = strtol(tmp, NULL, 16);
-		      result = tre_ast_new_literal(ctx->mem, (int)val,
-						   (int)val, ctx->position);
-		      ctx->position++;
-		      break;
-		    }
-		  else if (*ctx->re)
-		    {
-		      /* Wide char. */
-		      char tmp[32];
-		      long val;
-		      int i = 0;
-		      ctx->re++;
-		      while (*ctx->re && i < sizeof tmp)
-			{
-			  if (ctx->re[0] == CHAR_RBRACE)
-			    break;
-			  if (tre_isxdigit(ctx->re[0]))
-			    {
-			      tmp[i] = (char)ctx->re[0];
-			      i++;
-			      ctx->re++;
-			      continue;
-			    }
-			  return REG_EBRACE;
-			}
-		      ctx->re++;
-		      tmp[i] = 0;
-		      val = strtol(tmp, NULL, 16);
-		      result = tre_ast_new_literal(ctx->mem, (int)val, (int)val,
-						   ctx->position);
-		      ctx->position++;
-		      break;
-		    }
-		  /*FALLTHROUGH*/
+		}
 
-		default:
-		  if (tre_isdigit(*ctx->re))
-		    {
-		      /* Back reference. */
-		      int val = *ctx->re - '0';
-		      result = tre_ast_new_literal(ctx->mem, BACKREF, val,
-						   ctx->position);
-		      if (result == NULL)
+		if (class && neg->negate) {
+			if (neg->len >= MAX_NEG_CLASSES)
+				return REG_ESPACE;
+			neg->a[neg->len++] = class;
+		} else  {
+			tre_literal_t *lit = tre_new_lit(ls);
+			if (!lit)
+				return REG_ESPACE;
+			lit->code_min = min;
+			lit->code_max = max;
+			lit->class = class;
+			lit->position = -1;
+
+			/* Add opposite-case codepoints if REG_ICASE is present.
+			   It seems that POSIX requires that bracket negation
+			   should happen before case-folding, but most practical
+			   implementations do it the other way around. Changing
+			   the order would need efficient representation of
+			   case-fold ranges and bracket range sets even with
+			   simple patterns so this is ok for now. */
+			if (ctx->cflags & REG_ICASE && !class)
+				if (add_icase_literals(ls, min, max))
+					return REG_ESPACE;
+		}
+	}
+}
+
+static reg_errcode_t parse_bracket(tre_parse_ctx_t *ctx, const char *s)
+{
+	int i, max, min, negmax, negmin;
+	tre_ast_node_t *node = 0, *n;
+	tre_ctype_t *nc = 0;
+	tre_literal_t *lit;
+	struct literals ls;
+	struct neg neg;
+	reg_errcode_t err;
+
+	ls.mem = ctx->mem;
+	ls.len = 0;
+	ls.cap = 32;
+	ls.a = xmalloc(ls.cap * sizeof *ls.a);
+	if (!ls.a)
+		return REG_ESPACE;
+	neg.len = 0;
+	neg.negate = *s == '^';
+	if (neg.negate)
+		s++;
+
+	err = parse_bracket_terms(ctx, s, &ls, &neg);
+	if (err != REG_OK)
+		goto parse_bracket_done;
+
+	if (neg.negate) {
+		/* Sort the array if we need to negate it. */
+		qsort(ls.a, ls.len, sizeof *ls.a, tre_compare_lit);
+		/* extra lit for the last negated range */
+		lit = tre_new_lit(&ls);
+		if (!lit) {
+			err = REG_ESPACE;
+			goto parse_bracket_done;
+		}
+		lit->code_min = TRE_CHAR_MAX+1;
+		lit->code_max = TRE_CHAR_MAX+1;
+		lit->position = -1;
+		/* negated classes */
+		if (neg.len) {
+			nc = tre_mem_alloc(ctx->mem, (neg.len+1)*sizeof *neg.a);
+			if (!nc) {
+				err = REG_ESPACE;
+				goto parse_bracket_done;
+			}
+			memcpy(nc, neg.a, neg.len*sizeof *neg.a);
+			nc[neg.len] = 0;
+		}
+	}
+
+	/* Build a union of the items in the array, negated if necessary. */
+	negmax = negmin = 0;
+	for (i = 0; i < ls.len; i++) {
+		lit = ls.a[i];
+		min = lit->code_min;
+		max = lit->code_max;
+		if (neg.negate) {
+			if (min <= negmin) {
+				/* Overlap. */
+				negmin = MAX(max + 1, negmin);
+				continue;
+			}
+			negmax = min - 1;
+			lit->code_min = negmin;
+			lit->code_max = negmax;
+			negmin = max + 1;
+		}
+		lit->position = ctx->position;
+		lit->neg_classes = nc;
+		n = tre_ast_new_node(ctx->mem, LITERAL, lit);
+		node = tre_ast_new_union(ctx->mem, node, n);
+		if (!node) {
+			err = REG_ESPACE;
+			break;
+		}
+	}
+
+parse_bracket_done:
+	xfree(ls.a);
+	ctx->position++;
+	ctx->n = node;
+	return err;
+}
+
+static const char *parse_dup_count(const char *s, int *n)
+{
+	*n = -1;
+	if (!isdigit(*s))
+		return s;
+	*n = 0;
+	for (;;) {
+		*n = 10 * *n + (*s - '0');
+		s++;
+		if (!isdigit(*s) || *n > RE_DUP_MAX)
+			break;
+	}
+	return s;
+}
+
+static const char *parse_dup(const char *s, int ere, int *pmin, int *pmax)
+{
+	int min, max;
+
+	s = parse_dup_count(s, &min);
+	if (*s == ',')
+		s = parse_dup_count(s+1, &max);
+	else
+		max = min;
+
+	if (
+		(max < min && max >= 0) ||
+		max > RE_DUP_MAX ||
+		min > RE_DUP_MAX ||
+		min < 0 ||
+		(!ere && *s++ != '\\') ||
+		*s++ != '}'
+	)
+		return 0;
+	*pmin = min;
+	*pmax = max;
+	return s;
+}
+
+static int hexval(unsigned c)
+{
+	if (c-'0'<10) return c-'0';
+	c |= 32;
+	if (c-'a'<6) return c-'a'+10;
+	return -1;
+}
+
+static reg_errcode_t marksub(tre_parse_ctx_t *ctx, tre_ast_node_t *node, int subid)
+{
+	if (node->submatch_id >= 0) {
+		tre_ast_node_t *n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+		if (!n)
 			return REG_ESPACE;
-		      ctx->position++;
-		      ctx->max_backref = MAX(val, ctx->max_backref);
-		      ctx->re++;
-		    }
-		  else
-		    {
-		      /* Escaped character. */
-		      goto parse_literal;
-		    }
-		  break;
-		}
-	      if (result == NULL)
-		return REG_ESPACE;
-	      break;
-
-	    case CHAR_PERIOD:	 /* the any-symbol */
-	      if (ctx->cflags & REG_NEWLINE)
-		{
-		  tre_ast_node_t *tmp1;
-		  tre_ast_node_t *tmp2;
-		  tmp1 = tre_ast_new_literal(ctx->mem, 0, '\n' - 1,
-					     ctx->position);
-		  if (!tmp1)
-		    return REG_ESPACE;
-		  tmp2 = tre_ast_new_literal(ctx->mem, '\n' + 1, TRE_CHAR_MAX,
-					     ctx->position + 1);
-		  if (!tmp2)
-		    return REG_ESPACE;
-		  result = tre_ast_new_union(ctx->mem, tmp1, tmp2);
-		  if (!result)
-		    return REG_ESPACE;
-		  ctx->position += 2;
-		}
-	      else
-		{
-		  result = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX,
-					       ctx->position);
-		  if (!result)
-		    return REG_ESPACE;
-		  ctx->position++;
-		}
-	      ctx->re++;
-	      break;
-
-	    case CHAR_CARET:	 /* beginning of line assertion */
-	      /* '^' has a special meaning everywhere in EREs, and at
-		 beginning of BRE. */
-	      if (ctx->cflags & REG_EXTENDED
-		  || ctx->re == ctx->re_start)
-		{
-		  if (!(ctx->cflags & REG_EXTENDED))
-		    STACK_PUSHX(stack, int, PARSE_CATENATION);
-		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
-					       ASSERT_AT_BOL, -1);
-		  if (result == NULL)
-		    return REG_ESPACE;
-		  ctx->re++;
-		}
-	      else
-		goto parse_literal;
-	      break;
-
-	    case CHAR_DOLLAR:	 /* end of line assertion. */
-	      /* '$' is special everywhere in EREs, and in the end of the
-		 string in BREs. */
-	      if (ctx->cflags & REG_EXTENDED
-		  || !*(ctx->re + 1))
-		{
-		  result = tre_ast_new_literal(ctx->mem, ASSERTION,
-					       ASSERT_AT_EOL, -1);
-		  if (result == NULL)
-		    return REG_ESPACE;
-		  ctx->re++;
-		}
-	      else
-		goto parse_literal;
-	      break;
-
-	    case CHAR_RPAREN:
-	      if (!depth)
-	        goto parse_literal;
-	    case CHAR_STAR:
-	    case CHAR_PIPE:
-	    case CHAR_LBRACE:
-	    case CHAR_PLUS:
-	    case CHAR_QUESTIONMARK:
-	      if (!(ctx->cflags & REG_EXTENDED))
-	        goto parse_literal;
-
-	    case 0:
-	    empty_atom:
-	      result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
-	      if (!result)
-		return REG_ESPACE;
-	      break;
-
-	    default:
-	    parse_literal:
-
-	      clen = mbtowc(&wc, ctx->re, -1);
-	      if (clen<0) clen=1, wc=WEOF;
-
-	      /* Note that we can't use an tre_isalpha() test here, since there
-		 may be characters which are alphabetic but neither upper or
-		 lower case. */
-	      if (ctx->cflags & REG_ICASE
-		  && (tre_isupper(wc) || tre_islower(wc)))
-		{
-		  tre_ast_node_t *tmp1;
-		  tre_ast_node_t *tmp2;
-
-		  /* XXX - Can there be more than one opposite-case
-		     counterpoints for some character in some locale?  Or
-		     more than two characters which all should be regarded
-		     the same character if case is ignored?  If yes, there
-		     does not seem to be a portable way to detect it.  I guess
-		     that at least for multi-character collating elements there
-		     could be several opposite-case counterpoints, but they
-		     cannot be supported portably anyway. */
-		  tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(wc),
-					     tre_toupper(wc),
-					     ctx->position);
-		  if (!tmp1)
-		    return REG_ESPACE;
-		  tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(wc),
-					     tre_tolower(wc),
-					     ctx->position);
-		  if (!tmp2)
-		    return REG_ESPACE;
-		  result = tre_ast_new_union(ctx->mem, tmp1, tmp2);
-		  if (!result)
-		    return REG_ESPACE;
-		}
-	      else
-		{
-		  result = tre_ast_new_literal(ctx->mem, wc, wc,
-					       ctx->position);
-		  if (!result)
-		    return REG_ESPACE;
-		}
-	      ctx->position++;
-	      ctx->re += clen;
-	      break;
-	    }
-	  break;
-
-	case PARSE_MARK_FOR_SUBMATCH:
-	  {
-	    int submatch_id = tre_stack_pop_int(stack);
-
-	    if (result->submatch_id >= 0)
-	      {
-		tre_ast_node_t *n, *tmp_node;
-		n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
-		if (n == NULL)
-		  return REG_ESPACE;
-		tmp_node = tre_ast_new_catenation(ctx->mem, n, result);
-		if (tmp_node == NULL)
-		  return REG_ESPACE;
-		tmp_node->num_submatches = result->num_submatches;
-		result = tmp_node;
-	      }
-	    result->submatch_id = submatch_id;
-	    result->num_submatches++;
-	    break;
-	  }
-
-	case PARSE_RESTORE_CFLAGS:
-	  ctx->cflags = tre_stack_pop_int(stack);
-	  break;
-
-	default:
-	  assert(0);
-	  break;
+		n = tre_ast_new_catenation(ctx->mem, n, node);
+		if (!n)
+			return REG_ESPACE;
+		n->num_submatches = node->num_submatches;
+		node = n;
 	}
-    }
-
-  /* Check for missing closing parentheses. */
-  if (depth > 0)
-    return REG_EPAREN;
-
-  if (status == REG_OK)
-    ctx->result = result;
-
-  return status;
+	node->submatch_id = subid;
+	node->num_submatches++;
+	ctx->n = node;
+	return REG_OK;
 }
 
+/*
+BRE grammar:
+Regex  =  Branch  |  '^'  |  '$'  |  '^$'  |  '^' Branch  |  Branch '$'  |  '^' Branch '$'
+Branch =  Atom  |  Branch Atom
+Atom   =  char  |  quoted_char  |  '.'  |  Bracket  |  Atom Dup  |  '\(' Branch '\)'  |  back_ref
+Dup    =  '*'  |  '\{' Count '\}'  |  '\{' Count ',\}'  |  '\{' Count ',' Count '\}'
+
+(leading ^ and trailing $ in a sub expr may be an anchor or literal as well)
+
+ERE grammar:
+Regex  =  Branch  |  Regex '|' Branch
+Branch =  Atom  |  Branch Atom
+Atom   =  char  |  quoted_char  |  '.'  |  Bracket  |  Atom Dup  |  '(' Regex ')'  |  '^'  |  '$'
+Dup    =  '*'  |  '+'  |  '?'  |  '{' Count '}'  |  '{' Count ',}'  |  '{' Count ',' Count '}'
+
+(a*+?, ^*, $+, \X, {, (|a) are unspecified)
+*/
+
+static reg_errcode_t parse_atom(tre_parse_ctx_t *ctx, const char *s)
+{
+	int len, ere = ctx->cflags & REG_EXTENDED;
+	const char *p;
+	tre_ast_node_t *node;
+	wchar_t wc;
+	switch (*s) {
+	case '[':
+		return parse_bracket(ctx, s+1);
+	case '\\':
+		p = tre_expand_macro(s+1);
+		if (p) {
+			/* assume \X expansion is a single atom */
+			reg_errcode_t err = parse_atom(ctx, p);
+			ctx->s = s+2;
+			return err;
+		}
+		/* extensions: \b, \B, \<, \>, \xHH \x{HHHH} */
+		switch (*++s) {
+		case 0:
+			return REG_EESCAPE;
+		case 'b':
+			node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB, -1);
+			break;
+		case 'B':
+			node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB_NEG, -1);
+			break;
+		case '<':
+			node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOW, -1);
+			break;
+		case '>':
+			node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOW, -1);
+			break;
+		case 'x':
+			s++;
+			int i, v = 0, c;
+			len = 2;
+			if (*s == '{') {
+				len = 8;
+				s++;
+			}
+			for (i=0; i<len && v<0x110000; i++) {
+				c = hexval(s[i]);
+				if (c < 0) break;
+				v = 16*v + c;
+			}
+			s += i;
+			if (len == 8) {
+				if (*s != '}')
+					return REG_EBRACE;
+				s++;
+			}
+			node = tre_ast_new_literal(ctx->mem, v, v, ctx->position++);
+			s--;
+			break;
+		case '{':
+		case '+':
+		case '?':
+			/* extension: treat \+, \? as repetitions in BRE */
+			/* reject repetitions after empty expression in BRE */
+			if (!ere)
+				return REG_BADRPT;
+		case '|':
+			/* extension: treat \| as alternation in BRE */
+			if (!ere) {
+				node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+				s--;
+				goto end;
+			}
+			/* fallthrough */
+		default:
+			if (!ere && (unsigned)*s-'1' < 9) {
+				/* back reference */
+				int val = *s - '0';
+				node = tre_ast_new_literal(ctx->mem, BACKREF, val, ctx->position++);
+				ctx->max_backref = MAX(val, ctx->max_backref);
+			} else {
+				/* extension: accept unknown escaped char
+				   as a literal */
+				goto parse_literal;
+			}
+		}
+		s++;
+		break;
+	case '.':
+		if (ctx->cflags & REG_NEWLINE) {
+			tre_ast_node_t *tmp1, *tmp2;
+			tmp1 = tre_ast_new_literal(ctx->mem, 0, '\n'-1, ctx->position++);
+			tmp2 = tre_ast_new_literal(ctx->mem, '\n'+1, TRE_CHAR_MAX, ctx->position++);
+			if (tmp1 && tmp2)
+				node = tre_ast_new_union(ctx->mem, tmp1, tmp2);
+			else
+				node = 0;
+		} else {
+			node = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX, ctx->position++);
+		}
+		s++;
+		break;
+	case '^':
+		/* '^' has a special meaning everywhere in EREs, and at beginning of BRE. */
+		if (!ere && s != ctx->re)
+			goto parse_literal;
+		node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOL, -1);
+		s++;
+		break;
+	case '$':
+		/* '$' is special everywhere in EREs, and in the end of the string in BREs. */
+		if (!ere && s[1])
+			goto parse_literal;
+		node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOL, -1);
+		s++;
+		break;
+	case '*':
+	case '{':
+	case '+':
+	case '?':
+		/* reject repetitions after empty expression in ERE */
+		if (ere)
+			return REG_BADRPT;
+	case '|':
+		if (!ere)
+			goto parse_literal;
+	case 0:
+		node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+		break;
+	default:
+parse_literal:
+		len = mbtowc(&wc, s, -1);
+		if (len < 0)
+			return REG_BADPAT;
+		if (ctx->cflags & REG_ICASE && (tre_isupper(wc) || tre_islower(wc))) {
+			tre_ast_node_t *tmp1, *tmp2;
+			/* multiple opposite case characters are not supported */
+			tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(wc), tre_toupper(wc), ctx->position);
+			tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(wc), tre_tolower(wc), ctx->position);
+			if (tmp1 && tmp2)
+				node = tre_ast_new_union(ctx->mem, tmp1, tmp2);
+			else
+				node = 0;
+		} else {
+			node = tre_ast_new_literal(ctx->mem, wc, wc, ctx->position);
+		}
+		ctx->position++;
+		s += len;
+		break;
+	}
+end:
+	if (!node)
+		return REG_ESPACE;
+	ctx->n = node;
+	ctx->s = s;
+	return REG_OK;
+}
+
+#define PUSHPTR(err, s, v) do { \
+	if ((err = tre_stack_push_voidptr(s, v)) != REG_OK) \
+		return err; \
+} while(0)
+
+#define PUSHINT(err, s, v) do { \
+	if ((err = tre_stack_push_int(s, v)) != REG_OK) \
+		return err; \
+} while(0)
+
+static reg_errcode_t tre_parse(tre_parse_ctx_t *ctx)
+{
+	tre_ast_node_t *nbranch=0, *nunion=0;
+	int ere = ctx->cflags & REG_EXTENDED;
+	const char *s = ctx->re;
+	int subid = 0;
+	int depth = 0;
+	reg_errcode_t err;
+	tre_stack_t *stack = ctx->stack;
+
+	PUSHINT(err, stack, subid++);
+	for (;;) {
+		if ((!ere && *s == '\\' && s[1] == '(') ||
+		    (ere && *s == '(')) {
+			PUSHPTR(err, stack, nunion);
+			PUSHPTR(err, stack, nbranch);
+			PUSHINT(err, stack, subid++);
+			s++;
+			if (!ere)
+				s++;
+			depth++;
+			nbranch = nunion = 0;
+			continue;
+		}
+		if ((!ere && *s == '\\' && s[1] == ')') ||
+		    (ere && *s == ')' && depth)) {
+			ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+			if (!ctx->n)
+				return REG_ESPACE;
+		} else {
+			err = parse_atom(ctx, s);
+			if (err != REG_OK)
+				return err;
+			s = ctx->s;
+		}
+
+	parse_iter:
+		for (;;) {
+			int min, max;
+
+			if (*s!='\\' && *s!='*') {
+				if (!ere)
+					break;
+				if (*s!='+' && *s!='?' && *s!='{')
+					break;
+			}
+			if (*s=='\\' && ere)
+				break;
+			/* extension: treat \+, \? as repetitions in BRE */
+			if (*s=='\\' && s[1]!='+' && s[1]!='?' && s[1]!='{')
+				break;
+			if (*s=='\\')
+				s++;
+
+			/* handle ^* at the start of a complete BRE. */
+			if (!ere && s==ctx->re+1 && s[-1]=='^')
+				break;
+
+			/* extension: multiple consecutive *+?{,} is unspecified,
+			   but (a+)+ has to be supported so accepting a++ makes
+			   sense, note however that the RE_DUP_MAX limit can be
+			   circumvented: (a{255}){255} uses a lot of memory.. */
+			if (*s=='{') {
+				s = parse_dup(s+1, ere, &min, &max);
+				if (!s)
+					return REG_BADBR;
+			} else {
+				min=0;
+				max=-1;
+				if (*s == '+')
+					min = 1;
+				if (*s == '?')
+					max = 1;
+				s++;
+			}
+			if (max == 0)
+				ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+			else
+				ctx->n = tre_ast_new_iter(ctx->mem, ctx->n, min, max, 0);
+			if (!ctx->n)
+				return REG_ESPACE;
+		}
+
+		nbranch = tre_ast_new_catenation(ctx->mem, nbranch, ctx->n);
+		if ((ere && *s == '|') ||
+		    (ere && *s == ')' && depth) ||
+		    (!ere && *s == '\\' && s[1] == ')') ||
+		    /* extension: treat \| as alternation in BRE */
+		    (!ere && *s == '\\' && s[1] == '|') ||
+		    !*s) {
+			/* extension: empty branch is unspecified (), (|a), (a|)
+			   here they are not rejected but match on empty string */
+			int c = *s;
+			nunion = tre_ast_new_union(ctx->mem, nunion, nbranch);
+			nbranch = 0;
+
+			if (c == '\\' && s[1] == '|') {
+				s+=2;
+			} else if (c == '|') {
+				s++;
+			} else {
+				if (c == '\\') {
+					if (!depth) return REG_EPAREN;
+					s+=2;
+				} else if (c == ')')
+					s++;
+				depth--;
+				err = marksub(ctx, nunion, tre_stack_pop_int(stack));
+				if (err != REG_OK)
+					return err;
+				if (!c && depth<0) {
+					ctx->submatch_id = subid;
+					return REG_OK;
+				}
+				if (!c || depth<0)
+					return REG_EPAREN;
+				nbranch = tre_stack_pop_voidptr(stack);
+				nunion = tre_stack_pop_voidptr(stack);
+				goto parse_iter;
+			}
+		}
+	}
+}
 
 
 /***********************************************************************
@@ -1521,6 +1106,7 @@
   c->right->firstpos = NULL;
   c->right->lastpos = NULL;
   c->right->num_tags = 0;
+  c->right->num_submatches = 0;
   node->obj = c;
   node->type = CATENATION;
   return REG_OK;
@@ -1551,6 +1137,7 @@
   c->left->firstpos = NULL;
   c->left->lastpos = NULL;
   c->left->num_tags = 0;
+  c->left->num_submatches = 0;
   node->obj = c;
   node->type = CATENATION;
   return REG_OK;
@@ -2023,7 +1610,8 @@
 		  {
 		    status = tre_add_tag_right(mem, left, tag_left);
 		    tnfa->tag_directions[tag_left] = TRE_TAG_MAXIMIZE;
-		    status = tre_add_tag_right(mem, right, tag_right);
+		    if (status == REG_OK)
+		      status = tre_add_tag_right(mem, right, tag_right);
 		    tnfa->tag_directions[tag_right] = TRE_TAG_MAXIMIZE;
 		  }
 		num_tags += 2;
@@ -3102,7 +2690,7 @@
 
   /* Allocate a stack used throughout the compilation process for various
      purposes. */
-  stack = tre_stack_new(512, 10240, 128);
+  stack = tre_stack_new(512, 1024000, 128);
   if (!stack)
     return REG_ESPACE;
   /* Allocate a fast memory allocator. */
@@ -3124,12 +2712,7 @@
   if (errcode != REG_OK)
     ERROR_EXIT(errcode);
   preg->re_nsub = parse_ctx.submatch_id - 1;
-  tree = parse_ctx.result;
-
-  /* Back references and approximate matching cannot currently be used
-     in the same regexp. */
-  if (parse_ctx.max_backref >= 0 && parse_ctx.have_approx)
-    ERROR_EXIT(REG_BADPAT);
+  tree = parse_ctx.n;
 
 #ifdef TRE_DEBUG
   tre_ast_print(tree);
@@ -3144,7 +2727,7 @@
   if (tnfa == NULL)
     ERROR_EXIT(REG_ESPACE);
   tnfa->have_backrefs = parse_ctx.max_backref >= 0;
-  tnfa->have_approx = parse_ctx.have_approx;
+  tnfa->have_approx = 0;
   tnfa->num_submatches = parse_ctx.submatch_id;
 
   /* Set up tags for submatch addressing.  If REG_NOSUB is set and the
diff --git a/system/lib/libc/musl/src/regex/regerror.c b/system/lib/libc/musl/src/regex/regerror.c
index df4afa4..5b347cc 100644
--- a/system/lib/libc/musl/src/regex/regerror.c
+++ b/system/lib/libc/musl/src/regex/regerror.c
@@ -1,6 +1,7 @@
 #include <string.h>
 #include <regex.h>
 #include <stdio.h>
+#include "locale_impl.h"
 
 /* Error message strings for error codes listed in `regex.h'.  This list
    needs to be in sync with the codes listed there, naturally. */
@@ -31,5 +32,6 @@
 	const char *s;
 	for (s=messages; e && *s; e--, s+=strlen(s)+1);
 	if (!*s) s++;
+	s = LCTRANS_CUR(s);
 	return 1+snprintf(buf, size, "%s", s);
 }
diff --git a/system/lib/libc/musl/src/sched/sched_getcpu.c b/system/lib/libc/musl/src/sched/sched_getcpu.c
new file mode 100644
index 0000000..e08cfdf
--- /dev/null
+++ b/system/lib/libc/musl/src/sched/sched_getcpu.c
@@ -0,0 +1,44 @@
+#define _GNU_SOURCE
+#include <errno.h>
+#include <sched.h>
+#include "syscall.h"
+#include "atomic.h"
+
+#ifdef VDSO_GETCPU_SYM
+
+void *__vdsosym(const char *, const char *);
+
+static void *volatile vdso_func;
+
+typedef long (*getcpu_f)(unsigned *, unsigned *, void *);
+
+static long getcpu_init(unsigned *cpu, unsigned *node, void *unused)
+{
+	void *p = __vdsosym(VDSO_GETCPU_VER, VDSO_GETCPU_SYM);
+	getcpu_f f = (getcpu_f)p;
+	a_cas_p(&vdso_func, (void *)getcpu_init, p);
+	return f ? f(cpu, node, unused) : -ENOSYS;
+}
+
+static void *volatile vdso_func = (void *)getcpu_init;
+
+#endif
+
+int sched_getcpu(void)
+{
+	int r;
+	unsigned cpu;
+
+#ifdef VDSO_GETCPU_SYM
+	getcpu_f f = (getcpu_f)vdso_func;
+	if (f) {
+		r = f(&cpu, 0, 0);
+		if (!r) return cpu;
+		if (r != -ENOSYS) return __syscall_ret(r);
+	}
+#endif
+
+	r = __syscall(SYS_getcpu, &cpu, 0, 0);
+	if (!r) return cpu;
+	return __syscall_ret(r);
+}
diff --git a/system/lib/libc/musl/src/search/hsearch.c b/system/lib/libc/musl/src/search/hsearch.c
index 6fe5ced..5c89651 100644
--- a/system/lib/libc/musl/src/search/hsearch.c
+++ b/system/lib/libc/musl/src/search/hsearch.c
@@ -1,6 +1,8 @@
+#define _GNU_SOURCE
 #include <stdlib.h>
 #include <string.h>
 #include <search.h>
+#include "libc.h"
 
 /*
 open addressing hash table with 2^n table size
@@ -14,14 +16,17 @@
 #define MINSIZE 8
 #define MAXSIZE ((size_t)-1/2 + 1)
 
-struct elem {
-	ENTRY item;
-	size_t hash;
+struct __tab {
+	ENTRY *entries;
+	size_t mask;
+	size_t used;
 };
 
-static size_t mask;
-static size_t used;
-static struct elem *tab;
+static struct hsearch_data htab;
+
+int __hcreate_r(size_t, struct hsearch_data *);
+void __hdestroy_r(struct hsearch_data *);
+int __hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *);
 
 static size_t keyhash(char *k)
 {
@@ -33,30 +38,30 @@
 	return h;
 }
 
-static int resize(size_t nel)
+static int resize(size_t nel, struct hsearch_data *htab)
 {
 	size_t newsize;
 	size_t i, j;
-	struct elem *e, *newe;
-	struct elem *oldtab = tab;
-	struct elem *oldend = tab + mask + 1;
+	ENTRY *e, *newe;
+	ENTRY *oldtab = htab->__tab->entries;
+	ENTRY *oldend = htab->__tab->entries + htab->__tab->mask + 1;
 
 	if (nel > MAXSIZE)
 		nel = MAXSIZE;
 	for (newsize = MINSIZE; newsize < nel; newsize *= 2);
-	tab = calloc(newsize, sizeof *tab);
-	if (!tab) {
-		tab = oldtab;
+	htab->__tab->entries = calloc(newsize, sizeof *htab->__tab->entries);
+	if (!htab->__tab->entries) {
+		htab->__tab->entries = oldtab;
 		return 0;
 	}
-	mask = newsize - 1;
+	htab->__tab->mask = newsize - 1;
 	if (!oldtab)
 		return 1;
 	for (e = oldtab; e < oldend; e++)
-		if (e->item.key) {
-			for (i=e->hash,j=1; ; i+=j++) {
-				newe = tab + (i & mask);
-				if (!newe->item.key)
+		if (e->key) {
+			for (i=keyhash(e->key),j=1; ; i+=j++) {
+				newe = htab->__tab->entries + (i & htab->__tab->mask);
+				if (!newe->key)
 					break;
 			}
 			*newe = *e;
@@ -67,29 +72,22 @@
 
 int hcreate(size_t nel)
 {
-	mask = 0;
-	used = 0;
-	tab = 0;
-	return resize(nel);
+	return __hcreate_r(nel, &htab);
 }
 
 void hdestroy(void)
 {
-	free(tab);
-	tab = 0;
-	mask = 0;
-	used = 0;
+	__hdestroy_r(&htab);
 }
 
-static struct elem *lookup(char *key, size_t hash)
+static ENTRY *lookup(char *key, size_t hash, struct hsearch_data *htab)
 {
 	size_t i, j;
-	struct elem *e;
+	ENTRY *e;
 
 	for (i=hash,j=1; ; i+=j++) {
-		e = tab + (i & mask);
-		if (!e->item.key ||
-		    (e->hash==hash && strcmp(e->item.key, key)==0))
+		e = htab->__tab->entries + (i & htab->__tab->mask);
+		if (!e->key || strcmp(e->key, key) == 0)
 			break;
 	}
 	return e;
@@ -97,22 +95,60 @@
 
 ENTRY *hsearch(ENTRY item, ACTION action)
 {
-	size_t hash = keyhash(item.key);
-	struct elem *e = lookup(item.key, hash);
+	ENTRY *e;
 
-	if (e->item.key)
-		return &e->item;
-	if (action == FIND)
+	__hsearch_r(item, action, &e, &htab);
+	return e;
+}
+
+int __hcreate_r(size_t nel, struct hsearch_data *htab)
+{
+	int r;
+
+	htab->__tab = calloc(1, sizeof *htab->__tab);
+	if (!htab->__tab)
 		return 0;
-	e->item = item;
-	e->hash = hash;
-	if (++used > mask - mask/4) {
-		if (!resize(2*used)) {
-			used--;
-			e->item.key = 0;
+	r = resize(nel, htab);
+	if (r == 0) {
+		free(htab->__tab);
+		htab->__tab = 0;
+	}
+	return r;
+}
+weak_alias(__hcreate_r, hcreate_r);
+
+void __hdestroy_r(struct hsearch_data *htab)
+{
+	if (htab->__tab) free(htab->__tab->entries);
+	free(htab->__tab);
+	htab->__tab = 0;
+}
+weak_alias(__hdestroy_r, hdestroy_r);
+
+int __hsearch_r(ENTRY item, ACTION action, ENTRY **retval, struct hsearch_data *htab)
+{
+	size_t hash = keyhash(item.key);
+	ENTRY *e = lookup(item.key, hash, htab);
+
+	if (e->key) {
+		*retval = e;
+		return 1;
+	}
+	if (action == FIND) {
+		*retval = 0;
+		return 0;
+	}
+	*e = item;
+	if (++htab->__tab->used > htab->__tab->mask - htab->__tab->mask/4) {
+		if (!resize(2*htab->__tab->used, htab)) {
+			htab->__tab->used--;
+			e->key = 0;
+			*retval = 0;
 			return 0;
 		}
-		e = lookup(item.key, hash);
+		e = lookup(item.key, hash, htab);
 	}
-	return &e->item;
+	*retval = e;
+	return 1;
 }
+weak_alias(__hsearch_r, hsearch_r);
diff --git a/system/lib/libc/musl/src/search/tsearch_avl.c b/system/lib/libc/musl/src/search/tsearch_avl.c
index 8620092..57194c8 100644
--- a/system/lib/libc/musl/src/search/tsearch_avl.c
+++ b/system/lib/libc/musl/src/search/tsearch_avl.c
@@ -77,38 +77,45 @@
 		return find(n->right, k, cmp);
 }
 
-static struct node *insert(struct node **n, const void *k,
-	int (*cmp)(const void *, const void *), int *new)
+static struct node *insert(struct node *n, const void *k,
+	int (*cmp)(const void *, const void *), struct node **found)
 {
-	struct node *r = *n;
+	struct node *r;
 	int c;
 
-	if (!r) {
-		*n = r = malloc(sizeof **n);
-		if (r) {
-			r->key = k;
-			r->left = r->right = 0;
-			r->height = 1;
+	if (!n) {
+		n = malloc(sizeof *n);
+		if (n) {
+			n->key = k;
+			n->left = n->right = 0;
+			n->height = 1;
 		}
-		*new = 1;
-		return r;
+		*found = n;
+		return n;
 	}
-	c = cmp(k, r->key);
-	if (c == 0)
-		return r;
-	if (c < 0)
-		r = insert(&r->left, k, cmp, new);
-	else
-		r = insert(&r->right, k, cmp, new);
-	if (*new)
-		*n = balance(*n);
+	c = cmp(k, n->key);
+	if (c == 0) {
+		*found = n;
+		return 0;
+	}
+	r = insert(c < 0 ? n->left : n->right, k, cmp, found);
+	if (r) {
+		if (c < 0)
+			n->left = r;
+		else
+			n->right = r;
+		r = balance(n);
+	}
 	return r;
 }
 
-static struct node *movr(struct node *n, struct node *r) {
-	if (!n)
-		return r;
-	n->right = movr(n->right, r);
+static struct node *remove_rightmost(struct node *n, struct node **rightmost)
+{
+	if (!n->right) {
+		*rightmost = n;
+		return n->left;
+	}
+	n->right = remove_rightmost(n->right, rightmost);
 	return balance(n);
 }
 
@@ -122,7 +129,13 @@
 	c = cmp(k, (*n)->key);
 	if (c == 0) {
 		struct node *r = *n;
-		*n = movr(r->left, r->right);
+		if (r->left) {
+			r->left = remove_rightmost(r->left, n);
+			(*n)->left = r->left;
+			(*n)->right = r->right;
+			*n = balance(*n);
+		} else
+			*n = r->right;
 		free(r);
 		return parent;
 	}
@@ -138,6 +151,8 @@
 void *tdelete(const void *restrict key, void **restrict rootp,
 	int(*compar)(const void *, const void *))
 {
+	if (!rootp)
+		return 0;
 	struct node *n = *rootp;
 	struct node *ret;
 	/* last argument is arbitrary non-null pointer
@@ -150,17 +165,21 @@
 void *tfind(const void *key, void *const *rootp,
 	int(*compar)(const void *, const void *))
 {
+	if (!rootp)
+		return 0;
 	return find(*rootp, key, compar);
 }
 
 void *tsearch(const void *key, void **rootp,
 	int (*compar)(const void *, const void *))
 {
-	int new = 0;
-	struct node *n = *rootp;
+	struct node *update;
 	struct node *ret;
-	ret = insert(&n, key, compar, &new);
-	*rootp = n;
+	if (!rootp)
+		return 0;
+	update = insert(*rootp, key, compar, &ret);
+	if (update)
+		*rootp = update;
 	return ret;
 }
 
diff --git a/system/lib/libc/musl/src/select/poll.c b/system/lib/libc/musl/src/select/poll.c
index f1e73e8..9e0bcbd 100644
--- a/system/lib/libc/musl/src/select/poll.c
+++ b/system/lib/libc/musl/src/select/poll.c
@@ -1,8 +1,16 @@
 #include <poll.h>
+#include <time.h>
+#include <signal.h>
 #include "syscall.h"
 #include "libc.h"
 
 int poll(struct pollfd *fds, nfds_t n, int timeout)
 {
+#ifdef SYS_poll
 	return syscall_cp(SYS_poll, fds, n, timeout);
+#else
+	return syscall_cp(SYS_ppoll, fds, n, timeout>=0 ?
+		&((struct timespec){ .tv_sec = timeout/1000,
+		.tv_nsec = timeout%1000*1000000 }) : 0, 0, _NSIG/8);
+#endif
 }
diff --git a/system/lib/libc/musl/src/select/pselect.c b/system/lib/libc/musl/src/select/pselect.c
index a19e153..4e2d7b0 100644
--- a/system/lib/libc/musl/src/select/pselect.c
+++ b/system/lib/libc/musl/src/select/pselect.c
@@ -1,11 +1,12 @@
 #include <sys/select.h>
 #include <signal.h>
+#include <stdint.h>
 #include "syscall.h"
 #include "libc.h"
 
 int pselect(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict efds, const struct timespec *restrict ts, const sigset_t *restrict mask)
 {
-	long data[2] = { (long)mask, _NSIG/8 };
+	syscall_arg_t data[2] = { (uintptr_t)mask, _NSIG/8 };
 	struct timespec ts_tmp;
 	if (ts) ts_tmp = *ts;
 	return syscall_cp(SYS_pselect6, n, rfds, wfds, efds, ts ? &ts_tmp : 0, data);
diff --git a/system/lib/libc/musl/src/select/select.c b/system/lib/libc/musl/src/select/select.c
index f93597b..7b5f6dc 100644
--- a/system/lib/libc/musl/src/select/select.c
+++ b/system/lib/libc/musl/src/select/select.c
@@ -1,8 +1,26 @@
 #include <sys/select.h>
+#include <signal.h>
+#include <stdint.h>
+#include <errno.h>
 #include "syscall.h"
 #include "libc.h"
 
 int select(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict efds, struct timeval *restrict tv)
 {
+#ifdef SYS_select
 	return syscall_cp(SYS_select, n, rfds, wfds, efds, tv);
+#else
+	syscall_arg_t data[2] = { 0, _NSIG/8 };
+	struct timespec ts;
+	if (tv) {
+		if (tv->tv_sec < 0 || tv->tv_usec < 0)
+			return __syscall_ret(-EINVAL);
+		time_t extra_secs = tv->tv_usec / 1000000;
+		ts.tv_nsec = tv->tv_usec % 1000000 * 1000;
+		const time_t max_time = (1ULL<<8*sizeof(time_t)-1)-1;
+		ts.tv_sec = extra_secs > max_time - tv->tv_sec ?
+			max_time : tv->tv_sec + extra_secs;
+	}
+	return syscall_cp(SYS_pselect6, n, rfds, wfds, efds, tv ? &ts : 0, data);
+#endif
 }
diff --git a/system/lib/libc/musl/src/signal/raise.c b/system/lib/libc/musl/src/signal/raise.c
index 35063c5..717b1c9 100644
--- a/system/lib/libc/musl/src/signal/raise.c
+++ b/system/lib/libc/musl/src/signal/raise.c
@@ -5,12 +5,11 @@
 
 int raise(int sig)
 {
-	int pid, tid, ret;
+	int tid, ret;
 	sigset_t set;
 	__block_app_sigs(&set);
 	tid = __syscall(SYS_gettid);
-	pid = __syscall(SYS_getpid);
-	ret = syscall(SYS_tgkill, pid, tid, sig);
+	ret = syscall(SYS_tkill, tid, sig);
 	__restore_sigs(&set);
 	return ret;
 }
diff --git a/system/lib/libc/musl/src/signal/sigaction.c b/system/lib/libc/musl/src/signal/sigaction.c
index f7ff4a6..6eca06f 100644
--- a/system/lib/libc/musl/src/signal/sigaction.c
+++ b/system/lib/libc/musl/src/signal/sigaction.c
@@ -6,11 +6,7 @@
 #include "libc.h"
 #include "ksigaction.h"
 
-void __restore(), __restore_rt();
-
-static pthread_t dummy(void) { return 0; }
-weak_alias(dummy, __pthread_self_def);
-
+static int unmask_done;
 static unsigned long handler_set[_NSIG/(8*sizeof(long))];
 
 void __get_handler_set(sigset_t *set)
@@ -21,15 +17,24 @@
 int __libc_sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)
 {
 	struct k_sigaction ksa, ksa_old;
-	if (sig >= (unsigned)_NSIG) {
-		errno = EINVAL;
-		return -1;
-	}
 	if (sa) {
 		if ((uintptr_t)sa->sa_handler > 1UL) {
 			a_or_l(handler_set+(sig-1)/(8*sizeof(long)),
 				1UL<<(sig-1)%(8*sizeof(long)));
-			__pthread_self_def();
+
+			/* If pthread_create has not yet been called,
+			 * implementation-internal signals might not
+			 * yet have been unblocked. They must be
+			 * unblocked before any signal handler is
+			 * installed, so that an application cannot
+			 * receive an illegal sigset_t (with them
+			 * blocked) as part of the ucontext_t passed
+			 * to the signal handler. */
+			if (!libc.threaded && !unmask_done) {
+				__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
+					SIGPT_SET, 0, _NSIG/8);
+				unmask_done = 1;
+			}
 		}
 		ksa.handler = sa->sa_handler;
 		ksa.flags = sa->sa_flags | SA_RESTORER;
@@ -48,7 +53,7 @@
 
 int __sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old)
 {
-	if (sig-32U < 3) {
+	if (sig-32U < 3 || sig-1U >= _NSIG-1) {
 		errno = EINVAL;
 		return -1;
 	}
diff --git a/system/lib/libc/musl/src/signal/siglongjmp.c b/system/lib/libc/musl/src/signal/siglongjmp.c
index b644ceb..bc317ac 100644
--- a/system/lib/libc/musl/src/signal/siglongjmp.c
+++ b/system/lib/libc/musl/src/signal/siglongjmp.c
@@ -5,6 +5,5 @@
 
 _Noreturn void siglongjmp(sigjmp_buf buf, int ret)
 {
-	if (buf->__fl) __restore_sigs(buf->__ss);
 	longjmp(buf, ret);
 }
diff --git a/system/lib/libc/musl/src/signal/signal.c b/system/lib/libc/musl/src/signal/signal.c
index c0f063e..29e03c8 100644
--- a/system/lib/libc/musl/src/signal/signal.c
+++ b/system/lib/libc/musl/src/signal/signal.c
@@ -13,3 +13,4 @@
 }
 
 weak_alias(signal, bsd_signal);
+weak_alias(signal, __sysv_signal);
diff --git a/system/lib/libc/musl/src/signal/sigsetjmp.c b/system/lib/libc/musl/src/signal/sigsetjmp.c
index cb2257f..e69de29 100644
--- a/system/lib/libc/musl/src/signal/sigsetjmp.c
+++ b/system/lib/libc/musl/src/signal/sigsetjmp.c
@@ -1,14 +0,0 @@
-#include <setjmp.h>
-#include <signal.h>
-
-/* !!! This function will not work unless the compiler performs
- * tail call optimization. Machine-specific asm versions should
- * be created instead even though the workaround (tail call)
- * is entirely non-machine-specific... */
-
-int sigsetjmp(sigjmp_buf buf, int save)
-{
-	if ((buf->__fl = save))
-		pthread_sigmask(SIG_SETMASK, 0, (sigset_t *)buf->__ss);
-	return setjmp(buf);
-}
diff --git a/system/lib/libc/musl/src/signal/sigsetjmp_tail.c b/system/lib/libc/musl/src/signal/sigsetjmp_tail.c
new file mode 100644
index 0000000..78762aa
--- /dev/null
+++ b/system/lib/libc/musl/src/signal/sigsetjmp_tail.c
@@ -0,0 +1,11 @@
+#include <setjmp.h>
+#include <signal.h>
+#include "syscall.h"
+
+__attribute__((__visibility__("hidden")))
+int __sigsetjmp_tail(sigjmp_buf jb, int ret)
+{
+	void *p = jb->__ss;
+	__syscall(SYS_rt_sigprocmask, SIG_SETMASK, ret?p:0, ret?0:p, _NSIG/8);
+	return ret;
+}
diff --git a/system/lib/libc/musl/src/stat/__fxstat.c b/system/lib/libc/musl/src/stat/__fxstat.c
deleted file mode 100644
index fd39ee3..0000000
--- a/system/lib/libc/musl/src/stat/__fxstat.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <sys/stat.h>
-#include "libc.h"
-
-int __fxstat(int ver, int fd, struct stat *buf)
-{
-	return fstat(fd, buf);
-}
-
-LFS64(__fxstat);
diff --git a/system/lib/libc/musl/src/stat/__fxstatat.c b/system/lib/libc/musl/src/stat/__fxstatat.c
deleted file mode 100644
index e389dec..0000000
--- a/system/lib/libc/musl/src/stat/__fxstatat.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <sys/stat.h>
-#include "libc.h"
-
-int __fxstatat(int ver, int fd, const char *path, struct stat *buf, int flag)
-{
-	return fstatat(fd, path, buf, flag);
-}
-
-LFS64(__fxstatat);
diff --git a/system/lib/libc/musl/src/stat/__lxstat.c b/system/lib/libc/musl/src/stat/__lxstat.c
deleted file mode 100644
index e9992ed..0000000
--- a/system/lib/libc/musl/src/stat/__lxstat.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <sys/stat.h>
-#include "libc.h"
-
-int __lxstat(int ver, const char *path, struct stat *buf)
-{
-	return lstat(path, buf);
-}
-
-LFS64(__lxstat);
diff --git a/system/lib/libc/musl/src/stat/__xstat.c b/system/lib/libc/musl/src/stat/__xstat.c
index 42011d5..73c873a 100644
--- a/system/lib/libc/musl/src/stat/__xstat.c
+++ b/system/lib/libc/musl/src/stat/__xstat.c
@@ -1,9 +1,37 @@
 #include <sys/stat.h>
 #include "libc.h"
 
+int __fxstat(int ver, int fd, struct stat *buf)
+{
+	return fstat(fd, buf);
+}
+
+int __fxstatat(int ver, int fd, const char *path, struct stat *buf, int flag)
+{
+	return fstatat(fd, path, buf, flag);
+}
+
+int __lxstat(int ver, const char *path, struct stat *buf)
+{
+	return lstat(path, buf);
+}
+
 int __xstat(int ver, const char *path, struct stat *buf)
 {
 	return stat(path, buf);
 }
 
+LFS64(__fxstat);
+LFS64(__fxstatat);
+LFS64(__lxstat);
 LFS64(__xstat);
+
+int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev)
+{
+	return mknod(path, mode, *dev);
+}
+
+int __xmknodat(int ver, int fd, const char *path, mode_t mode, dev_t *dev)
+{
+	return mknodat(fd, path, mode, *dev);
+}
diff --git a/system/lib/libc/musl/src/stat/chmod.c b/system/lib/libc/musl/src/stat/chmod.c
index beb66e5..d4f53c5 100644
--- a/system/lib/libc/musl/src/stat/chmod.c
+++ b/system/lib/libc/musl/src/stat/chmod.c
@@ -1,7 +1,12 @@
 #include <sys/stat.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int chmod(const char *path, mode_t mode)
 {
+#ifdef SYS_chmod
 	return syscall(SYS_chmod, path, mode);
+#else
+	return syscall(SYS_fchmodat, AT_FDCWD, path, mode);
+#endif
 }
diff --git a/system/lib/libc/musl/src/stat/fchmod.c b/system/lib/libc/musl/src/stat/fchmod.c
index 6d28141..93e1b64 100644
--- a/system/lib/libc/musl/src/stat/fchmod.c
+++ b/system/lib/libc/musl/src/stat/fchmod.c
@@ -13,5 +13,9 @@
 
 	char buf[15+3*sizeof(int)];
 	__procfdname(buf, fd);
+#ifdef SYS_chmod
 	return syscall(SYS_chmod, buf, mode);
+#else
+	return syscall(SYS_fchmodat, AT_FDCWD, buf, mode);
+#endif
 }
diff --git a/system/lib/libc/musl/src/stat/fstat.c b/system/lib/libc/musl/src/stat/fstat.c
index b561176..ab4afc0 100644
--- a/system/lib/libc/musl/src/stat/fstat.c
+++ b/system/lib/libc/musl/src/stat/fstat.c
@@ -14,7 +14,11 @@
 
 	char buf[15+3*sizeof(int)];
 	__procfdname(buf, fd);
+#ifdef SYS_stat
 	return syscall(SYS_stat, buf, st);
+#else
+	return syscall(SYS_fstatat, AT_FDCWD, buf, st, 0);
+#endif
 }
 
 LFS64(fstat);
diff --git a/system/lib/libc/musl/src/stat/futimesat.c b/system/lib/libc/musl/src/stat/futimesat.c
index dbefc84..b4eea1d 100644
--- a/system/lib/libc/musl/src/stat/futimesat.c
+++ b/system/lib/libc/musl/src/stat/futimesat.c
@@ -1,10 +1,23 @@
 #define _GNU_SOURCE
 #include <sys/time.h>
+#include <sys/stat.h>
+#include <errno.h>
 #include "syscall.h"
+#include "libc.h"
 
-#ifdef SYS_futimesat
-int futimesat(int dirfd, const char *pathname, const struct timeval times[2])
+int __futimesat(int dirfd, const char *pathname, const struct timeval times[2])
 {
-	return syscall(SYS_futimesat, dirfd, pathname, times);
+	struct timespec ts[2];
+	if (times) {
+		int i;
+		for (i=0; i<2; i++) {
+			if (times[i].tv_usec >= 1000000ULL)
+				return __syscall_ret(-EINVAL);
+			ts[i].tv_sec = times[i].tv_sec;
+			ts[i].tv_nsec = times[i].tv_usec * 1000;
+		}
+	}
+	return utimensat(dirfd, pathname, times ? ts : 0, 0);
 }
-#endif
+
+weak_alias(__futimesat, futimesat);
diff --git a/system/lib/libc/musl/src/stat/lstat.c b/system/lib/libc/musl/src/stat/lstat.c
index 8f60358..5e8b84f 100644
--- a/system/lib/libc/musl/src/stat/lstat.c
+++ b/system/lib/libc/musl/src/stat/lstat.c
@@ -1,10 +1,15 @@
 #include <sys/stat.h>
+#include <fcntl.h>
 #include "syscall.h"
 #include "libc.h"
 
 int lstat(const char *restrict path, struct stat *restrict buf)
 {
+#ifdef SYS_lstat
 	return syscall(SYS_lstat, path, buf);
+#else
+	return syscall(SYS_fstatat, AT_FDCWD, path, buf, AT_SYMLINK_NOFOLLOW);
+#endif
 }
 
 LFS64(lstat);
diff --git a/system/lib/libc/musl/src/stat/mkdir.c b/system/lib/libc/musl/src/stat/mkdir.c
index 770e1cc..32625b7 100644
--- a/system/lib/libc/musl/src/stat/mkdir.c
+++ b/system/lib/libc/musl/src/stat/mkdir.c
@@ -1,7 +1,12 @@
 #include <sys/stat.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int mkdir(const char *path, mode_t mode)
 {
+#ifdef SYS_mkdir
 	return syscall(SYS_mkdir, path, mode);
+#else
+	return syscall(SYS_mkdirat, AT_FDCWD, path, mode);
+#endif
 }
diff --git a/system/lib/libc/musl/src/stat/mknod.c b/system/lib/libc/musl/src/stat/mknod.c
index c319657..beebd84 100644
--- a/system/lib/libc/musl/src/stat/mknod.c
+++ b/system/lib/libc/musl/src/stat/mknod.c
@@ -1,7 +1,12 @@
 #include <sys/stat.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int mknod(const char *path, mode_t mode, dev_t dev)
 {
+#ifdef SYS_mknod
 	return syscall(SYS_mknod, path, mode, dev);
+#else
+	return syscall(SYS_mknodat, AT_FDCWD, path, mode, dev);
+#endif
 }
diff --git a/system/lib/libc/musl/src/stat/stat.c b/system/lib/libc/musl/src/stat/stat.c
index c6de716..b4433a0 100644
--- a/system/lib/libc/musl/src/stat/stat.c
+++ b/system/lib/libc/musl/src/stat/stat.c
@@ -1,10 +1,15 @@
 #include <sys/stat.h>
+#include <fcntl.h>
 #include "syscall.h"
 #include "libc.h"
 
 int stat(const char *restrict path, struct stat *restrict buf)
 {
+#ifdef SYS_stat
 	return syscall(SYS_stat, path, buf);
+#else
+	return syscall(SYS_fstatat, AT_FDCWD, path, buf, 0);
+#endif
 }
 
 LFS64(stat);
diff --git a/system/lib/libc/musl/src/stat/utimensat.c b/system/lib/libc/musl/src/stat/utimensat.c
index 929698b..159c8be 100644
--- a/system/lib/libc/musl/src/stat/utimensat.c
+++ b/system/lib/libc/musl/src/stat/utimensat.c
@@ -1,7 +1,37 @@
 #include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
 #include "syscall.h"
 
 int utimensat(int fd, const char *path, const struct timespec times[2], int flags)
 {
-	return syscall(SYS_utimensat, fd, path, times, flags);
+	int r = __syscall(SYS_utimensat, fd, path, times, flags);
+#ifdef SYS_futimesat
+	if (r != -ENOSYS || flags) return __syscall_ret(r);
+	struct timeval *tv = 0, tmp[2];
+	if (times) {
+		int i;
+		tv = tmp;
+		for (i=0; i<2; i++) {
+			if (times[i].tv_nsec >= 1000000000ULL) {
+				if (times[i].tv_nsec == UTIME_NOW &&
+				    times[1-i].tv_nsec == UTIME_NOW) {
+					tv = 0;
+					break;
+				}
+				if (times[i].tv_nsec == UTIME_OMIT)
+					return __syscall_ret(-ENOSYS);
+				return __syscall_ret(-EINVAL);
+			}
+			tmp[i].tv_sec = times[i].tv_sec;
+			tmp[i].tv_usec = times[i].tv_nsec / 1000;
+		}
+	}
+
+	r = __syscall(SYS_futimesat, fd, path, tv);
+	if (r != -ENOSYS || fd != AT_FDCWD) return __syscall_ret(r);
+	r = __syscall(SYS_utimes, path, tv);
+#endif
+	return __syscall_ret(r);
 }
diff --git a/system/lib/libc/musl/src/stdio/__fdopen.c b/system/lib/libc/musl/src/stdio/__fdopen.c
index a6ae73a..8d6ce81 100644
--- a/system/lib/libc/musl/src/stdio/__fdopen.c
+++ b/system/lib/libc/musl/src/stdio/__fdopen.c
@@ -1,6 +1,5 @@
 #include "stdio_impl.h"
 #include <stdlib.h>
-#include <termios.h>
 #include <sys/ioctl.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -9,7 +8,7 @@
 FILE *__fdopen(int fd, const char *mode)
 {
 	FILE *f;
-	struct termios tio;
+	struct winsize wsz;
 
 	/* Check for valid initial mode character */
 	if (!strchr("rwa", *mode)) {
@@ -43,7 +42,7 @@
 
 	/* Activate line buffered mode for terminals */
 	f->lbf = EOF;
-	if (!(f->flags & F_NOWR) && !__syscall(SYS_ioctl, fd, TCGETS, &tio))
+	if (!(f->flags & F_NOWR) && !__syscall(SYS_ioctl, fd, TIOCGWINSZ, &wsz))
 		f->lbf = '\n';
 
 	/* Initialize op ptrs. No problem if some are unneeded. */
@@ -55,13 +54,7 @@
 	if (!libc.threaded) f->lock = -1;
 
 	/* Add new FILE to open file list */
-	OFLLOCK();
-	f->next = libc.ofl_head;
-	if (libc.ofl_head) libc.ofl_head->prev = f;
-	libc.ofl_head = f;
-	OFLUNLOCK();
-
-	return f;
+	return __ofl_add(f);
 }
 
 weak_alias(__fdopen, fdopen);
diff --git a/system/lib/libc/musl/src/stdio/__fopen_rb_ca.c b/system/lib/libc/musl/src/stdio/__fopen_rb_ca.c
index 9202c8c..183a5d5 100644
--- a/system/lib/libc/musl/src/stdio/__fopen_rb_ca.c
+++ b/system/lib/libc/musl/src/stdio/__fopen_rb_ca.c
@@ -6,8 +6,9 @@
 {
 	memset(f, 0, sizeof *f);
 
-	f->fd = syscall(SYS_open, filename, O_RDONLY|O_LARGEFILE|O_CLOEXEC, 0);
+	f->fd = sys_open(filename, O_RDONLY|O_CLOEXEC);
 	if (f->fd < 0) return 0;
+	__syscall(SYS_fcntl, f->fd, F_SETFD, FD_CLOEXEC);
 
 	f->flags = F_NOWR | F_PERM;
 	f->buf = buf + UNGET;
diff --git a/system/lib/libc/musl/src/stdio/__stdio_close.c b/system/lib/libc/musl/src/stdio/__stdio_close.c
index 9f7ee18..79452bd 100644
--- a/system/lib/libc/musl/src/stdio/__stdio_close.c
+++ b/system/lib/libc/musl/src/stdio/__stdio_close.c
@@ -1,6 +1,13 @@
 #include "stdio_impl.h"
 
+static int dummy(int fd)
+{
+	return fd;
+}
+
+weak_alias(dummy, __aio_close);
+
 int __stdio_close(FILE *f)
 {
-	return syscall(SYS_close, f->fd);
+	return syscall(SYS_close, __aio_close(f->fd));
 }
diff --git a/system/lib/libc/musl/src/stdio/__stdio_exit.c b/system/lib/libc/musl/src/stdio/__stdio_exit.c
index 716e5f7..191b445 100644
--- a/system/lib/libc/musl/src/stdio/__stdio_exit.c
+++ b/system/lib/libc/musl/src/stdio/__stdio_exit.c
@@ -16,8 +16,7 @@
 void __stdio_exit(void)
 {
 	FILE *f;
-	OFLLOCK();
-	for (f=libc.ofl_head; f; f=f->next) close_file(f);
+	for (f=*__ofl_lock(); f; f=f->next) close_file(f);
 	close_file(__stdin_used);
 	close_file(__stdout_used);
 }
diff --git a/system/lib/libc/musl/src/stdio/__stdio_read.c b/system/lib/libc/musl/src/stdio/__stdio_read.c
index 05e56f9..f8fa6d3 100644
--- a/system/lib/libc/musl/src/stdio/__stdio_read.c
+++ b/system/lib/libc/musl/src/stdio/__stdio_read.c
@@ -1,12 +1,5 @@
 #include "stdio_impl.h"
 #include <sys/uio.h>
-#include <pthread.h>
-
-static void cleanup(void *p)
-{
-	FILE *f = p;
-	if (!f->lockcount) __unlockfile(f);
-}
 
 size_t __stdio_read(FILE *f, unsigned char *buf, size_t len)
 {
@@ -16,16 +9,9 @@
 	};
 	ssize_t cnt;
 
-	if (libc.main_thread) {
-		pthread_cleanup_push(cleanup, f);
-		cnt = syscall_cp(SYS_readv, f->fd, iov, 2);
-		pthread_cleanup_pop(0);
-	} else {
-		cnt = syscall(SYS_readv, f->fd, iov, 2);
-	}
+	cnt = syscall(SYS_readv, f->fd, iov, 2);
 	if (cnt <= 0) {
 		f->flags |= F_EOF ^ ((F_ERR^F_EOF) & cnt);
-		f->rpos = f->rend = 0;
 		return cnt;
 	}
 	if (cnt <= iov[0].iov_len) return cnt;
diff --git a/system/lib/libc/musl/src/stdio/__stdio_write.c b/system/lib/libc/musl/src/stdio/__stdio_write.c
index e52e91a..d2d8947 100644
--- a/system/lib/libc/musl/src/stdio/__stdio_write.c
+++ b/system/lib/libc/musl/src/stdio/__stdio_write.c
@@ -1,12 +1,5 @@
 #include "stdio_impl.h"
 #include <sys/uio.h>
-#include <pthread.h>
-
-static void cleanup(void *p)
-{
-	FILE *f = p;
-	if (!f->lockcount) __unlockfile(f);
-}
 
 size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len)
 {
@@ -19,13 +12,7 @@
 	int iovcnt = 2;
 	ssize_t cnt;
 	for (;;) {
-		if (libc.main_thread) {
-			pthread_cleanup_push(cleanup, f);
-			cnt = syscall_cp(SYS_writev, f->fd, iov, iovcnt);
-			pthread_cleanup_pop(0);
-		} else {
-			cnt = syscall(SYS_writev, f->fd, iov, iovcnt);
-		}
+		cnt = syscall(SYS_writev, f->fd, iov, iovcnt);
 		if (cnt == rem) {
 			f->wend = f->buf + f->buf_size;
 			f->wpos = f->wbase = f->buf;
@@ -38,11 +25,8 @@
 		}
 		rem -= cnt;
 		if (cnt > iov[0].iov_len) {
-			f->wpos = f->wbase = f->buf;
 			cnt -= iov[0].iov_len;
 			iov++; iovcnt--;
-		} else if (iovcnt == 2) {
-			f->wbase += cnt;
 		}
 		iov[0].iov_base = (char *)iov[0].iov_base + cnt;
 		iov[0].iov_len -= cnt;
diff --git a/system/lib/libc/musl/src/stdio/__stdout_write.c b/system/lib/libc/musl/src/stdio/__stdout_write.c
index 200fe2c..dd1ec60 100644
--- a/system/lib/libc/musl/src/stdio/__stdout_write.c
+++ b/system/lib/libc/musl/src/stdio/__stdout_write.c
@@ -1,12 +1,11 @@
 #include "stdio_impl.h"
-#include <termios.h>
 #include <sys/ioctl.h>
 
 size_t __stdout_write(FILE *f, const unsigned char *buf, size_t len)
 {
-	struct termios tio;
+	struct winsize wsz;
 	f->write = __stdio_write;
-	if (!(f->flags & F_SVB) && __syscall(SYS_ioctl, f->fd, TCGETS, &tio))
+	if (!(f->flags & F_SVB) && __syscall(SYS_ioctl, f->fd, TIOCGWINSZ, &wsz))
 		f->lbf = -1;
 	return __stdio_write(f, buf, len);
 }
diff --git a/system/lib/libc/musl/src/stdio/__toread.c b/system/lib/libc/musl/src/stdio/__toread.c
index 52624f3..35f67b8 100644
--- a/system/lib/libc/musl/src/stdio/__toread.c
+++ b/system/lib/libc/musl/src/stdio/__toread.c
@@ -3,14 +3,14 @@
 int __toread(FILE *f)
 {
 	f->mode |= f->mode-1;
-	if (f->wpos > f->buf) f->write(f, 0, 0);
+	if (f->wpos > f->wbase) f->write(f, 0, 0);
 	f->wpos = f->wbase = f->wend = 0;
-	if (f->flags & (F_EOF|F_NORD)) {
-		if (f->flags & F_NORD) f->flags |= F_ERR;
+	if (f->flags & F_NORD) {
+		f->flags |= F_ERR;
 		return EOF;
 	}
-	f->rpos = f->rend = f->buf;
-	return 0;
+	f->rpos = f->rend = f->buf + f->buf_size;
+	return (f->flags & F_EOF) ? EOF : 0;
 }
 
 void __stdio_exit_needed(void);
diff --git a/system/lib/libc/musl/src/stdio/__uflow.c b/system/lib/libc/musl/src/stdio/__uflow.c
index e28922c..2a88bca 100644
--- a/system/lib/libc/musl/src/stdio/__uflow.c
+++ b/system/lib/libc/musl/src/stdio/__uflow.c
@@ -1,11 +1,11 @@
 #include "stdio_impl.h"
 
-/* This function will never be called if there is already data
- * buffered for reading. Thus we can get by with very few branches. */
+/* This function assumes it will never be called if there is already
+ * data buffered for reading. */
 
 int __uflow(FILE *f)
 {
 	unsigned char c;
-	if ((f->rend || !__toread(f)) && f->read(f, &c, 1)==1) return c;
+	if (!__toread(f) && f->read(f, &c, 1)==1) return c;
 	return EOF;
 }
diff --git a/system/lib/libc/musl/src/stdio/fclose.c b/system/lib/libc/musl/src/stdio/fclose.c
index 38e8a1e..d687a87 100644
--- a/system/lib/libc/musl/src/stdio/fclose.c
+++ b/system/lib/libc/musl/src/stdio/fclose.c
@@ -1,18 +1,24 @@
 #include "stdio_impl.h"
+#include "libc.h"
+
+static void dummy(FILE *f) { }
+weak_alias(dummy, __unlist_locked_file);
 
 int fclose(FILE *f)
 {
 	int r;
 	int perm;
 	
-	FFINALLOCK(f);
+	FLOCK(f);
+
+	__unlist_locked_file(f);
 
 	if (!(perm = f->flags & F_PERM)) {
-		OFLLOCK();
+		FILE **head = __ofl_lock();
 		if (f->prev) f->prev->next = f->next;
 		if (f->next) f->next->prev = f->prev;
-		if (libc.ofl_head == f) libc.ofl_head = f->next;
-		OFLUNLOCK();
+		if (*head == f) *head = f->next;
+		__ofl_unlock();
 	}
 
 	r = fflush(f);
@@ -20,6 +26,7 @@
 
 	if (f->getln_buf) free(f->getln_buf);
 	if (!perm) free(f);
-	
+	else FUNLOCK(f);
+
 	return r;
 }
diff --git a/system/lib/libc/musl/src/stdio/fflush.c b/system/lib/libc/musl/src/stdio/fflush.c
index 7bf862a..3f462c8 100644
--- a/system/lib/libc/musl/src/stdio/fflush.c
+++ b/system/lib/libc/musl/src/stdio/fflush.c
@@ -35,13 +35,12 @@
 
 	r = __stdout_used ? fflush(__stdout_used) : 0;
 
-	OFLLOCK();
-	for (f=libc.ofl_head; f; f=f->next) {
+	for (f=*__ofl_lock(); f; f=f->next) {
 		FLOCK(f);
 		if (f->wpos > f->wbase) r |= __fflush_unlocked(f);
 		FUNLOCK(f);
 	}
-	OFLUNLOCK();
+	__ofl_unlock();
 	
 	return r;
 }
diff --git a/system/lib/libc/musl/src/stdio/fgetwc.c b/system/lib/libc/musl/src/stdio/fgetwc.c
index 8626d54..e455cfe 100644
--- a/system/lib/libc/musl/src/stdio/fgetwc.c
+++ b/system/lib/libc/musl/src/stdio/fgetwc.c
@@ -1,8 +1,9 @@
 #include "stdio_impl.h"
+#include "locale_impl.h"
 #include <wchar.h>
 #include <errno.h>
 
-wint_t __fgetwc_unlocked(FILE *f)
+static wint_t __fgetwc_unlocked_internal(FILE *f)
 {
 	mbstate_t st = { 0 };
 	wchar_t wc;
@@ -10,8 +11,6 @@
 	unsigned char b;
 	size_t l;
 
-	f->mode |= f->mode+1;
-
 	/* Convert character from buffer if possible */
 	if (f->rpos < f->rend) {
 		l = mbrtowc(&wc, (void *)f->rpos, f->rend - f->rpos, &st);
@@ -39,6 +38,16 @@
 	return wc;
 }
 
+wint_t __fgetwc_unlocked(FILE *f)
+{
+	locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
+	if (f->mode <= 0) fwide(f, 1);
+	*ploc = f->locale;
+	wchar_t wc = __fgetwc_unlocked_internal(f);
+	*ploc = loc;
+	return wc;
+}
+
 wint_t fgetwc(FILE *f)
 {
 	wint_t c;
diff --git a/system/lib/libc/musl/src/stdio/fmemopen.c b/system/lib/libc/musl/src/stdio/fmemopen.c
index d784960..7c193a5 100644
--- a/system/lib/libc/musl/src/stdio/fmemopen.c
+++ b/system/lib/libc/musl/src/stdio/fmemopen.c
@@ -110,11 +110,5 @@
 
 	if (!libc.threaded) f->lock = -1;
 
-	OFLLOCK();
-	f->next = libc.ofl_head;
-	if (libc.ofl_head) libc.ofl_head->prev = f;
-	libc.ofl_head = f;
-	OFLUNLOCK();
-
-	return f;
+	return __ofl_add(f);
 }
diff --git a/system/lib/libc/musl/src/stdio/fopen.c b/system/lib/libc/musl/src/stdio/fopen.c
index da17ce8..252f082 100644
--- a/system/lib/libc/musl/src/stdio/fopen.c
+++ b/system/lib/libc/musl/src/stdio/fopen.c
@@ -18,8 +18,10 @@
 	/* Compute the flags to pass to open() */
 	flags = __fmodeflags(mode);
 
-	fd = syscall_cp(SYS_open, filename, flags|O_LARGEFILE, 0666);
+	fd = sys_open(filename, flags, 0666);
 	if (fd < 0) return 0;
+	if (flags & O_CLOEXEC)
+		__syscall(SYS_fcntl, fd, F_SETFD, FD_CLOEXEC);
 
 	f = __fdopen(fd, mode);
 	if (f) return f;
diff --git a/system/lib/libc/musl/src/stdio/fputs.c b/system/lib/libc/musl/src/stdio/fputs.c
index 4737f44..1cf344f 100644
--- a/system/lib/libc/musl/src/stdio/fputs.c
+++ b/system/lib/libc/musl/src/stdio/fputs.c
@@ -3,7 +3,8 @@
 
 int fputs(const char *restrict s, FILE *restrict f)
 {
-	return (int)fwrite(s, strlen(s), 1, f) - 1;
+	size_t l = strlen(s);
+	return (fwrite(s, 1, l, f)==l) - 1;
 }
 
 weak_alias(fputs, fputs_unlocked);
diff --git a/system/lib/libc/musl/src/stdio/fputwc.c b/system/lib/libc/musl/src/stdio/fputwc.c
index 7b621dd..789fe9c 100644
--- a/system/lib/libc/musl/src/stdio/fputwc.c
+++ b/system/lib/libc/musl/src/stdio/fputwc.c
@@ -1,4 +1,5 @@
 #include "stdio_impl.h"
+#include "locale_impl.h"
 #include <wchar.h>
 #include <limits.h>
 #include <ctype.h>
@@ -7,8 +8,10 @@
 {
 	char mbc[MB_LEN_MAX];
 	int l;
+	locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
 
-	f->mode |= f->mode+1;
+	if (f->mode <= 0) fwide(f, 1);
+	*ploc = f->locale;
 
 	if (isascii(c)) {
 		c = putc_unlocked(c, f);
@@ -20,6 +23,8 @@
 		l = wctomb(mbc, c);
 		if (l < 0 || __fwritex((void *)mbc, l, f) < l) c = WEOF;
 	}
+	if (c==WEOF) f->flags |= F_ERR;
+	*ploc = loc;
 	return c;
 }
 
diff --git a/system/lib/libc/musl/src/stdio/fputws.c b/system/lib/libc/musl/src/stdio/fputws.c
index 5723cbc..0ed02f1 100644
--- a/system/lib/libc/musl/src/stdio/fputws.c
+++ b/system/lib/libc/musl/src/stdio/fputws.c
@@ -1,23 +1,28 @@
 #include "stdio_impl.h"
+#include "locale_impl.h"
 #include <wchar.h>
 
 int fputws(const wchar_t *restrict ws, FILE *restrict f)
 {
 	unsigned char buf[BUFSIZ];
 	size_t l=0;
+	locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
 
 	FLOCK(f);
 
-	f->mode |= f->mode+1;
+	fwide(f, 1);
+	*ploc = f->locale;
 
 	while (ws && (l = wcsrtombs((void *)buf, (void*)&ws, sizeof buf, 0))+1 > 1)
 		if (__fwritex(buf, l, f) < l) {
 			FUNLOCK(f);
+			*ploc = loc;
 			return -1;
 		}
 
 	FUNLOCK(f);
 
+	*ploc = loc;
 	return l; /* 0 or -1 */
 }
 
diff --git a/system/lib/libc/musl/src/stdio/fread.c b/system/lib/libc/musl/src/stdio/fread.c
index 33a65f5..aef75f7 100644
--- a/system/lib/libc/musl/src/stdio/fread.c
+++ b/system/lib/libc/musl/src/stdio/fread.c
@@ -7,6 +7,7 @@
 {
 	unsigned char *dest = destv;
 	size_t len = size*nmemb, l = len, k;
+	if (!size) nmemb = 0;
 
 	FLOCK(f);
 
diff --git a/system/lib/libc/musl/src/stdio/ftrylockfile.c b/system/lib/libc/musl/src/stdio/ftrylockfile.c
index eef4e25..eb13c83 100644
--- a/system/lib/libc/musl/src/stdio/ftrylockfile.c
+++ b/system/lib/libc/musl/src/stdio/ftrylockfile.c
@@ -2,9 +2,26 @@
 #include "pthread_impl.h"
 #include <limits.h>
 
+void __do_orphaned_stdio_locks()
+{
+	FILE *f;
+	for (f=__pthread_self()->stdio_locks; f; f=f->next_locked)
+		a_store(&f->lock, 0x40000000);
+}
+
+void __unlist_locked_file(FILE *f)
+{
+	if (f->lockcount) {
+		if (f->next_locked) f->next_locked->prev_locked = f->prev_locked;
+		if (f->prev_locked) f->prev_locked->next_locked = f->next_locked;
+		else __pthread_self()->stdio_locks = f->next_locked;
+	}
+}
+
 int ftrylockfile(FILE *f)
 {
-	int tid = pthread_self()->tid;
+	pthread_t self = __pthread_self();
+	int tid = self->tid;
 	if (f->lock == tid) {
 		if (f->lockcount == LONG_MAX)
 			return -1;
@@ -15,5 +32,9 @@
 	if (f->lock || a_cas(&f->lock, 0, tid))
 		return -1;
 	f->lockcount = 1;
+	f->prev_locked = 0;
+	f->next_locked = self->stdio_locks;
+	if (f->next_locked) f->next_locked->prev_locked = f;
+	self->stdio_locks = f;
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/stdio/funlockfile.c b/system/lib/libc/musl/src/stdio/funlockfile.c
index f8a2a07..30a07ef 100644
--- a/system/lib/libc/musl/src/stdio/funlockfile.c
+++ b/system/lib/libc/musl/src/stdio/funlockfile.c
@@ -1,7 +1,15 @@
 #include "stdio_impl.h"
 #include "pthread_impl.h"
 
+void __unlist_locked_file(FILE *);
+
 void funlockfile(FILE *f)
 {
-	if (!--f->lockcount) __unlockfile(f);
+	if (f->lockcount == 1) {
+		__unlist_locked_file(f);
+		f->lockcount = 0;
+		__unlockfile(f);
+	} else {
+		f->lockcount--;
+	}
 }
diff --git a/system/lib/libc/musl/src/stdio/fwide.c b/system/lib/libc/musl/src/stdio/fwide.c
index 8088e7a..8410b15 100644
--- a/system/lib/libc/musl/src/stdio/fwide.c
+++ b/system/lib/libc/musl/src/stdio/fwide.c
@@ -1,13 +1,14 @@
-#include <wchar.h>
 #include "stdio_impl.h"
-
-#define SH (8*sizeof(int)-1)
-#define NORMALIZE(x) ((x)>>SH | -((-(x))>>SH))
+#include "locale_impl.h"
 
 int fwide(FILE *f, int mode)
 {
 	FLOCK(f);
-	if (!f->mode) f->mode = NORMALIZE(mode);
+	if (mode) {
+		if (!f->locale) f->locale = MB_CUR_MAX==1
+			? C_LOCALE : UTF8_LOCALE;
+		if (!f->mode) f->mode = mode>0 ? 1 : -1;
+	}
 	mode = f->mode;
 	FUNLOCK(f);
 	return mode;
diff --git a/system/lib/libc/musl/src/stdio/fwrite.c b/system/lib/libc/musl/src/stdio/fwrite.c
index 81ec271..7a567b2 100644
--- a/system/lib/libc/musl/src/stdio/fwrite.c
+++ b/system/lib/libc/musl/src/stdio/fwrite.c
@@ -13,8 +13,8 @@
 		/* Match /^(.*\n|)/ */
 		for (i=l; i && s[i-1] != '\n'; i--);
 		if (i) {
-			if (f->write(f, s, i) < i)
-				return i;
+			size_t n = f->write(f, s, i);
+			if (n < i) return n;
 			s += i;
 			l -= i;
 		}
@@ -28,6 +28,7 @@
 size_t fwrite(const void *restrict src, size_t size, size_t nmemb, FILE *restrict f)
 {
 	size_t k, l = size*nmemb;
+	if (!size) nmemb = 0;
 	FLOCK(f);
 	k = __fwritex(src, l, f);
 	FUNLOCK(f);
diff --git a/system/lib/libc/musl/src/stdio/getdelim.c b/system/lib/libc/musl/src/stdio/getdelim.c
index 26093a6..1ccd802 100644
--- a/system/lib/libc/musl/src/stdio/getdelim.c
+++ b/system/lib/libc/musl/src/stdio/getdelim.c
@@ -13,29 +13,32 @@
 	size_t i=0;
 	int c;
 
+	FLOCK(f);
+
 	if (!n || !s) {
+		f->flags |= F_ERR;
+		FUNLOCK(f);
 		errno = EINVAL;
 		return -1;
 	}
 
 	if (!*s) *n=0;
 
-	FLOCK(f);
-
 	for (;;) {
 		z = memchr(f->rpos, delim, f->rend - f->rpos);
 		k = z ? z - f->rpos + 1 : f->rend - f->rpos;
-		if (i+k >= *n) {
+		if (i+k+1 >= *n) {
 			if (k >= SIZE_MAX/2-i) goto oom;
-			*n = i+k+2;
-			if (*n < SIZE_MAX/4) *n *= 2;
-			tmp = realloc(*s, *n);
+			size_t m = i+k+2;
+			if (!z && m < SIZE_MAX/4) m += m/2;
+			tmp = realloc(*s, m);
 			if (!tmp) {
-				*n = i+k+2;
-				tmp = realloc(*s, *n);
+				m = i+k+2;
+				tmp = realloc(*s, m);
 				if (!tmp) goto oom;
 			}
 			*s = tmp;
+			*n = m;
 		}
 		memcpy(*s+i, f->rpos, k);
 		f->rpos += k;
@@ -56,6 +59,7 @@
 
 	return i;
 oom:
+	f->flags |= F_ERR;
 	FUNLOCK(f);
 	errno = ENOMEM;
 	return -1;
diff --git a/system/lib/libc/musl/src/stdio/ofl.c b/system/lib/libc/musl/src/stdio/ofl.c
new file mode 100644
index 0000000..b143999
--- /dev/null
+++ b/system/lib/libc/musl/src/stdio/ofl.c
@@ -0,0 +1,16 @@
+#include "stdio_impl.h"
+#include "libc.h"
+
+static FILE *ofl_head;
+static volatile int ofl_lock[2];
+
+FILE **__ofl_lock()
+{
+	LOCK(ofl_lock);
+	return &ofl_head;
+}
+
+void __ofl_unlock()
+{
+	UNLOCK(ofl_lock);
+}
diff --git a/system/lib/libc/musl/src/stdio/ofl_add.c b/system/lib/libc/musl/src/stdio/ofl_add.c
new file mode 100644
index 0000000..d7de9f1
--- /dev/null
+++ b/system/lib/libc/musl/src/stdio/ofl_add.c
@@ -0,0 +1,11 @@
+#include "stdio_impl.h"
+
+FILE *__ofl_add(FILE *f)
+{
+	FILE **head = __ofl_lock();
+	f->next = *head;
+	if (*head) (*head)->prev = f;
+	*head = f;
+	__ofl_unlock();
+	return f;
+}
diff --git a/system/lib/libc/musl/src/stdio/open_memstream.c b/system/lib/libc/musl/src/stdio/open_memstream.c
index 9eafdfb..eab024d 100644
--- a/system/lib/libc/musl/src/stdio/open_memstream.c
+++ b/system/lib/libc/musl/src/stdio/open_memstream.c
@@ -59,14 +59,21 @@
 {
 	FILE *f;
 	struct cookie *c;
+	char *buf;
+
 	if (!(f=malloc(sizeof *f + sizeof *c + BUFSIZ))) return 0;
+	if (!(buf=malloc(sizeof *buf))) {
+		free(f);
+		return 0;
+	}
 	memset(f, 0, sizeof *f + sizeof *c);
 	f->cookie = c = (void *)(f+1);
 
 	c->bufp = bufp;
 	c->sizep = sizep;
-	c->pos = c->len = c->space = 0;
-	c->buf = 0;
+	c->pos = c->len = c->space = *sizep = 0;
+	c->buf = *bufp = buf;
+	*buf = 0;
 
 	f->flags = F_NORD;
 	f->fd = -1;
@@ -79,11 +86,5 @@
 
 	if (!libc.threaded) f->lock = -1;
 
-	OFLLOCK();
-	f->next = libc.ofl_head;
-	if (libc.ofl_head) libc.ofl_head->prev = f;
-	libc.ofl_head = f;
-	OFLUNLOCK();
-
-	return f;
+	return __ofl_add(f);
 }
diff --git a/system/lib/libc/musl/src/stdio/open_wmemstream.c b/system/lib/libc/musl/src/stdio/open_wmemstream.c
index 3537030..4d90cd9 100644
--- a/system/lib/libc/musl/src/stdio/open_wmemstream.c
+++ b/system/lib/libc/musl/src/stdio/open_wmemstream.c
@@ -61,14 +61,21 @@
 {
 	FILE *f;
 	struct cookie *c;
+	wchar_t *buf;
+
 	if (!(f=malloc(sizeof *f + sizeof *c))) return 0;
+	if (!(buf=malloc(sizeof *buf))) {
+		free(f);
+		return 0;
+	}
 	memset(f, 0, sizeof *f + sizeof *c);
 	f->cookie = c = (void *)(f+1);
 
 	c->bufp = bufp;
 	c->sizep = sizep;
-	c->pos = c->len = c->space = 0;
-	c->buf = 0;
+	c->pos = c->len = c->space = *sizep = 0;
+	c->buf = *bufp = buf;
+	*buf = 0;
 
 	f->flags = F_NORD;
 	f->fd = -1;
@@ -81,11 +88,5 @@
 
 	if (!libc.threaded) f->lock = -1;
 
-	OFLLOCK();
-	f->next = libc.ofl_head;
-	if (libc.ofl_head) libc.ofl_head->prev = f;
-	libc.ofl_head = f;
-	OFLUNLOCK();
-
-	return f;
+	return __ofl_add(f);
 }
diff --git a/system/lib/libc/musl/src/stdio/remove.c b/system/lib/libc/musl/src/stdio/remove.c
index e147ba2..942e301 100644
--- a/system/lib/libc/musl/src/stdio/remove.c
+++ b/system/lib/libc/musl/src/stdio/remove.c
@@ -1,9 +1,19 @@
 #include <stdio.h>
 #include <errno.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int remove(const char *path)
 {
-	int r = syscall(SYS_unlink, path);
-	return (r && errno == EISDIR) ? syscall(SYS_rmdir, path) : r;
+#ifdef SYS_unlink
+	int r = __syscall(SYS_unlink, path);
+#else
+	int r = __syscall(SYS_unlinkat, AT_FDCWD, path, 0);
+#endif
+#ifdef SYS_rmdir
+	if (r==-EISDIR) r = __syscall(SYS_rmdir, path);
+#else
+	if (r==-EISDIR) r = __syscall(SYS_unlinkat, AT_FDCWD, path, AT_REMOVEDIR);
+#endif
+	return __syscall_ret(r);
 }
diff --git a/system/lib/libc/musl/src/stdio/rename.c b/system/lib/libc/musl/src/stdio/rename.c
index 97f1453..04c90c0 100644
--- a/system/lib/libc/musl/src/stdio/rename.c
+++ b/system/lib/libc/musl/src/stdio/rename.c
@@ -1,7 +1,12 @@
 #include <stdio.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int rename(const char *old, const char *new)
 {
+#ifdef SYS_rename
 	return syscall(SYS_rename, old, new);
+#else
+	return syscall(SYS_renameat, AT_FDCWD, old, AT_FDCWD, new);
+#endif
 }
diff --git a/system/lib/libc/musl/src/stdio/tempnam.c b/system/lib/libc/musl/src/stdio/tempnam.c
index f73ca9f..5a55975 100644
--- a/system/lib/libc/musl/src/stdio/tempnam.c
+++ b/system/lib/libc/musl/src/stdio/tempnam.c
@@ -1,42 +1,49 @@
 #include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <limits.h>
 #include <string.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <time.h>
-#include "libc.h"
-#include "atomic.h"
+#include "syscall.h"
 
 #define MAXTRIES 100
 
+char *__randname(char *);
+
 char *tempnam(const char *dir, const char *pfx)
 {
-	static int index;
-	char *s;
-	struct timespec ts;
-	int pid = getpid();
-	size_t l;
-	int n;
-	int try=0;
+	char s[PATH_MAX];
+	size_t l, dl, pl;
+	int try;
+	int r;
 
 	if (!dir) dir = P_tmpdir;
 	if (!pfx) pfx = "temp";
 
-	if (access(dir, R_OK|W_OK|X_OK) != 0)
-		return NULL;
+	dl = strlen(dir);
+	pl = strlen(pfx);
+	l = dl + 1 + pl + 1 + 6;
 
-	l = strlen(dir) + 1 + strlen(pfx) + 3*(sizeof(int)*3+2) + 1;
-	s = malloc(l);
-	if (!s) return s;
-
-	do {
-		clock_gettime(CLOCK_REALTIME, &ts);
-		n = ts.tv_nsec ^ (uintptr_t)&s ^ (uintptr_t)s;
-		snprintf(s, l, "%s/%s-%d-%d-%x", dir, pfx, pid, a_fetch_add(&index, 1), n);
-	} while (!access(s, F_OK) && try++<MAXTRIES);
-	if (try>=MAXTRIES) {
-		free(s);
+	if (l >= PATH_MAX) {
+		errno = ENAMETOOLONG;
 		return 0;
 	}
-	return s;
+
+	memcpy(s, dir, dl);
+	s[dl] = '/';
+	memcpy(s+dl+1, pfx, pl);
+	s[dl+1+pl] = '_';
+	s[l] = 0;
+
+	for (try=0; try<MAXTRIES; try++) {
+		__randname(s+l-6);
+#ifdef SYS_lstat
+		r = __syscall(SYS_lstat, s, &(struct stat){0});
+#else
+		r = __syscall(SYS_fstatat, AT_FDCWD, s,
+			&(struct stat){0}, AT_SYMLINK_NOFOLLOW);
+#endif
+		if (r == -ENOENT) return strdup(s);
+	}
+	return 0;
 }
diff --git a/system/lib/libc/musl/src/stdio/tmpfile.c b/system/lib/libc/musl/src/stdio/tmpfile.c
index 926d660..525090a 100644
--- a/system/lib/libc/musl/src/stdio/tmpfile.c
+++ b/system/lib/libc/musl/src/stdio/tmpfile.c
@@ -1,23 +1,28 @@
 #include <stdio.h>
 #include <fcntl.h>
-#include <unistd.h>
 #include "stdio_impl.h"
 
 #define MAXTRIES 100
 
+char *__randname(char *);
+
 FILE *tmpfile(void)
 {
-	char buf[L_tmpnam], *s;
+	char s[] = "/tmp/tmpfile_XXXXXX";
 	int fd;
 	FILE *f;
 	int try;
 	for (try=0; try<MAXTRIES; try++) {
-		s = tmpnam(buf);
-		if (!s) return 0;
-		fd = syscall(SYS_open, s, O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600);
+		__randname(s+13);
+		fd = sys_open(s, O_RDWR|O_CREAT|O_EXCL, 0600);
 		if (fd >= 0) {
-			f = __fdopen(fd, "w+");
+#ifdef SYS_unlink
 			__syscall(SYS_unlink, s);
+#else
+			__syscall(SYS_unlinkat, AT_FDCWD, s, 0);
+#endif
+			f = __fdopen(fd, "w+");
+			if (!f) __syscall(SYS_close, fd);
 			return f;
 		}
 	}
diff --git a/system/lib/libc/musl/src/stdio/tmpnam.c b/system/lib/libc/musl/src/stdio/tmpnam.c
index 2bd72b3..449eb9b 100644
--- a/system/lib/libc/musl/src/stdio/tmpnam.c
+++ b/system/lib/libc/musl/src/stdio/tmpnam.c
@@ -1,31 +1,29 @@
 #include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <time.h>
-#include "libc.h"
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <string.h>
 #include "syscall.h"
-#include "atomic.h"
 
 #define MAXTRIES 100
 
-char *tmpnam(char *s)
+char *__randname(char *);
+
+char *tmpnam(char *buf)
 {
-	static int index;
-	static char s2[L_tmpnam];
-	struct timespec ts;
-	int try = 0;
-	unsigned n;
-
-	if (!s) s = s2;
-
-	if (__syscall(SYS_access, P_tmpdir, R_OK|W_OK|X_OK) != 0)
-		return NULL;
-
-	do {
-		__syscall(SYS_clock_gettime, CLOCK_REALTIME, &ts, 0);
-		n = ts.tv_nsec ^ (uintptr_t)&s ^ (uintptr_t)s;
-		snprintf(s, L_tmpnam, "/tmp/t%x-%x", a_fetch_add(&index, 1), n);
-	} while (!__syscall(SYS_access, s, F_OK) && try++<MAXTRIES);
-	return try>=MAXTRIES ? 0 : s;
+	static char internal[L_tmpnam];
+	char s[] = "/tmp/tmpnam_XXXXXX";
+	int try;
+	int r;
+	for (try=0; try<MAXTRIES; try++) {
+		__randname(s+12);
+#ifdef SYS_lstat
+		r = __syscall(SYS_lstat, s, &(struct stat){0});
+#else
+		r = __syscall(SYS_fstatat, AT_FDCWD, s,
+			&(struct stat){0}, AT_SYMLINK_NOFOLLOW);
+#endif
+		if (r == -ENOENT) return strcpy(buf ? buf : internal, s);
+	}
+	return 0;
 }
diff --git a/system/lib/libc/musl/src/stdio/ungetc.c b/system/lib/libc/musl/src/stdio/ungetc.c
index 7f56f8d..180673a 100644
--- a/system/lib/libc/musl/src/stdio/ungetc.c
+++ b/system/lib/libc/musl/src/stdio/ungetc.c
@@ -6,7 +6,8 @@
 
 	FLOCK(f);
 
-	if ((!f->rend && __toread(f)) || f->rpos <= f->buf - UNGET) {
+	if (!f->rpos) __toread(f);
+	if (!f->rpos || f->rpos <= f->buf - UNGET) {
 		FUNLOCK(f);
 		return EOF;
 	}
diff --git a/system/lib/libc/musl/src/stdio/ungetwc.c b/system/lib/libc/musl/src/stdio/ungetwc.c
index 8cc85a6..9edf366 100644
--- a/system/lib/libc/musl/src/stdio/ungetwc.c
+++ b/system/lib/libc/musl/src/stdio/ungetwc.c
@@ -1,4 +1,5 @@
 #include "stdio_impl.h"
+#include "locale_impl.h"
 #include <wchar.h>
 #include <limits.h>
 #include <ctype.h>
@@ -7,21 +8,20 @@
 wint_t ungetwc(wint_t c, FILE *f)
 {
 	unsigned char mbc[MB_LEN_MAX];
-	int l=1;
-
-	if (c == WEOF) return c;
-
-	/* Try conversion early so we can fail without locking if invalid */
-	if (!isascii(c) && (l = wctomb((void *)mbc, c)) < 0)
-		return WEOF;
+	int l;
+	locale_t *ploc = &CURRENT_LOCALE, loc = *ploc;
 
 	FLOCK(f);
 
-	f->mode |= f->mode+1;
+	if (f->mode <= 0) fwide(f, 1);
+	*ploc = f->locale;
 
-	if ((!f->rend && __toread(f)) || f->rpos < f->buf - UNGET + l) {
+	if (!f->rpos) __toread(f);
+	if (!f->rpos || c == WEOF || (l = wcrtomb((void *)mbc, c, 0)) < 0 ||
+	    f->rpos < f->buf - UNGET + l) {
 		FUNLOCK(f);
-		return EOF;
+		*ploc = loc;
+		return WEOF;
 	}
 
 	if (isascii(c)) *--f->rpos = c;
@@ -30,5 +30,6 @@
 	f->flags &= ~F_EOF;
 
 	FUNLOCK(f);
+	*ploc = loc;
 	return c;
 }
diff --git a/system/lib/libc/musl/src/stdio/vasprintf.c b/system/lib/libc/musl/src/stdio/vasprintf.c
index 68b7246..08251bc 100644
--- a/system/lib/libc/musl/src/stdio/vasprintf.c
+++ b/system/lib/libc/musl/src/stdio/vasprintf.c
@@ -3,26 +3,13 @@
 #include <stdarg.h>
 #include <stdlib.h>
 
-#define GUESS 240U
-
 int vasprintf(char **s, const char *fmt, va_list ap)
 {
 	va_list ap2;
-	char *a;
-	int l=GUESS;
-
-	if (!(a=malloc(GUESS))) return -1;
-
 	va_copy(ap2, ap);
-	l=vsnprintf(a, GUESS, fmt, ap2);
+	int l = vsnprintf(0, 0, fmt, ap2);
 	va_end(ap2);
 
-	if (l<GUESS) {
-		char *b = realloc(a, l+1U);
-		*s = b ? b : a;
-		return l;
-	}
-	free(a);
 	if (l<0 || !(*s=malloc(l+1U))) return -1;
 	return vsnprintf(*s, l+1U, fmt, ap);
 }
diff --git a/system/lib/libc/musl/src/stdio/vfprintf.c b/system/lib/libc/musl/src/stdio/vfprintf.c
index c60919b..2ecf769 100644
--- a/system/lib/libc/musl/src/stdio/vfprintf.c
+++ b/system/lib/libc/musl/src/stdio/vfprintf.c
@@ -13,8 +13,6 @@
 
 #define MAX(a,b) ((a)>(b) ? (a) : (b))
 #define MIN(a,b) ((a)<(b) ? (a) : (b))
-#define CONCAT2(x,y) x ## y
-#define CONCAT(x,y) CONCAT2(x,y)
 
 /* Convenient bit representation for modifier flags, which all fall
  * within 31 codepoints of the space character. */
@@ -227,7 +225,7 @@
 
 	if (!isfinite(y)) {
 		char *s = (t&32)?"inf":"INF";
-		if (y!=y) s=(t&32)?"nan":"NAN", pl=0;
+		if (y!=y) s=(t&32)?"nan":"NAN";
 		pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
 		out(f, prefix, pl);
 		out(f, s, 3);
@@ -343,7 +341,7 @@
 		x = *d % i;
 		/* Are there any significant digits past j? */
 		if (x || d+1!=z) {
-			long double round = CONCAT(0x1p,LDBL_MANT_DIG);
+			long double round = 2/LDBL_EPSILON;
 			long double small;
 			if (*d/i & 1) round += 2;
 			if (x<i/2) small=0x0.8p0;
diff --git a/system/lib/libc/musl/src/stdio/vfwprintf.c b/system/lib/libc/musl/src/stdio/vfwprintf.c
index ebdff00..f9f1ecf 100644
--- a/system/lib/libc/musl/src/stdio/vfwprintf.c
+++ b/system/lib/libc/musl/src/stdio/vfwprintf.c
@@ -288,26 +288,29 @@
 			z = wmemchr(a, 0, p);
 			if (z) p=z-a;
 			if (w<p) w=p;
-			if (!(fl&LEFT_ADJ)) fprintf(f, "%.*s", w-p, "");
+			if (!(fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
 			out(f, a, p);
-			if ((fl&LEFT_ADJ)) fprintf(f, "%.*s", w-p, "");
+			if ((fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
 			l=w;
 			continue;
+		case 'm':
+			arg.p = strerror(errno);
 		case 's':
+			if (!arg.p) arg.p = "(null)";
 			bs = arg.p;
 			if (p<0) p = INT_MAX;
 			for (i=l=0; l<p && (i=mbtowc(&wc, bs, MB_LEN_MAX))>0; bs+=i, l++);
 			if (i<0) return -1;
 			p=l;
 			if (w<p) w=p;
-			if (!(fl&LEFT_ADJ)) fprintf(f, "%.*s", w-p, "");
+			if (!(fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
 			bs = arg.p;
 			while (l--) {
 				i=mbtowc(&wc, bs, MB_LEN_MAX);
 				bs+=i;
 				fputwc(wc, f);
 			}
-			if ((fl&LEFT_ADJ)) fprintf(f, "%.*s", w-p, "");
+			if ((fl&LEFT_ADJ)) fprintf(f, "%*s", w-p, "");
 			l=w;
 			continue;
 		}
@@ -356,7 +359,7 @@
 	}
 
 	FLOCK(f);
-	f->mode |= f->mode+1;
+	fwide(f, 1);
 	olderr = f->flags & F_ERR;
 	f->flags &= ~F_ERR;
 	ret = wprintf_core(f, fmt, &ap2, nl_arg, nl_type);
diff --git a/system/lib/libc/musl/src/stdio/vfwscanf.c b/system/lib/libc/musl/src/stdio/vfwscanf.c
index ac5c2c2..223aad4 100644
--- a/system/lib/libc/musl/src/stdio/vfwscanf.c
+++ b/system/lib/libc/musl/src/stdio/vfwscanf.c
@@ -104,7 +104,7 @@
 
 	FLOCK(f);
 
-	f->mode |= f->mode+1;
+	fwide(f, 1);
 
 	for (p=fmt; *p; p++) {
 
diff --git a/system/lib/libc/musl/src/string/memmem.c b/system/lib/libc/musl/src/string/memmem.c
index d7e1221..4be6a31 100644
--- a/system/lib/libc/musl/src/string/memmem.c
+++ b/system/lib/libc/musl/src/string/memmem.c
@@ -140,6 +140,7 @@
 	h = memchr(h0, *n, k);
 	if (!h || l==1) return (void *)h;
 	k -= h - (const unsigned char *)h0;
+	if (k<l) return 0;
 	if (l==2) return twobyte_memmem(h, k, n);
 	if (l==3) return threebyte_memmem(h, k, n);
 	if (l==4) return fourbyte_memmem(h, k, n);
diff --git a/system/lib/libc/musl/src/string/strcasecmp.c b/system/lib/libc/musl/src/string/strcasecmp.c
index 02fd5f8..3cd5f2d 100644
--- a/system/lib/libc/musl/src/string/strcasecmp.c
+++ b/system/lib/libc/musl/src/string/strcasecmp.c
@@ -1,5 +1,6 @@
 #include <strings.h>
 #include <ctype.h>
+#include "libc.h"
 
 int strcasecmp(const char *_l, const char *_r)
 {
@@ -7,3 +8,10 @@
 	for (; *l && *r && (*l == *r || tolower(*l) == tolower(*r)); l++, r++);
 	return tolower(*l) - tolower(*r);
 }
+
+int __strcasecmp_l(const char *l, const char *r, locale_t loc)
+{
+	return strcasecmp(l, r);
+}
+
+weak_alias(__strcasecmp_l, strcasecmp_l);
diff --git a/system/lib/libc/musl/src/string/strncasecmp.c b/system/lib/libc/musl/src/string/strncasecmp.c
index 2465972..3af5300 100644
--- a/system/lib/libc/musl/src/string/strncasecmp.c
+++ b/system/lib/libc/musl/src/string/strncasecmp.c
@@ -1,5 +1,6 @@
 #include <strings.h>
 #include <ctype.h>
+#include "libc.h"
 
 int strncasecmp(const char *_l, const char *_r, size_t n)
 {
@@ -8,3 +9,10 @@
 	for (; *l && *r && n && (*l == *r || tolower(*l) == tolower(*r)); l++, r++, n--);
 	return tolower(*l) - tolower(*r);
 }
+
+int __strncasecmp_l(const char *l, const char *r, size_t n, locale_t loc)
+{
+	return strncasecmp(l, r, n);
+}
+
+weak_alias(__strncasecmp_l, strncasecmp_l);
diff --git a/system/lib/libc/musl/src/string/strsignal.c b/system/lib/libc/musl/src/string/strsignal.c
index 905c095..96bfe84 100644
--- a/system/lib/libc/musl/src/string/strsignal.c
+++ b/system/lib/libc/musl/src/string/strsignal.c
@@ -1,5 +1,6 @@
 #include <signal.h>
 #include <string.h>
+#include "locale_impl.h"
 
 #if (SIGHUP == 1) && (SIGINT == 2) && (SIGQUIT == 3) && (SIGILL == 4) \
  && (SIGTRAP == 5) && (SIGABRT == 6) && (SIGBUS == 7) && (SIGFPE == 8) \
@@ -104,12 +105,12 @@
 
 char *strsignal(int signum)
 {
-	char *s = (char *)strings;
+	const char *s = strings;
 
 	signum = sigmap(signum);
 	if (signum - 1U >= _NSIG-1) signum = 0;
 
 	for (; signum--; s++) for (; *s; s++);
 
-	return s;
+	return (char *)LCTRANS_CUR(s);
 }
diff --git a/system/lib/libc/musl/src/string/strverscmp.c b/system/lib/libc/musl/src/string/strverscmp.c
index 6f37cc6..4daf276 100644
--- a/system/lib/libc/musl/src/string/strverscmp.c
+++ b/system/lib/libc/musl/src/string/strverscmp.c
@@ -2,40 +2,33 @@
 #include <ctype.h>
 #include <string.h>
 
-int strverscmp(const char *l, const char *r)
+int strverscmp(const char *l0, const char *r0)
 {
-	int haszero=1;
-	while (*l==*r) {
-		if (!*l) return 0;
+	const unsigned char *l = (const void *)l0;
+	const unsigned char *r = (const void *)r0;
+	size_t i, dp, j;
+	int z = 1;
 
-		if (*l=='0') {
-			if (haszero==1) {
-				haszero=0;
-			}
-		} else if (isdigit(*l)) {
-			if (haszero==1) {
-				haszero=2;
-			}
-		} else {
-			haszero=1;
-		}
-		l++; r++;
+	/* Find maximal matching prefix and track its maximal digit
+	 * suffix and whether those digits are all zeros. */
+	for (dp=i=0; l[i]==r[i]; i++) {
+		int c = l[i];
+		if (!c) return 0;
+		if (!isdigit(c)) dp=i+1, z=1;
+		else if (c!='0') z=0;
 	}
-	if (haszero==1 && (*l=='0' || *r=='0')) {
-		haszero=0;
+
+	if (l[dp]!='0' && r[dp]!='0') {
+		/* If we're not looking at a digit sequence that began
+		 * with a zero, longest digit string is greater. */
+		for (j=i; isdigit(l[j]); j++)
+			if (!isdigit(r[j])) return 1;
+		if (isdigit(r[j])) return -1;
+	} else if (z && dp<i && (isdigit(l[i]) || isdigit(r[i]))) {
+		/* Otherwise, if common prefix of digit sequence is
+		 * all zeros, digits order less than non-digits. */
+		return (unsigned char)(l[i]-'0') - (unsigned char)(r[i]-'0');
 	}
-	if ((isdigit(*l) && isdigit(*r) ) && haszero) {
-		size_t lenl=0, lenr=0;
-		while (isdigit(l[lenl]) ) lenl++;
-		while (isdigit(r[lenr]) ) lenr++;
-		if (lenl==lenr) {
-			return (*l -  *r);
-		} else if (lenl>lenr) {
-			return 1;
-		} else {
-			return -1;
-		}
-	} else {
-		return (*l -  *r);
-	}
+
+	return l[i] - r[i];
 }
diff --git a/system/lib/libc/musl/src/temp/mkostemps.c b/system/lib/libc/musl/src/temp/mkostemps.c
index 7f8492a..512b5f1 100644
--- a/system/lib/libc/musl/src/temp/mkostemps.c
+++ b/system/lib/libc/musl/src/temp/mkostemps.c
@@ -15,6 +15,7 @@
 		return -1;
 	}
 
+	flags -= flags & O_ACCMODE;
 	int fd, retries = 100;
 	do {
 		__randname(template+l-len-6);
diff --git a/system/lib/libc/musl/src/termios/cfgetospeed.c b/system/lib/libc/musl/src/termios/cfgetospeed.c
index 0ebc198..55fa6f5 100644
--- a/system/lib/libc/musl/src/termios/cfgetospeed.c
+++ b/system/lib/libc/musl/src/termios/cfgetospeed.c
@@ -1,3 +1,4 @@
+#define _BSD_SOURCE
 #include <termios.h>
 #include <sys/ioctl.h>
 
diff --git a/system/lib/libc/musl/src/termios/cfsetospeed.c b/system/lib/libc/musl/src/termios/cfsetospeed.c
index 80c790f..b571f62 100644
--- a/system/lib/libc/musl/src/termios/cfsetospeed.c
+++ b/system/lib/libc/musl/src/termios/cfsetospeed.c
@@ -1,3 +1,4 @@
+#define _BSD_SOURCE
 #include <termios.h>
 #include <sys/ioctl.h>
 #include <errno.h>
diff --git a/system/lib/libc/musl/src/thread/__syscall_cp.c b/system/lib/libc/musl/src/thread/__syscall_cp.c
new file mode 100644
index 0000000..09a2be8
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/__syscall_cp.c
@@ -0,0 +1,21 @@
+#include "pthread_impl.h"
+#include "syscall.h"
+
+__attribute__((__visibility__("hidden")))
+long __syscall_cp_c();
+
+static long sccp(syscall_arg_t nr,
+                 syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
+                 syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
+{
+	return (__syscall)(nr, u, v, w, x, y, z);
+}
+
+weak_alias(sccp, __syscall_cp_c);
+
+long (__syscall_cp)(syscall_arg_t nr,
+                    syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
+                    syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
+{
+	return __syscall_cp_c(nr, u, v, w, x, y, z);
+}
diff --git a/system/lib/libc/musl/src/thread/__timedwait.c b/system/lib/libc/musl/src/thread/__timedwait.c
index d2a819f..9dae9e9 100644
--- a/system/lib/libc/musl/src/thread/__timedwait.c
+++ b/system/lib/libc/musl/src/thread/__timedwait.c
@@ -10,21 +10,28 @@
 #include "futex.h"
 #endif
 #include "syscall.h"
+#include "pthread_impl.h"
+
+int __pthread_setcancelstate(int, int *);
+int __clock_gettime(clockid_t, struct timespec *);
+
 
 #ifdef __EMSCRIPTEN__
 double _pthread_msecs_until(const struct timespec *restrict at);
 int _pthread_isduecanceled(struct pthread *pthread_ptr);
 #endif
 
-static int do_wait(volatile int *addr, int val,
+int __timedwait_cp(volatile int *addr, int val,
 	clockid_t clk, const struct timespec *at, int priv)
 {
 	int r;
 	struct timespec to, *top=0;
 
+	if (priv) priv = 128;
+
 	if (at) {
 		if (at->tv_nsec >= 1000000000UL) return EINVAL;
-		if (clock_gettime(clk, &to)) return EINVAL;
+		if (__clock_gettime(clk, &to)) return EINVAL;
 		to.tv_sec = at->tv_sec - to.tv_sec;
 		if ((to.tv_nsec = at->tv_nsec - to.tv_nsec) < 0) {
 			to.tv_sec--;
@@ -62,31 +69,27 @@
 		r = -emscripten_futex_wait((void*)addr, val, waitMsecs);
 	}
 #else
-	r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT, val, top);
+	r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT|priv, val, top);
+	if (r == ENOSYS) r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT, val, top);
 #endif
-	if (r == EINTR || r == EINVAL || r == ETIMEDOUT) return r;
-	return 0;
+	if (r != EINTR && r != ETIMEDOUT && r != ECANCELED) r = 0;
+
+	return r;
 }
 
 int __timedwait(volatile int *addr, int val,
-	clockid_t clk, const struct timespec *at,
-	void (*cleanup)(void *), void *arg, int priv)
+	clockid_t clk, const struct timespec *at, int priv)
 {
-	int r, cs;
-
-	if (!cleanup) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
-	pthread_cleanup_push(cleanup, arg);
-
+	int cs, r;
+	__pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
 #ifdef __EMSCRIPTEN__
 	emscripten_conditional_set_current_thread_status(EM_THREAD_STATUS_RUNNING, EM_THREAD_STATUS_WAITMUTEX);
 #endif
-	r = do_wait(addr, val, clk, at, priv);
+	r = __timedwait_cp(addr, val, clk, at, priv);
 #ifdef __EMSCRIPTEN__
 	emscripten_conditional_set_current_thread_status(EM_THREAD_STATUS_WAITMUTEX, EM_THREAD_STATUS_RUNNING);
 #endif
-
-	pthread_cleanup_pop(0);
-	if (!cleanup) pthread_setcancelstate(cs, 0);
+	__pthread_setcancelstate(cs, 0);
 
 	return r;
 }
diff --git a/system/lib/libc/musl/src/thread/__tls_get_addr.c b/system/lib/libc/musl/src/thread/__tls_get_addr.c
new file mode 100644
index 0000000..6945faa
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/__tls_get_addr.c
@@ -0,0 +1,16 @@
+#include <stddef.h>
+#include "pthread_impl.h"
+#include "libc.h"
+
+__attribute__((__visibility__("hidden")))
+void *__tls_get_new(size_t *);
+
+void *__tls_get_addr(size_t *v)
+{
+	pthread_t self = __pthread_self();
+	if (v[0]<=(size_t)self->dtv[0])
+		return (char *)self->dtv[v[0]]+v[1]+DTP_OFFSET;
+	return __tls_get_new(v);
+}
+
+weak_alias(__tls_get_addr, __tls_get_new);
diff --git a/system/lib/libc/musl/src/thread/__unmapself.c b/system/lib/libc/musl/src/thread/__unmapself.c
index e69de29..1d3bee1 100644
--- a/system/lib/libc/musl/src/thread/__unmapself.c
+++ b/system/lib/libc/musl/src/thread/__unmapself.c
@@ -0,0 +1,29 @@
+#include "pthread_impl.h"
+#include "atomic.h"
+#include "syscall.h"
+/* cheat and reuse CRTJMP macro from dynlink code */
+#include "dynlink.h"
+
+static volatile int lock;
+static void *unmap_base;
+static size_t unmap_size;
+static char shared_stack[256];
+
+static void do_unmap()
+{
+	__syscall(SYS_munmap, unmap_base, unmap_size);
+	__syscall(SYS_exit);
+}
+
+void __unmapself(void *base, size_t size)
+{
+	int tid=__pthread_self()->tid;
+	char *stack = shared_stack + sizeof shared_stack;
+	stack -= (uintptr_t)stack % 16;
+	while (lock || a_cas(&lock, 0, tid))
+		a_spin();
+	__syscall(SYS_set_tid_address, &lock);
+	unmap_base = base;
+	unmap_size = size;
+	CRTJMP(do_unmap, stack);
+}
diff --git a/system/lib/libc/musl/src/thread/__wait.c b/system/lib/libc/musl/src/thread/__wait.c
index 59a8a6f..265451d 100644
--- a/system/lib/libc/musl/src/thread/__wait.c
+++ b/system/lib/libc/musl/src/thread/__wait.c
@@ -8,9 +8,9 @@
 
 void __wait(volatile int *addr, volatile int *waiters, int val, int priv)
 {
-	int spins=10000;
-	if (priv) priv = 128; priv=0;
-	while (spins--) {
+	int spins=100;
+	if (priv) priv = FUTEX_PRIVATE;
+	while (spins-- && (!waiters || !*waiters)) {
 		if (*addr==val) a_spin();
 		else return;
 	}
@@ -39,7 +39,8 @@
 	}
 #else
 	while (*addr==val) {
-		__syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0);
+		__syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS
+		|| __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0);
 	}
 #endif
 	if (waiters) a_dec(waiters);
diff --git a/system/lib/libc/musl/src/thread/call_once.c b/system/lib/libc/musl/src/thread/call_once.c
new file mode 100644
index 0000000..a7bc935
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/call_once.c
@@ -0,0 +1,8 @@
+#include <threads.h>
+
+int __pthread_once(once_flag *, void (*)(void));
+
+void call_once(once_flag *flag, void (*func)(void))
+{
+	__pthread_once(flag, func);
+}
diff --git a/system/lib/libc/musl/src/thread/cnd_broadcast.c b/system/lib/libc/musl/src/thread/cnd_broadcast.c
new file mode 100644
index 0000000..85d4d3e
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/cnd_broadcast.c
@@ -0,0 +1,10 @@
+#include <threads.h>
+
+int __private_cond_signal(cnd_t *, int);
+
+int cnd_broadcast(cnd_t *c)
+{
+	/* This internal function never fails, and always returns zero,
+	 * which matches the value thrd_success is defined with. */
+	return __private_cond_signal(c, -1);
+}
diff --git a/system/lib/libc/musl/src/thread/cnd_destroy.c b/system/lib/libc/musl/src/thread/cnd_destroy.c
new file mode 100644
index 0000000..453c90b
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/cnd_destroy.c
@@ -0,0 +1,6 @@
+#include <threads.h>
+
+void cnd_destroy(cnd_t *c)
+{
+	/* For private cv this is a no-op */
+}
diff --git a/system/lib/libc/musl/src/thread/cnd_init.c b/system/lib/libc/musl/src/thread/cnd_init.c
new file mode 100644
index 0000000..18c5085
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/cnd_init.c
@@ -0,0 +1,7 @@
+#include <threads.h>
+
+int cnd_init(cnd_t *c)
+{
+	*c = (cnd_t){ 0 };
+	return thrd_success;
+}
diff --git a/system/lib/libc/musl/src/thread/cnd_signal.c b/system/lib/libc/musl/src/thread/cnd_signal.c
new file mode 100644
index 0000000..1211260
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/cnd_signal.c
@@ -0,0 +1,10 @@
+#include <threads.h>
+
+int __private_cond_signal(cnd_t *, int);
+
+int cnd_signal(cnd_t *c)
+{
+	/* This internal function never fails, and always returns zero,
+	 * which matches the value thrd_success is defined with. */
+	return __private_cond_signal(c, 1);
+}
diff --git a/system/lib/libc/musl/src/thread/cnd_timedwait.c b/system/lib/libc/musl/src/thread/cnd_timedwait.c
new file mode 100644
index 0000000..5997679
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/cnd_timedwait.c
@@ -0,0 +1,15 @@
+#include <threads.h>
+#include <errno.h>
+
+int __pthread_cond_timedwait(cnd_t *restrict, mtx_t *restrict, const struct timespec *restrict);
+
+int cnd_timedwait(cnd_t *restrict c, mtx_t *restrict m, const struct timespec *restrict ts)
+{
+	int ret = __pthread_cond_timedwait(c, m, ts);
+	switch (ret) {
+	/* May also return EINVAL or EPERM. */
+	default:        return thrd_error;
+	case 0:         return thrd_success;
+	case ETIMEDOUT: return thrd_timedout;
+	}
+}
diff --git a/system/lib/libc/musl/src/thread/cnd_wait.c b/system/lib/libc/musl/src/thread/cnd_wait.c
new file mode 100644
index 0000000..602796f
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/cnd_wait.c
@@ -0,0 +1,9 @@
+#include <threads.h>
+
+int cnd_wait(cnd_t *c, mtx_t *m)
+{
+	/* Calling cnd_timedwait with a null pointer is an extension.
+	 * It is convenient here to avoid duplication of the logic
+	 * for return values. */
+	return cnd_timedwait(c, m, 0);
+}
diff --git a/system/lib/libc/musl/src/thread/mtx_destroy.c b/system/lib/libc/musl/src/thread/mtx_destroy.c
new file mode 100644
index 0000000..40a0899
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/mtx_destroy.c
@@ -0,0 +1,5 @@
+#include <threads.h>
+
+void mtx_destroy(mtx_t *mtx)
+{
+}
diff --git a/system/lib/libc/musl/src/thread/mtx_init.c b/system/lib/libc/musl/src/thread/mtx_init.c
new file mode 100644
index 0000000..4826f76
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/mtx_init.c
@@ -0,0 +1,10 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int mtx_init(mtx_t *m, int type)
+{
+	*m = (mtx_t){
+		._m_type = ((type&mtx_recursive) ? PTHREAD_MUTEX_RECURSIVE : PTHREAD_MUTEX_NORMAL),
+	};
+	return thrd_success;
+}
diff --git a/system/lib/libc/musl/src/thread/mtx_lock.c b/system/lib/libc/musl/src/thread/mtx_lock.c
new file mode 100644
index 0000000..5c2415c
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/mtx_lock.c
@@ -0,0 +1,12 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int mtx_lock(mtx_t *m)
+{
+	if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY))
+		return thrd_success;
+	/* Calling mtx_timedlock with a null pointer is an extension.
+	 * It is convenient, here to avoid duplication of the logic
+	 * for return values. */
+	return mtx_timedlock(m, 0);
+}
diff --git a/system/lib/libc/musl/src/thread/mtx_timedlock.c b/system/lib/libc/musl/src/thread/mtx_timedlock.c
new file mode 100644
index 0000000..bcc152c
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/mtx_timedlock.c
@@ -0,0 +1,14 @@
+#include <threads.h>
+#include <errno.h>
+
+int __pthread_mutex_timedlock(mtx_t *restrict, const struct timespec *restrict);
+
+int mtx_timedlock(mtx_t *restrict m, const struct timespec *restrict ts)
+{
+	int ret = __pthread_mutex_timedlock(m, ts);
+	switch (ret) {
+	default:        return thrd_error;
+	case 0:         return thrd_success;
+	case ETIMEDOUT: return thrd_timedout;
+	}
+}
diff --git a/system/lib/libc/musl/src/thread/mtx_trylock.c b/system/lib/libc/musl/src/thread/mtx_trylock.c
new file mode 100644
index 0000000..61e7694
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/mtx_trylock.c
@@ -0,0 +1,17 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int __pthread_mutex_trylock(mtx_t *);
+
+int mtx_trylock(mtx_t *m)
+{
+	if (m->_m_type == PTHREAD_MUTEX_NORMAL)
+		return (a_cas(&m->_m_lock, 0, EBUSY) & EBUSY) ? thrd_busy : thrd_success;
+
+	int ret = __pthread_mutex_trylock(m);
+	switch (ret) {
+	default:    return thrd_error;
+	case 0:     return thrd_success;
+	case EBUSY: return thrd_busy;
+	}
+}
diff --git a/system/lib/libc/musl/src/thread/mtx_unlock.c b/system/lib/libc/musl/src/thread/mtx_unlock.c
new file mode 100644
index 0000000..5033ace
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/mtx_unlock.c
@@ -0,0 +1,11 @@
+#include <threads.h>
+
+int __pthread_mutex_unlock(mtx_t *);
+
+int mtx_unlock(mtx_t *mtx)
+{
+	/* The only cases where pthread_mutex_unlock can return an
+	 * error are undefined behavior for C11 mtx_unlock, so we can
+	 * assume it does not return an error and simply tail call. */
+	return __pthread_mutex_unlock(mtx);
+}
diff --git a/system/lib/libc/musl/src/thread/pthread_atfork.c b/system/lib/libc/musl/src/thread/pthread_atfork.c
index 95fce20..a40d7f6 100644
--- a/system/lib/libc/musl/src/thread/pthread_atfork.c
+++ b/system/lib/libc/musl/src/thread/pthread_atfork.c
@@ -8,7 +8,7 @@
 	struct atfork_funcs *prev, *next;
 } *funcs;
 
-static int lock[2];
+static volatile int lock[2];
 
 void __fork_handler(int who)
 {
diff --git a/system/lib/libc/musl/src/thread/pthread_attr_get.c b/system/lib/libc/musl/src/thread/pthread_attr_get.c
index 3bba19a..e2a44d1 100644
--- a/system/lib/libc/musl/src/thread/pthread_attr_get.c
+++ b/system/lib/libc/musl/src/thread/pthread_attr_get.c
@@ -79,7 +79,7 @@
 }
 int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict a, int *restrict pshared)
 {
-	*pshared = a->__attr>>31;
+	*pshared = a->__attr / 128U % 2;
 	return 0;
 }
 
diff --git a/system/lib/libc/musl/src/thread/pthread_barrier_destroy.c b/system/lib/libc/musl/src/thread/pthread_barrier_destroy.c
index e0da197..4ce0b2e 100644
--- a/system/lib/libc/musl/src/thread/pthread_barrier_destroy.c
+++ b/system/lib/libc/musl/src/thread/pthread_barrier_destroy.c
@@ -1,7 +1,5 @@
 #include "pthread_impl.h"
 
-void __vm_lock(int), __vm_unlock(void);
-
 int pthread_barrier_destroy(pthread_barrier_t *b)
 {
 	if (b->_b_limit < 0) {
@@ -11,8 +9,7 @@
 			while ((v = b->_b_lock) & INT_MAX)
 				__wait(&b->_b_lock, 0, v, 0);
 		}
-		__vm_lock(-1);
-		__vm_unlock();
+		__vm_wait();
 	}
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_barrier_wait.c b/system/lib/libc/musl/src/thread/pthread_barrier_wait.c
index 0018f63..46b8566 100644
--- a/system/lib/libc/musl/src/thread/pthread_barrier_wait.c
+++ b/system/lib/libc/musl/src/thread/pthread_barrier_wait.c
@@ -4,9 +4,6 @@
 
 #include "pthread_impl.h"
 
-void __vm_lock_impl(int);
-void __vm_unlock_impl(void);
-
 static int pshared_barrier_wait(pthread_barrier_t *b)
 {
 	int limit = (b->_b_limit & INT_MAX) + 1;
@@ -30,7 +27,7 @@
 			__wait(&b->_b_count, &b->_b_waiters2, v, 0);
 	}
 
-	__vm_lock_impl(+1);
+	__vm_lock();
 
 	/* Ensure all threads have a vm lock before proceeding */
 	if (a_fetch_add(&b->_b_count, -1)==1-limit) {
@@ -51,17 +48,17 @@
 	if (v==INT_MIN+1 || (v==1 && w))
 		__wake(&b->_b_lock, 1, 0);
 
-	__vm_unlock_impl();
+	__vm_unlock();
 
 	return ret;
 }
 
 struct instance
 {
-	int count;
-	int last;
-	int waiters;
-	int finished;
+	volatile int count;
+	volatile int last;
+	volatile int waiters;
+	volatile int finished;
 };
 
 int pthread_barrier_wait(pthread_barrier_t *b)
@@ -83,7 +80,7 @@
 	/* First thread to enter the barrier becomes the "instance owner" */
 	if (!inst) {
 		struct instance new_inst = { 0 };
-		int spins = 10000;
+		int spins = 200;
 		b->_b_inst = inst = &new_inst;
 		a_store(&b->_b_lock, 0);
 		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
@@ -94,7 +91,8 @@
 #ifdef __EMSCRIPTEN__
 			emscripten_futex_wait(&inst->finished, 1, INFINITY);
 #else
-			__syscall(SYS_futex, &inst->finished, FUTEX_WAIT,1,0);
+			__syscall(SYS_futex,&inst->finished,FUTEX_WAIT|128,1,0) != -ENOSYS
+			|| __syscall(SYS_futex,&inst->finished,FUTEX_WAIT,1,0);
 #endif
 		}
 		return PTHREAD_BARRIER_SERIAL_THREAD;
diff --git a/system/lib/libc/musl/src/thread/pthread_cancel.c b/system/lib/libc/musl/src/thread/pthread_cancel.c
new file mode 100644
index 0000000..3d22922
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/pthread_cancel.c
@@ -0,0 +1,97 @@
+#define _GNU_SOURCE
+#include <string.h>
+#include "pthread_impl.h"
+#include "syscall.h"
+#include "libc.h"
+
+__attribute__((__visibility__("hidden")))
+long __cancel(), __syscall_cp_asm(), __syscall_cp_c();
+
+long __cancel()
+{
+	pthread_t self = __pthread_self();
+	if (self->canceldisable == PTHREAD_CANCEL_ENABLE || self->cancelasync)
+		pthread_exit(PTHREAD_CANCELED);
+	self->canceldisable = PTHREAD_CANCEL_DISABLE;
+	return -ECANCELED;
+}
+
+long __syscall_cp_asm(volatile void *, syscall_arg_t,
+                      syscall_arg_t, syscall_arg_t, syscall_arg_t,
+                      syscall_arg_t, syscall_arg_t, syscall_arg_t);
+
+long __syscall_cp_c(syscall_arg_t nr,
+                    syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
+                    syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
+{
+	pthread_t self;
+	long r;
+	int st;
+
+	if ((st=(self=__pthread_self())->canceldisable)
+	    && (st==PTHREAD_CANCEL_DISABLE || nr==SYS_close))
+		return __syscall(nr, u, v, w, x, y, z);
+
+	r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z);
+	if (r==-EINTR && nr!=SYS_close && self->cancel &&
+	    self->canceldisable != PTHREAD_CANCEL_DISABLE)
+		r = __cancel();
+	return r;
+}
+
+static void _sigaddset(sigset_t *set, int sig)
+{
+	unsigned s = sig-1;
+	set->__bits[s/8/sizeof *set->__bits] |= 1UL<<(s&8*sizeof *set->__bits-1);
+}
+
+__attribute__((__visibility__("hidden")))
+extern const char __cp_begin[1], __cp_end[1], __cp_cancel[1];
+
+static void cancel_handler(int sig, siginfo_t *si, void *ctx)
+{
+	pthread_t self = __pthread_self();
+	ucontext_t *uc = ctx;
+	uintptr_t pc = uc->uc_mcontext.MC_PC;
+
+	a_barrier();
+	if (!self->cancel || self->canceldisable == PTHREAD_CANCEL_DISABLE) return;
+
+	_sigaddset(&uc->uc_sigmask, SIGCANCEL);
+
+	if (self->cancelasync || pc >= (uintptr_t)__cp_begin && pc < (uintptr_t)__cp_end) {
+		uc->uc_mcontext.MC_PC = (uintptr_t)__cp_cancel;
+		return;
+	}
+
+	__syscall(SYS_tkill, self->tid, SIGCANCEL);
+}
+
+void __testcancel()
+{
+	pthread_t self = __pthread_self();
+	if (self->cancel && !self->canceldisable)
+		__cancel();
+}
+
+static void init_cancellation()
+{
+	struct sigaction sa = {
+		.sa_flags = SA_SIGINFO | SA_RESTART,
+		.sa_sigaction = cancel_handler
+	};
+	memset(&sa.sa_mask, -1, _NSIG/8);
+	__libc_sigaction(SIGCANCEL, &sa, 0);
+}
+
+int pthread_cancel(pthread_t t)
+{
+	static int init;
+	if (!init) {
+		init_cancellation();
+		init = 1;
+	}
+	a_store(&t->cancel, 1);
+	if (t == pthread_self() && !t->cancelasync) return 0;
+	return pthread_kill(t, SIGCANCEL);
+}
diff --git a/system/lib/libc/musl/src/thread/pthread_cleanup_push.c b/system/lib/libc/musl/src/thread/pthread_cleanup_push.c
new file mode 100644
index 0000000..9b21764
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/pthread_cleanup_push.c
@@ -0,0 +1,20 @@
+#include "pthread_impl.h"
+
+static void dummy(struct __ptcb *cb)
+{
+}
+weak_alias(dummy, __do_cleanup_push);
+weak_alias(dummy, __do_cleanup_pop);
+
+void _pthread_cleanup_push(struct __ptcb *cb, void (*f)(void *), void *x)
+{
+	cb->__f = f;
+	cb->__x = x;
+	__do_cleanup_push(cb);
+}
+
+void _pthread_cleanup_pop(struct __ptcb *cb, int run)
+{
+	__do_cleanup_pop(cb);
+	if (run) cb->__f(cb->__x);
+}
diff --git a/system/lib/libc/musl/src/thread/pthread_cond_broadcast.c b/system/lib/libc/musl/src/thread/pthread_cond_broadcast.c
index e7c0667..69f840f 100644
--- a/system/lib/libc/musl/src/thread/pthread_cond_broadcast.c
+++ b/system/lib/libc/musl/src/thread/pthread_cond_broadcast.c
@@ -1,56 +1,12 @@
 #include "pthread_impl.h"
 
+int __private_cond_signal(pthread_cond_t *, int);
+
 int pthread_cond_broadcast(pthread_cond_t *c)
 {
-	pthread_mutex_t *m;
-
+	if (!c->_c_shared) return __private_cond_signal(c, -1);
 	if (!c->_c_waiters) return 0;
-
 	a_inc(&c->_c_seq);
-
-#ifdef __EMSCRIPTEN__
-	// XXX Emscripten: TODO: This is suboptimal but works naively correctly for now. The Emscripten-specific code path below
-	// has a bug and does not work for some reason. Figure it out and remove this code block.
 	__wake(&c->_c_seq, -1, 0);
 	return 0;
-#endif
-
-	/* If cond var is process-shared, simply wake all waiters. */
-	if (c->_c_mutex == (void *)-1) {
-		__wake(&c->_c_seq, -1, 0);
-		return 0;
-	}
-
-	/* Block waiters from returning so we can use the mutex. */
-	while (a_swap(&c->_c_lock, 1))
-		__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
-	if (!c->_c_waiters)
-		goto out;
-	m = c->_c_mutex;
-
-	/* Move waiter count to the mutex */
-	a_fetch_add(&m->_m_waiters, c->_c_waiters2);
-	c->_c_waiters2 = 0;
-
-#ifdef __EMSCRIPTEN__
-	int futexResult;
-	do {
-		// We want to wake one and requeue all others, without comparing the value, but SAB spec doesn't
-		// have requeue without comparing, so implement it by spinning instead.
-		futexResult = emscripten_futex_wake_or_requeue(&c->_c_seq, !m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid,
-			&m->_m_lock, c->_c_seq);
-	} while(futexResult == -EAGAIN);
-#else
-	/* Perform the futex requeue, waking one waiter unless we know
-	 * that the calling thread holds the mutex. */
-	__syscall(SYS_futex, &c->_c_seq, FUTEX_REQUEUE,
-		!m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid,
-		INT_MAX, &m->_m_lock);
-#endif
-
-out:
-	a_store(&c->_c_lock, 0);
-	if (c->_c_lockwait) __wake(&c->_c_lock, 1, 0);
-
-	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_cond_destroy.c b/system/lib/libc/musl/src/thread/pthread_cond_destroy.c
index a096c55..8c55516 100644
--- a/system/lib/libc/musl/src/thread/pthread_cond_destroy.c
+++ b/system/lib/libc/musl/src/thread/pthread_cond_destroy.c
@@ -2,12 +2,13 @@
 
 int pthread_cond_destroy(pthread_cond_t *c)
 {
-	int priv = c->_c_mutex != (void *)-1;
-	int cnt;
-	c->_c_destroy = 1;
-	if (c->_c_waiters)
-		__wake(&c->_c_seq, -1, priv);
-	while ((cnt = c->_c_waiters))
-		__wait(&c->_c_waiters, 0, cnt, priv);
+	if (c->_c_shared && c->_c_waiters) {
+		int cnt;
+		a_or(&c->_c_waiters, 0x80000000);
+		a_inc(&c->_c_seq);
+		__wake(&c->_c_seq, -1, 0);
+		while ((cnt = c->_c_waiters) & 0x7fffffff)
+			__wait(&c->_c_waiters, 0, cnt, 0);
+	}
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_cond_init.c b/system/lib/libc/musl/src/thread/pthread_cond_init.c
index 357ecd5..8c484dd 100644
--- a/system/lib/libc/musl/src/thread/pthread_cond_init.c
+++ b/system/lib/libc/musl/src/thread/pthread_cond_init.c
@@ -5,7 +5,7 @@
 	*c = (pthread_cond_t){0};
 	if (a) {
 		c->_c_clock = a->__attr & 0x7fffffff;
-		if (a->__attr>>31) c->_c_mutex = (void *)-1;
+		if (a->__attr>>31) c->_c_shared = (void *)-1;
 	}
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_cond_signal.c b/system/lib/libc/musl/src/thread/pthread_cond_signal.c
index 71bcdcd..119c00a 100644
--- a/system/lib/libc/musl/src/thread/pthread_cond_signal.c
+++ b/system/lib/libc/musl/src/thread/pthread_cond_signal.c
@@ -1,9 +1,12 @@
 #include "pthread_impl.h"
 
+int __private_cond_signal(pthread_cond_t *, int);
+
 int pthread_cond_signal(pthread_cond_t *c)
 {
+	if (!c->_c_shared) return __private_cond_signal(c, 1);
 	if (!c->_c_waiters) return 0;
 	a_inc(&c->_c_seq);
-	if (c->_c_waiters) __wake(&c->_c_seq, 1, 0);
+	__wake(&c->_c_seq, 1, 0);
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_cond_timedwait.c b/system/lib/libc/musl/src/thread/pthread_cond_timedwait.c
index 16152e3..9feb9b2 100644
--- a/system/lib/libc/musl/src/thread/pthread_cond_timedwait.c
+++ b/system/lib/libc/musl/src/thread/pthread_cond_timedwait.c
@@ -1,78 +1,184 @@
 #include "pthread_impl.h"
 
-struct cm {
-	pthread_cond_t *c;
-	pthread_mutex_t *m;
+void __pthread_testcancel(void);
+int __pthread_mutex_lock(pthread_mutex_t *);
+int __pthread_mutex_unlock(pthread_mutex_t *);
+int __pthread_setcancelstate(int, int *);
+
+/*
+ * struct waiter
+ *
+ * Waiter objects have automatic storage on the waiting thread, and
+ * are used in building a linked list representing waiters currently
+ * waiting on the condition variable or a group of waiters woken
+ * together by a broadcast or signal; in the case of signal, this is a
+ * degenerate list of one member.
+ *
+ * Waiter lists attached to the condition variable itself are
+ * protected by the lock on the cv. Detached waiter lists are never
+ * modified again, but can only be traversed in reverse order, and are
+ * protected by the "barrier" locks in each node, which are unlocked
+ * in turn to control wake order.
+ *
+ * Since process-shared cond var semantics do not necessarily allow
+ * one thread to see another's automatic storage (they may be in
+ * different processes), the waiter list is not used for the
+ * process-shared case, but the structure is still used to store data
+ * needed by the cancellation cleanup handler.
+ */
+
+struct waiter {
+	struct waiter *prev, *next;
+	volatile int state, barrier;
+	volatile int *notify;
 };
 
-static void unwait(pthread_cond_t *c, pthread_mutex_t *m)
-{
-	/* Removing a waiter is non-trivial if we could be using requeue
-	 * based broadcast signals, due to mutex access issues, etc. */
+/* Self-synchronized-destruction-safe lock functions */
 
-	if (c->_c_mutex == (void *)-1) {
-		a_dec(&c->_c_waiters);
-		if (c->_c_destroy) __wake(&c->_c_waiters, 1, 0);
-		return;
+static inline void lock(volatile int *l)
+{
+	if (a_cas(l, 0, 1)) {
+		a_cas(l, 1, 2);
+		do __wait(l, 0, 2, 1);
+		while (a_cas(l, 0, 2));
 	}
-
-	while (a_swap(&c->_c_lock, 1))
-		__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
-
-	if (c->_c_waiters2) c->_c_waiters2--;
-	else a_dec(&m->_m_waiters);
-
-	a_store(&c->_c_lock, 0);
-	if (c->_c_lockwait) __wake(&c->_c_lock, 1, 1);
-
-	a_dec(&c->_c_waiters);
-	if (c->_c_destroy) __wake(&c->_c_waiters, 1, 1);
 }
 
-static void cleanup(void *p)
+static inline void unlock(volatile int *l)
 {
-	struct cm *cm = p;
-	unwait(cm->c, cm->m);
-	pthread_mutex_lock(cm->m);
+	if (a_swap(l, 0)==2)
+		__wake(l, 1, 1);
 }
 
-int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts)
+static inline void unlock_requeue(volatile int *l, volatile int *r, int w)
 {
-	struct cm cm = { .c=c, .m=m };
-	int r, e=0, seq;
+	a_store(l, 0);
+#ifdef __EMSCRIPTEN__
+	int futexResult;
+	do {
+		// We want to wake one and requeue all others, without comparing the value, but SAB spec doesn't
+		// have requeue without comparing, so implement it by spinning instead.
+		futexResult = emscripten_futex_wake_or_requeue(l, 0, r, *l);
+	} while(futexResult == -EAGAIN);
+#else
+	if (w) __wake(l, 1, 1);
+	else __syscall(SYS_futex, l, FUTEX_REQUEUE|128, 0, 1, r) != -ENOSYS
+		|| __syscall(SYS_futex, l, FUTEX_REQUEUE, 0, 1, r);
+#endif
+}
 
-	if (m->_m_type && (m->_m_lock&INT_MAX) != pthread_self()->tid)
+enum {
+	WAITING,
+	SIGNALED,
+	LEAVING,
+};
+
+int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts)
+{
+	struct waiter node = { 0 };
+	int e, seq, clock = c->_c_clock, cs, shared=0, oldstate, tmp;
+	volatile int *fut;
+
+	if ((m->_m_type&15) && (m->_m_lock&INT_MAX) != __pthread_self()->tid)
 		return EPERM;
 
 	if (ts && ts->tv_nsec >= 1000000000UL)
 		return EINVAL;
 
-	pthread_testcancel();
+	__pthread_testcancel();
 
-	a_inc(&c->_c_waiters);
+	if (c->_c_shared) {
+		shared = 1;
+		fut = &c->_c_seq;
+		seq = c->_c_seq;
+		a_inc(&c->_c_waiters);
+	} else {
+		lock(&c->_c_lock);
 
-	if (c->_c_mutex != (void *)-1) {
-		c->_c_mutex = m;
-		while (a_swap(&c->_c_lock, 1))
-			__wait(&c->_c_lock, &c->_c_lockwait, 1, 1);
-		c->_c_waiters2++;
-		a_store(&c->_c_lock, 0);
-		if (c->_c_lockwait) __wake(&c->_c_lock, 1, 1);
+		seq = node.barrier = 2;
+		fut = &node.barrier;
+		node.state = WAITING;
+		node.next = c->_c_head;
+		c->_c_head = &node;
+		if (!c->_c_tail) c->_c_tail = &node;
+		else node.next->prev = &node;
+
+		unlock(&c->_c_lock);
 	}
 
-	seq = c->_c_seq;
+	__pthread_mutex_unlock(m);
 
-	pthread_mutex_unlock(m);
+	__pthread_setcancelstate(PTHREAD_CANCEL_MASKED, &cs);
+	if (cs == PTHREAD_CANCEL_DISABLE) __pthread_setcancelstate(cs, 0);
 
-	do {
-		e = __timedwait(&c->_c_seq, seq, c->_c_clock, ts, cleanup, &cm, 0);
-	}
-	while (c->_c_seq == seq && (!e || e==EINTR));
+	do e = __timedwait_cp(fut, seq, clock, ts, !shared);
+	while (*fut==seq && (!e || e==EINTR));
 	if (e == EINTR) e = 0;
 
-	unwait(c, m);
+	if (shared) {
+		/* Suppress cancellation if a signal was potentially
+		 * consumed; this is a legitimate form of spurious
+		 * wake even if not. */
+		if (e == ECANCELED && c->_c_seq != seq) e = 0;
+		if (a_fetch_add(&c->_c_waiters, -1) == -0x7fffffff)
+			__wake(&c->_c_waiters, 1, 0);
+		oldstate = WAITING;
+		goto relock;
+	}
 
-	if ((r=pthread_mutex_lock(m))) return r;
+	oldstate = a_cas(&node.state, WAITING, LEAVING);
+
+	if (oldstate == WAITING) {
+		/* Access to cv object is valid because this waiter was not
+		 * yet signaled and a new signal/broadcast cannot return
+		 * after seeing a LEAVING waiter without getting notified
+		 * via the futex notify below. */
+
+		lock(&c->_c_lock);
+		
+		if (c->_c_head == &node) c->_c_head = node.next;
+		else if (node.prev) node.prev->next = node.next;
+		if (c->_c_tail == &node) c->_c_tail = node.prev;
+		else if (node.next) node.next->prev = node.prev;
+		
+		unlock(&c->_c_lock);
+
+		if (node.notify) {
+			if (a_fetch_add(node.notify, -1)==1)
+				__wake(node.notify, 1, 1);
+		}
+	} else {
+		/* Lock barrier first to control wake order. */
+		lock(&node.barrier);
+	}
+
+relock:
+	/* Errors locking the mutex override any existing error or
+	 * cancellation, since the caller must see them to know the
+	 * state of the mutex. */
+	if ((tmp = pthread_mutex_lock(m))) e = tmp;
+
+	if (oldstate == WAITING) goto done;
+
+	if (!node.next) a_inc(&m->_m_waiters);
+
+	/* Unlock the barrier that's holding back the next waiter, and
+	 * either wake it or requeue it to the mutex. */
+	if (node.prev)
+		unlock_requeue(&node.prev->barrier, &m->_m_lock, m->_m_type & 128);
+	else
+		a_dec(&m->_m_waiters);
+
+	/* Since a signal was consumed, cancellation is not permitted. */
+	if (e == ECANCELED) e = 0;
+
+done:
+	__pthread_setcancelstate(cs, 0);
+
+	if (e == ECANCELED) {
+		__pthread_testcancel();
+		__pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
+	}
 
 #ifdef __EMSCRIPTEN__
 	pthread_testcancel();
@@ -80,3 +186,42 @@
 
 	return e;
 }
+
+int __private_cond_signal(pthread_cond_t *c, int n)
+{
+	struct waiter *p, *first=0;
+	volatile int ref = 0;
+	int cur;
+
+	lock(&c->_c_lock);
+	for (p=c->_c_tail; n && p; p=p->prev) {
+		if (a_cas(&p->state, WAITING, SIGNALED) != WAITING) {
+			ref++;
+			p->notify = &ref;
+		} else {
+			n--;
+			if (!first) first=p;
+		}
+	}
+	/* Split the list, leaving any remainder on the cv. */
+	if (p) {
+		if (p->next) p->next->prev = 0;
+		p->next = 0;
+	} else {
+		c->_c_head = 0;
+	}
+	c->_c_tail = p;
+	unlock(&c->_c_lock);
+
+	/* Wait for any waiters in the LEAVING state to remove
+	 * themselves from the list before returning or allowing
+	 * signaled threads to proceed. */
+	while ((cur = ref)) __wait(&ref, 0, cur, 1);
+
+	/* Allow first signaled waiter, if any, to proceed. */
+	if (first) unlock(&first->barrier);
+
+	return 0;
+}
+
+weak_alias(__pthread_cond_timedwait, pthread_cond_timedwait);
diff --git a/system/lib/libc/musl/src/thread/pthread_create.c b/system/lib/libc/musl/src/thread/pthread_create.c
index f1d286b..9f6b98e 100644
--- a/system/lib/libc/musl/src/thread/pthread_create.c
+++ b/system/lib/libc/musl/src/thread/pthread_create.c
@@ -4,6 +4,11 @@
 #include "libc.h"
 #include <sys/mman.h>
 #include <string.h>
+#include <stddef.h>
+
+void *__mmap(void *, size_t, int, int, int, off_t);
+int __munmap(void *, size_t);
+int __mprotect(void *, size_t, int);
 
 static void dummy_0()
 {
@@ -11,12 +16,16 @@
 weak_alias(dummy_0, __acquire_ptc);
 weak_alias(dummy_0, __release_ptc);
 weak_alias(dummy_0, __pthread_tsd_run_dtors);
+weak_alias(dummy_0, __do_orphaned_stdio_locks);
+weak_alias(dummy_0, __dl_thread_cleanup);
 
-_Noreturn void pthread_exit(void *result)
+_Noreturn void __pthread_exit(void *result)
 {
-	pthread_t self = pthread_self();
+	pthread_t self = __pthread_self();
 	sigset_t set;
 
+	self->canceldisable = 1;
+	self->cancelasync = 0;
 	self->result = result;
 
 	while (self->cancelbuf) {
@@ -58,6 +67,28 @@
 		exit(0);
 	}
 
+	/* Process robust list in userspace to handle non-pshared mutexes
+	 * and the detached thread case where the robust list head will
+	 * be invalid when the kernel would process it. */
+	__vm_lock();
+	volatile void *volatile *rp;
+	while ((rp=self->robust_list.head) && rp != &self->robust_list.head) {
+		pthread_mutex_t *m = (void *)((char *)rp
+			- offsetof(pthread_mutex_t, _m_next));
+		int waiters = m->_m_waiters;
+		int priv = (m->_m_type & 128) ^ 128;
+		self->robust_list.pending = rp;
+		self->robust_list.head = *rp;
+		int cont = a_swap(&m->_m_lock, 0x40000000);
+		self->robust_list.pending = 0;
+		if (cont < 0 || waiters)
+			__wake(&m->_m_lock, 1, priv);
+	}
+	__vm_unlock();
+
+	__do_orphaned_stdio_locks();
+	__dl_thread_cleanup();
+
 	if (self->detached && self->map_base) {
 		/* Detached threads must avoid the kernel clear_child_tid
 		 * feature, since the virtual address will have been
@@ -68,6 +99,15 @@
 		 * detached later (== 2), we need to clear it here. */
 		if (self->detached == 2) __syscall(SYS_set_tid_address, 0);
 
+		/* Robust list will no longer be valid, and was already
+		 * processed above, so unregister it with the kernel. */
+		if (self->robust_list.off)
+			__syscall(SYS_set_robust_list, 0, 3*sizeof(long));
+
+		/* Since __unmapself bypasses the normal munmap code path,
+		 * explicitly wait for vmlock holders first. */
+		__vm_wait();
+
 		/* The following call unmaps the thread's stack mapping
 		 * and then exits without touching the stack. */
 		__unmapself(self->map_base, self->map_size);
@@ -78,7 +118,7 @@
 
 void __do_cleanup_push(struct __ptcb *cb)
 {
-	struct pthread *self = pthread_self();
+	struct pthread *self = __pthread_self();
 	cb->__next = self->cancelbuf;
 	self->cancelbuf = cb;
 }
@@ -102,7 +142,15 @@
 	if (self->unblock_cancel)
 		__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
 			SIGPT_SET, 0, _NSIG/8);
-	pthread_exit(self->start(self->start_arg));
+	__pthread_exit(self->start(self->start_arg));
+	return 0;
+}
+
+static int start_c11(void *p)
+{
+	pthread_t self = p;
+	int (*start)(void*) = (int(*)(void*)) self->start;
+	__pthread_exit((void *)(uintptr_t)start(self->start_arg));
 	return 0;
 }
 
@@ -114,6 +162,8 @@
 static void *dummy_tsd[1] = { 0 };
 weak_alias(dummy_tsd, __pthread_tsd_main);
 
+volatile int __block_new_threads = 0;
+
 static FILE *volatile dummy_file = 0;
 weak_alias(dummy_file, __stdin_used);
 weak_alias(dummy_file, __stdout_used);
@@ -126,11 +176,11 @@
 
 void *__copy_tls(unsigned char *);
 
-int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg)
+int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg)
 {
-	int ret;
+	int ret, c11 = (attrp == __ATTRP_C11_THREAD);
 	size_t size, guard;
-	struct pthread *self = pthread_self(), *new;
+	struct pthread *self, *new;
 	unsigned char *map = 0, *stack = 0, *tsd = 0, *stack_limit;
 	unsigned flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND
 		| CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS
@@ -138,18 +188,23 @@
 	int do_sched = 0;
 	pthread_attr_t attr = {0};
 
-	if (!self) return ENOSYS;
+	if (!libc.can_do_threads) return ENOSYS;
+	self = __pthread_self();
 	if (!libc.threaded) {
-		for (FILE *f=libc.ofl_head; f; f=f->next)
+		for (FILE *f=*__ofl_lock(); f; f=f->next)
 			init_file_lock(f);
+		__ofl_unlock();
 		init_file_lock(__stdin_used);
 		init_file_lock(__stdout_used);
 		init_file_lock(__stderr_used);
+		__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK, SIGPT_SET, 0, _NSIG/8);
+		self->tsd = (void **)__pthread_tsd_main;
 		libc.threaded = 1;
 	}
-	if (attrp) attr = *attrp;
+	if (attrp && !c11) attr = *attrp;
 
 	__acquire_ptc();
+	if (__block_new_threads) __wait(&__block_new_threads, 0, 1, 1);
 
 	if (attr._a_stackaddr) {
 		size_t need = libc.tls_size + __pthread_tsd_size;
@@ -175,14 +230,15 @@
 
 	if (!tsd) {
 		if (guard) {
-			map = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
+			map = __mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
 			if (map == MAP_FAILED) goto fail;
-			if (mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)) {
-				munmap(map, size);
+			if (__mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)
+			    && errno != ENOSYS) {
+				__munmap(map, size);
 				goto fail;
 			}
 		} else {
-			map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+			map = __mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
 			if (map == MAP_FAILED) goto fail;
 		}
 		tsd = map + size - __pthread_tsd_size;
@@ -197,12 +253,11 @@
 	new->map_size = size;
 	new->stack = stack;
 	new->stack_size = stack - stack_limit;
-	new->pid = self->pid;
-	new->errno_ptr = &new->errno_val;
 	new->start = entry;
 	new->start_arg = arg;
 	new->self = new;
 	new->tsd = (void *)tsd;
+	new->locale = &libc.global_locale;
 	if (attr._a_detach) {
 		new->detached = 1;
 		flags -= CLONE_CHILD_CLEARTID;
@@ -211,11 +266,12 @@
 		do_sched = new->startlock[0] = 1;
 		__block_app_sigs(new->sigmask);
 	}
+	new->robust_list.head = &new->robust_list.head;
 	new->unblock_cancel = self->cancel;
-	new->canary = self->canary;
+	new->CANARY = self->CANARY;
 
 	a_inc(&libc.threads_minus_1);
-	ret = __clone(start, stack, flags, new, &new->tid, TP_ADJ(new), &new->tid);
+	ret = __clone((c11 ? start_c11 : start), stack, flags, new, &new->tid, TP_ADJ(new), &new->tid);
 
 	__release_ptc();
 
@@ -225,7 +281,7 @@
 
 	if (ret < 0) {
 		a_dec(&libc.threads_minus_1);
-		if (map) munmap(map, size);
+		if (map) __munmap(map, size);
 		return EAGAIN;
 	}
 
@@ -243,3 +299,6 @@
 	__release_ptc();
 	return EAGAIN;
 }
+
+weak_alias(__pthread_exit, pthread_exit);
+weak_alias(__pthread_create, pthread_create);
diff --git a/system/lib/libc/musl/src/thread/pthread_detach.c b/system/lib/libc/musl/src/thread/pthread_detach.c
index 651c38e..ed77f74 100644
--- a/system/lib/libc/musl/src/thread/pthread_detach.c
+++ b/system/lib/libc/musl/src/thread/pthread_detach.c
@@ -1,11 +1,17 @@
 #include "pthread_impl.h"
+#include <threads.h>
 
-int pthread_detach(pthread_t t)
+int __pthread_join(pthread_t, void **);
+
+static int __pthread_detach(pthread_t t)
 {
 	/* Cannot detach a thread that's already exiting */
 	if (a_swap(t->exitlock, 1))
-		return pthread_join(t, 0);
+		return __pthread_join(t, 0);
 	t->detached = 2;
 	__unlock(t->exitlock);
 	return 0;
 }
+
+weak_alias(__pthread_detach, pthread_detach);
+weak_alias(__pthread_detach, thrd_detach);
diff --git a/system/lib/libc/musl/src/thread/pthread_equal.c b/system/lib/libc/musl/src/thread/pthread_equal.c
index 3e3df4f..7c31482 100644
--- a/system/lib/libc/musl/src/thread/pthread_equal.c
+++ b/system/lib/libc/musl/src/thread/pthread_equal.c
@@ -1,6 +1,11 @@
 #include <pthread.h>
+#include <threads.h>
+#include "libc.h"
 
-int (pthread_equal)(pthread_t a, pthread_t b)
+static int __pthread_equal(pthread_t a, pthread_t b)
 {
 	return a==b;
 }
+
+weak_alias(__pthread_equal, pthread_equal);
+weak_alias(__pthread_equal, thrd_equal);
diff --git a/system/lib/libc/musl/src/thread/pthread_getspecific.c b/system/lib/libc/musl/src/thread/pthread_getspecific.c
index b2a282c..d9342a5 100644
--- a/system/lib/libc/musl/src/thread/pthread_getspecific.c
+++ b/system/lib/libc/musl/src/thread/pthread_getspecific.c
@@ -1,7 +1,11 @@
 #include "pthread_impl.h"
+#include <threads.h>
 
-void *pthread_getspecific(pthread_key_t k)
+static void *__pthread_getspecific(pthread_key_t k)
 {
 	struct pthread *self = __pthread_self();
 	return self->tsd[k];
 }
+
+weak_alias(__pthread_getspecific, pthread_getspecific);
+weak_alias(__pthread_getspecific, tss_get);
diff --git a/system/lib/libc/musl/src/thread/pthread_join.c b/system/lib/libc/musl/src/thread/pthread_join.c
index 719c91c..5211148 100644
--- a/system/lib/libc/musl/src/thread/pthread_join.c
+++ b/system/lib/libc/musl/src/thread/pthread_join.c
@@ -1,15 +1,36 @@
 #include "pthread_impl.h"
 #include <sys/mman.h>
 
-static void dummy(void *p)
-{
-}
+int __munmap(void *, size_t);
+void __pthread_testcancel(void);
+int __pthread_setcancelstate(int, int *);
 
-int pthread_join(pthread_t t, void **res)
+int __pthread_timedjoin_np(pthread_t t, void **res, const struct timespec *at)
 {
-	int tmp;
-	while ((tmp = t->tid)) __timedwait(&t->tid, tmp, 0, 0, dummy, 0, 0);
+	int tmp, cs, r = 0;
+	__pthread_testcancel();
+	__pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+	if (cs == PTHREAD_CANCEL_ENABLE) __pthread_setcancelstate(cs, 0);
+	while ((tmp = t->tid) && r != ETIMEDOUT && r != EINVAL)
+		r = __timedwait_cp(&t->tid, tmp, CLOCK_REALTIME, at, 0);
+	__pthread_setcancelstate(cs, 0);
+	if (r == ETIMEDOUT || r == EINVAL) return r;
+	a_barrier();
 	if (res) *res = t->result;
-	if (t->map_base) munmap(t->map_base, t->map_size);
+	if (t->map_base) __munmap(t->map_base, t->map_size);
 	return 0;
 }
+
+int __pthread_join(pthread_t t, void **res)
+{
+	return __pthread_timedjoin_np(t, res, 0);
+}
+
+int __pthread_tryjoin_np(pthread_t t, void **res)
+{
+	return t->tid ? EBUSY : __pthread_join(t, res);
+}
+
+weak_alias(__pthread_tryjoin_np, pthread_tryjoin_np);
+weak_alias(__pthread_timedjoin_np, pthread_timedjoin_np);
+weak_alias(__pthread_join, pthread_join);
diff --git a/system/lib/libc/musl/src/thread/pthread_key_create.c b/system/lib/libc/musl/src/thread/pthread_key_create.c
index 8acf513..e77deb1 100644
--- a/system/lib/libc/musl/src/thread/pthread_key_create.c
+++ b/system/lib/libc/musl/src/thread/pthread_key_create.c
@@ -7,20 +7,22 @@
 volatile size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX;
 void *__pthread_tsd_main[PTHREAD_KEYS_MAX] = { 0 };
 
-static void (*keys[PTHREAD_KEYS_MAX])(void *);
+static void (*volatile keys[PTHREAD_KEYS_MAX])(void *);
 
 static void nodtor(void *dummy)
 {
 }
 
-int pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
+int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
 {
 	unsigned i = (uintptr_t)&k / 16 % PTHREAD_KEYS_MAX;
 	unsigned j = i;
+	pthread_t self = __pthread_self();
 
-#ifndef __EMSCRIPTEN__ // XXX Emscripten does not need specific initialization for threads, the runtime has initialized everything prior to running.
-	__pthread_self_init();
-#endif
+	/* This can only happen in the main thread before
+	 * pthread_create has been called. */
+	if (!self->tsd) self->tsd = __pthread_tsd_main;
+
 	if (!dtor) dtor = nodtor;
 	do {
 		if (!a_cas_p(keys+j, 0, (void *)dtor)) {
@@ -31,7 +33,7 @@
 	return EAGAIN;
 }
 
-int pthread_key_delete(pthread_key_t k)
+int __pthread_key_delete(pthread_key_t k)
 {
 	keys[k] = 0;
 	return 0;
@@ -43,7 +45,7 @@
 void __pthread_tsd_run_dtors()
 #endif
 {
-	pthread_t self = pthread_self();
+	pthread_t self = __pthread_self();
 	int i, j, not_finished = self->tsd_used;
 	for (j=0; not_finished && j<PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
 		not_finished = 0;
@@ -57,3 +59,6 @@
 		}
 	}
 }
+
+weak_alias(__pthread_key_delete, pthread_key_delete);
+weak_alias(__pthread_key_create, pthread_key_create);
diff --git a/system/lib/libc/musl/src/thread/pthread_kill.c b/system/lib/libc/musl/src/thread/pthread_kill.c
index d9a5096..acdb1ea 100644
--- a/system/lib/libc/musl/src/thread/pthread_kill.c
+++ b/system/lib/libc/musl/src/thread/pthread_kill.c
@@ -4,7 +4,7 @@
 {
 	int r;
 	__lock(t->killlock);
-	r = t->dead ? ESRCH : -__syscall(SYS_tgkill, t->pid, t->tid, sig);
+	r = t->dead ? ESRCH : -__syscall(SYS_tkill, t->tid, sig);
 	__unlock(t->killlock);
 	return r;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_mutex_consistent.c b/system/lib/libc/musl/src/thread/pthread_mutex_consistent.c
index 7dfb904..96b83b5 100644
--- a/system/lib/libc/musl/src/thread/pthread_mutex_consistent.c
+++ b/system/lib/libc/musl/src/thread/pthread_mutex_consistent.c
@@ -2,9 +2,9 @@
 
 int pthread_mutex_consistent(pthread_mutex_t *m)
 {
-	if (m->_m_type < 8) return EINVAL;
-	if ((m->_m_lock & 0x3fffffff) != pthread_self()->tid)
+	if (!(m->_m_type & 8)) return EINVAL;
+	if ((m->_m_lock & 0x7fffffff) != __pthread_self()->tid)
 		return EPERM;
-	m->_m_type -= 8;
+	m->_m_type &= ~8U;
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_mutex_init.c b/system/lib/libc/musl/src/thread/pthread_mutex_init.c
index 9d85a35..acf45a7 100644
--- a/system/lib/libc/musl/src/thread/pthread_mutex_init.c
+++ b/system/lib/libc/musl/src/thread/pthread_mutex_init.c
@@ -3,6 +3,6 @@
 int pthread_mutex_init(pthread_mutex_t *restrict m, const pthread_mutexattr_t *restrict a)
 {
 	*m = (pthread_mutex_t){0};
-	if (a) m->_m_type = a->__attr & 7;
+	if (a) m->_m_type = a->__attr;
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_mutex_lock.c b/system/lib/libc/musl/src/thread/pthread_mutex_lock.c
index 42b5af6..d0c93ca 100644
--- a/system/lib/libc/musl/src/thread/pthread_mutex_lock.c
+++ b/system/lib/libc/musl/src/thread/pthread_mutex_lock.c
@@ -1,9 +1,14 @@
 #include "pthread_impl.h"
 
-int pthread_mutex_lock(pthread_mutex_t *m)
+int __pthread_mutex_timedlock(pthread_mutex_t *restrict, const struct timespec *restrict);
+
+int __pthread_mutex_lock(pthread_mutex_t *m)
 {
-	if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY))
+	if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL
+	    && !a_cas(&m->_m_lock, 0, EBUSY))
 		return 0;
 
-	return pthread_mutex_timedlock(m, 0);
+	return __pthread_mutex_timedlock(m, 0);
 }
+
+weak_alias(__pthread_mutex_lock, pthread_mutex_lock);
diff --git a/system/lib/libc/musl/src/thread/pthread_mutex_timedlock.c b/system/lib/libc/musl/src/thread/pthread_mutex_timedlock.c
index c24270d..0a240e7 100644
--- a/system/lib/libc/musl/src/thread/pthread_mutex_timedlock.c
+++ b/system/lib/libc/musl/src/thread/pthread_mutex_timedlock.c
@@ -1,24 +1,34 @@
 #include "pthread_impl.h"
 
-int pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at)
+int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at)
 {
-	int r, t;
-
-	if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY))
+	if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL
+	    && !a_cas(&m->_m_lock, 0, EBUSY))
 		return 0;
 
+	int r, t, priv = (m->_m_type & 128) ^ 128;
+
+	r = pthread_mutex_trylock(m);
+	if (r != EBUSY) return r;
+	
+	int spins = 100;
+	while (spins-- && m->_m_lock && !m->_m_waiters) a_spin();
+
 	while ((r=pthread_mutex_trylock(m)) == EBUSY) {
-		if (!(r=m->_m_lock) || (r&0x40000000)) continue;
+		if (!(r=m->_m_lock) || ((r&0x40000000) && (m->_m_type&4)))
+			continue;
 		if ((m->_m_type&3) == PTHREAD_MUTEX_ERRORCHECK
-		 && (r&0x1fffffff) == pthread_self()->tid)
+		 && (r&0x7fffffff) == __pthread_self()->tid)
 			return EDEADLK;
 
 		a_inc(&m->_m_waiters);
 		t = r | 0x80000000;
 		a_cas(&m->_m_lock, r, t);
-		r = __timedwait(&m->_m_lock, t, CLOCK_REALTIME, at, 0, 0, 0);
+		r = __timedwait(&m->_m_lock, t, CLOCK_REALTIME, at, priv);
 		a_dec(&m->_m_waiters);
 		if (r && r != EINTR) break;
 	}
 	return r;
 }
+
+weak_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock);
diff --git a/system/lib/libc/musl/src/thread/pthread_mutex_trylock.c b/system/lib/libc/musl/src/thread/pthread_mutex_trylock.c
index 1cf3cb9..fa53091 100644
--- a/system/lib/libc/musl/src/thread/pthread_mutex_trylock.c
+++ b/system/lib/libc/musl/src/thread/pthread_mutex_trylock.c
@@ -1,54 +1,60 @@
 #include "pthread_impl.h"
 
-int pthread_mutex_trylock(pthread_mutex_t *m)
+int __pthread_mutex_trylock_owner(pthread_mutex_t *m)
 {
-	int tid, old, own;
-	pthread_t self;
-
-	if (m->_m_type == PTHREAD_MUTEX_NORMAL)
-		return a_cas(&m->_m_lock, 0, EBUSY) & EBUSY;
-
-	self = pthread_self();
-	tid = self->tid;
-
-	if (m->_m_type >= 4) {
-#ifndef __EMSCRIPTEN__ // XXX Emscripten does not have a concept of multiple processes or kernel space, so robust mutex lists don't need to register to kernel.
-		if (!self->robust_list.off)
-			__syscall(SYS_set_robust_list,
-				&self->robust_list, 3*sizeof(long));
-#endif
-		self->robust_list.off = (char*)&m->_m_lock-(char *)&m->_m_next;
-		self->robust_list.pending = &m->_m_next;
-	}
+	int old, own;
+	int type = m->_m_type & 15;
+	pthread_t self = __pthread_self();
+	int tid = self->tid;
 
 	old = m->_m_lock;
 	own = old & 0x7fffffff;
-	if (own == tid && (m->_m_type&3) == PTHREAD_MUTEX_RECURSIVE) {
+	if (own == tid && (type&3) == PTHREAD_MUTEX_RECURSIVE) {
 		if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN;
 		m->_m_count++;
 		return 0;
 	}
+	if (own == 0x7fffffff) return ENOTRECOVERABLE;
 
-	if ((own && !(own & 0x40000000)) || a_cas(&m->_m_lock, old, tid)!=old)
-		return EBUSY;
-
-	if (m->_m_type < 4) return 0;
-
-	if (m->_m_type >= 8) {
-		m->_m_lock = 0;
-		return ENOTRECOVERABLE;
+	if (m->_m_type & 128) {
+		if (!self->robust_list.off) {
+			self->robust_list.off = (char*)&m->_m_lock-(char *)&m->_m_next;
+#ifndef __EMSCRIPTEN__ // XXX Emscripten does not have a concept of multiple processes or kernel space, so robust mutex lists don't need to register to kernel.
+			__syscall(SYS_set_robust_list, &self->robust_list, 3*sizeof(long));
+#endif
+		}
+		if (m->_m_waiters) tid |= 0x80000000;
+		self->robust_list.pending = &m->_m_next;
 	}
-	m->_m_next = self->robust_list.head;
+
+	if ((own && (!(own & 0x40000000) || !(type & 4)))
+	    || a_cas(&m->_m_lock, old, tid) != old) {
+		self->robust_list.pending = 0;
+		return EBUSY;
+	}
+
+	volatile void *next = self->robust_list.head;
+	m->_m_next = next;
 	m->_m_prev = &self->robust_list.head;
-	if (self->robust_list.head)
-		self->robust_list.head[-1] = &m->_m_next;
+	if (next != &self->robust_list.head) *(volatile void *volatile *)
+		((char *)next - sizeof(void *)) = &m->_m_next;
 	self->robust_list.head = &m->_m_next;
 	self->robust_list.pending = 0;
+
 	if (own) {
 		m->_m_count = 0;
-		m->_m_type += 8;
+		m->_m_type |= 8;
 		return EOWNERDEAD;
 	}
 
 	return 0;
 }
+
+int __pthread_mutex_trylock(pthread_mutex_t *m)
+{
+	if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL)
+		return a_cas(&m->_m_lock, 0, EBUSY) & EBUSY;
+	return __pthread_mutex_trylock_owner(m);
+}
+
+weak_alias(__pthread_mutex_trylock, pthread_mutex_trylock);
diff --git a/system/lib/libc/musl/src/thread/pthread_mutex_unlock.c b/system/lib/libc/musl/src/thread/pthread_mutex_unlock.c
index 5fc0f4e..7dd00d2 100644
--- a/system/lib/libc/musl/src/thread/pthread_mutex_unlock.c
+++ b/system/lib/libc/musl/src/thread/pthread_mutex_unlock.c
@@ -1,37 +1,37 @@
 #include "pthread_impl.h"
 
-void __vm_lock_impl(int);
-void __vm_unlock_impl(void);
-
-int pthread_mutex_unlock(pthread_mutex_t *m)
+int __pthread_mutex_unlock(pthread_mutex_t *m)
 {
 	pthread_t self;
 	int waiters = m->_m_waiters;
 	int cont;
-	int robust = 0;
+	int type = m->_m_type & 15;
+	int priv = (m->_m_type & 128) ^ 128;
 
-	if (m->_m_type != PTHREAD_MUTEX_NORMAL) {
-		if (!m->_m_lock)
+	if (type != PTHREAD_MUTEX_NORMAL) {
+		self = __pthread_self();
+		if ((m->_m_lock&0x7fffffff) != self->tid)
 			return EPERM;
-		self = pthread_self();
-		if ((m->_m_lock&0x1fffffff) != self->tid)
-			return EPERM;
-		if ((m->_m_type&3) == PTHREAD_MUTEX_RECURSIVE && m->_m_count)
+		if ((type&3) == PTHREAD_MUTEX_RECURSIVE && m->_m_count)
 			return m->_m_count--, 0;
-		if (m->_m_type >= 4) {
-			robust = 1;
+		if (!priv) {
 			self->robust_list.pending = &m->_m_next;
-			*(void **)m->_m_prev = m->_m_next;
-			if (m->_m_next) ((void **)m->_m_next)[-1] = m->_m_prev;
-			__vm_lock_impl(+1);
+			__vm_lock();
 		}
+		volatile void *prev = m->_m_prev;
+		volatile void *next = m->_m_next;
+		*(volatile void *volatile *)prev = next;
+		if (next != &self->robust_list.head) *(volatile void *volatile *)
+			((char *)next - sizeof(void *)) = prev;
 	}
-	cont = a_swap(&m->_m_lock, 0);
-	if (robust) {
+	cont = a_swap(&m->_m_lock, (type & 8) ? 0x7fffffff : 0);
+	if (type != PTHREAD_MUTEX_NORMAL && !priv) {
 		self->robust_list.pending = 0;
-		__vm_unlock_impl();
+		__vm_unlock();
 	}
 	if (waiters || cont<0)
-		__wake(&m->_m_lock, 1, 0);
+		__wake(&m->_m_lock, 1, priv);
 	return 0;
 }
+
+weak_alias(__pthread_mutex_unlock, pthread_mutex_unlock);
diff --git a/system/lib/libc/musl/src/thread/pthread_mutexattr_setpshared.c b/system/lib/libc/musl/src/thread/pthread_mutexattr_setpshared.c
index 8c7a1e2..100f6ff 100644
--- a/system/lib/libc/musl/src/thread/pthread_mutexattr_setpshared.c
+++ b/system/lib/libc/musl/src/thread/pthread_mutexattr_setpshared.c
@@ -3,7 +3,7 @@
 int pthread_mutexattr_setpshared(pthread_mutexattr_t *a, int pshared)
 {
 	if (pshared > 1U) return EINVAL;
-	a->__attr &= 0x7fffffff;
-	a->__attr |= pshared<<31;
+	a->__attr &= ~128U;
+	a->__attr |= pshared<<7;
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_once.c b/system/lib/libc/musl/src/thread/pthread_once.c
index 7b9ccf2..a8f8aeb 100644
--- a/system/lib/libc/musl/src/thread/pthread_once.c
+++ b/system/lib/libc/musl/src/thread/pthread_once.c
@@ -2,26 +2,19 @@
 
 static void undo(void *control)
 {
-	a_store(control, 0);
-	__wake(control, 1, 0);
+	/* Wake all waiters, since the waiter status is lost when
+	 * resetting control to the initial state. */
+	if (a_swap(control, 0) == 3)
+		__wake(control, -1, 1);
 }
 
-int pthread_once(pthread_once_t *control, void (*init)(void))
+int __pthread_once_full(pthread_once_t *control, void (*init)(void))
 {
-	static int waiters;
-
-	/* Return immediately if init finished before, but ensure that
-	 * effects of the init routine are visible to the caller. */
-	if (*control == 2) {
-		/* Otherwise-useless cas just to get a barrier. */
-		a_cas(&(int){0},0,0);
-		return 0;
-	}
-
-	/* Try to enter initializing state. Three possibilities:
+	/* Try to enter initializing state. Four possibilities:
 	 *  0 - we're the first or the other cancelled; run init
 	 *  1 - another thread is running init; wait
-	 *  2 - another thread finished running init; just return */
+	 *  2 - another thread finished running init; just return
+	 *  3 - another thread is running init, waiters present; wait */
 
 	for (;;) switch (a_cas(control, 0, 1)) {
 	case 0:
@@ -29,13 +22,29 @@
 		init();
 		pthread_cleanup_pop(0);
 
-		a_store(control, 2);
-		if (waiters) __wake(control, -1, 0);
+		if (a_swap(control, 2) == 3)
+			__wake(control, -1, 1);
 		return 0;
 	case 1:
-		__wait(control, &waiters, 1, 0);
+		/* If this fails, so will __wait. */
+		a_cas(control, 1, 3);
+	case 3:
+		__wait(control, 0, 3, 1);
 		continue;
 	case 2:
 		return 0;
 	}
 }
+
+int __pthread_once(pthread_once_t *control, void (*init)(void))
+{
+	/* Return immediately if init finished before, but ensure that
+	 * effects of the init routine are visible to the caller. */
+	if (*(volatile int *)control == 2) {
+		a_barrier();
+		return 0;
+	}
+	return __pthread_once_full(control, init);
+}
+
+weak_alias(__pthread_once, pthread_once);
diff --git a/system/lib/libc/musl/src/thread/pthread_rwlock_init.c b/system/lib/libc/musl/src/thread/pthread_rwlock_init.c
index 82df52e..a2c0b47 100644
--- a/system/lib/libc/musl/src/thread/pthread_rwlock_init.c
+++ b/system/lib/libc/musl/src/thread/pthread_rwlock_init.c
@@ -3,7 +3,6 @@
 int pthread_rwlock_init(pthread_rwlock_t *restrict rw, const pthread_rwlockattr_t *restrict a)
 {
 	*rw = (pthread_rwlock_t){0};
-	if (a) {
-	}
+	if (a) rw->_rw_shared = a->__attr[0]*128;
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_rwlock_timedrdlock.c b/system/lib/libc/musl/src/thread/pthread_rwlock_timedrdlock.c
index c0c94c9..0d5d0d6 100644
--- a/system/lib/libc/musl/src/thread/pthread_rwlock_timedrdlock.c
+++ b/system/lib/libc/musl/src/thread/pthread_rwlock_timedrdlock.c
@@ -3,12 +3,19 @@
 int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rw, const struct timespec *restrict at)
 {
 	int r, t;
+
+	r = pthread_rwlock_tryrdlock(rw);
+	if (r != EBUSY) return r;
+	
+	int spins = 100;
+	while (spins-- && rw->_rw_lock && !rw->_rw_waiters) a_spin();
+
 	while ((r=pthread_rwlock_tryrdlock(rw))==EBUSY) {
 		if (!(r=rw->_rw_lock) || (r&0x7fffffff)!=0x7fffffff) continue;
 		t = r | 0x80000000;
 		a_inc(&rw->_rw_waiters);
 		a_cas(&rw->_rw_lock, r, t);
-		r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, 0, 0, 0);
+		r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, rw->_rw_shared^128);
 		a_dec(&rw->_rw_waiters);
 		if (r && r != EINTR) return r;
 	}
diff --git a/system/lib/libc/musl/src/thread/pthread_rwlock_timedwrlock.c b/system/lib/libc/musl/src/thread/pthread_rwlock_timedwrlock.c
index 5434f57..e1d6162 100644
--- a/system/lib/libc/musl/src/thread/pthread_rwlock_timedwrlock.c
+++ b/system/lib/libc/musl/src/thread/pthread_rwlock_timedwrlock.c
@@ -8,12 +8,19 @@
 	if (rw->_rw_wr_owner == (int)pthread_self()) return EDEADLK;
 #endif
 	int r, t;
+	
+	r = pthread_rwlock_trywrlock(rw);
+	if (r != EBUSY) return r;
+	
+	int spins = 100;
+	while (spins-- && rw->_rw_lock && !rw->_rw_waiters) a_spin();
+
 	while ((r=pthread_rwlock_trywrlock(rw))==EBUSY) {
 		if (!(r=rw->_rw_lock)) continue;
 		t = r | 0x80000000;
 		a_inc(&rw->_rw_waiters);
 		a_cas(&rw->_rw_lock, r, t);
-		r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, 0, 0, 0);
+		r = __timedwait(&rw->_rw_lock, t, CLOCK_REALTIME, at, rw->_rw_shared^128);
 		a_dec(&rw->_rw_waiters);
 		if (r && r != EINTR) return r;
 	}
diff --git a/system/lib/libc/musl/src/thread/pthread_rwlock_unlock.c b/system/lib/libc/musl/src/thread/pthread_rwlock_unlock.c
index 97e6a79..61d5e38 100644
--- a/system/lib/libc/musl/src/thread/pthread_rwlock_unlock.c
+++ b/system/lib/libc/musl/src/thread/pthread_rwlock_unlock.c
@@ -2,7 +2,7 @@
 
 int pthread_rwlock_unlock(pthread_rwlock_t *rw)
 {
-	int val, cnt, waiters, new;
+	int val, cnt, waiters, new, priv = rw->_rw_shared^128;
 
 #ifdef __EMSCRIPTEN__
 	/// XXX Emscripten: The spec allows detecting when multiple write locks would deadlock, which we do here to avoid hangs.
@@ -18,7 +18,7 @@
 	} while (a_cas(&rw->_rw_lock, val, new) != val);
 
 	if (!new && (waiters || val<0))
-		__wake(&rw->_rw_lock, cnt, 0);
+		__wake(&rw->_rw_lock, cnt, priv);
 
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_self.c b/system/lib/libc/musl/src/thread/pthread_self.c
index aed4b5f..241a620 100644
--- a/system/lib/libc/musl/src/thread/pthread_self.c
+++ b/system/lib/libc/musl/src/thread/pthread_self.c
@@ -1,45 +1,11 @@
 #include "pthread_impl.h"
+#include <threads.h>
+#include "libc.h"
 
-static struct pthread *main_thread = &(struct pthread){0};
-
-/* pthread_key_create.c overrides this */
-static const void *dummy[1] = { 0 };
-weak_alias(dummy, __pthread_tsd_main);
-
-static int init_main_thread()
+static pthread_t __pthread_self_internal()
 {
-	__syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
-		SIGPT_SET, 0, _NSIG/8);
-	if (__set_thread_area(TP_ADJ(main_thread)) < 0) return -1;
-	main_thread->canceldisable = libc.canceldisable;
-	main_thread->tsd = (void **)__pthread_tsd_main;
-	main_thread->errno_ptr = __errno_location();
-	main_thread->self = main_thread;
-	main_thread->tid = main_thread->pid =
-		__syscall(SYS_set_tid_address, &main_thread->tid);
-	if (!main_thread->dtv)
-		main_thread->dtv = (void *)dummy;
-	libc.main_thread = main_thread;
-	return 0;
-}
-
-pthread_t __pthread_self_def()
-{
-	static int init, failed;
-	if (!init) {
-		if (failed) return 0;
-		if (init_main_thread() < 0) failed = 1;
-		if (failed) return 0;
-		init = 1;
-	}
 	return __pthread_self();
 }
 
-weak_alias(__pthread_self_def, pthread_self);
-weak_alias(__pthread_self_def, __pthread_self_init);
-
-void *__install_initial_tls(void *p)
-{
-	main_thread = p;
-	return __pthread_self_def();
-}
+weak_alias(__pthread_self_internal, pthread_self);
+weak_alias(__pthread_self_internal, thrd_current);
diff --git a/system/lib/libc/musl/src/thread/pthread_setcancelstate.c b/system/lib/libc/musl/src/thread/pthread_setcancelstate.c
index ba2b231..5ab8c33 100644
--- a/system/lib/libc/musl/src/thread/pthread_setcancelstate.c
+++ b/system/lib/libc/musl/src/thread/pthread_setcancelstate.c
@@ -1,15 +1,12 @@
 #include "pthread_impl.h"
 
-int pthread_setcancelstate(int new, int *old)
+int __pthread_setcancelstate(int new, int *old)
 {
-	if (new > 1U) return EINVAL;
-	if (libc.main_thread) {
-		struct pthread *self = __pthread_self();
-		if (old) *old = self->canceldisable;
-		self->canceldisable = new;
-	} else {
-		if (old) *old = libc.canceldisable;
-		libc.canceldisable = new;
-	}
+	if (new > 2U) return EINVAL;
+	struct pthread *self = __pthread_self();
+	if (old) *old = self->canceldisable;
+	self->canceldisable = new;
 	return 0;
 }
+
+weak_alias(__pthread_setcancelstate, pthread_setcancelstate);
diff --git a/system/lib/libc/musl/src/thread/pthread_setcanceltype.c b/system/lib/libc/musl/src/thread/pthread_setcanceltype.c
index ce2fff0..bf0a3f3 100644
--- a/system/lib/libc/musl/src/thread/pthread_setcanceltype.c
+++ b/system/lib/libc/musl/src/thread/pthread_setcanceltype.c
@@ -2,7 +2,7 @@
 
 int pthread_setcanceltype(int new, int *old)
 {
-	struct pthread *self = pthread_self();
+	struct pthread *self = __pthread_self();
 	if (new > 1U) return EINVAL;
 	if (old) *old = self->cancelasync;
 	self->cancelasync = new;
diff --git a/system/lib/libc/musl/src/thread/pthread_spin_lock.c b/system/lib/libc/musl/src/thread/pthread_spin_lock.c
index df575f0..ded2b65 100644
--- a/system/lib/libc/musl/src/thread/pthread_spin_lock.c
+++ b/system/lib/libc/musl/src/thread/pthread_spin_lock.c
@@ -1,7 +1,8 @@
 #include "pthread_impl.h"
+#include <errno.h>
 
 int pthread_spin_lock(pthread_spinlock_t *s)
 {
-	while (a_swap(s, 1)) a_spin();
+	while (*(volatile int *)s || a_cas(s, 0, EBUSY)) a_spin();
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_spin_trylock.c b/system/lib/libc/musl/src/thread/pthread_spin_trylock.c
index 59de695..5284fda 100644
--- a/system/lib/libc/musl/src/thread/pthread_spin_trylock.c
+++ b/system/lib/libc/musl/src/thread/pthread_spin_trylock.c
@@ -1,6 +1,7 @@
 #include "pthread_impl.h"
+#include <errno.h>
 
 int pthread_spin_trylock(pthread_spinlock_t *s)
 {
-	return -a_swap(s, 1) & EBUSY;
+	return a_cas(s, 0, EBUSY);
 }
diff --git a/system/lib/libc/musl/src/thread/pthread_testcancel.c b/system/lib/libc/musl/src/thread/pthread_testcancel.c
index 33238c0..ee48e6d 100644
--- a/system/lib/libc/musl/src/thread/pthread_testcancel.c
+++ b/system/lib/libc/musl/src/thread/pthread_testcancel.c
@@ -1,8 +1,15 @@
 #include "pthread_impl.h"
+#include "libc.h"
 
-void __testcancel(void);
+static void dummy()
+{
+}
 
-void pthread_testcancel()
+weak_alias(dummy, __testcancel);
+
+void __pthread_testcancel()
 {
 	__testcancel();
 }
+
+weak_alias(__pthread_testcancel, pthread_testcancel);
diff --git a/system/lib/libc/musl/src/thread/sem_init.c b/system/lib/libc/musl/src/thread/sem_init.c
index e8e419c..5509243 100644
--- a/system/lib/libc/musl/src/thread/sem_init.c
+++ b/system/lib/libc/musl/src/thread/sem_init.c
@@ -10,5 +10,6 @@
 	}
 	sem->__val[0] = value;
 	sem->__val[1] = 0;
+	sem->__val[2] = pshared ? 0 : 128;
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/sem_open.c b/system/lib/libc/musl/src/thread/sem_open.c
index 9a95d25..fda0acd 100644
--- a/system/lib/libc/musl/src/thread/sem_open.c
+++ b/system/lib/libc/musl/src/thread/sem_open.c
@@ -20,7 +20,7 @@
 	sem_t *sem;
 	int refcnt;
 } *semtab;
-static int lock[2];
+static volatile int lock[2];
 
 #define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
 
@@ -126,6 +126,7 @@
 		e = link(tmp, name) ? errno : 0;
 		unlink(tmp);
 		if (!e) break;
+		munmap(map, sizeof(sem_t));
 		/* Failure is only fatal when doing an exclusive open;
 		 * otherwise, next iteration will try to open the
 		 * existing file. */
diff --git a/system/lib/libc/musl/src/thread/sem_post.c b/system/lib/libc/musl/src/thread/sem_post.c
index 14a2dfe..31e3293 100644
--- a/system/lib/libc/musl/src/thread/sem_post.c
+++ b/system/lib/libc/musl/src/thread/sem_post.c
@@ -3,7 +3,7 @@
 
 int sem_post(sem_t *sem)
 {
-	int val, waiters;
+	int val, waiters, priv = sem->__val[2];
 	do {
 		val = sem->__val[0];
 		waiters = sem->__val[1];
@@ -12,6 +12,6 @@
 			return -1;
 		}
 	} while (a_cas(sem->__val, val, val+1+(val<0)) != val);
-	if (val<0 || waiters) __wake(sem->__val, 1, 0);
+	if (val<0 || waiters) __wake(sem->__val, 1, priv);
 	return 0;
 }
diff --git a/system/lib/libc/musl/src/thread/sem_timedwait.c b/system/lib/libc/musl/src/thread/sem_timedwait.c
index 768b5f5..9782e6a 100644
--- a/system/lib/libc/musl/src/thread/sem_timedwait.c
+++ b/system/lib/libc/musl/src/thread/sem_timedwait.c
@@ -8,16 +8,24 @@
 
 int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict at)
 {
+	pthread_testcancel();
+
+	if (!sem_trywait(sem)) return 0;
+
+	int spins = 100;
+	while (spins-- && sem->__val[0] <= 0 && !sem->__val[1]) a_spin();
+
 	while (sem_trywait(sem)) {
 		int r;
 		a_inc(sem->__val+1);
 		a_cas(sem->__val, 0, -1);
-		r = __timedwait(sem->__val, -1, CLOCK_REALTIME, at, cleanup, sem->__val+1, 0);
+		pthread_cleanup_push(cleanup, (void *)(sem->__val+1));
+		r = __timedwait_cp(sem->__val, -1, CLOCK_REALTIME, at, sem->__val[2]);
+		pthread_cleanup_pop(1);
+		if (r && r != EINTR) {
 #ifdef __EMSCRIPTEN__
-		if (r == ECANCELED) r = EINTR;
+			if (r == ECANCELED) r = EINTR;
 #endif
-		a_dec(sem->__val+1);
-		if (r) {
 			errno = r;
 			return -1;
 		}
diff --git a/system/lib/libc/musl/src/thread/synccall.c b/system/lib/libc/musl/src/thread/synccall.c
index a21578d..000ec4e 100644
--- a/system/lib/libc/musl/src/thread/synccall.c
+++ b/system/lib/libc/musl/src/thread/synccall.c
@@ -1,90 +1,178 @@
 #include "pthread_impl.h"
 #include <semaphore.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <ctype.h>
+#include "futex.h"
+#include "atomic.h"
+#include "../dirent/__dirent.h"
 
 static struct chain {
 	struct chain *next;
-	sem_t sem, sem2;
-} *head, *cur;
+	int tid;
+	sem_t target_sem, caller_sem;
+} *volatile head;
 
+static volatile int synccall_lock[2];
+static volatile int target_tid;
 static void (*callback)(void *), *context;
-static int chainlen;
-static sem_t chainlock, chaindone;
+static volatile int dummy = 0;
+weak_alias(dummy, __block_new_threads);
 
-static void handler(int sig, siginfo_t *si, void *ctx)
+static void handler(int sig)
 {
 	struct chain ch;
-	pthread_t self = __pthread_self();
 	int old_errno = errno;
 
-	if (chainlen == libc.threads_minus_1) return;
+	sem_init(&ch.target_sem, 0, 0);
+	sem_init(&ch.caller_sem, 0, 0);
 
-	sigqueue(self->pid, SIGSYNCCALL, (union sigval){0});
+	ch.tid = __syscall(SYS_gettid);
 
-	sem_init(&ch.sem, 0, 0);
-	sem_init(&ch.sem2, 0, 0);
+	do ch.next = head;
+	while (a_cas_p(&head, ch.next, &ch) != ch.next);
 
-	while (sem_wait(&chainlock));
-	ch.next = head;
-	head = &ch;
-	if (++chainlen == libc.threads_minus_1) sem_post(&chaindone);
-	sem_post(&chainlock);
+	if (a_cas(&target_tid, ch.tid, 0) == (ch.tid | 0x80000000))
+		__syscall(SYS_futex, &target_tid, FUTEX_UNLOCK_PI|FUTEX_PRIVATE);
 
-	while (sem_wait(&ch.sem));
+	sem_wait(&ch.target_sem);
 	callback(context);
-	sem_post(&ch.sem2);
-	while (sem_wait(&ch.sem));
+	sem_post(&ch.caller_sem);
+	sem_wait(&ch.target_sem);
 
 	errno = old_errno;
 }
 
 void __synccall(void (*func)(void *), void *ctx)
 {
-	pthread_t self;
-	struct sigaction sa;
-	struct chain *next;
 	sigset_t oldmask;
+	int cs, i, r, pid, self;;
+	DIR dir = {0};
+	struct dirent *de;
+	struct sigaction sa = { .sa_flags = 0, .sa_handler = handler };
+	struct chain *cp, *next;
+	struct timespec ts;
 
-	if (!libc.threads_minus_1) {
-		func(ctx);
-		return;
-	}
+	/* Blocking signals in two steps, first only app-level signals
+	 * before taking the lock, then all signals after taking the lock,
+	 * is necessary to achieve AS-safety. Blocking them all first would
+	 * deadlock if multiple threads called __synccall. Waiting to block
+	 * any until after the lock would allow re-entry in the same thread
+	 * with the lock already held. */
+	__block_app_sigs(&oldmask);
+	LOCK(synccall_lock);
+	__block_all_sigs(0);
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
 
-	__inhibit_ptc();
-
-	__block_all_sigs(&oldmask);
-
-	sem_init(&chaindone, 0, 0);
-	sem_init(&chainlock, 0, 1);
-	chainlen = 0;
 	head = 0;
+
+	if (!libc.threaded) goto single_threaded;
+
 	callback = func;
 	context = ctx;
 
-	sa.sa_flags = SA_SIGINFO | SA_RESTART;
-	sa.sa_sigaction = handler;
-	sigfillset(&sa.sa_mask);
+	/* This atomic store ensures that any signaled threads will see the
+	 * above stores, and prevents more than a bounded number of threads,
+	 * those already in pthread_create, from creating new threads until
+	 * the value is cleared to zero again. */
+	a_store(&__block_new_threads, 1);
+
+	/* Block even implementation-internal signals, so that nothing
+	 * interrupts the SIGSYNCCALL handlers. The main possible source
+	 * of trouble is asynchronous cancellation. */
+	memset(&sa.sa_mask, -1, sizeof sa.sa_mask);
 	__libc_sigaction(SIGSYNCCALL, &sa, 0);
 
-	self = __pthread_self();
-	sigqueue(self->pid, SIGSYNCCALL, (union sigval){0});
-	while (sem_wait(&chaindone));
+	pid = __syscall(SYS_getpid);
+	self = __syscall(SYS_gettid);
 
-	sa.sa_flags = 0;
+	/* Since opendir is not AS-safe, the DIR needs to be setup manually
+	 * in automatic storage. Thankfully this is easy. */
+	dir.fd = open("/proc/self/task", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+	if (dir.fd < 0) goto out;
+
+	/* Initially send one signal per counted thread. But since we can't
+	 * synchronize with thread creation/exit here, there could be too
+	 * few signals. This initial signaling is just an optimization, not
+	 * part of the logic. */
+	for (i=libc.threads_minus_1; i; i--)
+		__syscall(SYS_kill, pid, SIGSYNCCALL);
+
+	/* Loop scanning the kernel-provided thread list until it shows no
+	 * threads that have not already replied to the signal. */
+	for (;;) {
+		int miss_cnt = 0;
+		while ((de = readdir(&dir))) {
+			if (!isdigit(de->d_name[0])) continue;
+			int tid = atoi(de->d_name);
+			if (tid == self || !tid) continue;
+
+			/* Set the target thread as the PI futex owner before
+			 * checking if it's in the list of caught threads. If it
+			 * adds itself to the list after we check for it, then
+			 * it will see its own tid in the PI futex and perform
+			 * the unlock operation. */
+			a_store(&target_tid, tid);
+
+			/* Thread-already-caught is a success condition. */
+			for (cp = head; cp && cp->tid != tid; cp=cp->next);
+			if (cp) continue;
+
+			r = -__syscall(SYS_tgkill, pid, tid, SIGSYNCCALL);
+
+			/* Target thread exit is a success condition. */
+			if (r == ESRCH) continue;
+
+			/* The FUTEX_LOCK_PI operation is used to loan priority
+			 * to the target thread, which otherwise may be unable
+			 * to run. Timeout is necessary because there is a race
+			 * condition where the tid may be reused by a different
+			 * process. */
+			clock_gettime(CLOCK_REALTIME, &ts);
+			ts.tv_nsec += 10000000;
+			if (ts.tv_nsec >= 1000000000) {
+				ts.tv_sec++;
+				ts.tv_nsec -= 1000000000;
+			}
+			r = -__syscall(SYS_futex, &target_tid,
+				FUTEX_LOCK_PI|FUTEX_PRIVATE, 0, &ts);
+
+			/* Obtaining the lock means the thread responded. ESRCH
+			 * means the target thread exited, which is okay too. */
+			if (!r || r == ESRCH) continue;
+
+			miss_cnt++;
+		}
+		if (!miss_cnt) break;
+		rewinddir(&dir);
+	}
+	close(dir.fd);
+
+	/* Serialize execution of callback in caught threads. */
+	for (cp=head; cp; cp=cp->next) {
+		sem_post(&cp->target_sem);
+		sem_wait(&cp->caller_sem);
+	}
+
 	sa.sa_handler = SIG_IGN;
 	__libc_sigaction(SIGSYNCCALL, &sa, 0);
 
-	for (cur=head; cur; cur=cur->next) {
-		sem_post(&cur->sem);
-		while (sem_wait(&cur->sem2));
-	}
+single_threaded:
 	func(ctx);
 
-	for (cur=head; cur; cur=next) {
-		next = cur->next;
-		sem_post(&cur->sem);
+	/* Only release the caught threads once all threads, including the
+	 * caller, have returned from the callback function. */
+	for (cp=head; cp; cp=next) {
+		next = cp->next;
+		sem_post(&cp->target_sem);
 	}
 
-	__restore_sigs(&oldmask);
+out:
+	a_store(&__block_new_threads, 0);
+	__wake(&__block_new_threads, -1, 1);
 
-	__release_ptc();
+	pthread_setcancelstate(cs, 0);
+	UNLOCK(synccall_lock);
+	__restore_sigs(&oldmask);
 }
diff --git a/system/lib/libc/musl/src/thread/thrd_create.c b/system/lib/libc/musl/src/thread/thrd_create.c
new file mode 100644
index 0000000..e033669
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/thrd_create.c
@@ -0,0 +1,14 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int __pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict);
+
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
+{
+	int ret = __pthread_create(thr, __ATTRP_C11_THREAD, (void *(*)(void *))func, arg);
+	switch (ret) {
+	case 0:      return thrd_success;
+	case EAGAIN: return thrd_nomem;
+	default:     return thrd_error;
+	}
+}
diff --git a/system/lib/libc/musl/src/thread/thrd_exit.c b/system/lib/libc/musl/src/thread/thrd_exit.c
new file mode 100644
index 0000000..b66bd99
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/thrd_exit.c
@@ -0,0 +1,9 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+_Noreturn void __pthread_exit(void *);
+
+_Noreturn void thrd_exit(int result)
+{
+	__pthread_exit((void*)(intptr_t)result);
+}
diff --git a/system/lib/libc/musl/src/thread/thrd_join.c b/system/lib/libc/musl/src/thread/thrd_join.c
new file mode 100644
index 0000000..ac66789
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/thrd_join.c
@@ -0,0 +1,12 @@
+#include <stdint.h>
+#include <threads.h>
+
+int __pthread_join(thrd_t, void**);
+
+int thrd_join(thrd_t t, int *res)
+{
+        void *pthread_res;
+        __pthread_join(t, &pthread_res);
+        if (res) *res = (int)(intptr_t)pthread_res;
+        return thrd_success;
+}
diff --git a/system/lib/libc/musl/src/thread/thrd_sleep.c b/system/lib/libc/musl/src/thread/thrd_sleep.c
new file mode 100644
index 0000000..e8dfe40
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/thrd_sleep.c
@@ -0,0 +1,13 @@
+#include <threads.h>
+#include <errno.h>
+#include "syscall.h"
+
+int thrd_sleep(const struct timespec *req, struct timespec *rem)
+{
+	int ret = __syscall(SYS_nanosleep, req, rem);
+	switch (ret) {
+	case 0:      return 0;
+	case -EINTR: return -1; /* value specified by C11 */
+	default:     return -2;
+	}
+}
diff --git a/system/lib/libc/musl/src/thread/thrd_yield.c b/system/lib/libc/musl/src/thread/thrd_yield.c
new file mode 100644
index 0000000..f7ad132
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/thrd_yield.c
@@ -0,0 +1,7 @@
+#include <threads.h>
+#include "syscall.h"
+
+void thrd_yield()
+{
+	__syscall(SYS_sched_yield);
+}
diff --git a/system/lib/libc/musl/src/thread/tss_create.c b/system/lib/libc/musl/src/thread/tss_create.c
new file mode 100644
index 0000000..251d22b
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/tss_create.c
@@ -0,0 +1,11 @@
+#include <threads.h>
+
+int __pthread_key_create(tss_t *, void (*)(void *));
+
+int tss_create(tss_t *tss, tss_dtor_t dtor)
+{
+	/* Different error returns are possible. C glues them together into
+	 * just failure notification. Can't be optimized to a tail call,
+	 * unless thrd_error equals EAGAIN. */
+	return __pthread_key_create(tss, dtor) ? thrd_error : thrd_success;
+}
diff --git a/system/lib/libc/musl/src/thread/tss_delete.c b/system/lib/libc/musl/src/thread/tss_delete.c
new file mode 100644
index 0000000..35db103
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/tss_delete.c
@@ -0,0 +1,8 @@
+#include <threads.h>
+
+int __pthread_key_delete(tss_t k);
+
+void tss_delete(tss_t key)
+{
+	__pthread_key_delete(key);
+}
diff --git a/system/lib/libc/musl/src/thread/tss_set.c b/system/lib/libc/musl/src/thread/tss_set.c
new file mode 100644
index 0000000..70c4fb7
--- /dev/null
+++ b/system/lib/libc/musl/src/thread/tss_set.c
@@ -0,0 +1,13 @@
+#include "pthread_impl.h"
+#include <threads.h>
+
+int tss_set(tss_t k, void *x)
+{
+	struct pthread *self = __pthread_self();
+	/* Avoid unnecessary COW */
+	if (self->tsd[k] != x) {
+		self->tsd[k] = x;
+		self->tsd_used = 1;
+	}
+	return thrd_success;
+}
diff --git a/system/lib/libc/musl/src/thread/vmlock.c b/system/lib/libc/musl/src/thread/vmlock.c
index aba9e31..75f3cb7 100644
--- a/system/lib/libc/musl/src/thread/vmlock.c
+++ b/system/lib/libc/musl/src/thread/vmlock.c
@@ -1,22 +1,21 @@
 #include "pthread_impl.h"
 
-static int vmlock[2];
+static volatile int vmlock[2];
 
-void __vm_lock(int inc)
+void __vm_wait()
 {
-	for (;;) {
-		int v = vmlock[0];
-		if (inc*v < 0) __wait(vmlock, vmlock+1, v, 1);
-		else if (a_cas(vmlock, v, v+inc)==v) break;
-	}
+	int tmp;
+	while ((tmp=vmlock[0]))
+		__wait(vmlock, vmlock+1, tmp, 1);
 }
 
-void __vm_unlock(void)
+void __vm_lock()
 {
-	int inc = vmlock[0]>0 ? -1 : 1;
-	if (a_fetch_add(vmlock, inc)==-inc && vmlock[1])
+	a_inc(vmlock);
+}
+
+void __vm_unlock()
+{
+	if (a_fetch_add(vmlock, -1)==1 && vmlock[1])
 		__wake(vmlock, -1, 1);
 }
-
-weak_alias(__vm_lock, __vm_lock_impl);
-weak_alias(__vm_unlock, __vm_unlock_impl);
diff --git a/system/lib/libc/musl/src/time/__map_file.c b/system/lib/libc/musl/src/time/__map_file.c
index 84ae839..b91eb8e 100644
--- a/system/lib/libc/musl/src/time/__map_file.c
+++ b/system/lib/libc/musl/src/time/__map_file.c
@@ -9,12 +9,12 @@
 {
 	struct stat st;
 	const unsigned char *map = MAP_FAILED;
-	int flags = O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NONBLOCK;
-	int fd = __syscall(SYS_open, pathname, flags);
+	int fd = __sys_open(pathname, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
 	if (fd < 0) return 0;
-	if (!__syscall(SYS_fstat, fd, &st))
+	if (!__syscall(SYS_fstat, fd, &st)) {
 		map = __mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+		*size = st.st_size;
+	}
 	__syscall(SYS_close, fd);
-	*size = st.st_size;
 	return map == MAP_FAILED ? 0 : map;
 }
diff --git a/system/lib/libc/musl/src/time/__secs_to_tm.c b/system/lib/libc/musl/src/time/__secs_to_tm.c
index f3c1cf9..3a3123a 100644
--- a/system/lib/libc/musl/src/time/__secs_to_tm.c
+++ b/system/lib/libc/musl/src/time/__secs_to_tm.c
@@ -10,10 +10,10 @@
 
 int __secs_to_tm(long long t, struct tm *tm)
 {
-	long long days, secs;
+	long long days, secs, years;
 	int remdays, remsecs, remyears;
 	int qc_cycles, c_cycles, q_cycles;
-	int years, months;
+	int months;
 	int wday, yday, leap;
 	static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29};
 
@@ -55,7 +55,7 @@
 	yday = remdays + 31 + 28 + leap;
 	if (yday >= 365+leap) yday -= 365+leap;
 
-	years = remyears + 4*q_cycles + 100*c_cycles + 400*qc_cycles;
+	years = remyears + 4*q_cycles + 100*c_cycles + 400LL*qc_cycles;
 
 	for (months=0; days_in_month[months] <= remdays; months++)
 		remdays -= days_in_month[months];
diff --git a/system/lib/libc/musl/src/time/__tz.c b/system/lib/libc/musl/src/time/__tz.c
index 17d1adf..8b84b9b 100644
--- a/system/lib/libc/musl/src/time/__tz.c
+++ b/system/lib/libc/musl/src/time/__tz.c
@@ -27,7 +27,7 @@
 static char *old_tz = old_tz_buf;
 static size_t old_tz_size = sizeof old_tz_buf;
 
-static int lock[2];
+static volatile int lock[2];
 
 static int getint(const char **p)
 {
@@ -118,14 +118,15 @@
 static void do_tzset()
 {
 	char buf[NAME_MAX+25], *pathname=buf+24;
-	const char *try, *s;
+	const char *try, *s, *p;
 	const unsigned char *map = 0;
 	size_t i;
 	static const char search[] =
 		"/usr/share/zoneinfo/\0/share/zoneinfo/\0/etc/zoneinfo/\0";
 
 	s = getenv("TZ");
-	if (!s || !*s) s = __gmt;
+	if (!s) s = "/etc/localtime";
+	if (!*s) s = __gmt;
 
 	if (old_tz && !strcmp(s, old_tz)) return;
 
@@ -144,19 +145,17 @@
 	}
 	if (old_tz) memcpy(old_tz, s, i+1);
 
-	if (*s == ':') s++;
-
 	/* Non-suid can use an absolute tzfile pathname or a relative
 	 * pathame beginning with "."; in secure mode, only the
 	 * standard path will be searched. */
-	if (*s == '/' || *s == '.') {
-		if (!libc.secure) map = __map_file(s, &map_size);
-	} else {
-		for (i=0; s[i] && s[i]!=','; i++) {
-			if (s[i]=='/') {
-				size_t l = strlen(s);
-				if (l > NAME_MAX || strchr(s, '.'))
-					break;
+	if (*s == ':' || ((p=strchr(s, '/')) && !memchr(s, ',', p-s))) {
+		if (*s == ':') s++;
+		if (*s == '/' || *s == '.') {
+			if (!libc.secure || !strcmp(s, "/etc/localtime"))
+				map = __map_file(s, &map_size);
+		} else {
+			size_t l = strlen(s);
+			if (l <= NAME_MAX && !strchr(s, '.')) {
 				memcpy(pathname, s, l+1);
 				pathname[l] = 0;
 				for (try=search; !map && *try; try+=l+1) {
@@ -164,9 +163,9 @@
 					memcpy(pathname-l, try, l);
 					map = __map_file(pathname-l, &map_size);
 				}
-				break;
 			}
 		}
+		if (!map) s = __gmt;
 	}
 	if (map && (map_size < 44 || memcmp(map, "TZif", 4))) {
 		__munmap((void *)map, map_size);
@@ -355,9 +354,9 @@
 		size_t alt, i = scan_trans(t, local, &alt);
 		if (i != -1) {
 			*isdst = types[6*i+4];
-			*offset = -(int32_t)zi_read32(types+6*i);
+			*offset = (int32_t)zi_read32(types+6*i);
 			*zonename = (const char *)abbrevs + types[6*i+5];
-			if (oppoff) *oppoff = -(int32_t)zi_read32(types+6*alt);
+			if (oppoff) *oppoff = (int32_t)zi_read32(types+6*alt);
 			UNLOCK(lock);
 			return;
 		}
@@ -391,15 +390,15 @@
 	}
 std:
 	*isdst = 0;
-	*offset = __timezone;
-	if (oppoff) *oppoff = dst_off;
+	*offset = -__timezone;
+	if (oppoff) *oppoff = -dst_off;
 	*zonename = __tzname[0];
 	UNLOCK(lock);
 	return;
 dst:
 	*isdst = 1;
-	*offset = dst_off;
-	if (oppoff) *oppoff = __timezone;
+	*offset = -dst_off;
+	if (oppoff) *oppoff = -__timezone;
 	*zonename = __tzname[1];
 	UNLOCK(lock);
 }
diff --git a/system/lib/libc/musl/src/time/clock_gettime.c b/system/lib/libc/musl/src/time/clock_gettime.c
index ce9f220..2412880 100644
--- a/system/lib/libc/musl/src/time/clock_gettime.c
+++ b/system/lib/libc/musl/src/time/clock_gettime.c
@@ -3,11 +3,47 @@
 #include <stdint.h>
 #include "syscall.h"
 #include "libc.h"
+#include "atomic.h"
 
-static int sc_clock_gettime(clockid_t clk, struct timespec *ts)
+#ifdef VDSO_CGT_SYM
+
+void *__vdsosym(const char *, const char *);
+
+static void *volatile vdso_func;
+
+static int cgt_init(clockid_t clk, struct timespec *ts)
 {
-	int r = __syscall(SYS_clock_gettime, clk, ts);
-	if (!r) return r;
+	void *p = __vdsosym(VDSO_CGT_VER, VDSO_CGT_SYM);
+	int (*f)(clockid_t, struct timespec *) =
+		(int (*)(clockid_t, struct timespec *))p;
+	a_cas_p(&vdso_func, (void *)cgt_init, p);
+	return f ? f(clk, ts) : -ENOSYS;
+}
+
+static void *volatile vdso_func = (void *)cgt_init;
+
+#endif
+
+int __clock_gettime(clockid_t clk, struct timespec *ts)
+{
+	int r;
+
+#ifdef VDSO_CGT_SYM
+	int (*f)(clockid_t, struct timespec *) =
+		(int (*)(clockid_t, struct timespec *))vdso_func;
+	if (f) {
+		r = f(clk, ts);
+		if (!r) return r;
+		if (r == -EINVAL) return __syscall_ret(r);
+		/* Fall through on errors other than EINVAL. Some buggy
+		 * vdso implementations return ENOSYS for clocks they
+		 * can't handle, rather than making the syscall. This
+		 * also handles the case where cgt_init fails to find
+		 * a vdso function to use. */
+	}
+#endif
+
+	r = __syscall(SYS_clock_gettime, clk, ts);
 	if (r == -ENOSYS) {
 		if (clk == CLOCK_REALTIME) {
 			__syscall(SYS_gettimeofday, ts, 0);
@@ -16,18 +52,7 @@
 		}
 		r = -EINVAL;
 	}
-	errno = -r;
-	return -1;
-}
-
-weak_alias(sc_clock_gettime, __vdso_clock_gettime);
-
-int (*__cgt)(clockid_t, struct timespec *) = __vdso_clock_gettime;
-
-int __clock_gettime(clockid_t clk, struct timespec *ts)
-{
-	/* Conditional is to make this work prior to dynamic linking */
-	return __cgt ? __cgt(clk, ts) : sc_clock_gettime(clk, ts);
+	return __syscall_ret(r);
 }
 
 weak_alias(__clock_gettime, clock_gettime);
diff --git a/system/lib/libc/musl/src/time/localtime_r.c b/system/lib/libc/musl/src/time/localtime_r.c
index 1d43d9f..2e62c29 100644
--- a/system/lib/libc/musl/src/time/localtime_r.c
+++ b/system/lib/libc/musl/src/time/localtime_r.c
@@ -11,7 +11,7 @@
 		return 0;
 	}
 	__secs_to_zone(*t, 0, &tm->tm_isdst, &tm->__tm_gmtoff, 0, &tm->__tm_zone);
-	if (__secs_to_tm((long long)*t - tm->__tm_gmtoff, tm) < 0) {
+	if (__secs_to_tm((long long)*t + tm->__tm_gmtoff, tm) < 0) {
 		errno = EOVERFLOW;
 		return 0;
 	}
diff --git a/system/lib/libc/musl/src/time/mktime.c b/system/lib/libc/musl/src/time/mktime.c
index 0ab4780..bad3f07 100644
--- a/system/lib/libc/musl/src/time/mktime.c
+++ b/system/lib/libc/musl/src/time/mktime.c
@@ -10,14 +10,14 @@
 	__secs_to_zone(t, 1, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone);
 
 	if (tm->tm_isdst>=0 && new.tm_isdst!=tm->tm_isdst)
-		t += opp - new.__tm_gmtoff;
+		t -= opp - new.__tm_gmtoff;
 
-	t += new.__tm_gmtoff;
+	t -= new.__tm_gmtoff;
 	if ((time_t)t != t) goto error;
 
 	__secs_to_zone(t, 0, &new.tm_isdst, &new.__tm_gmtoff, &opp, &new.__tm_zone);
 
-	if (__secs_to_tm(t - new.__tm_gmtoff, &new) < 0) goto error;
+	if (__secs_to_tm(t + new.__tm_gmtoff, &new) < 0) goto error;
 
 	*tm = new;
 	return t;
diff --git a/system/lib/libc/musl/src/time/strftime.c b/system/lib/libc/musl/src/time/strftime.c
index 75ebca6..f1ccc4d 100644
--- a/system/lib/libc/musl/src/time/strftime.c
+++ b/system/lib/libc/musl/src/time/strftime.c
@@ -5,6 +5,7 @@
 #include <locale.h>
 #include <time.h>
 #include <limits.h>
+#include "locale_impl.h"
 #include "libc.h"
 #include "time_impl.h"
 
@@ -20,24 +21,24 @@
 
 static int week_num(const struct tm *tm)
 {
-	int val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7;
+	int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
 	/* If 1 Jan is just 1-3 days past Monday,
 	 * the previous week is also in this year. */
-	if ((tm->tm_wday - tm->tm_yday - 2 + 371) % 7 <= 2)
+	if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2)
 		val++;
 	if (!val) {
 		val = 52;
 		/* If 31 December of prev year a Thursday,
 		 * or Friday of a leap year, then the
 		 * prev year has 53 weeks. */
-		int dec31 = (tm->tm_wday - tm->tm_yday - 1 + 7) % 7;
+		int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7;
 		if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1)))
 			val++;
 	} else if (val == 53) {
 		/* If 1 January is not a Thursday, and not
 		 * a Wednesday of a leap year, then this
 		 * year has only 52 weeks. */
-		int jan1 = (tm->tm_wday - tm->tm_yday + 371) % 7;
+		int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7;
 		if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year)))
 			val = 1;
 	}
@@ -51,21 +52,25 @@
 {
 	nl_item item;
 	long long val;
-	const char *fmt;
+	const char *fmt = "-";
 	int width = 2;
 
 	switch (f) {
 	case 'a':
+		if (tm->tm_wday > 6U) goto string;
 		item = ABDAY_1 + tm->tm_wday;
 		goto nl_strcat;
 	case 'A':
+		if (tm->tm_wday > 6U) goto string;
 		item = DAY_1 + tm->tm_wday;
 		goto nl_strcat;
 	case 'h':
 	case 'b':
+		if (tm->tm_mon > 11U) goto string;
 		item = ABMON_1 + tm->tm_mon;
 		goto nl_strcat;
 	case 'B':
+		if (tm->tm_mon > 11U) goto string;
 		item = MON_1 + tm->tm_mon;
 		goto nl_strcat;
 	case 'c':
@@ -125,7 +130,7 @@
 		fmt = "%H:%M";
 		goto recu_strftime;
 	case 's':
-		val = __tm_to_secs(tm) + tm->__tm_gmtoff;
+		val = __tm_to_secs(tm) - tm->__tm_gmtoff;
 		width = 1;
 		goto number;
 	case 'S':
@@ -142,10 +147,10 @@
 		width = 1;
 		goto number;
 	case 'U':
-		val = (tm->tm_yday + 7 - tm->tm_wday) / 7;
+		val = (tm->tm_yday + 7U - tm->tm_wday) / 7;
 		goto number;
 	case 'W':
-		val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7;
+		val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
 		goto number;
 	case 'V':
 		val = week_num(tm);
@@ -164,7 +169,7 @@
 		val = tm->tm_year % 100;
 		goto number;
 	case 'Y':
-		val = tm->tm_year + 1900;
+		val = tm->tm_year + 1900LL;
 		if (val >= 10000) {
 			*l = snprintf(*s, sizeof *s, "+%lld", val);
 			return *s;
@@ -177,7 +182,7 @@
 			return "";
 		}
 		*l = snprintf(*s, sizeof *s, "%+.2d%.2d",
-			(-tm->__tm_gmtoff)/3600,
+			(tm->__tm_gmtoff)/3600,
 			abs(tm->__tm_gmtoff%3600)/60);
 		return *s;
 	case 'Z':
@@ -263,7 +268,7 @@
 
 size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm)
 {
-	return __strftime_l(s, n, f, tm, 0);
+	return __strftime_l(s, n, f, tm, CURRENT_LOCALE);
 }
 
 weak_alias(__strftime_l, strftime_l);
diff --git a/system/lib/libc/musl/src/time/strptime.c b/system/lib/libc/musl/src/time/strptime.c
index 4d9eea4..f41f55f 100644
--- a/system/lib/libc/musl/src/time/strptime.c
+++ b/system/lib/libc/musl/src/time/strptime.c
@@ -11,6 +11,7 @@
 	int i, w, neg, adj, min, range, *dest, dummy;
 	const char *ex;
 	size_t len;
+	int want_century = 0, century = 0;
 	while (*f) {
 		if (*f != '%') {
 			if (isspace(*f)) for (; *s && isspace(*s); s++);
@@ -40,9 +41,9 @@
 			if (!s) return 0;
 			break;
 		case 'C':
-			/* FIXME */
-			dest = &dummy;
+			dest = &century;
 			if (w<0) w=2;
+			want_century |= 2;
 			goto numeric_digits;
 		case 'd': case 'e':
 			dest = &tm->tm_mday;
@@ -135,14 +136,15 @@
 			if (!s) return 0;
 			break;
 		case 'y':
-			/* FIXME */
-			dest = &dummy;
+			dest = &tm->tm_year;
 			w = 2;
+			want_century |= 1;
 			goto numeric_digits;
 		case 'Y':
 			dest = &tm->tm_year;
 			if (w<0) w=4;
 			adj = 1900;
+			want_century = 0;
 			goto numeric_digits;
 		case '%':
 			if (*s++ != '%') return 0;
@@ -187,5 +189,9 @@
 			;
 		}
 	}
+	if (want_century) {
+		if (want_century & 2) tm->tm_year += century * 100 - 1900;
+		else if (tm->tm_year <= 68) tm->tm_year += 100;
+	}
 	return (char *)s;
 }
diff --git a/system/lib/libc/musl/src/time/timespec_get.c b/system/lib/libc/musl/src/time/timespec_get.c
new file mode 100644
index 0000000..03c5a77
--- /dev/null
+++ b/system/lib/libc/musl/src/time/timespec_get.c
@@ -0,0 +1,12 @@
+#include <time.h>
+
+int __clock_gettime(clockid_t, struct timespec *);
+
+/* There is no other implemented value than TIME_UTC; all other values
+ * are considered erroneous. */
+int timespec_get(struct timespec * ts, int base)
+{
+	if (base != TIME_UTC) return 0;
+	int ret = __clock_gettime(CLOCK_REALTIME, ts);
+	return ret < 0 ? 0 : base;
+}
diff --git a/system/lib/libc/musl/src/time/utime.c b/system/lib/libc/musl/src/time/utime.c
index b2b5741..e7592b2 100644
--- a/system/lib/libc/musl/src/time/utime.c
+++ b/system/lib/libc/musl/src/time/utime.c
@@ -1,14 +1,11 @@
 #include <utime.h>
-#include <sys/time.h>
-#include "syscall.h"
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
 
 int utime(const char *path, const struct utimbuf *times)
 {
-	if (times) {
-		struct timeval tv[2] = {
-			{ .tv_sec = times->actime },
-			{ .tv_sec = times->modtime } };
-		return syscall(SYS_utimes, path, tv);
-	}
-	return syscall(SYS_utimes, path, 0);
+	return utimensat(AT_FDCWD, path, times ? ((struct timespec [2]){
+		{ .tv_sec = times->actime }, { .tv_sec = times->modtime }})
+		: 0, 0);
 }
diff --git a/system/lib/libc/musl/src/time/wcsftime.c b/system/lib/libc/musl/src/time/wcsftime.c
index 8d2a2eb..638e64f 100644
--- a/system/lib/libc/musl/src/time/wcsftime.c
+++ b/system/lib/libc/musl/src/time/wcsftime.c
@@ -1,6 +1,7 @@
 #include <wchar.h>
 #include <time.h>
 #include <locale.h>
+#include "locale_impl.h"
 #include "libc.h"
 
 const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc);
@@ -64,7 +65,7 @@
 
 size_t wcsftime(wchar_t *restrict wcs, size_t n, const wchar_t *restrict f, const struct tm *restrict tm)
 {
-	return __wcsftime_l(wcs, n, f, tm, 0);
+	return __wcsftime_l(wcs, n, f, tm, CURRENT_LOCALE);
 }
 
 weak_alias(__wcsftime_l, wcsftime_l);
diff --git a/system/lib/libc/musl/src/unistd/access.c b/system/lib/libc/musl/src/unistd/access.c
index e7ce73a..d6eed68 100644
--- a/system/lib/libc/musl/src/unistd/access.c
+++ b/system/lib/libc/musl/src/unistd/access.c
@@ -1,7 +1,12 @@
 #include <unistd.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int access(const char *filename, int amode)
 {
+#ifdef SYS_access
 	return syscall(SYS_access, filename, amode);
+#else
+	return syscall(SYS_faccessat, AT_FDCWD, filename, amode, 0);
+#endif
 }
diff --git a/system/lib/libc/musl/src/unistd/chown.c b/system/lib/libc/musl/src/unistd/chown.c
index 95f6f61..14b0325 100644
--- a/system/lib/libc/musl/src/unistd/chown.c
+++ b/system/lib/libc/musl/src/unistd/chown.c
@@ -1,7 +1,12 @@
 #include <unistd.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int chown(const char *path, uid_t uid, gid_t gid)
 {
+#ifdef SYS_chown
 	return syscall(SYS_chown, path, uid, gid);
+#else
+	return syscall(SYS_fchownat, AT_FDCWD, path, uid, gid, 0);
+#endif
 }
diff --git a/system/lib/libc/musl/src/unistd/close.c b/system/lib/libc/musl/src/unistd/close.c
index e8f813d..fa3c6ca 100644
--- a/system/lib/libc/musl/src/unistd/close.c
+++ b/system/lib/libc/musl/src/unistd/close.c
@@ -3,9 +3,17 @@
 #include "syscall.h"
 #include "libc.h"
 
+static int dummy(int fd)
+{
+	return fd;
+}
+
+weak_alias(dummy, __aio_close);
+
 int close(int fd)
 {
+	fd = __aio_close(fd);
 	int r = __syscall_cp(SYS_close, fd);
-	if (r == -EINTR) r = -EINPROGRESS;
+	if (r == -EINTR) r = 0;
 	return __syscall_ret(r);
 }
diff --git a/system/lib/libc/musl/src/unistd/ctermid.c b/system/lib/libc/musl/src/unistd/ctermid.c
index 7768405..1612770 100644
--- a/system/lib/libc/musl/src/unistd/ctermid.c
+++ b/system/lib/libc/musl/src/unistd/ctermid.c
@@ -1,19 +1,7 @@
 #include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-#include "syscall.h"
+#include <string.h>
 
 char *ctermid(char *s)
 {
-	static char s2[L_ctermid];
-	int fd;
-	if (!s) s = s2;
-	*s = 0;
-	fd = open("/dev/tty", O_WRONLY | O_NOCTTY | O_CLOEXEC);
-	if (fd >= 0) {
-		ttyname_r(fd, s, L_ctermid);
-		__syscall(SYS_close, fd);
-	}
-	return s;
+	return s ? strcpy(s, "/dev/tty") : "/dev/tty";
 }
diff --git a/system/lib/libc/musl/src/unistd/dup2.c b/system/lib/libc/musl/src/unistd/dup2.c
index 87a0d44..8f43c6d 100644
--- a/system/lib/libc/musl/src/unistd/dup2.c
+++ b/system/lib/libc/musl/src/unistd/dup2.c
@@ -1,10 +1,20 @@
 #include <unistd.h>
 #include <errno.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int dup2(int old, int new)
 {
 	int r;
+#ifdef SYS_dup2
 	while ((r=__syscall(SYS_dup2, old, new))==-EBUSY);
+#else
+	if (old==new) {
+		r = __syscall(SYS_fcntl, old, F_GETFD);
+		if (r >= 0) return old;
+	} else {
+		while ((r=__syscall(SYS_dup3, old, new, 0))==-EBUSY);
+	}
+#endif
 	return __syscall_ret(r);
 }
diff --git a/system/lib/libc/musl/src/unistd/dup3.c b/system/lib/libc/musl/src/unistd/dup3.c
index 1f7134b..0eb6caf 100644
--- a/system/lib/libc/musl/src/unistd/dup3.c
+++ b/system/lib/libc/musl/src/unistd/dup3.c
@@ -8,6 +8,7 @@
 int __dup3(int old, int new, int flags)
 {
 	int r;
+#ifdef SYS_dup2
 	if (old==new) return __syscall_ret(-EINVAL);
 	if (flags & O_CLOEXEC) {
 		while ((r=__syscall(SYS_dup3, old, new, flags))==-EBUSY);
@@ -15,6 +16,9 @@
 	}
 	while ((r=__syscall(SYS_dup2, old, new))==-EBUSY);
 	if (flags & O_CLOEXEC) __syscall(SYS_fcntl, new, F_SETFD, FD_CLOEXEC);
+#else
+	while ((r=__syscall(SYS_dup3, old, new, flags))==-EBUSY);
+#endif
 	return __syscall_ret(r);
 }
 
diff --git a/system/lib/libc/musl/src/unistd/fchown.c b/system/lib/libc/musl/src/unistd/fchown.c
index 36633b0..0345984 100644
--- a/system/lib/libc/musl/src/unistd/fchown.c
+++ b/system/lib/libc/musl/src/unistd/fchown.c
@@ -13,5 +13,10 @@
 
 	char buf[15+3*sizeof(int)];
 	__procfdname(buf, fd);
+#ifdef SYS_chown
 	return syscall(SYS_chown, buf, uid, gid);
+#else
+	return syscall(SYS_fchownat, AT_FDCWD, buf, uid, gid);
+#endif
+
 }
diff --git a/system/lib/libc/musl/src/unistd/fdatasync.c b/system/lib/libc/musl/src/unistd/fdatasync.c
index dd4d41c..3895ae5 100644
--- a/system/lib/libc/musl/src/unistd/fdatasync.c
+++ b/system/lib/libc/musl/src/unistd/fdatasync.c
@@ -3,5 +3,5 @@
 
 int fdatasync(int fd)
 {
-	return syscall(SYS_fdatasync, fd);
+	return syscall_cp(SYS_fdatasync, fd);
 }
diff --git a/system/lib/libc/musl/src/unistd/fsync.c b/system/lib/libc/musl/src/unistd/fsync.c
index dc4727c..7a1c80b 100644
--- a/system/lib/libc/musl/src/unistd/fsync.c
+++ b/system/lib/libc/musl/src/unistd/fsync.c
@@ -3,5 +3,5 @@
 
 int fsync(int fd)
 {
-	return syscall(SYS_fsync, fd);
+	return syscall_cp(SYS_fsync, fd);
 }
diff --git a/system/lib/libc/musl/src/unistd/getpgrp.c b/system/lib/libc/musl/src/unistd/getpgrp.c
index 433f42e..90e9bb0 100644
--- a/system/lib/libc/musl/src/unistd/getpgrp.c
+++ b/system/lib/libc/musl/src/unistd/getpgrp.c
@@ -3,5 +3,5 @@
 
 pid_t getpgrp(void)
 {
-	return __syscall(SYS_getpgrp);
+	return __syscall(SYS_getpgid, 0);
 }
diff --git a/system/lib/libc/musl/src/unistd/isatty.c b/system/lib/libc/musl/src/unistd/isatty.c
index cff6e9f..98732ba 100644
--- a/system/lib/libc/musl/src/unistd/isatty.c
+++ b/system/lib/libc/musl/src/unistd/isatty.c
@@ -1,8 +1,9 @@
 #include <unistd.h>
-#include <termios.h>
+#include <sys/ioctl.h>
+#include "syscall.h"
 
 int isatty(int fd)
 {
-	struct termios t;
-	return tcgetattr(fd, &t) == 0;
+	struct winsize wsz;
+	return !syscall(SYS_ioctl, fd, TIOCGWINSZ, &wsz);
 }
diff --git a/system/lib/libc/musl/src/unistd/lchown.c b/system/lib/libc/musl/src/unistd/lchown.c
index de871ae..ccd5ee0 100644
--- a/system/lib/libc/musl/src/unistd/lchown.c
+++ b/system/lib/libc/musl/src/unistd/lchown.c
@@ -1,7 +1,12 @@
 #include <unistd.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int lchown(const char *path, uid_t uid, gid_t gid)
 {
+#ifdef SYS_lchown
 	return syscall(SYS_lchown, path, uid, gid);
+#else
+	return syscall(SYS_fchownat, AT_FDCWD, path, uid, gid, AT_SYMLINK_NOFOLLOW);
+#endif
 }
diff --git a/system/lib/libc/musl/src/unistd/link.c b/system/lib/libc/musl/src/unistd/link.c
index 20193f2..feec18e 100644
--- a/system/lib/libc/musl/src/unistd/link.c
+++ b/system/lib/libc/musl/src/unistd/link.c
@@ -1,7 +1,12 @@
 #include <unistd.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int link(const char *existing, const char *new)
 {
+#ifdef SYS_link
 	return syscall(SYS_link, existing, new);
+#else
+	return syscall(SYS_linkat, AT_FDCWD, existing, AT_FDCWD, new, 0);
+#endif
 }
diff --git a/system/lib/libc/musl/src/unistd/lseek.c b/system/lib/libc/musl/src/unistd/lseek.c
index 0a5ed39..51166e5 100644
--- a/system/lib/libc/musl/src/unistd/lseek.c
+++ b/system/lib/libc/musl/src/unistd/lseek.c
@@ -6,7 +6,13 @@
 {
 #ifdef SYS__llseek
 	off_t result;
-	return syscall(SYS__llseek, fd, offset>>32, offset, &result, whence) ? -1 : result;
+	off_t offset_hi =
+#ifndef __EMSCRIPTEN__
+		offset >> 32;
+#else
+		0;
+#endif
+	return syscall(SYS__llseek, fd, offset_hi, offset, &result, whence) ? -1 : result;
 #else
 	return syscall(SYS_lseek, fd, offset, whence);
 #endif
diff --git a/system/lib/libc/musl/src/unistd/pause.c b/system/lib/libc/musl/src/unistd/pause.c
index f7ed17d..56eb171 100644
--- a/system/lib/libc/musl/src/unistd/pause.c
+++ b/system/lib/libc/musl/src/unistd/pause.c
@@ -1,8 +1,13 @@
 #include <unistd.h>
+#include <signal.h>
 #include "syscall.h"
 #include "libc.h"
 
 int pause(void)
 {
+#ifdef SYS_pause
 	return syscall_cp(SYS_pause);
+#else
+	return syscall_cp(SYS_ppoll, 0, 0, 0, 0);
+#endif
 }
diff --git a/system/lib/libc/musl/src/unistd/pipe.c b/system/lib/libc/musl/src/unistd/pipe.c
index 36c6f13..d07b8d2 100644
--- a/system/lib/libc/musl/src/unistd/pipe.c
+++ b/system/lib/libc/musl/src/unistd/pipe.c
@@ -3,5 +3,9 @@
 
 int pipe(int fd[2])
 {
+#ifdef SYS_pipe
 	return syscall(SYS_pipe, fd);
+#else
+	return syscall(SYS_pipe2, fd, 0);
+#endif
 }
diff --git a/system/lib/libc/musl/src/unistd/readlink.c b/system/lib/libc/musl/src/unistd/readlink.c
index ec291e3..a152d52 100644
--- a/system/lib/libc/musl/src/unistd/readlink.c
+++ b/system/lib/libc/musl/src/unistd/readlink.c
@@ -1,7 +1,12 @@
 #include <unistd.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize)
 {
+#ifdef SYS_readlink
 	return syscall(SYS_readlink, path, buf, bufsize);
+#else
+	return syscall(SYS_readlinkat, AT_FDCWD, path, buf, bufsize);
+#endif
 }
diff --git a/system/lib/libc/musl/src/unistd/rmdir.c b/system/lib/libc/musl/src/unistd/rmdir.c
index dfe1605..6825ffc 100644
--- a/system/lib/libc/musl/src/unistd/rmdir.c
+++ b/system/lib/libc/musl/src/unistd/rmdir.c
@@ -1,7 +1,12 @@
 #include <unistd.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int rmdir(const char *path)
 {
+#ifdef SYS_rmdir
 	return syscall(SYS_rmdir, path);
+#else
+	return syscall(SYS_unlinkat, AT_FDCWD, path, AT_REMOVEDIR);
+#endif
 }
diff --git a/system/lib/libc/musl/src/unistd/setxid.c b/system/lib/libc/musl/src/unistd/setxid.c
index 43ecc0d..dc1377f 100644
--- a/system/lib/libc/musl/src/unistd/setxid.c
+++ b/system/lib/libc/musl/src/unistd/setxid.c
@@ -1,54 +1,43 @@
 #include <unistd.h>
 #include <errno.h>
-#include <sys/resource.h>
 #include "syscall.h"
 #include "libc.h"
+#include "pthread_impl.h"
 
 struct ctx {
 	int id, eid, sid;
-	int nr, rlim, err;
+	int nr, err;
 };
 
-/* We jump through hoops to eliminate the possibility of partial failures. */
-
-int __setrlimit(int, const struct rlimit *);
-
 static void do_setxid(void *p)
 {
 	struct ctx *c = p;
-	if (c->err) return;
-	if (c->rlim && c->id >= 0 && c->id != getuid()) {
-		struct rlimit inf = { RLIM_INFINITY, RLIM_INFINITY }, old;
-		getrlimit(RLIMIT_NPROC, &old);
-		if ((c->err = -__setrlimit(RLIMIT_NPROC, &inf)) && libc.threads_minus_1)
-			return;
-#ifndef __EMSCRIPTEN__
-		c->err = -__syscall(c->nr, c->id, c->eid, c->sid);
+	if (c->err>0) return;
+#ifdef __EMSCRIPTEN__
+	c->err = EPERM; // we don't allow dynamic syscalls, and don't need to support these anyhow
+	return;
 #else
-    c->err = EPERM; // we don't allow dynamic syscalls, and don't need to support these anyhow
-#endif
-		__setrlimit(RLIMIT_NPROC, &old);
-		return;
+	int ret = -__syscall(c->nr, c->id, c->eid, c->sid);
+	if (ret && !c->err) {
+		/* If one thread fails to set ids after another has already
+		 * succeeded, forcibly killing the process is the only safe
+		 * thing to do. State is inconsistent and dangerous. Use
+		 * SIGKILL because it is uncatchable. */
+		__block_all_sigs(0);
+		__syscall(SYS_kill, __syscall(SYS_getpid), SIGKILL);
 	}
-#ifndef __EMSCRIPTEN__
-	c->err = -__syscall(c->nr, c->id, c->eid, c->sid);
-#else
-  c->err = EPERM; // we don't allow dynamic syscalls, and don't need to support these anyhow
+	c->err = ret;
 #endif
 }
 
 int __setxid(int nr, int id, int eid, int sid)
 {
-	struct ctx c = { .nr = nr, .id = id, .eid = eid, .sid = sid };
-	switch (nr) {
-	case SYS_setuid:
-	case SYS_setreuid:
-	case SYS_setresuid:
-		c.rlim = 1;
-	}
+	/* err is initially nonzero so that failure of the first thread does not
+	 * trigger the safety kill above. */
+	struct ctx c = { .nr = nr, .id = id, .eid = eid, .sid = sid, .err = -1 };
 	__synccall(do_setxid, &c);
 	if (c.err) {
-		errno = c.err;
+		if (c.err>0) errno = c.err;
 		return -1;
 	}
 	return 0;
diff --git a/system/lib/libc/musl/src/unistd/symlink.c b/system/lib/libc/musl/src/unistd/symlink.c
index 5902d45..0973d78 100644
--- a/system/lib/libc/musl/src/unistd/symlink.c
+++ b/system/lib/libc/musl/src/unistd/symlink.c
@@ -1,7 +1,12 @@
 #include <unistd.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int symlink(const char *existing, const char *new)
 {
+#ifdef SYS_symlink
 	return syscall(SYS_symlink, existing, new);
+#else
+	return syscall(SYS_symlinkat, existing, AT_FDCWD, new);
+#endif
 }
diff --git a/system/lib/libc/musl/src/unistd/unlink.c b/system/lib/libc/musl/src/unistd/unlink.c
index bdb37be..c40c28d 100644
--- a/system/lib/libc/musl/src/unistd/unlink.c
+++ b/system/lib/libc/musl/src/unistd/unlink.c
@@ -1,7 +1,12 @@
 #include <unistd.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 int unlink(const char *path)
 {
+#ifdef SYS_unlink
 	return syscall(SYS_unlink, path);
+#else
+	return syscall(SYS_unlinkat, AT_FDCWD, path, 0);
+#endif
 }
diff --git a/system/lib/pthread/library_pthread.c b/system/lib/pthread/library_pthread.c
index b14a771..1a9f69e 100644
--- a/system/lib/pthread/library_pthread.c
+++ b/system/lib/pthread/library_pthread.c
@@ -18,6 +18,7 @@
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
+#include "../internal/libc.h"
 #include "../internal/pthread_impl.h"
 #include <assert.h>
 
@@ -31,6 +32,8 @@
 // Extra pthread_attr_t field:
 #define _a_transferredcanvases __u.__s[9]
 
+void __pthread_testcancel();
+
 int emscripten_pthread_attr_gettransferredcanvases(const pthread_attr_t *a, const char **str)
 {
 	*str = (const char *)a->_a_transferredcanvases;
@@ -54,7 +57,7 @@
 	assert(mutex);
 	assert(mutex->_m_lock == 0);
 	mutex->_m_lock = pthread_self()->tid;
-	if (_pthread_getcanceltype() == PTHREAD_CANCEL_ASYNCHRONOUS) pthread_testcancel();
+	if (_pthread_getcanceltype() == PTHREAD_CANCEL_ASYNCHRONOUS) __pthread_testcancel();
 }
 
 double _pthread_msecs_until(const struct timespec *restrict at)
@@ -93,7 +96,7 @@
 				double msecs = INFINITY;
 				if (threadCancelType == PTHREAD_CANCEL_ASYNCHRONOUS) {
 					// Sleep in small slices so that we can test cancellation to honor PTHREAD_CANCEL_ASYNCHRONOUS.
-					pthread_testcancel();
+					__pthread_testcancel();
 					msecs = 100;
 				}
 				emscripten_futex_wait(&mutex->_m_addr, 2, msecs);
@@ -174,7 +177,7 @@
 				// Sleep in small slices if thread type is PTHREAD_CANCEL_ASYNCHRONOUS
 				// so that we can honor PTHREAD_CANCEL_ASYNCHRONOUS requests.
 				if (threadCancelType == PTHREAD_CANCEL_ASYNCHRONOUS) {
-					pthread_testcancel();
+					__pthread_testcancel();
 					if (msecs > 100) msecs = 100;
 				}
 				int ret = emscripten_futex_wait(&mutex->_m_addr, 2, msecs);
@@ -227,7 +230,7 @@
 	return pthread_ptr->threadStatus == 2/*canceled*/;
 }
 
-void pthread_testcancel()
+void __pthread_testcancel()
 {
 	struct pthread *self = pthread_self();
 	if (self->canceldisable) return;
@@ -257,7 +260,7 @@
 #endif
 	while(now < target) {
 		if (is_main_thread) emscripten_main_thread_process_queued_calls(); // Assist other threads by executing proxied operations that are effectively singlethreaded.
-		pthread_testcancel(); // pthreads spec: usleep is a cancellation point, so it must test if this thread is cancelled during the sleep.
+		__pthread_testcancel(); // pthreads spec: usleep is a cancellation point, so it must test if this thread is cancelled during the sleep.
 		now = emscripten_get_now();
 		double msecsToSleep = target - now;
 		if (msecsToSleep > 1.0) {
@@ -884,3 +887,5 @@
 {
 	return _emscripten_atomic_fetch_and_xor_u64(ptr, value);
 }
+
+weak_alias(__pthread_testcancel, pthread_testcancel);
diff --git a/tests/core/test_simd_float32x4.c b/tests/core/test_simd_float32x4.c
index c299bf9..37dcdbf 100644
--- a/tests/core/test_simd_float32x4.c
+++ b/tests/core/test_simd_float32x4.c
@@ -5,9 +5,25 @@
 #include <math.h>
 #include <assert.h>
 
+void printFloat(float n) {
+    if (isnan(n)) {
+        printf("nan");
+    } else {
+        printf("%f", n);
+    }
+}
+
 void dump(const char *name, float32x4 vec)
 {
-    printf("%s: %f %f %f %f\n", name, emscripten_float32x4_extractLane(vec, 0), emscripten_float32x4_extractLane(vec, 1), emscripten_float32x4_extractLane(vec, 2), emscripten_float32x4_extractLane(vec, 3));
+    printf("%s: ", name);
+    printFloat(emscripten_float32x4_extractLane(vec, 0));
+    printf(" ");
+    printFloat(emscripten_float32x4_extractLane(vec, 1));
+    printf(" ");
+    printFloat(emscripten_float32x4_extractLane(vec, 2));
+    printf(" ");
+    printFloat(emscripten_float32x4_extractLane(vec, 3));
+    printf("\n");
 }
 #define DUMP(V) dump(#V, (V))
 
diff --git a/tests/core/test_simd_float64x2.c b/tests/core/test_simd_float64x2.c
index 7a2c87f..5454df2 100644
--- a/tests/core/test_simd_float64x2.c
+++ b/tests/core/test_simd_float64x2.c
@@ -5,9 +5,21 @@
 #include <math.h>
 #include <assert.h>
 
+void printFloat(double n) {
+    if (isnan(n)) {
+        printf("nan");
+    } else {
+        printf("%f", n);
+    }
+}
+
 void dump(const char *name, float64x2 vec)
 {
-    printf("%s: %f %f\n", name, emscripten_float64x2_extractLane(vec, 0), emscripten_float64x2_extractLane(vec, 1));
+    printf("%s: ", name);
+    printFloat(emscripten_float64x2_extractLane(vec, 0));
+    printf(" ");
+    printFloat(emscripten_float64x2_extractLane(vec, 1));
+    printf("\n");
 }
 #define DUMP(V) dump(#V, (V))
 
diff --git a/tests/core/test_strftime.c b/tests/core/test_strftime.cpp
similarity index 97%
rename from tests/core/test_strftime.c
rename to tests/core/test_strftime.cpp
index 020fece..04bc896 100644
--- a/tests/core/test_strftime.c
+++ b/tests/core/test_strftime.cpp
@@ -7,6 +7,9 @@
   printf("%s: %d\n", comment, result);
   if (!result) {
     printf("\nERROR: %s (\"%s\")\n", comment, parsed);
+#ifdef REPORT_RESULT
+    abort();
+#endif
   }
 }
 
@@ -171,4 +174,8 @@
   size = strftime(s, 10, "%I %M %p", &tm);
   test(!cmp(s, "12 01 PM"), "strftime test #35", s);
 
+#ifdef REPORT_RESULT
+  int result = 0;
+  REPORT_RESULT();
+#endif
 }
diff --git a/tests/core/test_wprintf.c b/tests/core/test_wprintf.cpp
similarity index 100%
rename from tests/core/test_wprintf.c
rename to tests/core/test_wprintf.cpp
diff --git a/tests/core/test_zerodiv.c b/tests/core/test_zerodiv.c
index 2143077..dce51ff 100644
--- a/tests/core/test_zerodiv.c
+++ b/tests/core/test_zerodiv.c
@@ -1,4 +1,14 @@
+#include <math.h>
 #include <stdio.h>
+
+void printCanonicalizedNan(char* name, float value) {
+  if (!isnan(value)) {
+    printf("%s: %f\n", name, value);
+  } else {
+    printf("%s: nan\n", name);
+  }
+}
+
 int main(int argc, const char* argv[]) {
   float f1 = 1.0f;
   float f2 = 0.0f;
@@ -9,10 +19,10 @@
   float f5 = f2 / f2;
   float f6 = f2 / f_zero;
 
-  printf("f3: %f\n", f3);
-  printf("f4: %f\n", f4);
-  printf("f5: %f\n", f5);
-  printf("f6: %f\n", f6);
+  printCanonicalizedNan("f3", f3);
+  printCanonicalizedNan("f4", f4);
+  printCanonicalizedNan("f5", f5);
+  printCanonicalizedNan("f6", f6);
 
   return 0;
 }
diff --git a/tests/hyperbolic/src.c b/tests/hyperbolic/src.c
index 4644f2d..95dc4d3 100644
--- a/tests/hyperbolic/src.c
+++ b/tests/hyperbolic/src.c
@@ -1,6 +1,15 @@
 #include <stdio.h>
 #include <math.h>
 
+// Canonicalize nan output: wasm and asm encode negative nans differently
+void printCanonicalizedNan(char* funcName, double value) {
+  if (!isnan(value)) {
+    printf("%s: %g\n", funcName, value);
+  } else {
+    printf("%s: nan\n", funcName);
+  }
+}
+
 int main() {
   double i;
   for (i = -10; i < 10; i += 0.125) {
@@ -8,9 +17,9 @@
     printf("sinh: %g\n", sinh(i));
     printf("cosh: %g\n", cosh(i));
     printf("tanh: %g\n", tanh(i));
-    printf("asinh: %g\n", asinh(i));
-    printf("acosh: %g\n", acosh(i));
-    printf("atanh: %g\n", atanh(i));
+    printCanonicalizedNan("asinh", asinh(i));
+    printCanonicalizedNan("acosh", acosh(i));
+    printCanonicalizedNan("atanh", atanh(i));
     printf("\n");
   }
   return 0;
diff --git a/tests/langinfo/output.txt b/tests/langinfo/output.txt
index 07fb77f..07f3b73 100644
--- a/tests/langinfo/output.txt
+++ b/tests/langinfo/output.txt
@@ -1,4 +1,4 @@
-         CODESET: "UTF-8"
+         CODESET: "ASCII"
          D_T_FMT: "%a %b %e %T %Y"
            D_FMT: "%m/%d/%y"
            T_FMT: "%H:%M:%S"
@@ -52,5 +52,5 @@
          THOUSEP: ""
          YESEXPR: "^[yY]"
           NOEXPR: "^[nN]"
-        CRNCYSTR: "(null)"
-     (bad value): "(null)"
+        CRNCYSTR: ""
+     (bad value): ""
diff --git a/tests/pthread/test_pthread_locale.c b/tests/pthread/test_pthread_locale.c
new file mode 100644
index 0000000..01c3304
--- /dev/null
+++ b/tests/pthread/test_pthread_locale.c
@@ -0,0 +1,54 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+// #include <emscripten/emscripten.h>
+#include <emscripten/threading.h>
+#include "../../system/lib/libc/musl/src/internal/pthread_impl.h"
+
+#define NUM_THREADS  1
+
+locale_t do_test() {
+  pthread_t thread = pthread_self();
+  locale_t loc = thread->locale;
+  printf("  pthread_self() = %p\n", thread);
+  printf("  pthread_self()->locale = %p\n", loc);
+
+  if (!loc) {
+    puts("ERROR: loc is null");
+  }
+  return loc;
+}
+
+void *thread_test(void *t) 
+{
+  puts("Doing test in child thread");
+  pthread_exit((void*)do_test());
+}
+
+int main (int argc, char *argv[])
+{
+  puts("Doing test in main thread");
+  locale_t main_loc = do_test();
+  locale_t child_loc;
+
+  if (!emscripten_has_threading_support())
+  {
+    child_loc = main_loc;
+  }
+  else
+  {
+    long id = 1;
+    pthread_t thread;
+
+    pthread_create(&thread, NULL, thread_test, (void *)id);
+
+    pthread_join(thread, (void**)&child_loc);
+  }
+
+#ifdef REPORT_RESULT
+  int result = (main_loc == child_loc);
+  REPORT_RESULT();
+#endif
+
+  pthread_exit(NULL);
+}
diff --git a/tests/test_browser.py b/tests/test_browser.py
index 12ce0c0..fc451b1 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -3420,3 +3420,12 @@
 
   def test_asmfs_relative_paths(self):
     self.btest('asmfs/relative_paths.cpp', expected='0', args=['-s', 'ASMFS=1', '-s', 'USE_PTHREADS=1', '-s', 'FETCH_DEBUG=1', '--proxy-to-worker'])
+
+  def test_pthread_locale(self):
+    for args in [
+        [],
+        ['-s', 'USE_PTHREADS=1', '-s', 'PTHREAD_POOL_SIZE=2'],
+        ['-s', 'USE_PTHREADS=1', '--proxy-to-worker', '-s', 'PTHREAD_POOL_SIZE=2'],
+    ]:
+      print "Testing with: ", args
+      self.btest('pthread/test_pthread_locale.c', expected='1', args=args)
diff --git a/tests/test_core.py b/tests/test_core.py
index 0eb8454..6b9e87d 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -224,7 +224,6 @@
     # No other configuration is supported, so always run this.
     self.do_run(open(path_from_root('tests', 'asmjs-unknown-emscripten.c')).read(), '')
 
-  @no_wasm_backend('printf is incorrectly handling float values')
   def test_cube2md5(self):
     self.emcc_args += ['--embed-file', 'cube2md5.txt']
     shutil.copyfile(path_from_root('tests', 'cube2md5.txt'), os.path.join(self.get_dir(), 'cube2md5.txt'))
@@ -381,8 +380,8 @@
     printf("Alignment: %d addr: 0x%x\n", ((int)&v) % 16, (int)&v);
     printf("Alignment: %d addr: 0x%x\n", ((int)&m) % 16, (int)&m);
 }
-    ''', ('Alignment: 0 addr: 0xa20\nAlignment: 0 addr: 0xa60\n',   # asmjs
-          'Alignment: 0 addr: 0xe10\nAlignment: 0 addr: 0xe50\n',   # asm2wasm
+    ''', ('Alignment: 0 addr: 0xb20\nAlignment: 0 addr: 0xb60\n',   # asmjs
+          'Alignment: 0 addr: 0xf10\nAlignment: 0 addr: 0xf50\n',   # asm2wasm
           'Alignment: 0 addr: 0x410\nAlignment: 0 addr: 0x450\n',)) # wasm_backend
 
     test()
@@ -1658,7 +1657,14 @@
 
     if '-O2' in self.emcc_args:
       # Make sure ALLOW_MEMORY_GROWTH generates different code (should be less optimized)
-      code_start = 'var TOTAL_MEMORY'
+      possible_starts = ['// EMSCRIPTEN_START_FUNCS', 'var TOTAL_MEMORY']
+      code_start = None
+      for s in possible_starts:
+        if fail.find(s) >= 0:
+          code_start = s
+          break
+      assert code_start is not None, 'Generated code must contain one of ' + str(possible_starts)
+
       fail = fail[fail.find(code_start):]
       win = win[win.find(code_start):]
       assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller' + str([len(fail), len(win)])
@@ -2666,7 +2672,7 @@
           raise Exception('Could not find symbol table!')
       table = table[table.find('{'):table.find('}')+1]
       # ensure there aren't too many globals; we don't want unnamed_addr
-      assert table.count(',') <= 23, table.count(',')
+      assert table.count(',') <= 27, table.count(',')
 
     test_path = path_from_root('tests', 'core', 'test_dlfcn_self')
     src, output = (test_path + s for s in ('.c', '.out'))
@@ -3951,11 +3957,9 @@
       print 'flip assertions off'
     self.do_run_in_out_file_test('tests', 'core', 'fnmatch')
 
-  @no_wasm_backend('sscanf seems to be misreading float values')
   def test_sscanf(self):
     self.do_run_in_out_file_test('tests', 'core', 'test_sscanf')
 
-  @no_wasm_backend('sscanf seems to be misreading float values')
   def test_sscanf_2(self):
     # doubles
     for ftype in ['float', 'double']:
@@ -4037,14 +4041,12 @@
   def test_sscanf_skip(self):
     self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_skip')
 
-  @no_wasm_backend('sscanf seems to be misreading float values')
   def test_sscanf_caps(self):
     self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_caps')
 
   def test_sscanf_hex(self):
     self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_hex')
 
-  @no_wasm_backend('sscanf seems to be misreading float values')
   def test_sscanf_float(self):
     self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_float')
 
@@ -4201,7 +4203,6 @@
     self.emcc_args += ['--embed-file', 'eol.txt']
     self.do_run(src, 'SUCCESS\n')
 
-  @no_wasm_backend('printf is incorrectly handling float values')
   def test_fscanf(self):
     open(os.path.join(self.get_dir(), 'three_numbers.txt'), 'w').write('''-1 0.1 -.1''')
     src = r'''
@@ -4273,7 +4274,6 @@
     self.do_run(src, '3\n')
 
   def test_readdir(self):
-    self.banned_js_engines = [V8_ENGINE] # stderr printing limitations in v8
     src = open(path_from_root('tests', 'dirent', 'test_readdir.c'), 'r').read()
     self.do_run(src, '''SIGILL: Illegal instruction
 success
@@ -4392,7 +4392,7 @@
   @no_wasm_backend('printf is incorrectly handling float values')
   def test_wprintf(self):
     test_path = path_from_root('tests', 'core', 'test_wprintf')
-    src, output = (test_path + s for s in ('.c', '.out'))
+    src, output = (test_path + s for s in ('.cpp', '.out'))
     orig_args = self.emcc_args
     for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]:
       self.emcc_args = orig_args + mode
@@ -6140,6 +6140,7 @@
     self.do_run_in_out_file_test('tests', 'core', 'test_demangle_stacks')
 
   @no_emterpreter
+  @no_wasm_backend('Need support for -g in wasm backend')
   def test_demangle_stacks_symbol_map(self):
     Settings.DEMANGLE_SUPPORT = 1
     if '-O' in str(self.emcc_args) and '-O0' not in self.emcc_args and '-O1' not in self.emcc_args and '-g' not in self.emcc_args:
diff --git a/tests/test_other.py b/tests/test_other.py
index 2f9297d..e3b301f 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -4616,7 +4616,6 @@
     assert 'callback post()' not in output
 
   def test_bad_locale(self):
-    logging.warning('TODO: check with upstream musl if this is correct behavior or not')
     open('src.cpp', 'w').write(r'''
 
 #include <locale.h>
@@ -4639,8 +4638,10 @@
     ''')
     Popen([PYTHON, EMCC, 'src.cpp']).communicate()
 
-    self.assertContained('locale set to C: C', run_js('a.out.js', args=['C']))
-    self.assertContained('locale set to waka: C.UTF-8', run_js('a.out.js', args=['waka'])) # the call still succeeds in musl, even if the locale requested was not selected
+    self.assertContained('locale set to C: C;C;C;C;C;C',
+                         run_js('a.out.js', args=['C']))
+    self.assertContained('locale set to waka: waka;waka;waka;waka;waka;waka',
+                         run_js('a.out.js', args=['waka']))
 
   def test_js_malloc(self):
     open('src.cpp', 'w').write(r'''
@@ -6271,16 +6272,23 @@
 
   def test_llvm_lto(self):
     sizes = {}
-    for lto in [0, 1, 2, 3]:
+    lto_levels = [0, 1, 2, 3]
+    for lto in lto_levels:
       cmd = [PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O2', '--llvm-lto', str(lto)]
       print cmd
       check_execute(cmd)
       self.assertContained('hello, world!', run_js('a.out.js'))
       sizes[lto] = os.stat('a.out.js').st_size
     print sizes
-    assert sizes[1] < sizes[0] # lto reduces size
-    assert sizes[2] > sizes[0] # fake lto is aggressive at increasing code size
-    assert sizes[3] not in set([sizes[0], sizes[1], sizes[2]]) # mode 3 is different (deterministic builds means this tests an actual change)
+
+    # LTO sizes should be distinct
+    for i in lto_levels:
+      assert sizes[i] not in set(sizes).difference(set([sizes[i]]))
+
+    # LTO should reduce code size
+    # Skip mode 2 because it has historically increased code size, but not always
+    assert sizes[1] < sizes[0]
+    assert sizes[3] < sizes[0]
 
   def test_split_memory(self): # make sure multiple split memory chunks get used
     open('src.c', 'w').write(r'''
diff --git a/tools/shared.py b/tools/shared.py
index 64af117..5824a6f 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -2607,7 +2607,7 @@
 def make_fetch_worker(source_file, output_file):
   src = open(source_file, 'r').read()
   funcs_to_import = ['alignUp', 'getTotalMemory', 'stringToUTF8', 'intArrayFromString', 'lengthBytesUTF8', 'stringToUTF8Array', '_emscripten_is_main_runtime_thread', '_emscripten_futex_wait']
-  asm_funcs_to_import = ['_malloc', '_free', '_sbrk', '_pthread_mutex_lock', '_pthread_mutex_unlock']
+  asm_funcs_to_import = ['_malloc', '_free', '_sbrk', '___pthread_mutex_lock', '___pthread_mutex_unlock']
   function_prologue = '''this.onerror = function(e) {
   console.error(e);
 }