Merge "Fix math tests."
diff --git a/libc/arch-arm/arm.mk b/libc/arch-arm/arm.mk
index 89a1ce0..cbc5fa7 100644
--- a/libc/arch-arm/arm.mk
+++ b/libc/arch-arm/arm.mk
@@ -70,7 +70,7 @@
 endif
 cpu_variant_mk := $(LOCAL_PATH)/arch-arm/$(TARGET_$(my_2nd_arch_prefix)CPU_VARIANT)/$(TARGET_$(my_2nd_arch_prefix)CPU_VARIANT).mk
 ifeq ($(wildcard $(cpu_variant_mk)),)
-$(error "TARGET_$(my_2nd_arch_prefix)CPU_VARIANT not set or set to an unknown value. Possible values are cortex-a7, cortex-a8, cortex-a9, cortex-a15, krait. Use generic for devices that do not have a CPU similar to any of the supported cpu variants.")
+$(error "TARGET_$(my_2nd_arch_prefix)CPU_VARIANT not set or set to an unknown value. Possible values are cortex-a7, cortex-a8, cortex-a9, cortex-a15, krait, denver. Use generic for devices that do not have a CPU similar to any of the supported cpu variants.")
 endif
 include $(cpu_variant_mk)
 libc_common_additional_dependencies += $(cpu_variant_mk)
