Merge "x86 libc: Fix the range to check the error"
diff --git a/libc/Android.mk b/libc/Android.mk
index 033c273..dfbd57b 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -479,7 +479,8 @@
 		-DINET6 \
 		-I$(LOCAL_PATH)/private \
 		-DUSE_DL_PREFIX \
-		-DPOSIX_MISTAKE
+		-DPOSIX_MISTAKE \
+                -DLOG_ON_HEAP_ERROR \
 
 # these macro definitions are required to implement the
 # 'timezone' and 'daylight' global variables, as well as
@@ -522,6 +523,10 @@
 #
 libc_crt_target_cflags += -I$(LOCAL_PATH)/private
 
+ifeq ($(TARGET_ARCH),arm)
+libc_crt_target_cflags += -DCRT_LEGACY_WORKAROUND
+endif
+
 # Define some common includes
 # ========================================================
 libc_common_c_includes := \
@@ -597,6 +602,9 @@
 
 LOCAL_SRC_FILES := $(libc_common_src_files)
 LOCAL_CFLAGS := $(libc_common_cflags)
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_CFLAGS += -DCRT_LEGACY_WORKAROUND
+endif
 LOCAL_C_INCLUDES := $(libc_common_c_includes)
 LOCAL_MODULE := libc_common
 LOCAL_SYSTEM_SHARED_LIBRARIES :=
diff --git a/libc/arch-arm/bionic/atexit.S b/libc/arch-arm/bionic/atexit.S
new file mode 100644
index 0000000..aa1e18d
--- /dev/null
+++ b/libc/arch-arm/bionic/atexit.S
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef CRT_LEGACY_WORKAROUND
+	.arch armv5te
+	.fpu softvfp
+	.eabi_attribute 20, 1
+	.eabi_attribute 21, 1
+	.eabi_attribute 23, 3
+	.eabi_attribute 24, 1
+	.eabi_attribute 25, 1
+	.eabi_attribute 26, 2
+	.eabi_attribute 30, 4
+	.eabi_attribute 18, 4
+	.code	16
+	.section	.text.atexit,"ax",%progbits
+	.align	2
+	.global	atexit
+	.hidden	atexit
+	.code	16
+	.thumb_func
+	.type	atexit, %function
+atexit:
+	.fnstart
+.LFB0:
+	.save	{r4, lr}
+	push	{r4, lr}
+.LCFI0:
+	ldr	r3, .L3
+	mov	r1, #0
+	@ sp needed for prologue
+.LPIC0:
+	add	r3, pc
+	ldr	r2, [r3]
+	bl	__cxa_atexit
+	pop	{r4, pc}
+.L4:
+	.align	2
+.L3:
+	.word	__dso_handle-(.LPIC0+4)
+.LFE0:
+	.fnend
+	.size	atexit, .-atexit
+#endif
diff --git a/libc/arch-arm/bionic/crtbegin_dynamic.S b/libc/arch-arm/bionic/crtbegin_dynamic.S
index d18e715..0999084 100644
--- a/libc/arch-arm/bionic/crtbegin_dynamic.S
+++ b/libc/arch-arm/bionic/crtbegin_dynamic.S
@@ -85,3 +85,4 @@
 	.long -1
 
 #include "__dso_handle.S"
+#include "atexit.S"
diff --git a/libc/arch-arm/bionic/crtbegin_so.S b/libc/arch-arm/bionic/crtbegin_so.S
index bb6b3e2..9275b1e 100644
--- a/libc/arch-arm/bionic/crtbegin_so.S
+++ b/libc/arch-arm/bionic/crtbegin_so.S
@@ -52,4 +52,10 @@
         .long -1
         .long __on_dlclose
 
+#ifdef CRT_LEGACY_WORKAROUND
 #include "__dso_handle.S"
+#else
+#include "__dso_handle_so.S"
+#endif
+
+#include "atexit.S"
diff --git a/libc/arch-arm/bionic/crtbegin_static.S b/libc/arch-arm/bionic/crtbegin_static.S
index 6f9cf25..13b05b2 100644
--- a/libc/arch-arm/bionic/crtbegin_static.S
+++ b/libc/arch-arm/bionic/crtbegin_static.S
@@ -86,3 +86,4 @@
 
 
 #include "__dso_handle.S"
