musl: update to version 1.1.10+git
authorFelix Fietkau <nbd@openwrt.org>
Tue, 16 Jun 2015 11:55:23 +0000 (11:55 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Tue, 16 Jun 2015 11:55:23 +0000 (11:55 +0000)
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 46001

toolchain/musl/Config.version
toolchain/musl/common.mk
toolchain/musl/patches/001-git-2015-06-04.patch [deleted file]
toolchain/musl/patches/001-git-2015-06-16.patch [new file with mode: 0644]
toolchain/musl/patches/900-iconv_size_hack.patch

index c560c60..d325f19 100644 (file)
@@ -3,6 +3,6 @@ if USE_MUSL
 config MUSL_VERSION
        string
        depends on USE_MUSL
-       default "1.1.9"
+       default "1.1.10"
 
 endif
index f4a34e4..88481d8 100644 (file)
@@ -11,7 +11,7 @@ PKG_NAME:=musl
 PKG_VERSION:=$(call qstrip,$(CONFIG_MUSL_VERSION))
 PKG_RELEASE=1
 
-PKG_MD5SUM:=14e8c5ac74f887d53256b3dcaf9b4aaa
+PKG_MD5SUM:=fc30892ee582c91920505bbd0021049f
 
 PKG_SOURCE_URL:=http://www.musl-libc.org/releases
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
diff --git a/toolchain/musl/patches/001-git-2015-06-04.patch b/toolchain/musl/patches/001-git-2015-06-04.patch
deleted file mode 100644 (file)
index 0baea67..0000000
+++ /dev/null
@@ -1,2015 +0,0 @@
-commit b6a6cd703ffefa6352249fb01f4da28d85d17306
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Thu Jun 4 11:45:17 2015 -0400
-
-    fix dynamic linker regression processing R_*_NONE type relocations
-    
-    commit f3ddd173806fd5c60b3f034528ca24542aecc5b9 inadvertently removed
-    the early check for "none" type relocations, causing the address
-    dso->base+0 to be dereferenced to obtain an addend. shared libraries,
-    (including libc.so) and PIE executables were unaffected, since their
-    base addresses are the actual address of their mappings and are
-    readable. non-PIE main executables, however, have a base address of 0
-    because their load addresses are absolute and not offset at load time.
-    
-    in practice none-type relocations do not arise with toolchains that
-    are in use except on mips, and on mips it's moderately rare for a
-    non-PIE executable to have a relocation table, since the mips-specific
-    got processing serves in its place for most purposes.
-
-commit 585ba14df4799d50ec9682ce75825d2eafec2a6a
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Wed Jun 3 02:00:44 2015 -0400
-
-    add additional Makefile dependency rules for rcrt1.o PIE start file
-
-commit 2b4fcfdacf93c3dfd6ac15e31790a9e154374679
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Thu May 28 23:08:12 2015 -0400
-
-    fix failure of ungetc and ungetwc to work on files in eof status
-    
-    these functions were written to handle clearing eof status, but failed
-    to account for the __toread function's handling of eof. with this
-    patch applied, __toread still returns EOF when the file is in eof
-    status, so that read operations will fail, but it also sets up valid
-    buffer pointers for read mode, which are set to the end of the buffer
-    rather than the beginning in order to make the whole buffer available
-    to ungetc/ungetwc.
-    
-    minor changes to __uflow were needed since it's now possible to have
-    non-zero buffer pointers while in eof status. as made, these changes
-    remove a 'fast path' bypassing the function call to __toread, which
-    could be reintroduced with slightly different logic, but since
-    ordinary files have a syscall in f->read, optimizing the code path
-    does not seem worthwhile.
-    
-    the __stdio_read function is also updated not to zero the read buffer
-    pointers on eof/error. while not necessary for correctness, this
-    change avoids the overhead of calling __toread in ungetc after
-    reaching eof, and it also reduces code size and increases consistency
-    with the fmemopen read operation which does not zero the pointers.
-
-commit b6e7c664677ab7c77f183b8c41105f2be519800c
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Thu May 28 15:37:23 2015 -0400
-
-    add missing legacy LFS64 macros in sys/resource.h
-    
-    based on patch by Felix Janda, with RLIM64_SAVED_CUR and
-    RLIM64_SAVED_MAX added for completeness.
-
-commit fc431d3f76bb9bde34a89e4a3e4d0c27de959855
-Author: Shiz <hi@shiz.me>
-Date:   Thu May 28 05:52:22 2015 +0200
-
-    configure: work around compilers that merely warn for unknown options
-    
-    some compilers (such as clang) accept unknown options without error,
-    but then print warnings on each invocation, cluttering the build
-    output and burying meaningful warnings. this patch makes configure's
-    tryflag and tryldflag functions use additional options to turn the
-    unknown-option warnings into errors, if available, but only at check
-    time. these options are not output in config.mak to avoid the risk of
-    spurious build breakage; if they work, they will have already done
-    their job at configure time.
-
-commit aeeac9ca5490d7d90fe061ab72da446c01ddf746
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Wed May 27 15:54:47 2015 -0400
-
-    implement fail-safe static locales for newlocale
-    
-    this frees applications which need to make temporary use of the C
-    locale (via uselocale) from the possibility that newlocale might fail.
-    
-    the C.UTF-8 locale is also provided as a static locale. presently they
-    behave the same, but this may change in the future.
-
-commit 11858d31aa020df3e7e7dedf49f9870ce12f31cc
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Wed May 27 03:32:46 2015 -0400
-
-    rename internal locale file handling locale maps
-    
-    since the __setlocalecat function was removed, the filename
-    __setlocalecat.c no longer made sense.
-
-commit 61a3364d246e72b903da8b76c2e27a225a51351e
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Wed May 27 03:22:52 2015 -0400
-
-    overhaul locale internals to treat categories roughly uniformly
-    
-    previously, LC_MESSAGES was treated specially as the only category
-    which could be set to a locale name without a definition file, in
-    order to facilitate gettext message translations when no libc locale
-    was available. LC_NUMERIC was completely un-settable, and LC_CTYPE
-    stored a flag intended to be used for a possible future byte-based C
-    locale, instead of storing a __locale_map pointer like the other
-    categories use.
-    
-    this patch changes all categories to be represented by pointers to
-    __locale_map structures, and allows locale names without definition
-    files to be treated as valid locales with trivial definition when used
-    in any category. outwardly visible functional changes should be minor,
-    limited mainly to the strings read back from setlocale and the way
-    gettext handles translations in categories other than LC_MESSAGES.
-    
-    various internal refactoring has also been performed, and improvements
-    in const correctness have been made.
-
-commit 63c188ec42e76ff768e81f6b65b11c68fc43351e
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Wed May 27 00:22:43 2015 -0400
-
-    replace atomics with locks in locale-setting code
-    
-    this is part of a general program of removing direct use of atomics
-    where they are not necessary to meet correctness or performance needs,
-    but in this case it's also an optimization. only the global locale
-    needs synchronization; allocated locales referenced with locale_t
-    handles are immutable during their lifetimes, and using atomics to
-    initialize them increases their cost of setup.
-
-commit dc031ee0b1ba11baa00cd7f0769e461a5f396c71
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Tue May 26 03:37:41 2015 -0400
-
-    add rcrt1 start file for fully static-linked PIE
-    
-    static-linked PIE files need startup code to relocate themselves, much
-    like the dynamic linker does. rcrt1.c reuses the code in dlstart.c,
-    stage 1 of the dynamic linker, which in turn reuses crt_arch.h, to
-    achieve static PIE with no new code. only relative relocations are
-    supported.
-    
-    existing toolchains that don't yet support static PIE directly can be
-    repurposed by passing "-shared -Wl,-Bstatic -Wl,-Bsymbolic" instead of
-    "-static -pie" and substituting rcrt1.o in place of crt1.o.
-    
-    all libraries being linked must be built as PIC/PIE; TEXTRELs are not
-    supported at this time.
-
-commit ed0c8249825161036356a3616e8c5247c15d0927
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Tue May 26 02:31:04 2015 -0400
-
-    fix incorrect application of visibility to Scrt1.o
-    
-    commit de2b67f8d41e08caa56bf6540277f6561edb647f attempted to avoid
-    having vis.h affect crt files, but the Makefile variable used,
-    CRT_LIBS, refers to the final output copies in the lib directory, not
-    the copies in the crt build directory, and thus the -DCRT was not
-    applied.
-    
-    while unlikely to be noticed, this regression probably broke
-    production of PIE executables whose main functions are not in the
-    executable but rather a shared library.
-
-commit 9bbddf730f7837cf87f4c789fbb41a312e295d6c
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Mon May 25 23:33:59 2015 -0400
-
-    reprocess all libc/ldso symbolic relocations in dynamic linking stage 3
-    
-    commit f3ddd173806fd5c60b3f034528ca24542aecc5b9 introduced early
-    relocations and subsequent reprocessing as part of the dynamic linker
-    bootstrap overhaul, to allow use of arbitrary libc functions before
-    the main application and libraries are loaded, but only reprocessed
-    GOT/PLT relocation types.
-    
-    commit c093e2e8201524db0d638920e76bcb6b1d925f3a added reprocessing of
-    non-GOT/PLT relocations to fix an actual regression that was observed
-    on powerpc, but only for RELA format tables with out-of-line addends.
-    REL table (inline addends at the relocation address) reprocessing is
-    trickier because the first relocation pass clobbers the addends.
-    
-    this patch extends symbolic relocation reprocessing for libc/ldso to
-    support all relocation types, whether REL or RELA format tables are
-    used. it is believed not to alter behavior on any existing archs for
-    the current dynamic linker and libc code. the motivations for this
-    change are consistency and future-proofing. it ensures that behavior
-    does not differ depending on whether REL or RELA tables are used,
-    which could lead to undetected arch-specific bugs. it also ensures
-    that, if in the future code depending on additional relocation types
-    is added to libc.so, either at the source level or as part of the
-    compiler runtime that gets pulled in (for example, soft-float with TLS
-    for fenv), the new code will work properly.
-    
-    the implementation concept is simple: stage 2 of the dynamic linker
-    counts the number of symbolic relocations in the libc/ldso REL table
-    and allocates a VLA to save their addends into; stage 3 then uses the
-    saved addends in place of the inline ones which were clobbered. for
-    stack safety, a hard limit (currently 4k) is imposed on the number of
-    such addends; this should be a couple orders of magnitude larger than
-    the actual need. this number is not a runtime variable that could
-    break fail-safety; it is constant for a given libc.so build.
-
-commit 768b82c6de24e480267c4c251c440edfc71800e3
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Mon May 25 19:15:17 2015 -0400
-
-    move call to dynamic linker stage-3 into stage-2 function
-    
-    this move eliminates a duplicate "by-hand" symbol lookup loop from the
-    stage-1 code and replaces it with a call to find_sym, which can be
-    used once we're in stage 2. it reduces the size of the stage 1 code,
-    which is helpful because stage 1 will become the crt start file for
-    static-PIE executables, and it will allow stage 3 to access stage 2's
-    automatic storage, which will be important in an upcoming commit.
-
-commit 967bcbf67c3ffac587de4d79abc1e5e072d83e3e
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Mon May 25 16:02:49 2015 -0400
-
-    mark mips crt code as code
-    
-    otherwise disassemblers treat it as data.
-
-commit 7b75c4877ddf22f219f944c61d939df1dee4f6d3
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Mon May 25 15:56:36 2015 -0400
-
-    mark mips cancellable syscall code as code
-    
-    otherwise disassemblers treat it as data.
-
-commit 0e0e49421f08cfd670975ecd3604f7f9015e1833
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Mon May 25 00:32:37 2015 -0400
-
-    simplify/shrink relocation processing in dynamic linker stage 1
-    
-    the outer-loop approach made sense when we were also processing
-    DT_JMPREL, which might be in REL or RELA form, to avoid major code
-    duplication. commit 09db855b35709aa627d7055c57a98e1e471920ab removed
-    processing of DT_JMPREL, and in the remaining two tables, the format
-    (REL or RELA) is known by the name of the table. simply writing two
-    versions of the loop results in smaller and simpler code.
-
-commit 09db855b35709aa627d7055c57a98e1e471920ab
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Mon May 25 00:25:56 2015 -0400
-
-    remove processing of DT_JMPREL from dynamic linker stage 1 bootstrap
-    
-    the DT_JMPREL relocation table necessarily consists entirely of
-    JMP_SLOT (REL_PLT in internal nomenclature) relocations, which are
-    symbolic; they cannot be resolved in stage 1, so there is no point in
-    processing them.
-
-commit 9f26ebded188ed78c3571a4ca1477dd6351bc647
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Sun May 24 23:03:47 2015 -0400
-
-    fix stack alignment code in mips crt_arch.h
-    
-    the instruction used to align the stack, "and $sp, $sp, -8", does not
-    actually exist; it's expanded to 2 instructions using the 'at'
-    (assembler temporary) register, and thus cannot be used in a branch
-    delay slot. since alignment mod 16 commutes with subtracting 8, simply
-    swapping these two operations fixes the problem.
-    
-    crt1.o was not affected because it's still being generated from a
-    dedicated asm source file. dlstart.lo was not affected because the
-    stack pointer it receives is already aligned by the kernel. but
-    Scrt1.o was affected in cases where the dynamic linker gave it a
-    misaligned stack pointer.
-
-commit 63caf1d207d143fe405bbe0cda9aac8deca1171a
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Fri May 22 01:50:05 2015 -0400
-
-    add .text section directive to all crt_arch.h files missing it
-    
-    i386 and x86_64 versions already had the .text directive; other archs
-    did not. normally, top-level (file scope) __asm__ starts in the .text
-    section anyway, but problems were reported with some versions of
-    clang, and it seems preferable to set it explicitly anyway, at least
-    for the sake of consistency between archs.
-
-commit 3b0e83264d156f9e496ab32badd89e4447b807aa
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Thu May 21 17:06:28 2015 -0400
-
-    remove outdated and misleading comment in iconv.c
-    
-    the comment claimed that EUC/GBK/Big5 are not implemented, which has
-    been incorrect since commit 19b4a0a20efc6b9df98b6a43536ecdd628ba4643.
-
-commit 39b8ce66f2ed9c17427ec3a48be9bda29b93b9d7
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Thu May 21 17:01:23 2015 -0400
-
-    in iconv_open, accept "CHAR" and "" as aliases for "UTF-8"
-    
-    while not a requirement, it's common convention in other iconv
-    implementations to accept "CHAR" as an alias for nl_langinfo(CODESET),
-    meaning the encoding used for char[] strings in the current locale,
-    and also "" as an alternate form. supporting this is not costly and
-    improves compatibility.
-
-commit c648cefb27984db60474ec1747cbfde83c2856d0
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Wed May 20 00:17:35 2015 -0400
-
-    fix inconsistency in a_and and a_or argument types on x86[_64]
-    
-    conceptually, and on other archs, these functions take a pointer to
-    int, but in the i386, x86_64, and x32 versions of atomic.h, they took
-    a pointer to void instead.
-
-commit 390f93ef69153bf2087fcf3baa1776ad9a6765ab
-Author: Bobby Bingham <koorogi@koorogi.info>
-Date:   Sun May 17 13:46:38 2015 -0500
-
-    inline llsc atomics when building for sh4a
-    
-    If we're building for sh4a, the compiler is already free to use
-    instructions only available on sh4a, so we can do the same and inline the
-    llsc atomics. If we're building for an older processor, we still do the
-    same runtime atomics selection as before.
-
-commit c093e2e8201524db0d638920e76bcb6b1d925f3a
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Mon May 18 16:51:54 2015 -0400
-
-    reprocess libc/ldso RELA relocations in stage 3 of dynamic linking
-    
-    this fixes a regression on powerpc that was introduced in commit
-    f3ddd173806fd5c60b3f034528ca24542aecc5b9. global data accesses on
-    powerpc seem to be using a translation-unit-local GOT filled via
-    R_PPC_ADDR32 relocations rather than R_PPC_GLOB_DAT. being a non-GOT
-    relocation type, these were not reprocessed after adding the main
-    application and its libraries to the chain, causing libc code not to
-    see copy relocations in the main program, and therefore to use the
-    pre-copy-relocation addresses for global data objects (like environ).
-    
-    the motivation for the dynamic linker only reprocessing GOT/PLT
-    relocation types in stage 3 is that these types always have a zero
-    addend, making them safe to process again even if the storage for the
-    addend has been clobbered. other relocation types which can be used
-    for address constants in initialized data objects may have non-zero
-    addends which will be clobbered during the first pass of relocation
-    processing if they're stored inline (REL form) rather than out-of-line
-    (RELA form).
-    
-    powerpc generally uses only RELA, so this patch is sufficient to fix
-    the regression in practice, but is not fully general, and would not
-    suffice if an alternate toolchain generated REL for powerpc.
-
-commit 43e9f652bf4b2195b04fc14c93db591b30a7b790
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Mon May 18 12:11:25 2015 -0400
-
-    fix null pointer dereference in dcngettext under specific conditions
-    
-    if setlocale has not been called, the current locale's messages_name
-    may be a null pointer. the code path where it's assumed to be non-null
-    was only reachable if bindtextdomain had already been called, which is
-    normally not done in programs which do not call setlocale, so the
-    omitted check went unnoticed.
-    
-    patch from Void Linux, with description rewritten.
-
-commit 68630b55c0c7219fe9df70dc28ffbf9efc8021d8
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Sat May 16 01:53:54 2015 -0400
-
-    eliminate costly tricks to avoid TLS access for current locale state
-    
-    the code being removed used atomics to track whether any threads might
-    be using a locale other than the current global locale, and whether
-    any threads might have abstract 8-bit (non-UTF-8) LC_CTYPE active, a
-    feature which was never committed (still pending). the motivations
-    were to support early execution prior to setup of the thread pointer,
-    to partially support systems (ancient kernels) where thread pointer
-    setup is not possible, and to avoid high performance cost on archs
-    where accessing the thread pointer may be very slow.
-    
-    since commit 19a1fe670acb3ab9ead0fe31859ca7d4fe40dd54, the thread
-    pointer is always available, so these hacks are no longer needed.
-    removing them greatly simplifies the affected code.
-
-commit 707d7c30f3379441de9b320536ddfd354f4c2143
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Sat May 16 01:15:40 2015 -0400
-
-    in i386 __set_thread_area, don't assume %gs register is initially zero
-    
-    commit f630df09b1fd954eda16e2f779da0b5ecc9d80d3 added logic to handle
-    the case where __set_thread_area is called more than once by reusing
-    the GDT slot already in the %gs register, and only setting up a new
-    GDT slot when %gs is zero. this created a hidden assumption that %gs
-    is zero when a new process image starts, which is true in practice on
-    Linux, but does not seem to be documented ABI, and fails to hold under
-    qemu app-level emulation.
-    
-    while it would in theory be possible to zero %gs in the entry point
-    code, this code is shared between static and dynamic binaries, and
-    dynamic binaries must not clobber the value of %gs already setup by
-    the dynamic linker.
-    
-    the alternative solution implemented in this commit simply uses global
-    data to store the GDT index that's selected. __set_thread_area should
-    only be called in the initial thread anyway (subsequent threads get
-    their thread pointer setup by __clone), but even if it were called by
-    another thread, it would simply read and write back the same GDT index
-    that was already assigned to the initial thread, and thus (in the x86
-    memory model) there is no data race.
-
-commit c0f10cf06725bd0de37f3ced7954a653bf9f1049
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Thu May 14 18:51:27 2015 -0400
-
-    make arm reloc.h CRTJMP macro compatible with thumb
-    
-    compilers targeting armv7 may be configured to produce thumb2 code
-    instead of arm code by default, and in the future we may wish to
-    support targets where only the thumb instruction set is available.
-    
-    the instructions this patch omits in thumb mode are needed only for
-    non-thumb versions of armv4 or earlier, which are not supported by any
-    current compilers/toolchains and thus rather pointless to have. at
-    some point these compatibility return sequences may be removed from
-    all asm source files, and in that case it would make sense to remove
-    them here too and remove the ifdef.
-
-commit 83340c7a580e91b22f58321b7cf6d976af61084c
-Author: Rich Felker <dalias@aerifal.cx>
-Date:   Thu May 14 18:26:16 2015 -0400
-
-    make arm crt_arch.h compatible with thumb code generation
-    
-    compilers targeting armv7 may be configured to produce thumb2 code
-    instead of arm code by default, and in the future we may wish to
-    support targets where only the thumb instruction set is available.
-    
-    the changes made here avoid operating directly on the sp register,
-    which is not possible in thumb code, and address an issue with the way
-    the address of _DYNAMIC is computed.
-    
-    previously, the relative address of _DYNAMIC was stored with an
-    additional offset of -8 versus the pc-relative add instruction, since
-    on arm the pc register evaluates to ".+8". in thumb code, it instead
-    evaluates to ".+4". both are two (normal-size) instructions beyond "."
-    in the current execution mode, so the numbered label 2 used in the
-    relative address expression is simply moved two instructions ahead to
-    be compatible with both instruction sets.
-
---- a/Makefile
-+++ b/Makefile
-@@ -44,7 +44,7 @@ ALL_INCLUDES = $(sort $(wildcard 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 = lib/crt1.o lib/Scrt1.o lib/rcrt1.o lib/crti.o lib/crtn.o
- STATIC_LIBS = lib/libc.a
- SHARED_LIBS = lib/libc.so
- TOOL_LIBS = lib/musl-gcc.specs
-@@ -85,11 +85,13 @@ src/internal/version.h: $(wildcard VERSI
- src/internal/version.lo: src/internal/version.h
--src/ldso/dlstart.lo src/ldso/dynlink.lo: src/internal/dynlink.h arch/$(ARCH)/reloc.h
-+crt/rcrt1.o src/ldso/dlstart.lo src/ldso/dynlink.lo: src/internal/dynlink.h arch/$(ARCH)/reloc.h
--crt/crt1.o crt/Scrt1.o src/ldso/dlstart.lo: $(wildcard arch/$(ARCH)/crt_arch.h)
-+crt/crt1.o crt/Scrt1.o crt/rcrt1.o src/ldso/dlstart.lo: $(wildcard arch/$(ARCH)/crt_arch.h)
--crt/Scrt1.o: CFLAGS += -fPIC
-+crt/rcrt1.o: src/ldso/dlstart.c
-+
-+crt/Scrt1.o crt/rcrt1.o: CFLAGS += -fPIC
- OPTIMIZE_SRCS = $(wildcard $(OPTIMIZE_GLOBS:%=src/%))
- $(OPTIMIZE_SRCS:%.c=%.o) $(OPTIMIZE_SRCS:%.c=%.lo): CFLAGS += -O3
-@@ -104,7 +106,7 @@ NOSSP_SRCS = $(wildcard crt/*.c) \
-       src/ldso/dlstart.c src/ldso/dynlink.c
- $(NOSSP_SRCS:%.c=%.o) $(NOSSP_SRCS:%.c=%.lo): CFLAGS += $(CFLAGS_NOSSP)
--$(CRT_LIBS): CFLAGS += -DCRT
-+$(CRT_LIBS:lib/%=crt/%): CFLAGS += -DCRT
- # This incantation ensures that changes to any subarch asm files will
- # force the corresponding object file to be rebuilt, even if the implicit
---- a/arch/aarch64/crt_arch.h
-+++ b/arch/aarch64/crt_arch.h
-@@ -1,4 +1,5 @@
- __asm__(
-+".text \n"
- ".global " START "\n"
- ".type " START ",%function\n"
- START ":\n"
---- a/arch/arm/crt_arch.h
-+++ b/arch/arm/crt_arch.h
-@@ -1,15 +1,18 @@
- __asm__(
-+".text \n"
- ".global " START " \n"
- ".type " START ",%function \n"
- START ": \n"
- "     mov fp, #0 \n"
- "     mov lr, #0 \n"
--"     mov a1, sp \n"
- "     ldr a2, 1f \n"
--"2:   add a2, pc, a2 \n"
--"     and sp, sp, #-16 \n"
-+"     add a2, pc, a2 \n"
-+"     mov a1, sp \n"
-+"2:   and ip, a1, #-16 \n"
-+"     mov sp, ip \n"
- "     bl " START "_c \n"
- ".weak _DYNAMIC \n"
- ".hidden _DYNAMIC \n"
--"1:   .word _DYNAMIC-2b-8 \n"
-+".align 2 \n"
-+"1:   .word _DYNAMIC-2b \n"
- );
---- a/arch/arm/reloc.h
-+++ b/arch/arm/reloc.h
-@@ -28,5 +28,10 @@
- #define REL_TPOFF       R_ARM_TLS_TPOFF32
- //#define REL_TLSDESC     R_ARM_TLS_DESC
-+#ifdef __thumb__
-+#define CRTJMP(pc,sp) __asm__ __volatile__( \
-+      "mov sp,%1 ; bx %0" : : "r"(pc), "r"(sp) : "memory" )
-+#else
- #define CRTJMP(pc,sp) __asm__ __volatile__( \
-       "mov sp,%1 ; tst %0,#1 ; moveq pc,%0 ; bx %0" : : "r"(pc), "r"(sp) : "memory" )
-+#endif
---- a/arch/i386/atomic.h
-+++ b/arch/i386/atomic.h
-@@ -50,16 +50,16 @@ static inline int a_cas(volatile int *p,
-       return t;
- }
--static inline void a_or(volatile void *p, int v)
-+static inline void a_or(volatile int *p, int v)
- {
-       __asm__( "lock ; orl %1, %0"
--              : "=m"(*(int *)p) : "r"(v) : "memory" );
-+              : "=m"(*p) : "r"(v) : "memory" );
- }
--static inline void a_and(volatile void *p, int v)
-+static inline void a_and(volatile int *p, int v)
- {
-       __asm__( "lock ; andl %1, %0"
--              : "=m"(*(int *)p) : "r"(v) : "memory" );
-+              : "=m"(*p) : "r"(v) : "memory" );
- }
- static inline int a_swap(volatile int *x, int v)
---- a/arch/microblaze/crt_arch.h
-+++ b/arch/microblaze/crt_arch.h
-@@ -1,4 +1,5 @@
- __asm__(
-+".text \n"
- ".global " START " \n"
- ".align  2 \n"
- START ": \n"
---- a/arch/mips/crt_arch.h
-+++ b/arch/mips/crt_arch.h
-@@ -1,6 +1,7 @@
- __asm__(
- ".set push\n"
- ".set noreorder\n"
-+".text \n"
- ".global _" START "\n"
- ".global " START "\n"
- ".type   _" START ", @function\n"
-@@ -21,8 +22,8 @@ __asm__(
- "     addu $5, $5, $gp \n"
- "     lw $25, 4($ra) \n"
- "     addu $25, $25, $gp \n"
--"     subu $sp, $sp, 16 \n"
-+"     and $sp, $sp, -8 \n"
- "     jalr $25 \n"
--"      and $sp, $sp, -8 \n"
-+"      subu $sp, $sp, 16 \n"
- ".set pop \n"
- );
---- a/arch/or1k/crt_arch.h
-+++ b/arch/or1k/crt_arch.h
-@@ -1,4 +1,5 @@
- __asm__(
-+".text \n"
- ".global " START " \n"
- ".align  4 \n"
- START ": \n"
---- a/arch/powerpc/crt_arch.h
-+++ b/arch/powerpc/crt_arch.h
-@@ -1,4 +1,5 @@
- __asm__(
-+".text \n"
- ".global " START " \n"
- ".type   " START ", %function \n"
- START ": \n"
---- a/arch/sh/atomic.h
-+++ b/arch/sh/atomic.h
-@@ -22,6 +22,88 @@ static inline int a_ctz_64(uint64_t x)
-       return a_ctz_l(y);
- }
-+#define LLSC_CLOBBERS "r0", "t", "memory"
-+#define LLSC_START(mem) "synco\n"  \
-+      "0:     movli.l @" mem ", r0\n"
-+#define LLSC_END(mem)              \
-+      "1:     movco.l r0, @" mem "\n"    \
-+      "       bf 0b\n"                   \
-+      "       synco\n"
-+
-+static inline int __sh_cas_llsc(volatile int *p, int t, int s)
-+{
-+      int old;
-+      __asm__ __volatile__(
-+              LLSC_START("%1")
-+              "       mov r0, %0\n"
-+              "       cmp/eq %0, %2\n"
-+              "       bf 1f\n"
-+              "       mov %3, r0\n"
-+              LLSC_END("%1")
-+              : "=&r"(old) : "r"(p), "r"(t), "r"(s) : LLSC_CLOBBERS);
-+      return old;
-+}
-+
-+static inline int __sh_swap_llsc(volatile int *x, int v)
-+{
-+      int old;
-+      __asm__ __volatile__(
-+              LLSC_START("%1")
-+              "       mov r0, %0\n"
-+              "       mov %2, r0\n"
-+              LLSC_END("%1")
-+              : "=&r"(old) : "r"(x), "r"(v) : LLSC_CLOBBERS);
-+      return old;
-+}
-+
-+static inline int __sh_fetch_add_llsc(volatile int *x, int v)
-+{
-+      int old;
-+      __asm__ __volatile__(
-+              LLSC_START("%1")
-+              "       mov r0, %0\n"
-+              "       add %2, r0\n"
-+              LLSC_END("%1")
-+              : "=&r"(old) : "r"(x), "r"(v) : LLSC_CLOBBERS);
-+      return old;
-+}
-+
-+static inline void __sh_store_llsc(volatile int *p, int x)
-+{
-+      __asm__ __volatile__(
-+              "       synco\n"
-+              "       mov.l %1, @%0\n"
-+              "       synco\n"
-+              : : "r"(p), "r"(x) : "memory");
-+}
-+
-+static inline void __sh_and_llsc(volatile int *x, int v)
-+{
-+      __asm__ __volatile__(
-+              LLSC_START("%0")
-+              "       and %1, r0\n"
-+              LLSC_END("%0")
-+              : : "r"(x), "r"(v) : LLSC_CLOBBERS);
-+}
-+
-+static inline void __sh_or_llsc(volatile int *x, int v)
-+{
-+      __asm__ __volatile__(
-+              LLSC_START("%0")
-+              "       or %1, r0\n"
-+              LLSC_END("%0")
-+              : : "r"(x), "r"(v) : LLSC_CLOBBERS);
-+}
-+
-+#ifdef __SH4A__
-+#define a_cas(p,t,s)     __sh_cas_llsc(p,t,s)
-+#define a_swap(x,v)      __sh_swap_llsc(x,v)
-+#define a_fetch_add(x,v) __sh_fetch_add_llsc(x, v)
-+#define a_store(x,v)     __sh_store_llsc(x, v)
-+#define a_and(x,v)       __sh_and_llsc(x, v)
-+#define a_or(x,v)        __sh_or_llsc(x, v)
-+#else
-+
- int  __sh_cas(volatile int *, int, int);
- int  __sh_swap(volatile int *, int);
- int  __sh_fetch_add(volatile int *, int);
-@@ -35,6 +117,7 @@ void __sh_or(volatile int *, int);
- #define a_store(x,v)     __sh_store(x, v)
- #define a_and(x,v)       __sh_and(x, v)
- #define a_or(x,v)        __sh_or(x, v)
-+#endif
- static inline void *a_cas_p(volatile void *p, void *t, void *s)
- {
---- a/arch/sh/crt_arch.h
-+++ b/arch/sh/crt_arch.h
-@@ -1,4 +1,5 @@
- __asm__(
-+".text \n"
- ".global " START " \n"
- START ": \n"
- "     mova 1f, r0 \n"
---- a/arch/sh/src/atomic.c
-+++ b/arch/sh/src/atomic.c
-@@ -1,12 +1,7 @@
--#include "libc.h"
-+#ifndef __SH4A__
--#define LLSC_CLOBBERS   "r0", "t", "memory"
--#define LLSC_START(mem) "synco\n"  \
--      "0:     movli.l @" mem ", r0\n"
--#define LLSC_END(mem)              \
--      "1:     movco.l r0, @" mem "\n"    \
--      "       bf 0b\n"                   \
--      "       synco\n"
-+#include "atomic.h"
-+#include "libc.h"
- /* gusa is a hack in the kernel which lets you create a sequence of instructions
-  * which will be restarted if the process is preempted in the middle of the
-@@ -34,114 +29,74 @@
- int __sh_cas(volatile int *p, int t, int s)
- {
-+      if (__hwcap & CPU_HAS_LLSC) return __sh_cas_llsc(p, t, s);
-+
-       int old;
--      if (__hwcap & CPU_HAS_LLSC) {
--              __asm__ __volatile__(
--                      LLSC_START("%1")
--                      "       mov r0, %0\n"
--                      "       cmp/eq %0, %2\n"
--                      "       bf 1f\n"
--                      "       mov %3, r0\n"
--                      LLSC_END("%1")
--                      : "=&r"(old) : "r"(p), "r"(t), "r"(s) : LLSC_CLOBBERS);
--      } else {
--              __asm__ __volatile__(
--                      GUSA_START_EVEN("%1", "%0")
--                      "       cmp/eq %0, %2\n"
--                      "       bf 1f\n"
--                      GUSA_END("%1", "%3")
--                      : "=&r"(old) : "r"(p), "r"(t), "r"(s) : GUSA_CLOBBERS, "t");
--      }
-+      __asm__ __volatile__(
-+              GUSA_START_EVEN("%1", "%0")
-+              "       cmp/eq %0, %2\n"
-+              "       bf 1f\n"
-+              GUSA_END("%1", "%3")
-+              : "=&r"(old) : "r"(p), "r"(t), "r"(s) : GUSA_CLOBBERS, "t");
-       return old;
- }
- int __sh_swap(volatile int *x, int v)
- {
-+      if (__hwcap & CPU_HAS_LLSC) return __sh_swap_llsc(x, v);
-+
-       int old;
--      if (__hwcap & CPU_HAS_LLSC) {
--              __asm__ __volatile__(
--                      LLSC_START("%1")
--                      "       mov r0, %0\n"
--                      "       mov %2, r0\n"
--                      LLSC_END("%1")
--                      : "=&r"(old) : "r"(x), "r"(v) : LLSC_CLOBBERS);
--      } else {
--              __asm__ __volatile__(
--                      GUSA_START_EVEN("%1", "%0")
--                      GUSA_END("%1", "%2")
--                      : "=&r"(old) : "r"(x), "r"(v) : GUSA_CLOBBERS);
--      }
-+      __asm__ __volatile__(
-+              GUSA_START_EVEN("%1", "%0")
-+              GUSA_END("%1", "%2")
-+              : "=&r"(old) : "r"(x), "r"(v) : GUSA_CLOBBERS);
-       return old;
- }
- int __sh_fetch_add(volatile int *x, int v)
- {
-+      if (__hwcap & CPU_HAS_LLSC) return __sh_fetch_add_llsc(x, v);
-+
-       int old, dummy;
--      if (__hwcap & CPU_HAS_LLSC) {
--              __asm__ __volatile__(
--                      LLSC_START("%1")
--                      "       mov r0, %0\n"
--                      "       add %2, r0\n"
--                      LLSC_END("%1")
--                      : "=&r"(old) : "r"(x), "r"(v) : LLSC_CLOBBERS);
--      } else {
--              __asm__ __volatile__(
--                      GUSA_START_EVEN("%2", "%0")
--                      "       mov %0, %1\n"
--                      "       add %3, %1\n"
--                      GUSA_END("%2", "%1")
--                      : "=&r"(old), "=&r"(dummy) : "r"(x), "r"(v) : GUSA_CLOBBERS);
--      }
-+      __asm__ __volatile__(
-+              GUSA_START_EVEN("%2", "%0")
-+              "       mov %0, %1\n"
-+              "       add %3, %1\n"
-+              GUSA_END("%2", "%1")
-+              : "=&r"(old), "=&r"(dummy) : "r"(x), "r"(v) : GUSA_CLOBBERS);
-       return old;
- }
- void __sh_store(volatile int *p, int x)
- {
--      if (__hwcap & CPU_HAS_LLSC) {
--              __asm__ __volatile__(
--                      "       synco\n"
--                      "       mov.l %1, @%0\n"
--                      "       synco\n"
--                      : : "r"(p), "r"(x) : "memory");
--      } else {
--              __asm__ __volatile__(
--                      "       mov.l %1, @%0\n"
--                      : : "r"(p), "r"(x) : "memory");
--      }
-+      if (__hwcap & CPU_HAS_LLSC) return __sh_store_llsc(p, x);
-+      __asm__ __volatile__(
-+              "       mov.l %1, @%0\n"
-+              : : "r"(p), "r"(x) : "memory");
- }
- void __sh_and(volatile int *x, int v)
- {
-+      if (__hwcap & CPU_HAS_LLSC) return __sh_and_llsc(x, v);
-+
-       int dummy;
--      if (__hwcap & CPU_HAS_LLSC) {
--              __asm__ __volatile__(
--                      LLSC_START("%0")
--                      "       and %1, r0\n"
--                      LLSC_END("%0")
--                      : : "r"(x), "r"(v) : LLSC_CLOBBERS);
--      } else {
--              __asm__ __volatile__(
--                      GUSA_START_ODD("%1", "%0")
--                      "       and %2, %0\n"
--                      GUSA_END("%1", "%0")
--                      : "=&r"(dummy) : "r"(x), "r"(v) : GUSA_CLOBBERS);
--      }
-+      __asm__ __volatile__(
-+              GUSA_START_ODD("%1", "%0")
-+              "       and %2, %0\n"
-+              GUSA_END("%1", "%0")
-+              : "=&r"(dummy) : "r"(x), "r"(v) : GUSA_CLOBBERS);
- }
- void __sh_or(volatile int *x, int v)
- {
-+      if (__hwcap & CPU_HAS_LLSC) return __sh_or_llsc(x, v);
-+
-       int dummy;
--      if (__hwcap & CPU_HAS_LLSC) {
--              __asm__ __volatile__(
--                      LLSC_START("%0")
--                      "       or %1, r0\n"
--                      LLSC_END("%0")
--                      : : "r"(x), "r"(v) : LLSC_CLOBBERS);
--      } else {
--              __asm__ __volatile__(
--                      GUSA_START_ODD("%1", "%0")
--                      "       or %2, %0\n"
--                      GUSA_END("%1", "%0")
--                      : "=&r"(dummy) : "r"(x), "r"(v) : GUSA_CLOBBERS);
--      }
-+      __asm__ __volatile__(
-+              GUSA_START_ODD("%1", "%0")
-+              "       or %2, %0\n"
-+              GUSA_END("%1", "%0")
-+              : "=&r"(dummy) : "r"(x), "r"(v) : GUSA_CLOBBERS);
- }
-+
-+#endif
---- a/arch/x32/atomic.h
-+++ b/arch/x32/atomic.h
-@@ -47,16 +47,16 @@ static inline int a_cas(volatile int *p,
-       return t;
- }
--static inline void a_or(volatile void *p, int v)
-+static inline void a_or(volatile int *p, int v)
- {
-       __asm__( "lock ; or %1, %0"
--              : "=m"(*(int *)p) : "r"(v) : "memory" );
-+              : "=m"(*p) : "r"(v) : "memory" );
- }
--static inline void a_and(volatile void *p, int v)
-+static inline void a_and(volatile int *p, int v)
- {
-       __asm__( "lock ; and %1, %0"
--              : "=m"(*(int *)p) : "r"(v) : "memory" );
-+              : "=m"(*p) : "r"(v) : "memory" );
- }
- static inline int a_swap(volatile int *x, int v)
---- a/arch/x86_64/atomic.h
-+++ b/arch/x86_64/atomic.h
-@@ -47,16 +47,16 @@ static inline int a_cas(volatile int *p,
-       return t;
- }
--static inline void a_or(volatile void *p, int v)
-+static inline void a_or(volatile int *p, int v)
- {
-       __asm__( "lock ; or %1, %0"
--              : "=m"(*(int *)p) : "r"(v) : "memory" );
-+              : "=m"(*p) : "r"(v) : "memory" );
- }
--static inline void a_and(volatile void *p, int v)
-+static inline void a_and(volatile int *p, int v)
- {
-       __asm__( "lock ; and %1, %0"
--              : "=m"(*(int *)p) : "r"(v) : "memory" );
-+              : "=m"(*p) : "r"(v) : "memory" );
- }
- static inline int a_swap(volatile int *x, int v)
---- a/configure
-+++ b/configure
-@@ -80,7 +80,7 @@ fi
- 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# }"
-@@ -94,7 +94,7 @@ fi
- 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# }"
-@@ -113,7 +113,9 @@ CFLAGS_C99FSE=
- CFLAGS_AUTO=
- CFLAGS_MEMOPS=
- CFLAGS_NOSSP=
-+CFLAGS_TRY=
- LDFLAGS_AUTO=
-+LDFLAGS_TRY=
- OPTIMIZE_GLOBS=
- prefix=/usr/local/musl
- exec_prefix='$(prefix)'
-@@ -205,6 +207,14 @@ exit 1
- fi
- #
-+# Figure out options to force errors on unknown flags.
-+#
-+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 to decide whether to build the
- # musl-gcc wrapper, and for critical bug detection in some gcc versions.
- #
---- a/crt/mips/crt1.s
-+++ b/crt/mips/crt1.s
-@@ -4,6 +4,8 @@
- .weak  _fini
- .global __start
- .global _start
-+.type __start,@function
-+.type _start,@function
- __start:
- _start:
-       subu    $fp, $fp, $fp            # Zero the frame pointer.
---- a/crt/mips/crti.s
-+++ b/crt/mips/crti.s
-@@ -2,6 +2,7 @@
- .section .init
- .global _init
-+.type _init,@function
- .align 2
- _init:
-       subu $sp,$sp,32
-@@ -10,6 +11,7 @@ _init:
- .section .fini
- .global _fini
-+.type _fini,@function
- .align 2
- _fini:
-       subu $sp,$sp,32
---- /dev/null
-+++ b/crt/rcrt1.c
-@@ -0,0 +1,15 @@
-+#define SHARED
-+#define START "_start"
-+#define _dlstart_c _start_c
-+#include "../src/ldso/dlstart.c"
-+
-+int main();
-+void _init() __attribute__((weak));
-+void _fini() __attribute__((weak));
-+_Noreturn int __libc_start_main(int (*)(), int, char **,
-+      void (*)(), void(*)(), void(*)());
-+
-+_Noreturn void __dls2(unsigned char *base, size_t *sp)
-+{
-+      __libc_start_main(main, *sp, (void *)(sp+1), _init, _fini, 0);
-+}
---- a/include/sys/resource.h
-+++ b/include/sys/resource.h
-@@ -96,6 +96,9 @@ int prlimit(pid_t, int, const struct rli
- #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
---- a/src/internal/dynlink.h
-+++ b/src/internal/dynlink.h
-@@ -51,7 +51,7 @@ enum {
- #define AUX_CNT 32
- #define DYN_CNT 32
--typedef void (*stage2_func)(unsigned char *);
-+typedef void (*stage2_func)(unsigned char *, size_t *);
- typedef _Noreturn void (*stage3_func)(size_t *);
- #endif
---- a/src/internal/libc.h
-+++ b/src/internal/libc.h
-@@ -8,9 +8,7 @@
- struct __locale_map;
- struct __locale_struct {
--      volatile int ctype_utf8;
--      char *messages_name;
--      struct __locale_map *volatile cat[4];
-+      const struct __locale_map *volatile cat[6];
- };
- struct __libc {
-@@ -23,8 +21,6 @@ struct __libc {
-       volatile int ofl_lock[2];
-       size_t tls_size;
-       size_t page_size;
--      volatile int uselocale_cnt;
--      volatile int bytelocale_cnt_minus_1;
-       struct __locale_struct global_locale;
- };
---- a/src/internal/locale_impl.h
-+++ b/src/internal/locale_impl.h
-@@ -9,22 +9,20 @@ struct __locale_map {
-       const void *map;
-       size_t map_size;
-       char name[LOCALE_NAME_MAX+1];
--      struct __locale_map *next;
-+      const struct __locale_map *next;
- };
--int __setlocalecat(locale_t, int, const char *);
-+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)-2])
-+#define LCTRANS(msg, lc, loc) __lctrans(msg, (loc)->cat[(lc)])
- #define LCTRANS_CUR(msg) __lctrans_cur(msg)
--#define CURRENT_LOCALE \
--      (libc.uselocale_cnt ? __pthread_self()->locale : &libc.global_locale)
-+#define CURRENT_LOCALE (__pthread_self()->locale)
--#define CURRENT_UTF8 \
--      (libc.bytelocale_cnt_minus_1<0 || __pthread_self()->locale->ctype_utf8)
-+#define CURRENT_UTF8 (!!__pthread_self()->locale->cat[LC_CTYPE])
- #undef MB_CUR_MAX
- #define MB_CUR_MAX (CURRENT_UTF8 ? 4 : 1)
---- a/src/ldso/dlstart.c
-+++ b/src/ldso/dlstart.c
-@@ -56,31 +56,22 @@ void _dlstart_c(size_t *sp, size_t *dynv
-               for (i=0; i<local_cnt; i++) got[i] += (size_t)base;
-       }
--      /* The use of the reloc_info structure and nested loops is a trick
--       * to work around the fact that we can't necessarily make function
--       * calls yet. Each struct in the array serves like the arguments
--       * to a function call. */
--      struct {
--              void *rel;
--              size_t size;
--              size_t stride;
--      } reloc_info[] = {
--              { base+dyn[DT_JMPREL], dyn[DT_PLTRELSZ], 2+(dyn[DT_PLTREL]==DT_RELA) },
--              { base+dyn[DT_REL], dyn[DT_RELSZ], 2 },
--              { base+dyn[DT_RELA], dyn[DT_RELASZ], 3 },
--              { 0, 0, 0 }
--      };
--
--      for (i=0; reloc_info[i].stride; i++) {
--              size_t *rel = reloc_info[i].rel;
--              size_t rel_size = reloc_info[i].size;
--              size_t stride = reloc_info[i].stride;
--              for (; rel_size; rel+=stride, rel_size-=stride*sizeof(size_t)) {
--                      if (!IS_RELATIVE(rel[1])) continue;
--                      size_t *rel_addr = (void *)(base + rel[0]);
--                      size_t addend = stride==3 ? rel[2] : *rel_addr;
--                      *rel_addr = (size_t)base + addend;
--              }
-+      size_t *rel, rel_size;
-+
-+      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])) continue;
-+              size_t *rel_addr = (void *)(base + rel[0]);
-+              *rel_addr += (size_t)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])) continue;
-+              size_t *rel_addr = (void *)(base + rel[0]);
-+              *rel_addr = (size_t)base + rel[2];
-       }
-       const char *strings = (void *)(base + dyn[DT_STRTAB]);
-@@ -93,16 +84,7 @@ void _dlstart_c(size_t *sp, size_t *dynv
-                && s[3]=='l' && s[4]=='s' && s[5]=='2' && !s[6])
-                       break;
-       }
--      ((stage2_func)(base + syms[i].st_value))(base);
--
--      /* Call dynamic linker stage-3, __dls3 */
--      for (i=0; ;i++) {
--              const char *s = strings + syms[i].st_name;
--              if (s[0]=='_' && s[1]=='_' && s[2]=='d'
--               && s[3]=='l' && s[4]=='s' && s[5]=='3' && !s[6])
--                      break;
--      }
--      ((stage3_func)(base + syms[i].st_value))(sp);
-+      ((stage2_func)(base + syms[i].st_value))(base, sp);
- }
- #endif
---- a/src/ldso/dynlink.c
-+++ b/src/ldso/dynlink.c
-@@ -74,7 +74,6 @@ struct dso {
-       volatile int new_dtv_idx, new_tls_idx;
-       struct td_index *td_index;
-       struct dso *fini_next;
--      int rel_early_relative, rel_update_got;
-       char *shortname;
-       char buf[];
- };
-@@ -96,6 +95,9 @@ static struct builtin_tls {
- } 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;
-@@ -256,10 +258,19 @@ static void do_relocs(struct dso *dso, s
-       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 (dso->rel_early_relative && IS_RELATIVE(rel[1])) continue;
-+              if (skip_relative && IS_RELATIVE(rel[1])) continue;
-               type = R_TYPE(rel[1]);
-+              if (type == REL_NONE) continue;
-               sym_index = R_SYM(rel[1]);
-               reloc_addr = (void *)(base + rel[0]);
-               if (sym_index) {
-@@ -280,12 +291,20 @@ static void do_relocs(struct dso *dso, s
-                       def.dso = dso;
-               }
--              int gotplt = (type == REL_GOT || type == REL_PLT);
--              if (dso->rel_update_got && !gotplt) continue;
--
--              addend = stride>2 ? rel[2]
--                      : gotplt || type==REL_COPY ? 0
--                      : *reloc_addr;
-+              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)def.dso->base+def.sym->st_value : 0;
-               tls_val = def.sym ? def.sym->st_value : 0;
-@@ -879,7 +898,7 @@ static void do_mips_relocs(struct dso *p
-       size_t i, j, rel[2];
-       unsigned char *base = p->base;
-       i=0; search_vec(p->dynv, &i, DT_MIPS_LOCAL_GOTNO);
--      if (p->rel_early_relative) {
-+      if (p==&ldso) {
-               got += i;
-       } else {
-               while (i--) *got++ += (size_t)base;
-@@ -1116,7 +1135,7 @@ static void update_tls_size()
-  * linker itself, but some of the relocations performed may need to be
-  * replaced later due to copy relocations in the main program. */
--void __dls2(unsigned char *base)
-+void __dls2(unsigned char *base, size_t *sp)
- {
-       Ehdr *ehdr = (void *)base;
-       ldso.base = base;
-@@ -1125,15 +1144,35 @@ void __dls2(unsigned char *base)
-       ldso.phnum = ehdr->e_phnum;
-       ldso.phdr = (void *)(base + ehdr->e_phoff);
-       ldso.phentsize = ehdr->e_phentsize;
--      ldso.rel_early_relative = 1;
-       kernel_mapped_dso(&ldso);
-       decode_dyn(&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 = (void *)(base+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])) 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;
--      ldso.rel_update_got = 1;
-+
-+      /* 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);
-+      ((stage3_func)(ldso.base+dls3_def.sym->st_value))(sp);
- }
- /* Stage 3 of the dynamic linker is called with the dynamic linker/libc
---- a/src/locale/__lctrans.c
-+++ b/src/locale/__lctrans.c
-@@ -16,5 +16,5 @@ const char *__lctrans(const char *msg, c
- const char *__lctrans_cur(const char *msg)
- {
--      return __lctrans_impl(msg, CURRENT_LOCALE->cat[LC_MESSAGES-2]);
-+      return __lctrans_impl(msg, CURRENT_LOCALE->cat[LC_MESSAGES]);
- }
---- a/src/locale/__setlocalecat.c
-+++ /dev/null
-@@ -1,111 +0,0 @@
--#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 struct __locale_map *findlocale(const char *name, size_t n)
--{
--      static void *volatile loc_head;
--      struct __locale_map *p, *new, *old_head;
--      const char *path = 0, *z;
--      char buf[256];
--      size_t l;
--      const void *map;
--      size_t map_size;
--
--      for (p=loc_head; p; p=p->next)
--              if (!strcmp(name, p->name)) return p;
--
--      if (!libc.secure) path = getenv("MUSL_LOCPATH");
--      /* FIXME: add a default path? */
--      if (!path) return 0;
--
--      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, name, n);
--              buf[l+1+n] = 0;
--              map = __map_file(buf, &map_size);
--              if (map) {
--                      new = malloc(sizeof *new);
--                      if (!new) {
--                              __munmap((void *)map, map_size);
--                              return 0;
--                      }
--                      new->map = map;
--                      new->map_size = map_size;
--                      memcpy(new->name, name, n);
--                      new->name[n] = 0;
--                      do {
--                              old_head = loc_head;
--                              new->next = old_head;
--                      } while (a_cas_p(&loc_head, old_head, new) != old_head);
--                      return new;
--              }
--      }
--      return 0;
--}
--
--static const char envvars[][12] = {
--      "LC_CTYPE",
--      "LC_NUMERIC",
--      "LC_TIME",
--      "LC_COLLATE",
--      "LC_MONETARY",
--      "LC_MESSAGES",
--};
--
--int __setlocalecat(locale_t loc, int cat, const char *val)
--{
--      if (!*val) {
--              (val = getenv("LC_ALL")) && *val ||
--              (val = getenv(envvars[cat])) && *val ||
--              (val = getenv("LANG")) && *val ||
--              (val = "C.UTF-8");
--      }
--
--      size_t n;
--      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");
--      struct __locale_map *data, *old;
--
--      switch (cat) {
--      case LC_CTYPE:
--              a_store(&loc->ctype_utf8, !builtin || val[1]=='.');
--              break;
--      case LC_MESSAGES:
--              if (builtin) {
--                      loc->messages_name[0] = 0;
--              } else {
--                      memcpy(loc->messages_name, val, n);
--                      loc->messages_name[n] = 0;
--              }
--              /* fall through */
--      default:
--              data = builtin ? 0 : findlocale(val, n);
--              if (data == loc->cat[cat-2]) break;
--              do old = loc->cat[cat-2];
--              while (a_cas_p(&loc->cat[cat-2], old, data) != old);
--      case LC_NUMERIC:
--              break;
--      }
--      return 0;
--}
---- a/src/locale/dcngettext.c
-+++ b/src/locale/dcngettext.c
-@@ -84,13 +84,15 @@ char *bindtextdomain(const char *domainn
- }
- static const char catnames[][12] = {
-+      "LC_CTYPE",
-+      "LC_NUMERIC",
-       "LC_TIME",
-       "LC_COLLATE",
-       "LC_MONETARY",
-       "LC_MESSAGES",
- };
--static const char catlens[] = { 7, 10, 11, 11 };
-+static const char catlens[] = { 8, 10, 7, 10, 11, 11 };
- struct msgcat {
-       struct msgcat *next;
-@@ -117,10 +119,12 @@ char *dcngettext(const char *domainname,
-       static struct msgcat *volatile cats;
-       struct msgcat *p;
-       struct __locale_struct *loc = CURRENT_LOCALE;
--      struct __locale_map *lm;
-+      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);
-@@ -129,25 +133,15 @@ char *dcngettext(const char *domainname,
-       dirname = gettextdir(domainname, &dirlen);
-       if (!dirname) goto notrans;
--      switch (category) {
--      case LC_MESSAGES:
--              locname = loc->messages_name;
--              if (!*locname) goto notrans;
--              break;
--      case LC_TIME:
--      case LC_MONETARY:
--      case LC_COLLATE:
--              lm = loc->cat[category-2];
--              if (!lm) goto notrans;
--              locname = lm->name;
--              break;
--      default:
-+      lm = loc->cat[category];
-+      if (!lm) {
- notrans:
-               return (char *) ((n == 1) ? msgid1 : msgid2);
-       }
-+      locname = lm->name;
--      catname = catnames[category-2];
--      catlen = catlens[category-2];
-+      catname = catnames[category];
-+      catlen = catlens[category];
-       loclen = strlen(locname);
-       size_t namelen = dirlen+1 + loclen+1 + catlen+1 + domlen+3;
---- a/src/locale/duplocale.c
-+++ b/src/locale/duplocale.c
-@@ -5,17 +5,10 @@
- locale_t __duplocale(locale_t old)
- {
--      locale_t new = calloc(1, sizeof *new + LOCALE_NAME_MAX + 1);
-+      locale_t new = malloc(sizeof *new);
-       if (!new) return 0;
--      new->messages_name = (void *)(new+1);
--
-       if (old == LC_GLOBAL_LOCALE) old = &libc.global_locale;
--      new->ctype_utf8 = old->ctype_utf8;
--      if (old->messages_name)
--              strcpy(new->messages_name, old->messages_name);
--
--      for (size_t i=0; i<sizeof new->cat/sizeof new->cat[0]; i++)
--              new->cat[i] = old->cat[i];
-+      *new = *old;
-       return new;
- }
---- a/src/locale/freelocale.c
-+++ b/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);
---- a/src/locale/iconv.c
-+++ b/src/locale/iconv.c
-@@ -23,19 +23,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 +84,7 @@ static int fuzzycmp(const unsigned char
- 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);
---- /dev/null
-+++ b/src/locale/locale_map.c
-@@ -0,0 +1,124 @@
-+#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",
-+};
-+
-+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_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 = empty_mo;
-+              new->map_size = sizeof empty_mo;
-+              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;
-+}
---- a/src/locale/newlocale.c
-+++ b/src/locale/newlocale.c
-@@ -3,22 +3,52 @@
- #include "locale_impl.h"
- #include "libc.h"
-+extern const struct __locale_map __c_dot_utf8;
-+
-+static const struct __locale_struct c_locale = { 0 };
-+static const struct __locale_struct c_dot_utf8_locale = {
-+      .cat[LC_CTYPE] = &__c_dot_utf8
-+};
-+
-+int __loc_is_allocated(locale_t loc)
-+{
-+      return loc && loc != &c_locale && loc != &c_dot_utf8_locale;
-+}
-+
- locale_t __newlocale(int mask, const char *name, locale_t loc)
- {
--      int i;
-+      int i, j;
-+      struct __locale_struct tmp;
-+      const struct __locale_map *lm;
--      if (!loc) {
--              loc = calloc(1, sizeof *loc + LOCALE_NAME_MAX + 1);
--              if (!loc) return 0;
--              loc->messages_name = (void *)(loc+1);
-+      /* For locales with allocated storage, modify in-place. */
-+      if (__loc_is_allocated(loc)) {
-               for (i=0; i<LC_ALL; i++)
--                      if (!(mask & (1<<i)))
--                              __setlocalecat(loc, 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;
-       }
--      for (i=0; i<LC_ALL; i++)
--              if (mask & (1<<i))
--                      __setlocalecat(loc, i, name);
-+      if (!j)
-+              return (locale_t)&c_locale;
-+      if (j==1 && tmp.cat[LC_CTYPE]==c_dot_utf8_locale.cat[LC_CTYPE])
-+              return (locale_t)&c_dot_utf8_locale;
-+
-+      if ((loc = malloc(sizeof *loc))) *loc = tmp;
-       return loc;
- }
---- a/src/locale/setlocale.c
-+++ b/src/locale/setlocale.c
-@@ -5,73 +5,66 @@
- #include "libc.h"
- #include "atomic.h"
--static char buf[2+4*(LOCALE_NAME_MAX+1)];
-+static char buf[LC_ALL*(LOCALE_NAME_MAX+1)];
--char *setlocale(int cat, const char *name)
-+static char *setlocale_one_unlocked(int cat, const char *name)
- {
--      struct __locale_map *lm;
--      int i, j;
-+      const struct __locale_map *lm;
--      if (!libc.global_locale.messages_name) {
--              libc.global_locale.messages_name =
--                      buf + 2 + 3*(LOCALE_NAME_MAX+1);
--      }
-+      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];
--                      if (name[0] && name[1]==';'
--                          && strlen(name) > 2 + 3*(LOCALE_NAME_MAX+1)) {
--                              part[0] = name[0];
--                              part[1] = 0;
--                              setlocale(LC_CTYPE, part);
--                              part[LOCALE_NAME_MAX] = 0;
--                              for (i=LC_TIME; i<LC_MESSAGES; i++) {
--                                      memcpy(part, name + 2 + (i-2)*(LOCALE_NAME_MAX+1), LOCALE_NAME_MAX);
--                                      for (j=LOCALE_NAME_MAX-1; j && part[j]==';'; j--)
--                                              part[j] = 0;
--                                      setlocale(i, part);
-+                      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(LC_MESSAGES, name + 2 + 3*(LOCALE_NAME_MAX+1));
--                      } else {
--                              for (i=0; i<LC_ALL; i++)
--                                      setlocale(i, name);
-+                              setlocale_one_unlocked(i, part);
-                       }
-               }
--              memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1));
--              buf[0] = libc.global_locale.ctype_utf8 ? 'U' : 'C';
--              for (i=LC_TIME; i<LC_MESSAGES; i++) {
--                      lm = libc.global_locale.cat[i-2];
--                      if (lm) memcpy(buf + 2 + (i-2)*(LOCALE_NAME_MAX+1),
--                              lm->name, strlen(lm->name));
-+              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;
-       }
--      if (name) {
--              int adj = libc.global_locale.ctype_utf8;
--              __setlocalecat(&libc.global_locale, cat, name);
--              adj -= libc.global_locale.ctype_utf8;
--              if (adj) a_fetch_add(&libc.bytelocale_cnt_minus_1, adj);
--      }
-+      char *ret = setlocale_one_unlocked(cat, name);
--      switch (cat) {
--      case LC_CTYPE:
--              return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C";
--      case LC_NUMERIC:
--              return "C";
--      case LC_MESSAGES:
--              return libc.global_locale.messages_name[0]
--                      ? libc.global_locale.messages_name : "C";
--      default:
--              lm = libc.global_locale.cat[cat-2];
--              return lm ? lm->name : "C";
--      }
-+      UNLOCK(lock);
-+
-+      return ret;
- }
---- a/src/locale/uselocale.c
-+++ b/src/locale/uselocale.c
-@@ -10,15 +10,7 @@ locale_t __uselocale(locale_t new)
-       if (new == LC_GLOBAL_LOCALE) new = global;
--      if (new && new != old) {
--              int adj = 0;
--              if (new == global) a_dec(&libc.uselocale_cnt);
--              else if (!new->ctype_utf8) adj++;
--              if (old == global) a_inc(&libc.uselocale_cnt);
--              else if (!old->ctype_utf8) adj--;
--              a_fetch_add(&libc.bytelocale_cnt_minus_1, adj);
--              self->locale = new;
--      }
-+      self->locale = new;
-       return old == global ? LC_GLOBAL_LOCALE : old;
- }
---- a/src/stdio/__stdio_read.c
-+++ b/src/stdio/__stdio_read.c
-@@ -21,7 +21,6 @@ size_t __stdio_read(FILE *f, unsigned ch
-       pthread_cleanup_pop(0);
-       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;
---- a/src/stdio/__toread.c
-+++ b/src/stdio/__toread.c
-@@ -5,12 +5,12 @@ int __toread(FILE *f)
-       f->mode |= f->mode-1;
-       if (f->wpos > f->buf) 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);
---- a/src/stdio/__uflow.c
-+++ b/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;
- }
---- a/src/stdio/ungetc.c
-+++ b/src/stdio/ungetc.c
-@@ -6,7 +6,8 @@ int ungetc(int c, FILE *f)
-       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;
-       }
---- a/src/stdio/ungetwc.c
-+++ b/src/stdio/ungetwc.c
-@@ -19,7 +19,8 @@ wint_t ungetwc(wint_t c, FILE *f)
-       f->mode |= f->mode+1;
--      if ((!f->rend && __toread(f)) || f->rpos < f->buf - UNGET + l) {
-+      if (!f->rpos) __toread(f);
-+      if (!f->rpos || f->rpos < f->buf - UNGET + l) {
-               FUNLOCK(f);
-               return EOF;
-       }
---- a/src/thread/i386/__set_thread_area.s
-+++ b/src/thread/i386/__set_thread_area.s
-@@ -6,10 +6,10 @@ __set_thread_area:
-       push $0x51
-       push $0xfffff
-       push 16(%esp)
--      xor %edx,%edx
--      mov %gs,%dx
--      sub $3,%edx
--      sar $3,%edx
-+      call 1f
-+1:    addl $4f-1b,(%esp)
-+      pop %ecx
-+      mov (%ecx),%edx
-       push %edx
-       mov %esp,%ebx
-       xor %eax,%eax
-@@ -18,6 +18,7 @@ __set_thread_area:
-       testl %eax,%eax
-       jnz 2f
-       movl (%esp),%edx
-+      movl %edx,(%ecx)
-       leal 3(,%edx,8),%edx
- 3:    movw %dx,%gs
- 1:
-@@ -38,3 +39,7 @@ __set_thread_area:
-       mov $7,%dl
-       inc %al
-       jmp 3b
-+
-+.data
-+      .align 4
-+4:    .long -1
---- a/src/thread/mips/syscall_cp.s
-+++ b/src/thread/mips/syscall_cp.s
-@@ -2,10 +2,13 @@
- .global __cp_begin
- .hidden __cp_begin
-+.type   __cp_begin,@function
- .global __cp_end
- .hidden __cp_end
-+.type   __cp_end,@function
- .global __cp_cancel
- .hidden __cp_cancel
-+.type   __cp_cancel,@function
- .hidden __cancel
- .global __syscall_cp_asm
- .hidden __syscall_cp_asm
---- a/src/thread/pthread_create.c
-+++ b/src/thread/pthread_create.c
-@@ -67,12 +67,6 @@ _Noreturn void __pthread_exit(void *resu
-               exit(0);
-       }
--      if (self->locale != &libc.global_locale) {
--              a_dec(&libc.uselocale_cnt);
--              if (self->locale->ctype_utf8)
--                      a_dec(&libc.bytelocale_cnt_minus_1);
--      }
--
-       /* 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. */
diff --git a/toolchain/musl/patches/001-git-2015-06-16.patch b/toolchain/musl/patches/001-git-2015-06-16.patch
new file mode 100644 (file)
index 0000000..5941adc
--- /dev/null
@@ -0,0 +1,1578 @@
+commit 1b0cdc8700d29ef018bf226d74b2b58b23bce91c
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Tue Jun 16 07:11:19 2015 +0000
+
+    refactor stdio open file list handling, move it out of global libc struct
+    
+    functions which open in-memory FILE stream variants all shared a tail
+    with __fdopen, adding the FILE structure to stdio's open file list.
+    replacing this common tail with a function call reduces code size and
+    duplication of logic. the list is also partially encapsulated now.
+    
+    function signatures were chosen to facilitate tail call optimization
+    and reduce the need for additional accessor functions.
+    
+    with these changes, static linked programs that do not use stdio no
+    longer have an open file list at all.
+
+commit f22a9edaf8a6f2ca1d314d18b3785558279a5c03
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Tue Jun 16 06:18:00 2015 +0000
+
+    byte-based C locale, phase 3: make MB_CUR_MAX variable to activate code
+    
+    this patch activates the new byte-based C locale (high bytes treated
+    as abstract code unit "characters" rather than decoded as multibyte
+    characters) by making the value of MB_CUR_MAX depend on the active
+    locale. for the C locale, the LC_CTYPE category pointer is null,
+    yielding a value of 1. all other locales yield a value of 4.
+
+commit 16f18d036d9a7bf590ee6eb86785c0a9658220b6
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Tue Jun 16 05:35:31 2015 +0000
+
+    byte-based C locale, phase 2: stdio and iconv (multibyte callers)
+    
+    this patch adjusts libc components which use the multibyte functions
+    internally, and which depend on them operating in a particular
+    encoding, to make the appropriate locale changes before calling them
+    and restore the calling thread's locale afterwards. activating the
+    byte-based C locale without these changes would cause regressions in
+    stdio and iconv.
+    
+    in the case of iconv, the current implementation was simply using the
+    multibyte functions as UTF-8 conversions. setting a multibyte UTF-8
+    locale for the duration of the iconv operation allows the code to
+    continue working.
+    
+    in the case of stdio, POSIX requires that FILE streams have an
+    encoding rule bound at the time of setting wide orientation. as long
+    as all locales, including the C locale, used the same encoding,
+    treating high bytes as UTF-8, there was no need to store an encoding
+    rule as part of the stream's state.
+    
+    a new locale field in the FILE structure points to the locale that
+    should be made active during fgetwc/fputwc/ungetwc on the stream. it
+    cannot point to the locale active at the time the stream becomes
+    oriented, because this locale could be mutable (the global locale) or
+    could be destroyed (locale_t objects produced by newlocale) before the
+    stream is closed. instead, a pointer to the static C or C.UTF-8 locale
+    object added in commit commit aeeac9ca5490d7d90fe061ab72da446c01ddf746
+    is used. this is valid since categories other than LC_CTYPE will not
+    affect these functions.
+
+commit 1507ebf837334e9e07cfab1ca1c2e88449069a80
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Tue Jun 16 04:44:17 2015 +0000
+
+    byte-based C locale, phase 1: multibyte character handling functions
+    
+    this patch makes the functions which work directly on multibyte
+    characters treat the high bytes as individual abstract code units
+    rather than as multibyte sequences when MB_CUR_MAX is 1. since
+    MB_CUR_MAX is presently defined as a constant 4, all of the new code
+    added is dead code, and optimizing compilers' code generation should
+    not be affected at all. a future commit will activate the new code.
+    
+    as abstract code units, bytes 0x80 to 0xff are represented by wchar_t
+    values 0xdf80 to 0xdfff, at the end of the surrogates range. this
+    ensures that they will never be misinterpreted as Unicode characters,
+    and that all wctype functions return false for these "characters"
+    without needing locale-specific logic. a high range outside of Unicode
+    such as 0x7fffff80 to 0x7fffffff was also considered, but since C11's
+    char16_t also needs to be able to represent conversions of these
+    bytes, the surrogate range was the natural choice.
+
+commit 38e2f727237230300fea6aff68802db04625fd23
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Tue Jun 16 04:21:38 2015 +0000
+
+    fix btowc corner case
+    
+    btowc is required to interpret its argument by conversion to unsigned
+    char, unless the argument is equal to EOF. since the conversion to
+    produces a non-character value anyway, we can just unconditionally
+    convert, for now.
+
+commit ee59c296d56bf26f49f354d6eb32b4b6d4190188
+Author: Szabolcs Nagy <nsz@port70.net>
+Date:   Wed Jun 3 10:32:14 2015 +0100
+
+    arm: add vdso support
+    
+    vdso will be available on arm in linux v4.2, the user-space code
+    for it is in kernel commit 8512287a8165592466cb9cb347ba94892e9c56a5
+
+commit e3bc22f1eff87b8f029a6ab31f1a269d69e4b053
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sun Jun 14 01:59:02 2015 +0000
+
+    refactor malloc's expand_heap to share with __simple_malloc
+    
+    this extends the brk/stack collision protection added to full malloc
+    in commit 276904c2f6bde3a31a24ebfa201482601d18b4f9 to also protect the
+    __simple_malloc function used in static-linked programs that don't
+    reference the free function.
+    
+    it also extends support for using mmap when brk fails, which full
+    malloc got in commit 5446303328adf4b4e36d9fba21848e6feb55fab4, to
+    __simple_malloc.
+    
+    since __simple_malloc may expand the heap by arbitrarily large
+    increments, the stack collision detection is enhanced to detect
+    interval overlap rather than just proximity of a single address to the
+    stack. code size is increased a bit, but this is partly offset by the
+    sharing of code between the two malloc implementations, which due to
+    linking semantics, both get linked in a program that needs the full
+    malloc with realloc/free support.
+
+commit 4ef9b828c1f39553a69e0635ac91f0fcadd6e8c6
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sat Jun 13 20:53:02 2015 +0000
+
+    remove cancellation points in stdio
+    
+    commit 58165923890865a6ac042fafce13f440ee986fd9 added these optional
+    cancellation points on the basis that cancellable stdio could be
+    useful, to unblock threads stuck on stdio operations that will never
+    complete. however, the only way to ensure that cancellation can
+    achieve this is to violate the rules for side effects when
+    cancellation is acted upon, discarding knowledge of any partial data
+    transfer already completed. our implementation exhibited this behavior
+    and was thus non-conforming.
+    
+    in addition to improving correctness, removing these cancellation
+    points moderately reduces code size, and should significantly improve
+    performance on i386, where sysenter/syscall instructions can be used
+    instead of "int $128" for non-cancellable syscalls.
+
+commit 536c6d5a4205e2a3f161f2983ce1e0ac3082187d
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sat Jun 13 05:17:16 2015 +0000
+
+    fix idiom for setting stdio stream orientation to wide
+    
+    the old idiom, f->mode |= f->mode+1, was adapted from the idiom for
+    setting byte orientation, f->mode |= f->mode-1, but the adaptation was
+    incorrect. unless the stream was alreasdy set byte-oriented, this code
+    incremented f->mode each time it was executed, which would eventually
+    lead to overflow. it could be fixed by changing it to f->mode |= 1,
+    but upcoming changes will require slightly more work at the time of
+    wide orientation, so it makes sense to just call fwide. as an
+    optimization in the single-character functions, fwide is only called
+    if the stream is not already wide-oriented.
+
+commit f8f565df467c13248104223f99abf7f37cef7584
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sat Jun 13 04:42:38 2015 +0000
+
+    add printing of null %s arguments as "(null)" in wide printf
+    
+    this is undefined, but supported in our implementation of the normal
+    printf, so for consistency the wide variant should support it too.
+
+commit f9e25d813860d53cd1e9b6145cc63375d2fe2529
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sat Jun 13 04:37:27 2015 +0000
+
+    add %m support to wide printf
+
+commit ec634aad91f57479ef17525e33ed446c780a61f4
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Thu Jun 11 05:01:04 2015 +0000
+
+    add sh asm for vfork
+
+commit c30cbcb0a646b1f13a22c645616dce624465b883
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Wed Jun 10 02:27:40 2015 +0000
+
+    implement arch-generic version of __unmapself
+    
+    this can be used to put off writing an asm version of __unmapself for
+    new archs, or as a permanent solution on archs where it's not
+    practical or even possible to run momentarily with no stack.
+    
+    the concept here is simple: the caller takes a lock on a global shared
+    stack and uses it to make the munmap and exit syscalls. the only trick
+    is unlocking, which must be done after the thread exits, and this is
+    achieved by using the set_tid_address syscall to have the kernel zero
+    and futex-wake the lock word as part of the exit syscall.
+
+commit 276904c2f6bde3a31a24ebfa201482601d18b4f9
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Tue Jun 9 20:30:35 2015 +0000
+
+    in malloc, refuse to use brk if it grows into stack
+    
+    the linux/nommu fdpic ELF loader sets up the brk range to overlap
+    entirely with the main thread's stack (but growing from opposite
+    ends), so that the resulting failure mode for malloc is not to return
+    a null pointer but to start returning pointers to memory that overlaps
+    with the caller's stack. needless to say this extremely dangerous and
+    makes brk unusable.
+    
+    since it's non-trivial to detect execution environments that might be
+    affected by this kernel bug, and since the severity of the bug makes
+    any sort of detection that might yield false-negatives unsafe, we
+    instead check the proximity of the brk to the stack pointer each time
+    the brk is to be expanded. both the main thread's stack (where the
+    real known risk lies) and the calling thread's stack are checked. an
+    arbitrary gap distance of 8 MB is imposed, chosen to be larger than
+    linux default main-thread stack reservation sizes and larger than any
+    reasonable stack configuration on nommu.
+    
+    the effeciveness of this patch relies on an assumption that the amount
+    by which the brk is being grown is smaller than the gap limit, which
+    is always true for malloc's use of brk. reliance on this assumption is
+    why the check is being done in malloc-specific code and not in __brk.
+
+commit bd1eaceaa3975bd2a2a34e211cff896affaecadf
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Tue Jun 9 20:09:27 2015 +0000
+
+    fix spurious errors from pwd/grp functions when nscd backend is absent
+    
+    for several pwd/grp functions, the only way the caller can distinguish
+    between a successful negative result ("no such user/group") and an
+    internal error is by clearing errno before the call and checking errno
+    afterwards. the nscd backend support code correctly simulated a
+    not-found response on systems where such a backend is not running, but
+    failed to restore errno.
+    
+    this commit also fixed an outdated/incorrect comment.
+
+commit 75ce4503950621b11fcc7f1fd1187dbcf3cde312
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sun Jun 7 20:55:23 2015 +0000
+
+    fix regression in pre-v7 arm on kernels with kuser helper removed
+    
+    the arm atomics/TLS runtime selection code is called from
+    __set_thread_area and depends on having libc.auxv and __hwcap
+    available. commit 71f099cb7db821c51d8f39dfac622c61e54d794c moved the
+    first call to __set_thread_area to the top of dynamic linking stage 3,
+    before this data is made available, causing the runtime detection code
+    to always see __hwcap as zero and thereby select the atomics/TLS
+    implementations based on kuser helper.
+    
+    upcoming work on superh will use similar runtime detection.
+    
+    ideally this early-init code should be cleanly refactored and shared
+    between the dynamic linker and static-linked startup.
+
+commit 32f3c4f70633488550c29a2444f819aafdf345ff
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sun Jun 7 03:09:16 2015 +0000
+
+    add multiple inclusion guard to locale_impl.h
+
+commit 04b8360adbb6487f61aa0c00e53ec3a90a5a0d29
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sun Jun 7 02:59:49 2015 +0000
+
+    remove redefinition of MB_CUR_MAX in locale_impl.h
+    
+    unless/until the byte-based C locale is implemented, defining
+    MB_CUR_MAX to 1 in the C locale is wrong. no internal code currently
+    uses the MB_CUR_MAX macro, but having it defined inconsistently is
+    error-prone. applications get the value from stdlib.h and were
+    unaffected.
+
+commit 16bf466532d7328e971012b0731ad493b017ad29
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sat Jun 6 18:53:02 2015 +0000
+
+    make static C and C.UTF-8 locales available outside of newlocale
+
+commit 312eea2ea4f4363fb01b73660c08bfcf43dd3bb4
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sat Jun 6 18:20:30 2015 +0000
+
+    remove another invalid skip of locking in ungetwc
+
+commit 3d7e32d28dc9962e9efc1c317c5b44b5b2df3008
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sat Jun 6 18:16:22 2015 +0000
+
+    add macro version of ctype.h isascii function
+    
+    presumably internal code (ungetwc and fputwc) was written assuming a
+    macro implementation existed; otherwise use of isascii is just a
+    pessimization.
+
+commit 7e816a6487932cbb3cb71d94b609e50e81f4e5bf
+Author: Rich Felker <dalias@aerifal.cx>
+Date:   Sat Jun 6 18:11:17 2015 +0000
+
+    remove invalid skip of locking in ungetwc
+    
+    aside from being invalid, the early check only optimized the error
+    case, and likely pessimized the common case by separating the
+    two branches on isascii(c) at opposite ends of the function.
+
+commit 63f4b9f18f3674124d8bcb119739fec85e6da005
+Author: Timo Teräs <timo.teras@iki.fi>
+Date:   Fri Jun 5 10:39:42 2015 +0300
+
+    fix uselocale((locale_t)0) not to modify locale
+    
+    commit 68630b55c0c7219fe9df70dc28ffbf9efc8021d8 made the new locale to
+    be assigned unconditonally resulting in crashes later on.
+
+--- a/arch/arm/syscall_arch.h
++++ b/arch/arm/syscall_arch.h
+@@ -72,3 +72,7 @@ static inline long __syscall6(long n, lo
+       register long r5 __asm__("r5") = f;
+       __asm_syscall("r"(r7), "0"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r5));
+ }
++
++#define VDSO_USEFUL
++#define VDSO_CGT_SYM "__vdso_clock_gettime"
++#define VDSO_CGT_VER "LINUX_2.6"
+--- a/include/ctype.h
++++ b/include/ctype.h
+@@ -64,6 +64,7 @@ int   isascii(int);
+ int   toascii(int);
+ #define _tolower(a) ((a)|0x20)
+ #define _toupper(a) ((a)&0x5f)
++#define isascii(a) (0 ? isascii(a) : (unsigned)(a) < 128)
+ #endif
+--- a/include/stdlib.h
++++ b/include/stdlib.h
+@@ -76,7 +76,8 @@ size_t wcstombs (char *__restrict, const
+ #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)
+--- a/src/ctype/__ctype_get_mb_cur_max.c
++++ b/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;
+ }
+--- a/src/ctype/isascii.c
++++ b/src/ctype/isascii.c
+@@ -1,4 +1,5 @@
+ #include <ctype.h>
++#undef isascii
+ int isascii(int c)
+ {
+--- a/src/internal/libc.h
++++ b/src/internal/libc.h
+@@ -17,8 +17,6 @@ struct __libc {
+       int secure;
+       volatile int threads_minus_1;
+       size_t *auxv;
+-      FILE *ofl_head;
+-      volatile int ofl_lock[2];
+       size_t tls_size;
+       size_t page_size;
+       struct __locale_struct global_locale;
+--- a/src/internal/locale_impl.h
++++ b/src/internal/locale_impl.h
+@@ -1,3 +1,6 @@
++#ifndef _LOCALE_IMPL_H
++#define _LOCALE_IMPL_H
++
+ #include <locale.h>
+ #include <stdlib.h>
+ #include "libc.h"
+@@ -12,6 +15,10 @@ struct __locale_map {
+       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 *);
+@@ -20,9 +27,14 @@ 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
+--- a/src/internal/stdio_impl.h
++++ b/src/internal/stdio_impl.h
+@@ -47,6 +47,7 @@ struct _IO_FILE {
+       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);
+@@ -75,8 +76,9 @@ int __putc_unlocked(int, FILE *);
+ 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)
+--- a/src/ldso/dynlink.c
++++ b/src/ldso/dynlink.c
+@@ -1192,6 +1192,17 @@ _Noreturn void __dls3(size_t *sp)
+       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. */
+@@ -1200,25 +1211,11 @@ _Noreturn void __dls3(size_t *sp)
+               a_crash();
+       }
+-      /* Find aux vector just past environ[] */
+-      for (i=argc+1; argv[i]; i++)
+-              if (!memcmp(argv[i], "LD_LIBRARY_PATH=", 16))
+-                      env_path = argv[i]+16;
+-              else if (!memcmp(argv[i], "LD_PRELOAD=", 11))
+-                      env_preload = argv[i]+11;
+-      auxv = (void *)(argv+i+1);
+-
+-      decode_vec(auxv, aux, AUX_CNT);
+-
+       /* Only trust user/env if kernel says we're not suid/sgid */
+-      if ((aux[0]&0x7800)!=0x7800 || aux[AT_UID]!=aux[AT_EUID]
+-        || aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]) {
+-              env_path = 0;
+-              env_preload = 0;
+-              libc.secure = 1;
++      if (!libc.secure) {
++              env_path = getenv("LD_LIBRARY_PATH");
++              env_preload = getenv("LD_PRELOAD");
+       }
+-      libc.page_size = aux[AT_PAGESZ];
+-      libc.auxv = auxv;
+       /* If the main program was already loaded by the kernel,
+        * AT_PHDR will point to some location other than the dynamic
+--- /dev/null
++++ b/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
++};
+--- a/src/locale/iconv.c
++++ b/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
+@@ -165,9 +166,12 @@ size_t iconv(iconv_t cd0, char **restric
+       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;
+@@ -431,6 +435,7 @@ size_t iconv(iconv_t cd0, char **restric
+                       break;
+               }
+       }
++      *ploc = loc;
+       return x;
+ ilseq:
+       err = EILSEQ;
+@@ -445,5 +450,6 @@ starved:
+       x = -1;
+ end:
+       errno = err;
++      *ploc = loc;
+       return x;
+ }
+--- a/src/locale/langinfo.c
++++ b/src/locale/langinfo.c
+@@ -33,7 +33,8 @@ char *__nl_langinfo_l(nl_item item, loca
+       int idx = item & 65535;
+       const char *str;
+-      if (item == CODESET) return "UTF-8";
++      if (item == CODESET)
++              return MB_CUR_MAX==1 ? "UTF-8-CODE-UNITS" : "UTF-8";
+       
+       switch (cat) {
+       case LC_NUMERIC:
+--- a/src/locale/locale_map.c
++++ b/src/locale/locale_map.c
+@@ -24,14 +24,6 @@ static const char envvars[][12] = {
+       "LC_MESSAGES",
+ };
+-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_map *__get_locale(int cat, const char *val)
+ {
+       static int lock[2];
+@@ -107,8 +99,8 @@ const struct __locale_map *__get_locale(
+        * sake of being able to do message translations at the
+        * application level. */
+       if (!new && (new = malloc(sizeof *new))) {
+-              new->map = empty_mo;
+-              new->map_size = sizeof empty_mo;
++              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;
+--- a/src/locale/newlocale.c
++++ b/src/locale/newlocale.c
+@@ -3,16 +3,9 @@
+ #include "locale_impl.h"
+ #include "libc.h"
+-extern const struct __locale_map __c_dot_utf8;
+-
+-static const struct __locale_struct c_locale = { 0 };
+-static const struct __locale_struct c_dot_utf8_locale = {
+-      .cat[LC_CTYPE] = &__c_dot_utf8
+-};
+-
+ int __loc_is_allocated(locale_t loc)
+ {
+-      return loc && loc != &c_locale && loc != &c_dot_utf8_locale;
++      return loc && loc != C_LOCALE && loc != UTF8_LOCALE;
+ }
+ locale_t __newlocale(int mask, const char *name, locale_t loc)
+@@ -44,9 +37,9 @@ locale_t __newlocale(int mask, const cha
+       }
+       if (!j)
+-              return (locale_t)&c_locale;
+-      if (j==1 && tmp.cat[LC_CTYPE]==c_dot_utf8_locale.cat[LC_CTYPE])
+-              return (locale_t)&c_dot_utf8_locale;
++              return C_LOCALE;
++      if (j==1 && tmp.cat[LC_CTYPE]==&__c_dot_utf8)
++              return UTF8_LOCALE;
+       if ((loc = malloc(sizeof *loc))) *loc = tmp;
+--- a/src/locale/uselocale.c
++++ b/src/locale/uselocale.c
+@@ -8,9 +8,7 @@ locale_t __uselocale(locale_t new)
+       locale_t old = self->locale;
+       locale_t global = &libc.global_locale;
+-      if (new == LC_GLOBAL_LOCALE) new = global;
+-
+-      self->locale = new;
++      if (new) self->locale = new == LC_GLOBAL_LOCALE ? global : new;
+       return old == global ? LC_GLOBAL_LOCALE : old;
+ }
+--- /dev/null
++++ b/src/malloc/expand_heap.c
+@@ -0,0 +1,72 @@
++#include <limits.h>
++#include <stdint.h>
++#include <errno.h>
++#include <sys/mman.h>
++#include "libc.h"
++#include "syscall.h"
++
++/* This function returns true if the interval [old,new]
++ * intersects the 'len'-sized interval below &libc.auxv
++ * (interpreted as the main-thread stack) or below &b
++ * (the current stack). It is used to defend against
++ * buggy brk implementations that can cross the stack. */
++
++static int traverses_stack_p(uintptr_t old, uintptr_t new)
++{
++      const uintptr_t len = 8<<20;
++      uintptr_t a, b;
++
++      b = (uintptr_t)libc.auxv;
++      a = b > len ? b-len : 0;
++      if (new>a && old<b) return 1;
++
++      b = (uintptr_t)&b;
++      a = b > len ? b-len : 0;
++      if (new>a && old<b) return 1;
++
++      return 0;
++}
++
++void *__mmap(void *, size_t, int, int, int, off_t);
++
++/* Expand the heap in-place if brk can be used, or otherwise via mmap,
++ * using an exponential lower bound on growth by mmap to make
++ * fragmentation asymptotically irrelevant. The size argument is both
++ * an input and an output, since the caller needs to know the size
++ * allocated, which will be larger than requested due to page alignment
++ * and mmap minimum size rules. The caller is responsible for locking
++ * to prevent concurrent calls. */
++
++void *__expand_heap(size_t *pn)
++{
++      static uintptr_t brk;
++      static unsigned mmap_step;
++      size_t n = *pn;
++
++      if (n > SIZE_MAX/2 - PAGE_SIZE) {
++              errno = ENOMEM;
++              return 0;
++      }
++      n += -n & PAGE_SIZE-1;
++
++      if (!brk) {
++              brk = __syscall(SYS_brk, 0);
++              brk += -brk & PAGE_SIZE-1;
++      }
++
++      if (n < SIZE_MAX-brk && !traverses_stack_p(brk, brk+n)
++          && __syscall(SYS_brk, brk+n)==brk+n) {
++              *pn = n;
++              brk += n;
++              return (void *)(brk-n);
++      }
++
++      size_t min = (size_t)PAGE_SIZE << mmap_step/2;
++      if (n < min) n = min;
++      void *area = __mmap(0, n, PROT_READ|PROT_WRITE,
++              MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
++      if (area == MAP_FAILED) return 0;
++      *pn = n;
++      mmap_step++;
++      return area;
++}
+--- a/src/malloc/lite_malloc.c
++++ b/src/malloc/lite_malloc.c
+@@ -4,43 +4,46 @@
+ #include <errno.h>
+ #include "libc.h"
+-uintptr_t __brk(uintptr_t);
+-
+ #define ALIGN 16
++void *__expand_heap(size_t *);
++
+ void *__simple_malloc(size_t n)
+ {
+-      static uintptr_t cur, brk;
+-      uintptr_t base, new;
++      static char *cur, *end;
+       static volatile int lock[2];
+-      size_t align=1;
++      size_t align=1, pad;
++      void *p;
+       if (!n) n++;
+-      if (n > SIZE_MAX/2) goto toobig;
+-
+       while (align<n && align<ALIGN)
+               align += align;
+-      n = n + align - 1 & -align;
+       LOCK(lock);
+-      if (!cur) cur = brk = __brk(0)+16;
+-      base = cur + align-1 & -align;
+-      if (n > SIZE_MAX - PAGE_SIZE - base) goto fail;
+-      if (base+n > brk) {
+-              new = base+n + PAGE_SIZE-1 & -PAGE_SIZE;
+-              if (__brk(new) != new) goto fail;
+-              brk = new;
+-      }
+-      cur = base+n;
+-      UNLOCK(lock);
+-      return (void *)base;
++      pad = -(uintptr_t)cur & align-1;
++
++      if (n <= SIZE_MAX/2 + ALIGN) n += pad;
++
++      if (n > end-cur) {
++              size_t m = n;
++              char *new = __expand_heap(&m);
++              if (!new) {
++                      UNLOCK(lock);
++                      return 0;
++              }
++              if (new != end) {
++                      cur = new;
++                      n -= pad;
++                      pad = 0;
++              }
++              end = new + m;
++      }
+-fail:
++      p = cur + pad;
++      cur += n;
+       UNLOCK(lock);
+-toobig:
+-      errno = ENOMEM;
+-      return 0;
++      return p;
+ }
+ weak_alias(__simple_malloc, malloc);
+--- a/src/malloc/malloc.c
++++ b/src/malloc/malloc.c
+@@ -13,7 +13,6 @@
+ #define inline inline __attribute__((always_inline))
+ #endif
+-uintptr_t __brk(uintptr_t);
+ void *__mmap(void *, size_t, int, int, int, off_t);
+ int __munmap(void *, size_t);
+ void *__mremap(void *, size_t, size_t, int, ...);
+@@ -31,13 +30,9 @@ struct bin {
+ };
+ static struct {
+-      uintptr_t brk;
+-      size_t *heap;
+       volatile uint64_t binmap;
+       struct bin bins[64];
+-      volatile int brk_lock[2];
+       volatile int free_lock[2];
+-      unsigned mmap_step;
+ } mal;
+@@ -152,69 +147,52 @@ void __dump_heap(int x)
+ }
+ #endif
++void *__expand_heap(size_t *);
++
+ static struct chunk *expand_heap(size_t n)
+ {
+-      static int init;
++      static int heap_lock[2];
++      static void *end;
++      void *p;
+       struct chunk *w;
+-      uintptr_t new;
+-
+-      lock(mal.brk_lock);
+-      if (!init) {
+-              mal.brk = __brk(0);
+-#ifdef SHARED
+-              mal.brk = mal.brk + PAGE_SIZE-1 & -PAGE_SIZE;
+-#endif
+-              mal.brk = mal.brk + 2*SIZE_ALIGN-1 & -SIZE_ALIGN;
+-              mal.heap = (void *)mal.brk;
+-              init = 1;
++      /* The argument n already accounts for the caller's chunk
++       * overhead needs, but if the heap can't be extended in-place,
++       * we need room for an extra zero-sized sentinel chunk. */
++      n += SIZE_ALIGN;
++
++      lock(heap_lock);
++
++      p = __expand_heap(&n);
++      if (!p) {
++              unlock(heap_lock);
++              return 0;
+       }
+-      if (n > SIZE_MAX - mal.brk - 2*PAGE_SIZE) goto fail;
+-      new = mal.brk + n + SIZE_ALIGN + PAGE_SIZE - 1 & -PAGE_SIZE;
+-      n = new - mal.brk;
+-
+-      if (__brk(new) != new) {
+-              size_t min = (size_t)PAGE_SIZE << mal.mmap_step/2;
+-              n += -n & PAGE_SIZE-1;
+-              if (n < min) n = min;
+-              void *area = __mmap(0, n, PROT_READ|PROT_WRITE,
+-                      MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+-              if (area == MAP_FAILED) goto fail;
+-
+-              mal.mmap_step++;
+-              area = (char *)area + SIZE_ALIGN - OVERHEAD;
+-              w = area;
++      /* If not just expanding existing space, we need to make a
++       * new sentinel chunk below the allocated space. */
++      if (p != end) {
++              /* Valid/safe because of the prologue increment. */
+               n -= SIZE_ALIGN;
++              p = (char *)p + SIZE_ALIGN;
++              w = MEM_TO_CHUNK(p);
+               w->psize = 0 | C_INUSE;
+-              w->csize = n | C_INUSE;
+-              w = NEXT_CHUNK(w);
+-              w->psize = n | C_INUSE;
+-              w->csize = 0 | C_INUSE;
+-
+-              unlock(mal.brk_lock);
+-
+-              return area;
+       }
+-      w = MEM_TO_CHUNK(mal.heap);
+-      w->psize = 0 | C_INUSE;
+-
+-      w = MEM_TO_CHUNK(new);
++      /* Record new heap end and fill in footer. */
++      end = (char *)p + n;
++      w = MEM_TO_CHUNK(end);
+       w->psize = n | C_INUSE;
+       w->csize = 0 | C_INUSE;
+-      w = MEM_TO_CHUNK(mal.brk);
++      /* Fill in header, which may be new or may be replacing a
++       * zero-size sentinel header at the old end-of-heap. */
++      w = MEM_TO_CHUNK(p);
+       w->csize = n | C_INUSE;
+-      mal.brk = new;
+-      
+-      unlock(mal.brk_lock);
++
++      unlock(heap_lock);
+       return w;
+-fail:
+-      unlock(mal.brk_lock);
+-      errno = ENOMEM;
+-      return 0;
+ }
+ static int adjust_size(size_t *n)
+--- a/src/multibyte/btowc.c
++++ b/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;
+ }
+--- a/src/multibyte/internal.h
++++ b/src/multibyte/internal.h
+@@ -23,3 +23,10 @@ extern const uint32_t bittab[];
+ #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"
+--- a/src/multibyte/mbrtowc.c
++++ b/src/multibyte/mbrtowc.c
+@@ -4,6 +4,7 @@
+  * unnecessary.
+  */
++#include <stdlib.h>
+ #include <wchar.h>
+ #include <errno.h>
+ #include "internal.h"
+@@ -27,6 +28,7 @@ size_t mbrtowc(wchar_t *restrict wc, con
+       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--;
+       }
+--- a/src/multibyte/mbsrtowcs.c
++++ b/src/multibyte/mbsrtowcs.c
+@@ -7,6 +7,8 @@
+ #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 +26,23 @@ size_t mbsrtowcs(wchar_t *restrict ws, c
+               }
+       }
++      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)) {
+--- a/src/multibyte/mbtowc.c
++++ b/src/multibyte/mbtowc.c
+@@ -4,6 +4,7 @@
+  * unnecessary.
+  */
++#include <stdlib.h>
+ #include <wchar.h>
+ #include <errno.h>
+ #include "internal.h"
+@@ -19,6 +20,7 @@ int mbtowc(wchar_t *restrict wc, const c
+       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];
+--- a/src/multibyte/wcrtomb.c
++++ b/src/multibyte/wcrtomb.c
+@@ -4,8 +4,10 @@
+  * 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 +15,13 @@ size_t wcrtomb(char *restrict s, wchar_t
+       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);
+--- a/src/multibyte/wctob.c
++++ b/src/multibyte/wctob.c
+@@ -1,8 +1,10 @@
+-#include <stdio.h>
+ #include <wchar.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;
+ }
+--- a/src/passwd/nscd_query.c
++++ b/src/passwd/nscd_query.c
+@@ -32,6 +32,7 @@ FILE *__nscd_query(int32_t req, const ch
+               },
+               .msg_iovlen = 2
+       };
++      int errno_save = errno;
+       *swap = 0;
+ retry:
+@@ -50,11 +51,14 @@ retry:
+               return f;
+       if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+-              /* If there isn't a running nscd we return -1 to indicate that
+-               * that is precisely what happened
+-               */
+-              if (errno == EACCES || errno == ECONNREFUSED || errno == ENOENT)
++              /* 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;
+       }
+--- /dev/null
++++ b/src/process/sh/vfork.s
+@@ -0,0 +1,23 @@
++.global __vfork
++.weak vfork
++.type __vfork,@function
++.type vfork,@function
++__vfork:
++vfork:
++      mov #95, r3
++      add r3, r3
++
++      trapa #16
++      or    r0, r0
++      or    r0, r0
++      or    r0, r0
++      or    r0, r0
++      or    r0, r0
++
++      mov r0, r4
++      mov.l 1f, r0
++2:    braf r0
++       nop
++      .align 2
++      .hidden __syscall_ret
++1:    .long __syscall_ret@PLT-(2b+4-.)
+--- a/src/regex/fnmatch.c
++++ b/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
+@@ -229,7 +230,7 @@ static int fnmatch_internal(const char *
+        * 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;
+--- a/src/stdio/__fdopen.c
++++ b/src/stdio/__fdopen.c
+@@ -54,13 +54,7 @@ FILE *__fdopen(int fd, const char *mode)
+       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);
+--- a/src/stdio/__stdio_exit.c
++++ b/src/stdio/__stdio_exit.c
+@@ -16,8 +16,7 @@ static void close_file(FILE *f)
+ 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);
+ }
+--- a/src/stdio/__stdio_read.c
++++ b/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,9 +9,7 @@ size_t __stdio_read(FILE *f, unsigned ch
+       };
+       ssize_t cnt;
+-      pthread_cleanup_push(cleanup, f);
+-      cnt = syscall_cp(SYS_readv, f->fd, iov, 2);
+-      pthread_cleanup_pop(0);
++      cnt = syscall(SYS_readv, f->fd, iov, 2);
+       if (cnt <= 0) {
+               f->flags |= F_EOF ^ ((F_ERR^F_EOF) & cnt);
+               return cnt;
+--- a/src/stdio/__stdio_write.c
++++ b/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,9 +12,7 @@ size_t __stdio_write(FILE *f, const unsi
+       int iovcnt = 2;
+       ssize_t cnt;
+       for (;;) {
+-              pthread_cleanup_push(cleanup, f);
+-              cnt = syscall_cp(SYS_writev, f->fd, iov, iovcnt);
+-              pthread_cleanup_pop(0);
++              cnt = syscall(SYS_writev, f->fd, iov, iovcnt);
+               if (cnt == rem) {
+                       f->wend = f->buf + f->buf_size;
+                       f->wpos = f->wbase = f->buf;
+@@ -34,11 +25,8 @@ size_t __stdio_write(FILE *f, const unsi
+               }
+               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;
+--- a/src/stdio/fclose.c
++++ b/src/stdio/fclose.c
+@@ -14,11 +14,11 @@ int fclose(FILE *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);
+--- a/src/stdio/fflush.c
++++ b/src/stdio/fflush.c
+@@ -35,13 +35,12 @@ int fflush(FILE *f)
+       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;
+ }
+--- a/src/stdio/fgetwc.c
++++ b/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 @@ wint_t __fgetwc_unlocked(FILE *f)
+       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 @@ wint_t __fgetwc_unlocked(FILE *f)
+       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;
+--- a/src/stdio/fmemopen.c
++++ b/src/stdio/fmemopen.c
+@@ -110,11 +110,5 @@ FILE *fmemopen(void *restrict buf, size_
+       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);
+ }
+--- a/src/stdio/fopen.c
++++ b/src/stdio/fopen.c
+@@ -18,7 +18,7 @@ FILE *fopen(const char *restrict filenam
+       /* Compute the flags to pass to open() */
+       flags = __fmodeflags(mode);
+-      fd = sys_open_cp(filename, flags, 0666);
++      fd = sys_open(filename, flags, 0666);
+       if (fd < 0) return 0;
+       if (flags & O_CLOEXEC)
+               __syscall(SYS_fcntl, fd, F_SETFD, FD_CLOEXEC);
+--- a/src/stdio/fputwc.c
++++ b/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 @@ wint_t __fputwc_unlocked(wchar_t c, FILE
+ {
+       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 @@ wint_t __fputwc_unlocked(wchar_t c, FILE
+               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;
+ }
+--- a/src/stdio/fputws.c
++++ b/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 */
+ }
+--- a/src/stdio/fwide.c
++++ b/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;
+--- /dev/null
++++ b/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);
++}
+--- /dev/null
++++ b/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;
++}
+--- a/src/stdio/open_memstream.c
++++ b/src/stdio/open_memstream.c
+@@ -79,11 +79,5 @@ FILE *open_memstream(char **bufp, size_t
+       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);
+ }
+--- a/src/stdio/open_wmemstream.c
++++ b/src/stdio/open_wmemstream.c
+@@ -81,11 +81,5 @@ FILE *open_wmemstream(wchar_t **bufp, si
+       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);
+ }
+--- a/src/stdio/ungetwc.c
++++ b/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>
+@@ -8,21 +9,19 @@ 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;
++      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->rpos) __toread(f);
+-      if (!f->rpos || f->rpos < f->buf - UNGET + l) {
++      if (!f->rpos || f->rpos < f->buf - UNGET + l || c == WEOF ||
++          (!isascii(c) && (l = wctomb((void *)mbc, c)) < 0)) {
+               FUNLOCK(f);
+-              return EOF;
++              *ploc = loc;
++              return WEOF;
+       }
+       if (isascii(c)) *--f->rpos = c;
+@@ -31,5 +30,6 @@ wint_t ungetwc(wint_t c, FILE *f)
+       f->flags &= ~F_EOF;
+       FUNLOCK(f);
++      *ploc = loc;
+       return c;
+ }
+--- a/src/stdio/vfwprintf.c
++++ b/src/stdio/vfwprintf.c
+@@ -293,7 +293,10 @@ static int wprintf_core(FILE *f, const w
+                       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++);
+@@ -356,7 +359,7 @@ int vfwprintf(FILE *restrict f, const wc
+       }
+       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);
+--- a/src/stdio/vfwscanf.c
++++ b/src/stdio/vfwscanf.c
+@@ -104,7 +104,7 @@ int vfwscanf(FILE *restrict f, const wch
+       FLOCK(f);
+-      f->mode |= f->mode+1;
++      fwide(f, 1);
+       for (p=fmt; *p; p++) {
+--- /dev/null
++++ b/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);
++}
+--- a/src/thread/pthread_create.c
++++ b/src/thread/pthread_create.c
+@@ -191,8 +191,9 @@ int __pthread_create(pthread_t *restrict
+       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);
index f4e2be4..343915f 100644 (file)
@@ -1,6 +1,6 @@
 --- a/src/locale/iconv.c
 +++ b/src/locale/iconv.c