diff --git a/libc/arch-arm/denver/bionic/__strcat_chk.S b/libc/arch-arm/denver/bionic/__strcat_chk.S
new file mode 100644
index 0000000..36da2d9
--- /dev/null
+++ b/libc/arch-arm/denver/bionic/__strcat_chk.S
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <private/bionic_asm.h>
+#include <private/libc_events.h>
+
+    .syntax unified
+
+    .thumb
+    .thumb_func
+
+// Get the length of src string, then get the source of the dst string.
+// Check that the two lengths together don't exceed the threshold, then
+// do a memcpy of the data.
+ENTRY(__strcat_chk)
+    pld     [r0, #0]
+    push    {r0, lr}
+    .save   {r0, lr}
+    .cfi_def_cfa_offset 8
+    .cfi_rel_offset r0, 0
+    .cfi_rel_offset lr, 4
+    push    {r4, r5}
+    .save   {r4, r5}
+    .cfi_adjust_cfa_offset 8
+    .cfi_rel_offset r4, 0
+    .cfi_rel_offset r5, 4
+
+    mov     lr, r2
+
+    // Save the dst register to r5
+    mov     r5, r0
+
+    // Zero out r4
+    eor     r4, r4, r4
+
+    // r1 contains the address of the string to count.
+.L_strlen_start:
+    mov     r0, r1
+    ands    r3, r1, #7
+    beq     .L_mainloop
+
+    // Align to a double word (64 bits).
+    rsb     r3, r3, #8
+    lsls    ip, r3, #31
+    beq     .L_align_to_32
+
+    ldrb    r2, [r1], #1
+    cbz     r2, .L_update_count_and_finish
+
+.L_align_to_32:
+    bcc     .L_align_to_64
+    ands    ip, r3, #2
+    beq     .L_align_to_64
+
+    ldrb    r2, [r1], #1
+    cbz     r2, .L_update_count_and_finish
+    ldrb    r2, [r1], #1
+    cbz     r2, .L_update_count_and_finish
+
+.L_align_to_64:
+    tst     r3, #4
+    beq     .L_mainloop
+    ldr     r3, [r1], #4
+
+    sub     ip, r3, #0x01010101
+    bic     ip, ip, r3
+    ands    ip, ip, #0x80808080
+    bne     .L_zero_in_second_register
+
+    .p2align 2
+.L_mainloop:
+    ldrd    r2, r3, [r1], #8
+
+    pld     [r1, #64]
+
+    sub     ip, r2, #0x01010101
+    bic     ip, ip, r2
+    ands    ip, ip, #0x80808080
+    bne     .L_zero_in_first_register
+
+    sub     ip, r3, #0x01010101
+    bic     ip, ip, r3
+    ands    ip, ip, #0x80808080
+    bne     .L_zero_in_second_register
+    b       .L_mainloop
+
+.L_update_count_and_finish:
+    sub     r3, r1, r0
+    sub     r3, r3, #1
+    b       .L_finish
+
+.L_zero_in_first_register:
+    sub     r3, r1, r0
+    lsls    r2, ip, #17
+    bne     .L_sub8_and_finish
+    bcs     .L_sub7_and_finish
+    lsls    ip, ip, #1
+    bne     .L_sub6_and_finish
+
+    sub     r3, r3, #5
+    b       .L_finish
+
+.L_sub8_and_finish:
+    sub     r3, r3, #8
+    b       .L_finish
+
+.L_sub7_and_finish:
+    sub     r3, r3, #7
+    b       .L_finish
+
+.L_sub6_and_finish:
+    sub     r3, r3, #6
+    b       .L_finish
+
+.L_zero_in_second_register:
+    sub     r3, r1, r0
+    lsls    r2, ip, #17
+    bne     .L_sub4_and_finish
+    bcs     .L_sub3_and_finish
+    lsls    ip, ip, #1
+    bne     .L_sub2_and_finish
+
+    sub     r3, r3, #1
+    b       .L_finish
+
+.L_sub4_and_finish:
+    sub     r3, r3, #4
+    b       .L_finish
+
+.L_sub3_and_finish:
+    sub     r3, r3, #3
+    b       .L_finish
+
+.L_sub2_and_finish:
+    sub     r3, r3, #2
+
+.L_finish:
+    cmp     r4, #0
+    bne     .L_strlen_done
+
+    // Time to get the dst string length.
+    mov     r1, r5
+
+    // Save the original source address to r5.
+    mov     r5, r0
+
+    // Save the current length (adding 1 for the terminator).
+    add     r4, r3, #1
+    b       .L_strlen_start
+
+    // r0 holds the pointer to the dst string.
+    // r3 holds the dst string length.
+    // r4 holds the src string length + 1.
+.L_strlen_done:
+    add     r2, r3, r4
+    cmp     r2, lr
+    bhi     __strcat_chk_failed
+
+    // Set up the registers for the memcpy code.
+    mov     r1, r5
+    pld     [r1, #64]
+    mov     r2, r4
+    add     r0, r0, r3
+    pop     {r4, r5}
+END(__strcat_chk)
+
+#define MEMCPY_BASE         __strcat_chk_memcpy_base
+#define MEMCPY_BASE_ALIGNED __strcat_chk_memcpy_base_aligned
+
+#include "memcpy_base.S"
+
+ENTRY_PRIVATE(__strcat_chk_failed)
+    .save   {r0, lr}
+    .save   {r4, r5}
+
+    .cfi_def_cfa_offset 8
+    .cfi_rel_offset r0, 0
+    .cfi_rel_offset lr, 4
+    .cfi_adjust_cfa_offset 8
+    .cfi_rel_offset r4, 0
+    .cfi_rel_offset r5, 4
+
+    ldr     r0, error_message
+    ldr     r1, error_code
+1:
+    add     r0, pc
+    bl      __fortify_chk_fail
+error_code:
+    .word   BIONIC_EVENT_STRCAT_BUFFER_OVERFLOW
+error_message:
+    .word   error_string-(1b+4)
+END(__strcat_chk_failed)
+
+    .data
+error_string:
+    .string "strcat: prevented write past end of buffer"
diff --git a/libc/arch-arm/denver/bionic/__strcpy_chk.S b/libc/arch-arm/denver/bionic/__strcpy_chk.S
new file mode 100644
index 0000000..c3e3e14
--- /dev/null
+++ b/libc/arch-arm/denver/bionic/__strcpy_chk.S
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <private/bionic_asm.h>
+#include <private/libc_events.h>
+
+    .syntax unified
+
+    .thumb
+    .thumb_func
+
+// Get the length of the source string first, then do a memcpy of the data
+// instead of a strcpy.
+ENTRY(__strcpy_chk)
+    pld     [r0, #0]
+    push    {r0, lr}
+    .save   {r0, lr}
+    .cfi_def_cfa_offset 8
+    .cfi_rel_offset r0, 0
+    .cfi_rel_offset lr, 4
+
+    mov     lr, r2
+    mov     r0, r1
+
+    ands    r3, r1, #7
+    beq     .L_mainloop
+
+    // Align to a double word (64 bits).
+    rsb     r3, r3, #8
+    lsls    ip, r3, #31
+    beq     .L_align_to_32
+
+    ldrb    r2, [r0], #1
+    cbz     r2, .L_update_count_and_finish
+
+.L_align_to_32:
+    bcc     .L_align_to_64
+    ands    ip, r3, #2
+    beq     .L_align_to_64
+
+    ldrb    r2, [r0], #1
+    cbz     r2, .L_update_count_and_finish
+    ldrb    r2, [r0], #1
+    cbz     r2, .L_update_count_and_finish
+
+.L_align_to_64:
+    tst     r3, #4
+    beq     .L_mainloop
+    ldr     r3, [r0], #4
+
+    sub     ip, r3, #0x01010101
+    bic     ip, ip, r3
+    ands    ip, ip, #0x80808080
+    bne     .L_zero_in_second_register
+
+    .p2align 2
+.L_mainloop:
+    ldrd    r2, r3, [r0], #8
+
+    pld     [r0, #64]
+
+    sub     ip, r2, #0x01010101
+    bic     ip, ip, r2
+    ands    ip, ip, #0x80808080
+    bne     .L_zero_in_first_register
+
+    sub     ip, r3, #0x01010101
+    bic     ip, ip, r3
+    ands    ip, ip, #0x80808080
+    bne     .L_zero_in_second_register
+    b       .L_mainloop
+
+.L_update_count_and_finish:
+    sub     r3, r0, r1
+    sub     r3, r3, #1
+    b       .L_check_size
+
+.L_zero_in_first_register:
+    sub     r3, r0, r1
+    lsls    r2, ip, #17
+    bne     .L_sub8_and_finish
+    bcs     .L_sub7_and_finish
+    lsls    ip, ip, #1
+    bne     .L_sub6_and_finish
+
+    sub     r3, r3, #5
+    b       .L_check_size
+
+.L_sub8_and_finish:
+    sub     r3, r3, #8
+    b       .L_check_size
+
+.L_sub7_and_finish:
+    sub     r3, r3, #7
+    b       .L_check_size
+
+.L_sub6_and_finish:
+    sub     r3, r3, #6
+    b       .L_check_size
+
+.L_zero_in_second_register:
+    sub     r3, r0, r1
+    lsls    r2, ip, #17
+    bne     .L_sub4_and_finish
+    bcs     .L_sub3_and_finish
+    lsls    ip, ip, #1
+    bne     .L_sub2_and_finish
+
+    sub     r3, r3, #1
+    b       .L_check_size
+
+.L_sub4_and_finish:
+    sub     r3, r3, #4
+    b       .L_check_size
+
+.L_sub3_and_finish:
+    sub     r3, r3, #3
+    b       .L_check_size
+
+.L_sub2_and_finish:
+    sub     r3, r3, #2
+
+.L_check_size:
+    pld     [r1, #0]
+    pld     [r1, #64]
+    ldr     r0, [sp]
+    cmp     r3, lr
+    bhs     __strcpy_chk_failed
+
+    // Add 1 for copy length to get the string terminator.
+    add     r2, r3, #1
+END(__strcpy_chk)
+
+#define MEMCPY_BASE         __strcpy_chk_memcpy_base
+#define MEMCPY_BASE_ALIGNED __strcpy_chk_memcpy_base_aligned
+#include "memcpy_base.S"
+
+ENTRY_PRIVATE(__strcpy_chk_failed)
+    .save   {r0, lr}
+    .cfi_def_cfa_offset 8
+    .cfi_rel_offset r0, 0
+    .cfi_rel_offset lr, 4
+
+    ldr     r0, error_message
+    ldr     r1, error_code
+1:
+    add     r0, pc
+    bl      __fortify_chk_fail
+error_code:
+    .word   BIONIC_EVENT_STRCPY_BUFFER_OVERFLOW
+error_message:
+    .word   error_string-(1b+4)
+END(__strcpy_chk_failed)
+
+    .data
+error_string:
+    .string "strcpy: prevented write past end of buffer"
diff --git a/libc/arch-arm/denver/bionic/memcpy.S b/libc/arch-arm/denver/bionic/memcpy.S
new file mode 100644
index 0000000..da4f3dd
--- /dev/null
+++ b/libc/arch-arm/denver/bionic/memcpy.S
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 2013 ARM Ltd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the company may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Prototype: void *memcpy (void *dst, const void *src, size_t count).
+
+#include <private/bionic_asm.h>
+#include <private/libc_events.h>
+
+        .text
+        .syntax unified
+        .fpu    neon
+
+ENTRY(__memcpy_chk)
+        cmp     r2, r3
+        bhi     __memcpy_chk_fail
+
+        // Fall through to memcpy...
+END(__memcpy_chk)
+
+ENTRY(memcpy)
+        pld     [r1, #64]
+        push    {r0, lr}
+        .save   {r0, lr}
+        .cfi_def_cfa_offset 8
+        .cfi_rel_offset r0, 0
+        .cfi_rel_offset lr, 4
+END(memcpy)
+
+#define MEMCPY_BASE         __memcpy_base
+#define MEMCPY_BASE_ALIGNED __memcpy_base_aligned
+#include "memcpy_base.S"
+
+ENTRY_PRIVATE(__memcpy_chk_fail)
+        // Preserve lr for backtrace.
+        push    {lr}
+        .save   {lr}
+        .cfi_def_cfa_offset 4
+        .cfi_rel_offset lr, 0
+
+        ldr     r0, error_message
+        ldr     r1, error_code
+1:
+        add     r0, pc
+        bl      __fortify_chk_fail
+error_code:
+        .word   BIONIC_EVENT_MEMCPY_BUFFER_OVERFLOW
+error_message:
+        .word   error_string-(1b+8)
+END(__memcpy_chk_fail)
+
+        .data
+error_string:
+        .string "memcpy: prevented write past end of buffer"
diff --git a/libc/arch-arm/denver/bionic/memcpy_base.S b/libc/arch-arm/denver/bionic/memcpy_base.S
new file mode 100644
index 0000000..2abb486
--- /dev/null
+++ b/libc/arch-arm/denver/bionic/memcpy_base.S
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ * Copyright (c) 2013-2014, NVIDIA Corporation.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define CACHE_LINE_SIZE         (64)
+#define PREFETCH_DISTANCE       (CACHE_LINE_SIZE*6)
+
+ENTRY_PRIVATE(MEMCPY_BASE)
+        .cfi_def_cfa_offset 8
+        .cfi_rel_offset r0, 0
+        .cfi_rel_offset lr, 4
+
+        cmp         r2, #0
+        beq         .L_memcpy_done
+        cmp         r0, r1
+        beq         .L_memcpy_done
+
+        /* preload next cache line */
+        pld         [r1, #CACHE_LINE_SIZE*1]
+
+        /* Deal with very small blocks (< 32bytes) asap */
+        cmp         r2, #32
+        blo         .L_memcpy_lt_32bytes
+        /* no need to align if len < 128 bytes */
+        cmp         r2, #128
+        blo         .L_memcpy_lt_128bytes
+
+        /* large copy, align dest to 64 byte boundry */
+        pld         [r1, #CACHE_LINE_SIZE*2]
+        rsb         r3, r0, #0
+        ands        r3, r3, #0x3F
+        pld         [r1, #CACHE_LINE_SIZE*3]
+        beq         .L_memcpy_dispatch
+        sub         r2, r2, r3
+        /* copy 1 byte */
+        movs        ip, r3, lsl #31
+        itt         mi
+        ldrbmi      ip, [r1], #1
+        strbmi      ip, [r0], #1
+        /* copy 2 bytes */
+        itt         cs
+        ldrhcs      ip, [r1], #2
+        strhcs      ip, [r0], #2
+        /* copy 4 bytes */
+        movs        ip, r3, lsl #29
+        itt         mi
+        ldrmi       ip, [r1], #4
+        strmi       ip, [r0], #4
+        /* copy 8 bytes */
+        bcc         1f
+        vld1.8      {d0}, [r1]!
+        vst1.8      {d0}, [r0, :64]!
+1:      /* copy 16 bytes */
+        movs        ip, r3, lsl #27
+        bpl         1f
+        vld1.8      {q0}, [r1]!
+        vst1.8      {q0}, [r0, :128]!
+1:      /* copy 32 bytes */
+        bcc         .L_memcpy_dispatch
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0, :256]!
+
+.L_memcpy_dispatch:
+        // pre-decrement by 128 to detect nearly-done condition easily, but
+        // also need to check if we have less than 128 bytes left at this
+        // point due to alignment code above
+        subs        r2, r2, #128
+        blo         .L_memcpy_lt_128presub
+
+        // Denver does better if both source and dest are aligned so
+        // we'll special-case that even though the code is virually identical
+        tst         r1, #0xF
+        bne         .L_memcpy_neon_unalign_src_pld
+
+        // DRAM memcpy should be throttled slightly to get full bandwidth
+        //
+        cmp         r2, #32768
+        bhi         .L_memcpy_neon_unalign_src_pld
+        .align      4
+1:
+        /* copy 128 bytes in each loop */
+        subs        r2, r2, #128
+
+        /* preload a cache line */
+        pld         [r1, #PREFETCH_DISTANCE]
+        /* copy a cache line */
+        vld1.8      {q0, q1}, [r1, :128]!
+        vst1.8      {q0, q1}, [r0, :256]!
+        vld1.8      {q0, q1}, [r1, :128]!
+        vst1.8      {q0, q1}, [r0, :256]!
+        /* preload a cache line */
+        pld         [r1, #PREFETCH_DISTANCE]
+        /* copy a cache line */
+        vld1.8      {q0, q1}, [r1, :128]!
+        vst1.8      {q0, q1}, [r0, :256]!
+        vld1.8      {q0, q1}, [r1, :128]!
+        vst1.8      {q0, q1}, [r0, :256]!
+
+        bhs         1b
+        adds        r2, r2, #128
+        bne         .L_memcpy_lt_128bytes_align
+        pop         {r0, pc}
+
+        .align      4
+.L_memcpy_neon_unalign_src_pld:
+1:
+        /* copy 128 bytes in each loop */
+        subs        r2, r2, #128
+
+        /* preload a cache line */
+        pld         [r1, #PREFETCH_DISTANCE]
+        /* copy a cache line */
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0, :256]!
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0, :256]!
+        /* preload a cache line */
+        pld         [r1, #PREFETCH_DISTANCE]
+        /* copy a cache line */
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0, :256]!
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0, :256]!
+
+        bhs         1b
+        adds        r2, r2, #128
+        bne         .L_memcpy_lt_128bytes_align
+        pop         {r0, pc}
+
+.L_memcpy_lt_128presub:
+        add         r2, r2, #128
+.L_memcpy_lt_128bytes_align:
+        /* copy 64 bytes */
+        movs        ip, r2, lsl #26
+        bcc         1f
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0, :256]!
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0, :256]!
+1:      /* copy 32 bytes */
+        bpl         1f
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0, :256]!
+1:      /* copy 16 bytes */
+        movs        ip, r2, lsl #28
+        bcc         1f
+        vld1.8      {q0}, [r1]!
+        vst1.8      {q0}, [r0, :128]!
+1:      /* copy 8 bytes */
+        bpl         1f
+        vld1.8      {d0}, [r1]!
+        vst1.8      {d0}, [r0, :64]!
+1:      /* copy 4 bytes */
+        tst         r2, #4
+        itt         ne
+        ldrne       ip, [r1], #4
+        strne       ip, [r0], #4
+        /* copy 2 bytes */
+        movs        ip, r2, lsl #31
+        itt         cs
+        ldrhcs      ip, [r1], #2
+        strhcs      ip, [r0], #2
+        /* copy 1 byte */
+        itt         mi
+        ldrbmi      ip, [r1]
+        strbmi      ip, [r0]
+
+        pop         {r0, pc}
+
+.L_memcpy_lt_128bytes:
+        /* copy 64 bytes */
+        movs        ip, r2, lsl #26
+        bcc         1f
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0]!
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0]!
+1:      /* copy 32 bytes */
+        bpl	    .L_memcpy_lt_32bytes
+        vld1.8      {q0, q1}, [r1]!
+        vst1.8      {q0, q1}, [r0]!
+.L_memcpy_lt_32bytes:
+        /* copy 16 bytes */
+        movs        ip, r2, lsl #28
+        bcc         1f
+        vld1.8      {q0}, [r1]!
+        vst1.8      {q0}, [r0]!
+1:      /* copy 8 bytes */
+        bpl         1f
+        vld1.8      {d0}, [r1]!
+        vst1.8      {d0}, [r0]!
+1:      /* copy 4 bytes */
+        tst         r2, #4
+        itt         ne
+        ldrne       ip, [r1], #4
+        strne       ip, [r0], #4
+        /* copy 2 bytes */
+        movs        ip, r2, lsl #31
+        itt         cs
+        ldrhcs      ip, [r1], #2
+        strhcs      ip, [r0], #2
+        /* copy 1 byte */
+        itt         mi
+        ldrbmi      ip, [r1]
+        strbmi      ip, [r0]
+
+.L_memcpy_done:
+        pop         {r0, pc}
+END(MEMCPY_BASE)
diff --git a/libc/arch-arm/denver/bionic/memset.S b/libc/arch-arm/denver/bionic/memset.S
new file mode 100644
index 0000000..bf3d9ad
--- /dev/null
+++ b/libc/arch-arm/denver/bionic/memset.S
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/cpu-features.h>
+#include <private/bionic_asm.h>
+#include <private/libc_events.h>
+
+        /*
+         * Optimized memset() for ARM.
+         *
+         * memset() returns its first argument.
+         */
+
+        .fpu        neon
+        .syntax     unified
+
+ENTRY(__memset_chk)
+        cmp         r2, r3
+        bls         .L_done
+
+        // Preserve lr for backtrace.
+        push        {lr}
+        .cfi_def_cfa_offset 4
+        .cfi_rel_offset lr, 0
+
+
+        ldr         r0, error_message
+        ldr         r1, error_code
+1:
+        add         r0, pc
+        bl          __fortify_chk_fail
+error_code:
+        .word       BIONIC_EVENT_MEMSET_BUFFER_OVERFLOW
+error_message:
+        .word       error_string-(1b+8)
+END(__memset_chk)
+
+ENTRY(bzero)
+        mov         r2, r1
+        mov         r1, #0
+.L_done:
+        // Fall through to memset...
+END(bzero)
+
+ENTRY(memset)
+        pldw        [r0]
+        mov         r3, r0
+
+        // Duplicate the low byte of r1
+        mov         r1, r1, lsl #24
+        orr         r1, r1, r1, lsr #8
+        orr         r1, r1, r1, lsr #16
+
+        cmp         r2, #16
+        blo         .L_less_than_16
+
+        // This section handles regions 16 bytes or larger
+        //
+        // Use aligned vst1.8 and vstm when possible.  Register values will be:
+        //   ip is scratch
+        //   q0, q1, and r1 contain the memset value
+        //   r2 is the number of bytes to set
+        //   r3 is the advancing destination pointer
+        vdup.32     q0, r1
+
+        ands        ip, r3, 0xF
+        beq         .L_memset_aligned
+
+        // Align dest pointer to 16-byte boundary.
+        pldw        [r0, #64]
+        rsb         ip, ip, #16
+
+        // Pre-adjust the byte count to reflect post-aligment value.  Expecting
+        // 8-byte alignment to be rather common so we special case that one.
+        sub         r2, r2, ip
+
+        /* set 1 byte */
+        tst         ip, #1
+        it          ne
+        strbne      r1, [r3], #1
+        /* set 2 bytes */
+        tst         ip, #2
+        it          ne
+        strhne      r1, [r3], #2
+        /* set 4 bytes */
+        movs        ip, ip, lsl #29
+        it          mi
+        strmi       r1, [r3], #4
+        /* set 8 bytes */
+        itt         cs
+        strcs       r1, [r3], #4
+        strcs       r1, [r3], #4
+
+.L_memset_aligned:
+        // Destination is now 16-byte aligned.  Determine how to handle
+        // remaining bytes.
+        vmov        q1, q0
+        cmp         r2, #128
+        blo         .L_less_than_128
+
+        // We need to set a larger block of memory.  Use four Q regs to
+        // set a full cache line in one instruction.  Pre-decrement
+        // r2 to simplify end-of-loop detection
+        vmov        q2, q0
+        vmov        q3, q0
+        pldw        [r0, #128]
+        sub         r2, r2, #128
+        .align 4
+.L_memset_loop_128:
+        pldw        [r3, #192]
+        vstm        r3!, {q0, q1, q2, q3}
+        vstm        r3!, {q0, q1, q2, q3}
+        subs        r2, r2, #128
+        bhs         .L_memset_loop_128
+
+        // Un-bias r2 so it contains the number of bytes left.  Early
+        // exit if we are done.
+        adds        r2, r2, #128
+        beq         2f
+
+        .align 4
+.L_less_than_128:
+        // set 64 bytes
+        movs        ip, r2, lsl #26
+        bcc         1f
+        vst1.8      {q0, q1}, [r3, :128]!
+        vst1.8      {q0, q1}, [r3, :128]!
+        beq         2f
+1:
+        // set 32 bytes
+        bpl         1f
+        vst1.8      {q0, q1}, [r3, :128]!
+1:
+        // set 16 bytes
+        movs        ip, r2, lsl #28
+        bcc         1f
+        vst1.8      {q0}, [r3, :128]!
+        beq         2f
+1:
+        // set 8 bytes
+        bpl         1f
+        vst1.8      {d0}, [r3, :64]!
+1:
+        // set 4 bytes
+        tst         r2, #4
+        it          ne
+        strne       r1, [r3], #4
+1:
+        // set 2 bytes
+        movs        ip, r2, lsl #31
+        it          cs
+        strhcs      r1, [r3], #2
+        // set 1 byte
+        it          mi
+        strbmi      r1, [r3]
+2:
+        bx          lr
+
+.L_less_than_16:
+        // Store up to 15 bytes without worrying about byte alignment
+        movs        ip, r2, lsl #29
+        bcc         1f
+        str         r1, [r3], #4
+        str         r1, [r3], #4
+        beq         2f
+1:
+        it          mi
+        strmi       r1, [r3], #4
+        movs        ip, r2, lsl #31
+        it          mi
+        strbmi      r1, [r3], #1
+        itt         cs
+        strbcs      r1, [r3], #1
+        strbcs      r1, [r3]
+2:
+        bx          lr
+END(memset)
+
+        .data
+error_string:
+        .string     "memset: prevented write past end of buffer"
diff --git a/libc/arch-arm/denver/denver.mk b/libc/arch-arm/denver/denver.mk
new file mode 100644
index 0000000..3fcc457
--- /dev/null
+++ b/libc/arch-arm/denver/denver.mk
@@ -0,0 +1,12 @@
+libc_bionic_src_files_arm += \
+    arch-arm/denver/bionic/memcpy.S \
+    arch-arm/denver/bionic/memset.S \
+    arch-arm/denver/bionic/__strcat_chk.S \
+    arch-arm/denver/bionic/__strcpy_chk.S
+
+# Use cortex-a15 versions of strcat/strcpy/strlen.
+libc_bionic_src_files_arm += \
+    arch-arm/cortex-a15/bionic/strcat.S \
+    arch-arm/cortex-a15/bionic/strcpy.S \
+    arch-arm/cortex-a15/bionic/strlen.S \
+    arch-arm/cortex-a15/bionic/strcmp.S
diff --git a/libc/arch-arm64/arm64.mk b/libc/arch-arm64/arm64.mk
index 88da1f3..7b6b1c3 100644
--- a/libc/arch-arm64/arm64.mk
+++ b/libc/arch-arm64/arm64.mk
@@ -14,6 +14,11 @@
     upstream-freebsd/lib/libc/string/wcslen.c \
     upstream-freebsd/lib/libc/string/wcsrchr.c \
     upstream-freebsd/lib/libc/string/wmemcmp.c \
+    upstream-openbsd/lib/libc/locale/_def_numeric.c \
+    upstream-openbsd/lib/libc/locale/_def_messages.c \
+    upstream-openbsd/lib/libc/locale/_def_monetary.c \
+    upstream-openbsd/lib/libc/locale/_def_time.c \
+    upstream-openbsd/lib/libc/locale/localeconv.c \
     upstream-openbsd/lib/libc/string/bcopy.c \
     upstream-openbsd/lib/libc/string/strcat.c \
     upstream-openbsd/lib/libc/string/strcpy.c \
diff --git a/libc/arch-mips64/mips64.mk b/libc/arch-mips64/mips64.mk
index 28806e1..9b29f0d 100644
--- a/libc/arch-mips64/mips64.mk
+++ b/libc/arch-mips64/mips64.mk
@@ -16,6 +16,11 @@
     upstream-freebsd/lib/libc/string/wcslen.c \
     upstream-freebsd/lib/libc/string/wcsrchr.c \
     upstream-freebsd/lib/libc/string/wmemcmp.c \
+    upstream-openbsd/lib/libc/locale/_def_numeric.c \
+    upstream-openbsd/lib/libc/locale/_def_messages.c \
+    upstream-openbsd/lib/libc/locale/_def_monetary.c \
+    upstream-openbsd/lib/libc/locale/_def_time.c \
+    upstream-openbsd/lib/libc/locale/localeconv.c \
     upstream-openbsd/lib/libc/string/bcopy.c \
     upstream-openbsd/lib/libc/string/strcat.c \
     upstream-openbsd/lib/libc/string/strcmp.c \
diff --git a/libc/arch-x86_64/x86_64.mk b/libc/arch-x86_64/x86_64.mk
index 9bce065..a3adaa4 100644
--- a/libc/arch-x86_64/x86_64.mk
+++ b/libc/arch-x86_64/x86_64.mk
@@ -18,6 +18,11 @@
     upstream-freebsd/lib/libc/string/wcslen.c \
     upstream-freebsd/lib/libc/string/wcsrchr.c \
     upstream-freebsd/lib/libc/string/wmemcmp.c \
+    upstream-openbsd/lib/libc/locale/_def_numeric.c \
+    upstream-openbsd/lib/libc/locale/_def_messages.c \
+    upstream-openbsd/lib/libc/locale/_def_monetary.c \
+    upstream-openbsd/lib/libc/locale/_def_time.c \
+    upstream-openbsd/lib/libc/locale/localeconv.c \
     upstream-openbsd/lib/libc/string/bcopy.c \
     upstream-openbsd/lib/libc/string/strcat.c \
     upstream-openbsd/lib/libc/string/strcmp.c \
diff --git a/libc/include/locale.h b/libc/include/locale.h
index b6dbfdb..4efc564 100644
--- a/libc/include/locale.h
+++ b/libc/include/locale.h
@@ -51,12 +51,41 @@
 
 extern char* setlocale(int, const char*);
 
-#if !defined(__LP64__)
-// TODO: LP32 had these bogus declarations but LP64 should have a real struct lconv and localeconv(3).
+#if defined(__LP64__)
+struct lconv {
+    char* decimal_point;
+    char* thousands_sep;
+    char* grouping;
+    char* int_curr_symbol;
+    char* currency_symbol;
+    char* mon_decimal_point;
+    char* mon_thousands_sep;
+    char* mon_grouping;
+    char* positive_sign;
+    char* negative_sign;
+    char  int_frac_digits;
+    char  frac_digits;
+    char  p_cs_precedes;
+    char  p_sep_by_space;
+    char  n_cs_precedes;
+    char  n_sep_by_space;
+    char  p_sign_posn;
+    char  n_sign_posn;
+    /* ISO-C99 */
+    char  int_p_cs_precedes;
+    char  int_p_sep_by_space;
+    char  int_n_cs_precedes;
+    char  int_n_sep_by_space;
+    char  int_p_sign_posn;
+    char  int_n_sign_posn;
+};
+#else
+// Keep old declaration for ILP32 for compatibility
 struct lconv { };
-struct lconv* localeconv(void);
 #endif
 
+struct lconv* localeconv(void);
+
 __END_DECLS
 
 #endif /* _LOCALE_H_ */
diff --git a/libc/include/sys/localedef.h b/libc/include/sys/localedef.h
new file mode 100644
index 0000000..b6b5eb1
--- /dev/null
+++ b/libc/include/sys/localedef.h
@@ -0,0 +1,101 @@
+/*	$OpenBSD: localedef.h,v 1.3 1996/04/21 22:31:47 deraadt Exp $	*/
+/*	$NetBSD: localedef.h,v 1.4 1996/04/09 20:55:31 cgd Exp $	*/
+
+/*
+ * Copyright (c) 1994 Winning Strategies, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Winning Strategies, Inc.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_LOCALEDEF_H_
+#define _SYS_LOCALEDEF_H_
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+typedef struct
+{
+	char *yesexpr;
+	char *noexpr;
+	char *yesstr;
+	char *nostr;
+} _MessagesLocale;
+
+extern const _MessagesLocale *_CurrentMessagesLocale;
+extern const _MessagesLocale  _DefaultMessagesLocale;
+
+
+typedef struct
+{
+	char *int_curr_symbol;
+	char *currency_symbol;
+	char *mon_decimal_point;
+	char *mon_thousands_sep;
+	char *mon_grouping;
+	char *positive_sign;
+	char *negative_sign;
+	char int_frac_digits;
+	char frac_digits;
+	char p_cs_precedes;
+	char p_sep_by_space;
+	char n_cs_precedes;
+	char n_sep_by_space;
+	char p_sign_posn;
+	char n_sign_posn;
+} _MonetaryLocale;
+
+extern const _MonetaryLocale *_CurrentMonetaryLocale;
+extern const _MonetaryLocale  _DefaultMonetaryLocale;
+
+
+typedef struct
+{
+	const char *decimal_point;
+	const char *thousands_sep;
+	const char *grouping;
+} _NumericLocale;
+
+extern const _NumericLocale *_CurrentNumericLocale;
+extern const _NumericLocale  _DefaultNumericLocale;
+
+
+typedef struct {
+	const char *abday[7];
+	const char *day[7];
+	const char *abmon[12];
+	const char *mon[12];
+	const char *am_pm[2];
+	const char *d_t_fmt;
+	const char *d_fmt;
+	const char *t_fmt;
+	const char *t_fmt_ampm;
+} _TimeLocale;
+
+extern const _TimeLocale *_CurrentTimeLocale;
+extern const _TimeLocale  _DefaultTimeLocale;
+
+#endif /* !_SYS_LOCALEDEF_H_ */
diff --git a/libc/stdlib/strtod.c b/libc/stdlib/strtod.c
index 0a19446..b39c90e 100644
--- a/libc/stdlib/strtod.c
+++ b/libc/stdlib/strtod.c
@@ -1332,13 +1332,14 @@
 	Bigint *bb1, *bd0;
 	Bigint *bb = NULL, *bd = NULL, *bs = NULL, *delta = NULL;/* pacify gcc */
 
-	CONST char decimal_point = '.';
-#if 0 /* BEGIN android-changed: no localeconv. */
+#if defined(__LP64__) /* BEGIN android-changed: no localeconv for ILP32. */
 #ifndef KR_headers
 	CONST char decimal_point = localeconv()->decimal_point[0];
 #else
 	CONST char decimal_point = '.';
 #endif
+#else
+	CONST char decimal_point = '.';
 #endif /* END android-changed */
 
 	sign = nz0 = nz = 0;
diff --git a/libc/upstream-openbsd/lib/libc/locale/_def_messages.c b/libc/upstream-openbsd/lib/libc/locale/_def_messages.c
new file mode 100644
index 0000000..1ed653a
--- /dev/null
+++ b/libc/upstream-openbsd/lib/libc/locale/_def_messages.c
@@ -0,0 +1,18 @@
+/*	$OpenBSD: _def_messages.c,v 1.5 2005/08/08 08:05:35 espie Exp $ */
+/*
+ * Written by J.T. Conklin <jtc@netbsd.org>.
+ * Public domain.
+ */
+
+#include <sys/localedef.h>
+#include <locale.h>
+
+const _MessagesLocale _DefaultMessagesLocale =
+{
+	"^[Yy]",
+	"^[Nn]",
+	"yes",
+	"no"
+} ;
+
+const _MessagesLocale *_CurrentMessagesLocale = &_DefaultMessagesLocale;
diff --git a/libc/upstream-openbsd/lib/libc/locale/_def_monetary.c b/libc/upstream-openbsd/lib/libc/locale/_def_monetary.c
new file mode 100644
index 0000000..aa92c75
--- /dev/null
+++ b/libc/upstream-openbsd/lib/libc/locale/_def_monetary.c
@@ -0,0 +1,30 @@
+/*	$OpenBSD: _def_monetary.c,v 1.4 2005/08/08 08:05:35 espie Exp $ */
+/*
+ * Written by J.T. Conklin <jtc@netbsd.org>.
+ * Public domain.
+ */
+
+#include <sys/localedef.h>
+#include <limits.h>
+#include <locale.h>
+
+const _MonetaryLocale _DefaultMonetaryLocale =
+{
+	"",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"",
+	CHAR_MAX,
+	CHAR_MAX,
+	CHAR_MAX,
+	CHAR_MAX,
+	CHAR_MAX,
+	CHAR_MAX,
+	CHAR_MAX,
+	CHAR_MAX
+};
+
+const _MonetaryLocale *_CurrentMonetaryLocale = &_DefaultMonetaryLocale;
diff --git a/libc/upstream-openbsd/lib/libc/locale/_def_numeric.c b/libc/upstream-openbsd/lib/libc/locale/_def_numeric.c
new file mode 100644
index 0000000..6511254
--- /dev/null
+++ b/libc/upstream-openbsd/lib/libc/locale/_def_numeric.c
@@ -0,0 +1,17 @@
+/*	$OpenBSD: _def_numeric.c,v 1.4 2005/08/08 08:05:35 espie Exp $ */
+/*
+ * Written by J.T. Conklin <jtc@netbsd.org>.
+ * Public domain.
+ */
+
+#include <sys/localedef.h>
+#include <locale.h>
+
+const _NumericLocale _DefaultNumericLocale =
+{
+	".",
+	"",
+	""
+};
+
+const _NumericLocale *_CurrentNumericLocale = &_DefaultNumericLocale;
diff --git a/libc/upstream-openbsd/lib/libc/locale/_def_time.c b/libc/upstream-openbsd/lib/libc/locale/_def_time.c
new file mode 100644
index 0000000..75179a6
--- /dev/null
+++ b/libc/upstream-openbsd/lib/libc/locale/_def_time.c
@@ -0,0 +1,36 @@
+/*	$OpenBSD: _def_time.c,v 1.5 2011/10/09 06:39:53 ajacoutot Exp $ */
+/*
+ * Written by J.T. Conklin <jtc@netbsd.org>.
+ * Public domain.
+ */
+
+#include <sys/localedef.h>
+#include <locale.h>
+
+const _TimeLocale _DefaultTimeLocale =
+{
+	{
+		"Sun","Mon","Tue","Wed","Thu","Fri","Sat",
+	},
+	{
+		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+		"Friday", "Saturday"
+	},
+	{
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+	},
+	{
+		"January", "February", "March", "April", "May", "June", "July",
+		"August", "September", "October", "November", "December"
+	},
+	{
+		"AM", "PM"
+	},
+	"%a %b %e %H:%M:%S %Y",
+	"%m/%d/%y",
+	"%H:%M:%S",
+	"%I:%M:%S %p"
+};
+
+const _TimeLocale *_CurrentTimeLocale = &_DefaultTimeLocale;
diff --git a/libc/upstream-openbsd/lib/libc/locale/localeconv.c b/libc/upstream-openbsd/lib/libc/locale/localeconv.c
new file mode 100644
index 0000000..989eb4b
--- /dev/null
+++ b/libc/upstream-openbsd/lib/libc/locale/localeconv.c
@@ -0,0 +1,59 @@
+/*	$OpenBSD: localeconv.c,v 1.5 2005/08/08 08:05:35 espie Exp $ */
+/*
+ * Written by J.T. Conklin <jtc@netbsd.org>.
+ * Public domain.
+ */
+
+#include <sys/localedef.h>
+#include <locale.h>
+
+/*
+ * The localeconv() function constructs a struct lconv from the current
+ * monetary and numeric locales.
+ *
+ * Because localeconv() may be called many times (especially by library
+ * routines like printf() & strtod()), the approprate members of the
+ * lconv structure are computed only when the monetary or numeric
+ * locale has been changed.
+ */
+int __mlocale_changed = 1;
+int __nlocale_changed = 1;
+
+/*
+ * Return the current locale conversion.
+ */
+struct lconv *
+localeconv(void)
+{
+    static struct lconv ret;
+
+    if (__mlocale_changed) {
+	/* LC_MONETARY */
+	ret.int_curr_symbol	= _CurrentMonetaryLocale->int_curr_symbol;
+	ret.currency_symbol	= _CurrentMonetaryLocale->currency_symbol;
+	ret.mon_decimal_point	= _CurrentMonetaryLocale->mon_decimal_point;
+	ret.mon_thousands_sep	= _CurrentMonetaryLocale->mon_thousands_sep;
+	ret.mon_grouping	= _CurrentMonetaryLocale->mon_grouping;
+	ret.positive_sign	= _CurrentMonetaryLocale->positive_sign;
+	ret.negative_sign	= _CurrentMonetaryLocale->negative_sign;
+	ret.int_frac_digits	= _CurrentMonetaryLocale->int_frac_digits;
+	ret.frac_digits		= _CurrentMonetaryLocale->frac_digits;
+	ret.p_cs_precedes	= _CurrentMonetaryLocale->p_cs_precedes;
+	ret.p_sep_by_space	= _CurrentMonetaryLocale->p_sep_by_space;
+	ret.n_cs_precedes	= _CurrentMonetaryLocale->n_cs_precedes;
+	ret.n_sep_by_space	= _CurrentMonetaryLocale->n_sep_by_space;
+	ret.p_sign_posn		= _CurrentMonetaryLocale->p_sign_posn;
+	ret.n_sign_posn		= _CurrentMonetaryLocale->n_sign_posn;
+	__mlocale_changed = 0;
+    }
+
+    if (__nlocale_changed) {
+	/* LC_NUMERIC */
+	ret.decimal_point	= (char *) _CurrentNumericLocale->decimal_point;
+	ret.thousands_sep	= (char *) _CurrentNumericLocale->thousands_sep;
+	ret.grouping		= (char *) _CurrentNumericLocale->grouping;
+	__nlocale_changed = 0;
+    }
+
+    return (&ret);
+}
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 156864c..de7b04d 100755
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -163,11 +163,10 @@
         write(2, msg, strlen(msg)); \
         abort(); \
     }
-#define UNUSED __attribute__((unused))
-DISALLOW_ALLOCATION(void*, malloc, (size_t u UNUSED));
-DISALLOW_ALLOCATION(void, free, (void* u UNUSED));
-DISALLOW_ALLOCATION(void*, realloc, (void* u1 UNUSED, size_t u2 UNUSED));
-DISALLOW_ALLOCATION(void*, calloc, (size_t u1 UNUSED, size_t u2 UNUSED));
+DISALLOW_ALLOCATION(void*, malloc, (size_t u __unused));
+DISALLOW_ALLOCATION(void, free, (void* u __unused));
+DISALLOW_ALLOCATION(void*, realloc, (void* u1 __unused, size_t u2 __unused));
+DISALLOW_ALLOCATION(void*, calloc, (size_t u1 __unused, size_t u2 __unused));
 
 static char tmp_err_buf[768];
 static char __linker_dl_err_buf[768];
@@ -1413,7 +1412,7 @@
 }
 #endif
 
-void soinfo::CallArray(const char* array_name UNUSED, linker_function_t* functions, size_t count, bool reverse) {
+void soinfo::CallArray(const char* array_name __unused, linker_function_t* functions, size_t count, bool reverse) {
   if (functions == NULL) {
     return;
   }
@@ -1432,7 +1431,7 @@
   TRACE("[ Done calling %s for '%s' ]", array_name, name);
 }
 
-void soinfo::CallFunction(const char* function_name UNUSED, linker_function_t function) {
+void soinfo::CallFunction(const char* function_name __unused, linker_function_t function) {
   if (function == NULL || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
     return;
   }
@@ -1908,7 +1907,7 @@
  * It helps to stack unwinding through signal handlers.
  * Also, it makes bionic more like glibc.
  */
-static void add_vdso(KernelArgumentBlock& args UNUSED) {
+static void add_vdso(KernelArgumentBlock& args __unused) {
 #if defined(AT_SYSINFO_EHDR)
   ElfW(Ehdr)* ehdr_vdso = reinterpret_cast<ElfW(Ehdr)*>(args.getauxval(AT_SYSINFO_EHDR));
   if (ehdr_vdso == NULL) {
diff --git a/tests/Android.mk b/tests/Android.mk
index a37ea4a..788dbcf 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -50,6 +50,7 @@
     inttypes_test.cpp \
     libc_logging_test.cpp \
     libgen_test.cpp \
+    locale_test.cpp \
     malloc_test.cpp \
     math_test.cpp \
     netdb_test.cpp \
diff --git a/tests/locale_test.cpp b/tests/locale_test.cpp
new file mode 100644
index 0000000..df58a70
--- /dev/null
+++ b/tests/locale_test.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <locale.h>
+
+TEST(locale, localeconv) {
+#ifdef __LP64__
+  ASSERT_STREQ(".", localeconv()->decimal_point);
+  ASSERT_STREQ("", localeconv()->currency_symbol);
+#else
+  GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}