+#include "atexit.S"
diff --git a/libc/arch-x86/bionic/atexit.S b/libc/arch-x86/bionic/atexit.S
new file mode 100644
index 0000000..b28f40b
--- /dev/null
+++ b/libc/arch-x86/bionic/atexit.S
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+	.text
+	.p2align 4,,15
+	.globl	atexit
+	.hidden	atexit
+	.type	atexit, @function
+atexit:
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%ebx
+	call	__x86.get_pc_thunk.bx
+	addl	$_GLOBAL_OFFSET_TABLE_, %ebx
+	subl	$20, %esp
+	movl	$0, 4(%esp)
+	movl	__dso_handle@GOTOFF(%ebx), %eax
+	movl	%eax, 8(%esp)
+	movl	8(%ebp), %eax
+	movl	%eax, (%esp)
+	call	__cxa_atexit@PLT
+	addl	$20, %esp
+	popl	%ebx
+	popl	%ebp
+	ret
+	.size	atexit, .-atexit
+
+	.section	.text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
+	.globl	__x86.get_pc_thunk.bx
+	.hidden	__x86.get_pc_thunk.bx
+	.type	__x86.get_pc_thunk.bx, @function
+__x86.get_pc_thunk.bx:
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	movl	(%esp), %ebx
+	ret
diff --git a/libc/arch-x86/bionic/crtbegin_dynamic.S b/libc/arch-x86/bionic/crtbegin_dynamic.S
index 540556b..9ba0d2f 100644
--- a/libc/arch-x86/bionic/crtbegin_dynamic.S
+++ b/libc/arch-x86/bionic/crtbegin_dynamic.S
@@ -133,5 +133,5 @@
 	.weak	__deregister_frame_info_bases
 
 #include "__dso_handle.S"
+#include "atexit.S"
 #include "__stack_chk_fail_local.S"
-
diff --git a/libc/arch-x86/bionic/crtbegin_so.S b/libc/arch-x86/bionic/crtbegin_so.S
index 2741742..99662fe 100644
--- a/libc/arch-x86/bionic/crtbegin_so.S
+++ b/libc/arch-x86/bionic/crtbegin_so.S
@@ -1,3 +1,31 @@
+/*
+ * 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.
+ */
+
 .section .init_array, "aw"
 .align 4
 .type __INIT_ARRAY__, @object
@@ -78,21 +106,6 @@
 	.weak	__register_frame_info_bases
 	.weak	__deregister_frame_info_bases
 
-	.section	.text.__x86.get_pc_thunk.bx,"axG",@progbits,__x86.get_pc_thunk.bx,comdat
-	.globl	__x86.get_pc_thunk.bx
-	.hidden	__x86.get_pc_thunk.bx
-	.type	__x86.get_pc_thunk.bx, @function
-__x86.get_pc_thunk.bx:
-	nop
-	nop
-	nop
-	nop
-	nop
-	nop
-	nop
-	nop
-	movl	(%esp), %ebx
-	ret
-
-#include "__dso_handle.S"
+#include "__dso_handle_so.S"
+#include "atexit.S"
 #include "__stack_chk_fail_local.S"
diff --git a/libc/arch-x86/bionic/crtbegin_static.S b/libc/arch-x86/bionic/crtbegin_static.S
index a8d62d6..8e70330 100644
--- a/libc/arch-x86/bionic/crtbegin_static.S
+++ b/libc/arch-x86/bionic/crtbegin_static.S
@@ -132,4 +132,5 @@
 	.weak	__deregister_frame_info_bases
 
 #include "__dso_handle.S"
+#include "atexit.S"
 #include "__stack_chk_fail_local.S"
diff --git a/libc/arch-x86/include/machine/_types.h b/libc/arch-x86/include/machine/_types.h
index e9280a5..5e28c64 100644
--- a/libc/arch-x86/include/machine/_types.h
+++ b/libc/arch-x86/include/machine/_types.h
@@ -38,7 +38,7 @@
 /* the kernel defines size_t as unsigned int, but g++ wants it to be unsigned long */
 #ifndef _SIZE_T_DEFINED_
 #  define _SIZE_T_DEFINED_
-#  ifdef ANDROID
+#  ifdef __ANDROID__
      typedef unsigned int  size_t;
 #  else
      typedef unsigned long  size_t;
diff --git a/libc/bionic/dlmalloc.c b/libc/bionic/dlmalloc.c
index 19fbb75..8c75e9c 100644
--- a/libc/bionic/dlmalloc.c
+++ b/libc/bionic/dlmalloc.c
@@ -2265,13 +2265,53 @@
 
 #else /* PROCEED_ON_ERROR */
 
-#ifndef CORRUPTION_ERROR_ACTION
-#define CORRUPTION_ERROR_ACTION(m) ABORT
-#endif /* CORRUPTION_ERROR_ACTION */
+/* The following Android-specific code is used to print an informative
+ * fatal error message to the log when we detect that a heap corruption
+ * was detected. We need to be careful about not using a log function
+ * that may require an allocation here!
+ */
+#ifdef LOG_ON_HEAP_ERROR
 