-@@ -38,6 +38,7 @@ static const unsigned char charmaps[] =
+@@ -39,6 +39,7 @@ static const unsigned char charmaps[] =
  "ucs4\0ucs4be\0utf32\0utf32be\0\0\300"
  "ucs4le\0utf32le\0\0\303"
  "ascii\0usascii\0iso646\0iso646us\0\0\307"
@@ -8,7 +8,7 @@
  "eucjp\0\0\320"
  "shiftjis\0sjis\0\0\321"
  "gb18030\0\0\330"
-@@ -45,6 +46,7 @@ static const unsigned char charmaps[] =
+@@ -46,6 +47,7 @@ static const unsigned char charmaps[] =
  "gb2312\0\0\332"
  "big5\0bigfive\0cp950\0big5hkscs\0\0\340"
  "euckr\0ksc5601\0ksx1001\0cp949\0\0\350"
@@ -16,7 +16,7 @@
  #include "codepages.h"
  ;
  
-@@ -52,6 +54,7 @@ static const unsigned short legacy_chars
+@@ -53,6 +55,7 @@ static const unsigned short legacy_chars
  #include "legacychars.h"
  };
  
@@ -24,7 +24,7 @@
  static const unsigned short jis0208[84][94] = {
  #include "jis0208.h"
  };
-@@ -71,6 +74,7 @@ static const unsigned short hkscs[] = {
+@@ -72,6 +75,7 @@ static const unsigned short hkscs[] = {
  static const unsigned short ksc[93][94] = {
  #include "ksc.h"
  };
@@ -32,7 +32,7 @@
  
  static int fuzzycmp(const unsigned char *a, const unsigned char *b)
  {
-@@ -212,6 +216,7 @@ size_t iconv(iconv_t cd0, char **restric
+@@ -216,6 +220,7 @@ size_t iconv(iconv_t cd0, char **restric
                                c = ((c-0xd7c0)<<10) + (d-0xdc00);
                        }
                        break;
@@ -40,7 +40,7 @@
                case SHIFT_JIS:
                        if (c-0xa1 <= 0xdf-0xa1) {
                                c += 0xff61-0xa1;
-@@ -358,6 +363,7 @@ size_t iconv(iconv_t cd0, char **restric
+@@ -362,6 +367,7 @@ size_t iconv(iconv_t cd0, char **restric
                        c = ksc[c][d];
                        if (!c) goto ilseq;
                        break;