-#ifndef USAGE_ERROR_ACTION
-#define USAGE_ERROR_ACTION(m,p) ABORT
-#endif /* USAGE_ERROR_ACTION */
+#  include <private/logd.h>
+
+static void __bionic_heap_error(const char* msg, const char* function)
+{
+    /* We format the buffer explicitely, i.e. without using snprintf()
+     * which may use malloc() internally. Not something we can trust
+     * if we just detected a corrupted heap.
+     */
+    char buffer[256];
+    strlcpy(buffer, "@@@ ABORTING: ", sizeof(buffer));
+    strlcat(buffer, msg, sizeof(buffer));
+    if (function != NULL) {
+        strlcat(buffer, " IN ", sizeof(buffer));
+        strlcat(buffer, function, sizeof(buffer));
+    }
+    __libc_android_log_write(ANDROID_LOG_FATAL,"libc",buffer);
+    abort();
+}
+
+#  ifndef CORRUPTION_ERROR_ACTION
+#    define CORRUPTION_ERROR_ACTION(m)  \
+    __bionic_heap_error("HEAP MEMORY CORRUPTION", __FUNCTION__)
+#  endif
+#  ifndef USAGE_ERROR_ACTION
+#    define USAGE_ERROR_ACTION(m,p)   \
+    __bionic_heap_error("INVALID HEAP ADDRESS", __FUNCTION__)
+#  endif
+
+#else /* !LOG_ON_HEAP_ERROR */
+
+#  ifndef CORRUPTION_ERROR_ACTION
+#    define CORRUPTION_ERROR_ACTION(m) ABORT
+#  endif /* CORRUPTION_ERROR_ACTION */
+
+#  ifndef USAGE_ERROR_ACTION
+#    define USAGE_ERROR_ACTION(m,p) ABORT
+#  endif /* USAGE_ERROR_ACTION */
+
+#endif /* !LOG_ON_HEAP_ERROR */
+
 
 #endif /* PROCEED_ON_ERROR */
 
diff --git a/libc/bionic/logd_write.c b/libc/bionic/logd_write.c
index 63dfd59..2bc39fa 100644
--- a/libc/bionic/logd_write.c
+++ b/libc/bionic/logd_write.c
@@ -48,6 +48,16 @@
 
 #include <pthread.h>
 
+/* IMPORTANT IMPORTANT IMPORTANT: TECHNICAL NOTE
+ *
+ * Some of the functions below can be called when our malloc() implementation
+ * has detected that the heap is corrupted, or even from a signal handler.
+ *
+ * These functions should *not* use a function that allocates heap memory
+ * or is not signal-safe. Using direct system calls is acceptable, and we
+ * also assume that pthread_mutex_lock/unlock can be used too.
+ */
+
 #define LOG_BUF_SIZE    1024
 
 typedef enum {
@@ -77,9 +87,10 @@
     { __write_to_log_init, -1, "/dev/"LOGGER_LOG_RADIO }
 };
 
+/* Important: see technical note at start of source file */
 static int __write_to_log_null(log_id_t log_id, struct iovec *vec)
 {
-    /* 
+    /*
      * ALTERED behaviour from previous version
      * always returns successful result
      */
@@ -97,23 +108,21 @@
  *  it's supposed, that log_id contains valid id always.
  *  this check must be performed in higher level functions
  */
+/* Important: see technical note at start of source file */
 static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec)
 {
-    ssize_t ret;
-
-    do {
-        ret = writev(log_channels[log_id].fd, vec, 3);
-    } while ((ret < 0) && (errno == EINTR));
-
-    return ret;
+    return TEMP_FAILURE_RETRY( writev(log_channels[log_id].fd, vec, 3) );
 }
 
+/* Important: see technical note at start of source file */
 static int __write_to_log_init(log_id_t log_id, struct iovec *vec)
 {
     if ((LOG_ID_NONE < log_id) && (log_id < LOG_ID_MAX)) {
+        int fd;
+
         pthread_mutex_lock(&log_init_lock);
 
-        int fd = open(log_channels[log_id].path, O_WRONLY);
+        fd = TEMP_FAILURE_RETRY(open(log_channels[log_id].path, O_WRONLY));
 
         log_channels[log_id].logger =
             (fd < 0) ? __write_to_log_null : __write_to_log_kernel;
@@ -130,7 +139,9 @@
     return -1;
 }
 
-static int __android_log_write(int prio, const char *tag, const char *msg)
+/* Important: see technical note at start of source file */
+__LIBC_HIDDEN__
+int __libc_android_log_write(int prio, const char *tag, const char *msg)
 {
     struct iovec vec[3];
     log_id_t log_id = LOG_ID_MAIN;
@@ -151,7 +162,11 @@
     return log_channels[log_id].logger(log_id, vec);
 }
 
-
+/* The functions below are not designed to be called from a heap panic
+ * function or from a signal handler. As such, they are free to use complex
+ * C library functions like vsnprintf()
+ */
+__LIBC_HIDDEN__
 int __libc_android_log_vprint(int prio, const char *tag, const char *fmt,
                               va_list ap)
 {
@@ -159,9 +174,10 @@
 
     vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
 
-    return __android_log_write(prio, tag, buf);
+    return __libc_android_log_write(prio, tag, buf);
 }
 
+__LIBC_HIDDEN__
 int __libc_android_log_print(int prio, const char *tag, const char *fmt, ...)
 {
     va_list ap;
@@ -171,20 +187,21 @@
     vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
     va_end(ap);
 
-    return __android_log_write(prio, tag, buf);
+    return __libc_android_log_write(prio, tag, buf);
 }
 
+__LIBC_HIDDEN__
 int __libc_android_log_assert(const char *cond, const char *tag,
 			      const char *fmt, ...)
 {
     va_list ap;
-    char buf[LOG_BUF_SIZE];    
+    char buf[LOG_BUF_SIZE];
 
     va_start(ap, fmt);
     vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
     va_end(ap);
 
-    __android_log_write(ANDROID_LOG_FATAL, tag, buf);
+    __libc_android_log_write(ANDROID_LOG_FATAL, tag, buf);
 
     exit(1);
 
diff --git a/libc/include/resolv.h b/libc/include/resolv.h
index 4247d68..bb21c23 100644
--- a/libc/include/resolv.h
+++ b/libc/include/resolv.h
@@ -34,6 +34,7 @@
 #include <sys/socket.h>
 #include <stdio.h>
 #include <arpa/nameser.h>
+#include <netinet/in.h>
 
 __BEGIN_DECLS
 
@@ -49,6 +50,21 @@
 extern int   b64_ntop(u_char const *, size_t, char *, size_t);
 extern int   b64_pton(char const *, u_char *, size_t);
 
+/* Set name of default interface */
+extern void _resolv_set_default_iface(const char* ifname);
+
+/* set name servers for an interface */
+extern void _resolv_set_nameservers_for_iface(const char* ifname, char** servers, int numservers);
+
+/* tell resolver of the address of an interface */
+extern void _resolv_set_addr_of_iface(const char* ifname, struct in_addr* addr);
+
+/* flush the cache associated with the default interface */
+extern void _resolv_flush_cache_for_default_iface();
+
+/* flush the cache associated with a certain interface */
+extern void _resolv_flush_cache_for_iface(const char* ifname);
+
 __END_DECLS
 
 #endif /* _RESOLV_H_ */
diff --git a/libc/netbsd/resolv/res_cache.c b/libc/netbsd/resolv/res_cache.c
index e6302ed..20f37e1 100644
--- a/libc/netbsd/resolv/res_cache.c
+++ b/libc/netbsd/resolv/res_cache.c
@@ -27,6 +27,7 @@
  */
 
 #include "resolv_cache.h"
+#include <resolv.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
@@ -35,6 +36,12 @@
 #include <errno.h>
 #include "arpa_nameser.h"
 #include <sys/system_properties.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <linux/if.h>
+
+#include <arpa/inet.h>
+#include "resolv_private.h"
 
 /* This code implements a small and *simple* DNS resolver cache.
  *
@@ -159,9 +166,6 @@
 #include <stdio.h>
 #include <stdarg.h>
 
-#include <arpa/inet.h>
-#include "resolv_private.h"
-
 /** BOUNDED BUFFER FORMATTING
  **/
 
@@ -1165,6 +1169,14 @@
     Entry*           entries;
 } Cache;
 
+typedef struct resolv_cache_info {
+    char                        ifname[IF_NAMESIZE + 1];
+    struct in_addr              ifaddr;
+    Cache*                      cache;
+    struct resolv_cache_info*   next;
+    char*                       nameservers[MAXNS +1];
+    struct addrinfo*            nsaddrinfo[MAXNS + 1];
+} CacheInfo;
 
 #define  HTABLE_VALID(x)  ((x) != NULL && (x) != HTABLE_DELETED)
 
@@ -1543,11 +1555,47 @@
 /****************************************************************************/
 /****************************************************************************/
 
-static struct resolv_cache*  _res_cache;
 static pthread_once_t        _res_cache_once;
 
+// Head of the list of caches.  Protected by _res_cache_list_lock.
+static struct resolv_cache_info _res_cache_list;
+
+// name of the current default inteface
+static char            _res_default_ifname[IF_NAMESIZE + 1];
+
+// lock protecting everything in the _resolve_cache_info structs (next ptr, etc)
+static pthread_mutex_t _res_cache_list_lock;
+
+
+/* lookup the default interface name */
+static char *_get_default_iface_locked();
+/* insert resolv_cache_info into the list of resolv_cache_infos */
+static void _insert_cache_info_locked(struct resolv_cache_info* cache_info);
+/* creates a resolv_cache_info */
+static struct resolv_cache_info* _create_cache_info( void );
+/* gets cache associated with an interface name, or NULL if none exists */
+static struct resolv_cache* _find_named_cache_locked(const char* ifname);
+/* gets a resolv_cache_info associated with an interface name, or NULL if not found */
+static struct resolv_cache_info* _find_cache_info_locked(const char* ifname);
+/* free dns name server list of a resolv_cache_info structure */
+static void _free_nameservers(struct resolv_cache_info* cache_info);
+/* look up the named cache, and creates one if needed */
+static struct resolv_cache* _get_res_cache_for_iface_locked(const char* ifname);
+/* empty the named cache */
+static void _flush_cache_for_iface_locked(const char* ifname);
+/* empty the nameservers set for the named cache */
+static void _free_nameservers_locked(struct resolv_cache_info* cache_info);
+/* lookup the namserver for the name interface */
+static int _get_nameserver_locked(const char* ifname, int n, char* addr, int addrLen);
+/* lookup the addr of the nameserver for the named interface */
+static struct addrinfo* _get_nameserver_addr_locked(const char* ifname, int n);
+/* lookup the inteface's address */
+static struct in_addr* _get_addr_locked(const char * ifname);
+
+
+
 static void
-_res_cache_init( void )
+_res_cache_init(void)
 {
     const char*  env = getenv(CONFIG_ENV);
 
@@ -1556,29 +1604,394 @@
         return;
     }
 
-    _res_cache = _resolv_cache_create();
+    memset(&_res_default_ifname, 0, sizeof(_res_default_ifname));
+    memset(&_res_cache_list, 0, sizeof(_res_cache_list));
+    pthread_mutex_init(&_res_cache_list_lock, NULL);
 }
 
-
 struct resolv_cache*
-__get_res_cache( void )
+__get_res_cache(void)
 {
-    pthread_once( &_res_cache_once, _res_cache_init );
-    return _res_cache;
+    struct resolv_cache *cache;
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+
+    pthread_mutex_lock(&_res_cache_list_lock);
+
+    char* ifname = _get_default_iface_locked();
+
+    // if default interface not set then use the first cache
+    // associated with an interface as the default one.
+    if (ifname[0] == '\0') {
+        struct resolv_cache_info* cache_info = _res_cache_list.next;
+        while (cache_info) {
+            if (cache_info->ifname[0] != '\0') {
+                ifname = cache_info->ifname;
+            }
+
+            cache_info = cache_info->next;
+        }
+    }
+    cache = _get_res_cache_for_iface_locked(ifname);
+
+    pthread_mutex_unlock(&_res_cache_list_lock);
+    XLOG("_get_res_cache. default_ifname = %s\n", ifname);
+    return cache;
+}
+
+static struct resolv_cache*
+_get_res_cache_for_iface_locked(const char* ifname)
+{
+    if (ifname == NULL)
+        return NULL;
+
+    struct resolv_cache* cache = _find_named_cache_locked(ifname);
+    if (!cache) {
+        struct resolv_cache_info* cache_info = _create_cache_info();
+        if (cache_info) {
+            cache = _resolv_cache_create();
+            if (cache) {
+                int len = sizeof(cache_info->ifname);
+                cache_info->cache = cache;
+                strncpy(cache_info->ifname, ifname, len - 1);
+                cache_info->ifname[len - 1] = '\0';
+
+                _insert_cache_info_locked(cache_info);
+            } else {
+                free(cache_info);
+            }
+        }
+    }
+    return cache;
 }
 
 void
-_resolv_cache_reset( unsigned  generation )
+_resolv_cache_reset(unsigned  generation)
 {
     XLOG("%s: generation=%d", __FUNCTION__, generation);
 
-    if (_res_cache == NULL)
-        return;
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
 
-    pthread_mutex_lock( &_res_cache->lock );
-    if (_res_cache->generation != generation) {
-        _cache_flush_locked(_res_cache);
-        _res_cache->generation = generation;
+    char* ifname = _get_default_iface_locked();
+    // if default interface not set then use the first cache
+    // associated with an interface as the default one.
+    // Note: Copied the code from __get_res_cache since this
+    // method will be deleted/obsolete when cache per interface
+    // implemented all over
+    if (ifname[0] == '\0') {
+        struct resolv_cache_info* cache_info = _res_cache_list.next;
+        while (cache_info) {
+            if (cache_info->ifname[0] != '\0') {
+                ifname = cache_info->ifname;
+            }
+
+            cache_info = cache_info->next;
+        }
     }
-    pthread_mutex_unlock( &_res_cache->lock );
+    struct resolv_cache* cache = _get_res_cache_for_iface_locked(ifname);
+
+    if (cache == NULL) {
+        pthread_mutex_unlock(&_res_cache_list_lock);
+        return;
+    }
+
+    pthread_mutex_lock( &cache->lock );
+    if (cache->generation != generation) {
+        _cache_flush_locked(cache);
+        cache->generation = generation;
+    }
+    pthread_mutex_unlock( &cache->lock );
+
+    pthread_mutex_unlock(&_res_cache_list_lock);
+}
+
+void
+_resolv_flush_cache_for_default_iface(void)
+{
+    char* ifname;
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+
+    ifname = _get_default_iface_locked();
+    _flush_cache_for_iface_locked(ifname);
+
+    pthread_mutex_unlock(&_res_cache_list_lock);
+}
+
+void
+_resolv_flush_cache_for_iface(const char* ifname)
+{
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+
+    _flush_cache_for_iface_locked(ifname);
+
+    pthread_mutex_unlock(&_res_cache_list_lock);
+}
+
+static void
+_flush_cache_for_iface_locked(const char* ifname)
+{
+    struct resolv_cache* cache = _find_named_cache_locked(ifname);
+    if (cache) {
+        pthread_mutex_lock(&cache->lock);
+        _cache_flush_locked(cache);
+        pthread_mutex_unlock(&cache->lock);
+    }
+}
+
+static struct resolv_cache_info*
+_create_cache_info(void)
+{
+    struct resolv_cache_info*  cache_info;
+
+    cache_info = calloc(sizeof(*cache_info), 1);
+    return cache_info;
+}
+
+static void
+_insert_cache_info_locked(struct resolv_cache_info* cache_info)
+{
+    struct resolv_cache_info* last;
+
+    for (last = &_res_cache_list; last->next; last = last->next);
+
+    last->next = cache_info;
+
+}
+
+static struct resolv_cache*
+_find_named_cache_locked(const char* ifname) {
+
+    struct resolv_cache_info* info = _find_cache_info_locked(ifname);
+
+    if (info != NULL) return info->cache;
+
+    return NULL;
+}
+
+static struct resolv_cache_info*
+_find_cache_info_locked(const char* ifname)
+{
+    if (ifname == NULL)
+        return NULL;
+
+    struct resolv_cache_info* cache_info = _res_cache_list.next;
+
+    while (cache_info) {
+        if (strcmp(cache_info->ifname, ifname) == 0) {
+            break;
+        }
+
+        cache_info = cache_info->next;
+    }
+    return cache_info;
+}
+
+static char*
+_get_default_iface_locked(void)
+{
+    char* iface = _res_default_ifname;
+
+    return iface;
+}
+
+void
+_resolv_set_default_iface(const char* ifname)
+{
+    XLOG("_resolv_set_default_if ifname %s\n",ifname);
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+
+    int size = sizeof(_res_default_ifname);
+    memset(_res_default_ifname, 0, size);
+    strncpy(_res_default_ifname, ifname, size - 1);
+    _res_default_ifname[size - 1] = '\0';
+
+    pthread_mutex_unlock(&_res_cache_list_lock);
+}
+
+void
+_resolv_set_nameservers_for_iface(const char* ifname, char** servers, int numservers)
+{
+    int i, rt, index;
+    struct addrinfo hints;
+    char sbuf[NI_MAXSERV];
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+
+    pthread_mutex_lock(&_res_cache_list_lock);
+    // creates the cache if not created
+    _get_res_cache_for_iface_locked(ifname);
+
+    struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname);
+
+    if (cache_info != NULL) {
+        // free current before adding new
+        _free_nameservers_locked(cache_info);
+
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_family = PF_UNSPEC;
+        hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+        hints.ai_flags = AI_NUMERICHOST;
+        sprintf(sbuf, "%u", NAMESERVER_PORT);
+
+        index = 0;
+        for (i = 0; i < numservers && i < MAXNS; i++) {
+            rt = getaddrinfo(servers[i], sbuf, &hints, &cache_info->nsaddrinfo[index]);
+            if (rt == 0) {
+                cache_info->nameservers[index] = strdup(servers[i]);
+                index++;
+            } else {
+                cache_info->nsaddrinfo[index] = NULL;
+            }
+        }
+    }
+    pthread_mutex_unlock(&_res_cache_list_lock);
+}
+
+static void
+_free_nameservers_locked(struct resolv_cache_info* cache_info)
+{
+    int i;
+    for (i = 0; i <= MAXNS; i++) {
+        free(cache_info->nameservers[i]);
+        cache_info->nameservers[i] = NULL;
+        if (cache_info->nsaddrinfo[i] != NULL) {
+            freeaddrinfo(cache_info->nsaddrinfo[i]);
+            cache_info->nsaddrinfo[i] = NULL;
+        }
+    }
+}
+
+int
+_resolv_cache_get_nameserver(int n, char* addr, int addrLen)
+{
+    char *ifname;
+    int result = 0;
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+
+    ifname = _get_default_iface_locked();
+    result = _get_nameserver_locked(ifname, n, addr, addrLen);
+
+    pthread_mutex_unlock(&_res_cache_list_lock);
+    return result;
+}
+
+static int
+_get_nameserver_locked(const char* ifname, int n, char* addr, int addrLen)
+{
+    int len = 0;
+    char* ns;
+    struct resolv_cache_info* cache_info;
+
+    if (n < 1 || n > MAXNS || !addr)
+        return 0;
+
+    cache_info = _find_cache_info_locked(ifname);
+    if (cache_info) {
+        ns = cache_info->nameservers[n - 1];
+        if (ns) {
+            len = strlen(ns);
+            if (len < addrLen) {
+                strncpy(addr, ns, len);
+                addr[len] = '\0';
+            } else {
+                len = 0;
+            }
+        }
+    }
+
+    return len;
+}
+
+struct addrinfo*
+_cache_get_nameserver_addr(int n)
+{
+    struct addrinfo *result;
+    char* ifname;
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+
+    ifname = _get_default_iface_locked();
+
+    result = _get_nameserver_addr_locked(ifname, n);
+    pthread_mutex_unlock(&_res_cache_list_lock);
+    return result;
+}
+
+static struct addrinfo*
+_get_nameserver_addr_locked(const char* ifname, int n)
+{
+    struct addrinfo* ai = NULL;
+    struct resolv_cache_info* cache_info;
+
+    if (n < 1 || n > MAXNS)
+        return NULL;
+
+    cache_info = _find_cache_info_locked(ifname);
+    if (cache_info) {
+        ai = cache_info->nsaddrinfo[n - 1];
+    }
+    return ai;
+}
+
+void
+_resolv_set_addr_of_iface(const char* ifname, struct in_addr* addr)
+{
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+    struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname);
+    if (cache_info) {
+        memcpy(&cache_info->ifaddr, addr, sizeof(*addr));
+
+        if (DEBUG) {
+            char* addr_s = inet_ntoa(cache_info->ifaddr);
+            XLOG("address of interface %s is %s\n", ifname, addr_s);
+        }
+    }
+    pthread_mutex_unlock(&_res_cache_list_lock);
+}
+
+struct in_addr*
+_resolv_get_addr_of_default_iface(void)
+{
+    struct in_addr* ai = NULL;
+    char* ifname;
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+    ifname = _get_default_iface_locked();
+    ai = _get_addr_locked(ifname);
+    pthread_mutex_unlock(&_res_cache_list_lock);
+
+    return ai;
+}
+
+struct in_addr*
+_resolv_get_addr_of_iface(const char* ifname)
+{
+    struct in_addr* ai = NULL;
+
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+    ai =_get_addr_locked(ifname);
+    pthread_mutex_unlock(&_res_cache_list_lock);
+    return ai;
+}
+
+static struct in_addr*
+_get_addr_locked(const char * ifname)
+{
+    struct resolv_cache_info* cache_info = _find_cache_info_locked(ifname);
+    if (cache_info) {
+        return &cache_info->ifaddr;
+    }
+    return NULL;
 }
diff --git a/libc/netbsd/resolv/res_state.c b/libc/netbsd/resolv/res_state.c
index 322ace9..e05846a 100644
--- a/libc/netbsd/resolv/res_state.c
+++ b/libc/netbsd/resolv/res_state.c
@@ -170,7 +170,6 @@
         pthread_setspecific( _res_key, NULL );
         return NULL;
     }
-    _resolv_cache_reset(rt->_serial);
     return rt;
 }
 
diff --git a/libc/private/__dso_handle.S b/libc/private/__dso_handle.S
index a6395dd6..3e80128 100644
--- a/libc/private/__dso_handle.S
+++ b/libc/private/__dso_handle.S
@@ -32,7 +32,11 @@
 #
         .section .bss
         .align 4
+
+#ifndef CRT_LEGACY_WORKAROUND
 	.hidden __dso_handle
+#endif
+
         .globl __dso_handle
 __dso_handle:
         .long 0
diff --git a/libc/private/__dso_handle_so.S b/libc/private/__dso_handle_so.S
new file mode 100644
index 0000000..77a5d7f
--- /dev/null
+++ b/libc/private/__dso_handle_so.S
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+# The __dso_handle global variable is used by static
+# C++ constructors and destructors in the binary.
+# See http://www.codesourcery.com/public/cxx-abi/abi.html#dso-dtor
+#
+	.data
+        .align 4
+	.hidden __dso_handle
+        .globl __dso_handle
+__dso_handle:
+        .long __dso_handle
diff --git a/libc/private/logd.h b/libc/private/logd.h
index 43fa742..4a9b62e 100644
--- a/libc/private/logd.h
+++ b/libc/private/logd.h
@@ -44,6 +44,7 @@
     ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
 };
 
+int __libc_android_log_write(int prio, const char* tag, const char* buffer);
 int __libc_android_log_print(int prio, const char *tag, const char *fmt, ...);
 int __libc_android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap);
 
diff --git a/libc/private/resolv_cache.h b/libc/private/resolv_cache.h
index cd876fb..2a54453 100644
--- a/libc/private/resolv_cache.h
+++ b/libc/private/resolv_cache.h
@@ -30,13 +30,45 @@
 
 struct resolv_cache;  /* forward */
 
-/* get cache instance, can be NULL if cache is disabled
- * (e.g. through an environment variable) */
+/* gets the cache for the default interface. Might be NULL*/
 extern struct resolv_cache*  __get_res_cache(void);
 
+/* get the cache for a specified interface. Can be NULL*/
+extern struct resolv_cache* __get_res_cache_for_iface(const char* ifname);
+
 /* this gets called everytime we detect some changes in the DNS configuration
  * and will flush the cache */
-extern void   _resolv_cache_reset( unsigned  generation );
+extern void  _resolv_cache_reset( unsigned  generation );
+
+/* Gets the address of the n:th name server for the default interface
+ * Return length of address on success else 0.
+ * Note: The first name server is at n = 1 */
+extern int _resolv_cache_get_nameserver(int n, char* addr, int addrLen);
+
+/* Gets the address of the n:th name server for a certain interface
+ * Return length of address on success else 0.
+ * Note: The first name server is at n = 1 */
+extern int _resolv_cache_get_nameserver_for_iface(const char* ifname, int n,
+        char* addr, int addrLen);
+
+/* Gets addrinfo of the n:th name server associated with an interface.
+ * NULL is returned if no address if found.
+ * Note: The first name server is at n = 1. */
+extern struct addrinfo* _resolv_cache_get_nameserver_addr_for_iface(const char* ifname, int n);
+
+/* Gets addrinfo of the n:th name server associated with the default interface
+ * NULL is returned if no address if found.
+ * Note: The first name server is at n = 1. */
+extern struct addrinfo* _resolv_cache_get_nameserver_addr(int n);
+
+/* gets the address associated with the default interface */
+extern struct in_addr* _resolv_get_addr_of_default_iface();
+
+/* gets the address associated with the specified interface */
+extern struct in_addr* _resolv_get_addr_of_iface(const char* ifname);
+
+/* Get name of default interface */
+extern char* _resolv_get_default_iface();
 
 typedef enum {
     RESOLV_CACHE_UNSUPPORTED,  /* the cache can't handle that kind of queries */
diff --git a/libc/stdio/wcio.h b/libc/stdio/wcio.h
index f8ac1b2..dd6db21 100644
--- a/libc/stdio/wcio.h
+++ b/libc/stdio/wcio.h
@@ -41,7 +41,7 @@
 
 /* BIONIC: disable wchar support */
 #define WCIO_GET(fp) \
-	((struct whcar_io_data*) 0)
+	((struct wchar_io_data*) 0)
 
 #define _SET_ORIENTATION(fp, mode) ((void)0)
 
diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c
index 4ba2177..f4bcab9 100644
--- a/libc/stdlib/atexit.c
+++ b/libc/stdlib/atexit.c
@@ -104,6 +104,7 @@
 	return (ret);
 }
 
+#ifdef CRT_LEGACY_WORKAROUND
 /*
  * Register a function to be performed at exit.
  */
@@ -112,6 +113,7 @@
 {
 	return (__cxa_atexit((void (*)(void *))func, NULL, NULL));
 }
+#endif
 
 /*
  * Call all handlers registered with __cxa_atexit() for the shared
diff --git a/libc/tzcode/strftime.c b/libc/tzcode/strftime.c
index ab713fb..a2cc3b3 100644
--- a/libc/tzcode/strftime.c
+++ b/libc/tzcode/strftime.c
@@ -407,9 +407,9 @@
                     tm = *t;
                     mkt = mktime64(&tm);
                     if (TYPE_SIGNED(time64_t))
-                        (void) sprintf(buf, "%lld",
+                        (void) snprintf(buf, sizeof(buf), "%lld",
                             (long long) mkt);
-                    else    (void) sprintf(buf, "%llu",
+                    else    (void) snprintf(buf, sizeof(buf), "%llu",
                             (unsigned long long) mkt);
                     pt = _add(buf, pt, ptlim, modifier);
                 }
diff --git a/libc/unistd/open.c b/libc/unistd/open.c
index e8b1c89..03cba45 100644
--- a/libc/unistd/open.c
+++ b/libc/unistd/open.c
@@ -35,9 +35,7 @@
 {
     mode_t  mode = 0;
 
-#if !defined(__i386__)
     flags |= O_LARGEFILE;
-#endif
 
     if (flags & O_CREAT)
     {
diff --git a/libc/unistd/openat.c b/libc/unistd/openat.c
index 88b39a4..6b7b367 100644
--- a/libc/unistd/openat.c
+++ b/libc/unistd/openat.c
@@ -35,9 +35,7 @@
 {
     mode_t  mode = 0;
 
-#if !defined(__i386__)
     flags |= O_LARGEFILE;
-#endif
 
     if (flags & O_CREAT)
     {
diff --git a/libc/unistd/time.c b/libc/unistd/time.c
index 13d7366..7b450c7 100644
--- a/libc/unistd/time.c
+++ b/libc/unistd/time.c
@@ -34,12 +34,15 @@
 time(time_t *t)
 {
 	struct timeval tt;
+	time_t ret;
 
 	if (gettimeofday(&tt, (struct timezone *)0) < 0)
-		return (-1);
-	if (t)
-		*t = (time_t)tt.tv_sec;
-	return (tt.tv_sec);
+		ret = -1;
+	else
+		ret = tt.tv_sec;
+	if (t != NULL)
+		*t = ret;
+	return ret;
 }
 
 
diff --git a/libc/zoneinfo/MODULE_LICENSE_PUBLIC_DOMAIN b/libc/zoneinfo/MODULE_LICENSE_PUBLIC_DOMAIN
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libc/zoneinfo/MODULE_LICENSE_PUBLIC_DOMAIN
diff --git a/libc/zoneinfo/zoneinfo.dat b/libc/zoneinfo/zoneinfo.dat
index 27ca5d0..bac23f0 100644
--- a/libc/zoneinfo/zoneinfo.dat
+++ b/libc/zoneinfo/zoneinfo.dat
Binary files differ
diff --git a/libc/zoneinfo/zoneinfo.idx b/libc/zoneinfo/zoneinfo.idx
index 09bd15f..7750e4d 100644
--- a/libc/zoneinfo/zoneinfo.idx
+++ b/libc/zoneinfo/zoneinfo.idx
Binary files differ
diff --git a/libc/zoneinfo/zoneinfo.version b/libc/zoneinfo/zoneinfo.version
index 76dcafb..4abe0cc 100644
--- a/libc/zoneinfo/zoneinfo.version
+++ b/libc/zoneinfo/zoneinfo.version
@@ -1 +1 @@
-2010k
+2011h