am 89118032: Merge "Fix bug in debuggerd so it can successfully find the exidx section in libraries. This should fix the bug where the exception stack wasn\'t being printed past the PC." into gingerbread

Merge commit '891180320f0b08758d053a8562dfcd601ef846b0' into gingerbread-plus-aosp

* commit '891180320f0b08758d053a8562dfcd601ef846b0':
  Fix bug in debuggerd so it can successfully find the exidx section in
diff --git a/adb/sockets.c b/adb/sockets.c
index 9f1b598..43925e4 100644
--- a/adb/sockets.c
+++ b/adb/sockets.c
@@ -65,8 +65,11 @@
     asocket *result = NULL;
 
     adb_mutex_lock(&socket_list_lock);
-    for(s = local_socket_list.next; s != &local_socket_list && !result; s = s->next) {
-        if(s->id == id) result = s;
+    for (s = local_socket_list.next; s != &local_socket_list; s = s->next) {
+        if (s->id == id) {
+            result = s;
+            break;
+        }
     }
     adb_mutex_unlock(&socket_list_lock);
 
@@ -366,7 +369,7 @@
 asocket *create_local_socket(int fd)
 {
     asocket *s = calloc(1, sizeof(asocket));
-    if(s == 0) fatal("cannot allocate socket");
+    if (s == NULL) fatal("cannot allocate socket");
     install_local_socket(s);
     s->fd = fd;
     s->enqueue = local_socket_enqueue;
@@ -482,7 +485,7 @@
     asocket *s = calloc(1, sizeof(aremotesocket));
     adisconnect*  dis = &((aremotesocket*)s)->disconnect;
 
-    if(s == 0) fatal("cannot allocate socket");
+    if (s == NULL) fatal("cannot allocate socket");
     s->id = id;
     s->enqueue = remote_socket_enqueue;
     s->ready = remote_socket_ready;
@@ -761,8 +764,7 @@
 {
     D("Creating smart socket \n");
     asocket *s = calloc(1, sizeof(asocket));
-    if(s == 0) fatal("cannot allocate socket");
-    s->id = 0;
+    if (s == NULL) fatal("cannot allocate socket");
     s->enqueue = smart_socket_enqueue;
     s->ready = smart_socket_ready;
     s->close = smart_socket_close;
diff --git a/adb/usb_vendors.c b/adb/usb_vendors.c
index 9e1600f..993206f 100644
--- a/adb/usb_vendors.c
+++ b/adb/usb_vendors.c
@@ -69,6 +69,8 @@
 #define VENDOR_ID_PANTECH       0x10A9
 // Qualcomm's USB Vendor ID
 #define VENDOR_ID_QUALCOMM      0x05c6
+// On-The-Go-Video's USB Vendor ID
+#define VENDOR_ID_OTGV          0x2257
 // NEC's USB Vendor ID
 #define VENDOR_ID_NEC           0x0409
 // Panasonic Mobile Communication's USB Vendor ID
@@ -94,6 +96,7 @@
     VENDOR_ID_KYOCERA,
     VENDOR_ID_PANTECH,
     VENDOR_ID_QUALCOMM,
+    VENDOR_ID_OTGV,
     VENDOR_ID_NEC,
     VENDOR_ID_PMC,
 };
@@ -157,7 +160,7 @@
 /* builds the path to the adb vendor id file. returns 0 if success */
 int build_path(char* buff, size_t len, const char* format, const char* home)
 {
-    if (snprintf(buff, len, format, home, ANDROID_PATH, ANDROID_ADB_INI) >= len) {
+    if (snprintf(buff, len, format, home, ANDROID_PATH, ANDROID_ADB_INI) >= (signed)len) {
         return 1;
     }
 
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 3c1cf02..ccc001b 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -5,7 +5,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c
+LOCAL_SRC_FILES:= debuggerd.c getevent.c unwind-arm.c pr-support.c utility.c symbol_table.c
 LOCAL_CFLAGS := -Wall
 LOCAL_MODULE := debuggerd
 
diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c
index 7c36367..24b7e72 100644
--- a/debuggerd/debuggerd.c
+++ b/debuggerd/debuggerd.c
@@ -63,7 +63,7 @@
 /* Log information onto the tombstone */
 void _LOG(int tfd, bool in_tombstone_only, const char *fmt, ...)
 {
-    char buf[128];
+    char buf[512];
 
     va_list ap;
     va_start(ap, fmt);
@@ -106,10 +106,11 @@
 
     mi->start = strtoul(line, 0, 16);
     mi->end = strtoul(line + 9, 0, 16);
-    /* To be filled in parse_exidx_info if the mapped section starts with
+    /* To be filled in parse_elf_info if the mapped section starts with
      * elf_header
      */
     mi->exidx_start = mi->exidx_end = 0;
+    mi->symbols = 0;
     mi->next = 0;
     strcpy(mi->name, line + 49);
 
@@ -353,7 +354,7 @@
     if(sig) dump_fault_addr(tfd, tid, sig);
 }
 
-static void parse_exidx_info(mapinfo *milist, pid_t pid)
+static void parse_elf_info(mapinfo *milist, pid_t pid)
 {
     mapinfo *mi;
     for (mi = milist; mi != NULL; mi = mi->next) {
@@ -383,6 +384,9 @@
                     break;
                 }
             }
+
+            /* Try to load symbols from this file */
+            mi->symbols = symbol_table_create(mi->name);
         }
     }
 }
@@ -420,7 +424,7 @@
         fclose(fp);
     }
 
-    parse_exidx_info(milist, tid);
+    parse_elf_info(milist, tid);
 
     /* If stack unwinder fails, use the default solution to dump the stack
      * content.
@@ -439,6 +443,7 @@
 
     while(milist) {
         mapinfo *next = milist->next;
+        symbol_table_free(milist->symbols);
         free(milist);
         milist = next;
     }
diff --git a/debuggerd/symbol_table.c b/debuggerd/symbol_table.c
new file mode 100644
index 0000000..150c058
--- /dev/null
+++ b/debuggerd/symbol_table.c
@@ -0,0 +1,178 @@
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "symbol_table.h"
+
+#include <linux/elf.h>
+
+// Compare func for qsort
+static int qcompar(const void *a, const void *b)
+{
+    return ((struct symbol*)a)->addr - ((struct symbol*)b)->addr;
+}
+
+// Compare func for bsearch
+static int bcompar(const void *addr, const void *element)
+{
+    struct symbol *symbol = (struct symbol*)element;
+
+    if((unsigned int)addr < symbol->addr) {
+        return -1;
+    }
+
+    if((unsigned int)addr - symbol->addr >= symbol->size) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ *  Create a symbol table from a given file
+ *
+ *  Parameters:
+ *      filename - Filename to process
+ *
+ *  Returns:
+ *      A newly-allocated SymbolTable structure, or NULL if error.
+ *      Free symbol table with symbol_table_free()
+ */
+struct symbol_table *symbol_table_create(const char *filename)
+{
+    struct symbol_table *table = NULL;
+
+    // Open the file, and map it into memory
+    struct stat sb;
+    int length;
+    char *base;
+
+    int fd = open(filename, O_RDONLY);
+
+    if(fd < 0) {
+        goto out;
+    }
+
+    fstat(fd, &sb);
+    length = sb.st_size;
+
+    base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
+
+    if(!base) {
+        goto out_close;
+    }
+
+    // Parse the file header
+    Elf32_Ehdr *hdr = (Elf32_Ehdr*)base;
+    Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff);
+
+    // Search for the dynamic symbols section
+    int dynsym_idx = -1;
+    int i;
+
+    for(i = 0; i < hdr->e_shnum; i++) {
+        if(shdr[i].sh_type == SHT_DYNSYM ) {
+            dynsym_idx = i;
+        }
+    }
+
+    if(dynsym_idx == -1) {
+        goto out_unmap;
+    }
+
+    Elf32_Sym *dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset);
+    int numsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize;
+
+    table = malloc(sizeof(struct symbol_table));
+    if(!table) {
+        goto out_unmap;
+    }
+    table->num_symbols = 0;
+
+    // Iterate through the dynamic symbol table, and count how many symbols
+    // are actually defined
+    for(i = 0; i < numsyms; i++) {
+        if(dynsyms[i].st_shndx != SHN_UNDEF) {
+            table->num_symbols++;
+        }
+    }
+
+    int dynstr_idx = shdr[dynsym_idx].sh_link;
+    char *dynstr = base + shdr[dynstr_idx].sh_offset;
+
+    // Now, create an entry in our symbol table structure for each symbol...
+    table->symbols = malloc(table->num_symbols * sizeof(struct symbol));
+    if(!table->symbols) {
+        free(table);
+        table = NULL;
+        goto out_unmap;
+    }
+
+    // ...and populate them
+    int j = 0;
+    for(i = 0; i < numsyms; i++) {
+        if(dynsyms[i].st_shndx != SHN_UNDEF) {
+            table->symbols[j].name = strdup(dynstr + dynsyms[i].st_name);
+            table->symbols[j].addr = dynsyms[i].st_value;
+            table->symbols[j].size = dynsyms[i].st_size;
+            j++;
+        }
+    }
+
+    // Sort the symbol table entries, so they can be bsearched later
+    qsort(table->symbols, table->num_symbols, sizeof(struct symbol), qcompar);
+
+out_unmap:
+    munmap(base, length);
+
+out_close:
+    close(fd);
+
+out:
+    return table;
+}
+
+/*
+ * Free a symbol table
+ *
+ * Parameters:
+ *     table - Table to free
+ */
+void symbol_table_free(struct symbol_table *table)
+{
+    int i;
+
+    if(!table) {
+        return;
+    }
+
+    for(i=0; i<table->num_symbols; i++) {
+        free(table->symbols[i].name);
+    }
+
+    free(table->symbols);
+    free(table);
+}
+
+/*
+ * Search for an address in the symbol table
+ *
+ * Parameters:
+ *      table - Table to search in
+ *      addr - Address to search for.
+ *
+ * Returns:
+ *      A pointer to the Symbol structure corresponding to the
+ *      symbol which contains this address, or NULL if no symbol
+ *      contains it.
+ */
+const struct symbol *symbol_table_lookup(struct symbol_table *table, unsigned int addr)
+{
+    if(!table) {
+        return NULL;
+    }
+
+    return bsearch((void*)addr, table->symbols, table->num_symbols, sizeof(struct symbol), bcompar);
+}
diff --git a/debuggerd/symbol_table.h b/debuggerd/symbol_table.h
new file mode 100644
index 0000000..d9d2520
--- /dev/null
+++ b/debuggerd/symbol_table.h
@@ -0,0 +1,19 @@
+#ifndef SYMBOL_TABLE_H
+#define SYMBOL_TABLE_H
+
+struct symbol {
+    unsigned int addr;
+    unsigned int size;
+    char *name;
+};
+
+struct symbol_table {
+    struct symbol *symbols;
+    int num_symbols;
+};
+
+struct symbol_table *symbol_table_create(const char *filename);
+void symbol_table_free(struct symbol_table *table);
+const struct symbol *symbol_table_lookup(struct symbol_table *table, unsigned int addr);
+
+#endif
diff --git a/debuggerd/unwind-arm.c b/debuggerd/unwind-arm.c
index 9642d2e..b081161 100644
--- a/debuggerd/unwind-arm.c
+++ b/debuggerd/unwind-arm.c
@@ -37,6 +37,8 @@
 #include <unwind.h>
 #include "utility.h"
 
+#include "symbol_table.h"
+
 typedef struct _ZSt9type_info type_info; /* This names C++ type_info type */
 
 void __attribute__((weak)) __cxa_call_unexpected(_Unwind_Control_Block *ucbp);
@@ -393,6 +395,7 @@
     phase2_vrs *vrs = (phase2_vrs*) context;
     const mapinfo *mi;
     bool only_in_tombstone = !at_fault;
+    const struct symbol* sym = 0;
 
     if (stack_level < STACK_CONTENT_DEPTH) {
         sp_list[stack_level] = vrs->core.r[R_SP];
@@ -451,9 +454,20 @@
     rel_pc = pc;
     mi = pc_to_mapinfo(map, pc, &rel_pc);
 
-    _LOG(tfd, only_in_tombstone, 
-         "         #%02d  pc %08x  %s\n", stack_level, rel_pc, 
-         mi ? mi->name : "");
+    /* See if we can determine what symbol this stack frame resides in */
+    if (mi != 0 && mi->symbols != 0) {
+        sym = symbol_table_lookup(mi->symbols, rel_pc);
+    }
+
+    if (sym) {
+        _LOG(tfd, only_in_tombstone,
+            "         #%02d  pc %08x  %s (%s)\n", stack_level, rel_pc,
+            mi ? mi->name : "", sym->name);
+    } else {
+        _LOG(tfd, only_in_tombstone,
+            "         #%02d  pc %08x  %s\n", stack_level, rel_pc,
+            mi ? mi->name : "");
+    }
 
     return _URC_NO_REASON;
 }
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 49f5951..2ffdf56 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -21,6 +21,8 @@
 #include <stddef.h>
 #include <stdbool.h>
 
+#include "symbol_table.h"
+
 #ifndef PT_ARM_EXIDX
 #define PT_ARM_EXIDX    0x70000001      /* .ARM.exidx segment */
 #endif
@@ -33,6 +35,7 @@
     unsigned end;
     unsigned exidx_start;
     unsigned exidx_end;
+    struct symbol_table *symbols;
     char name[];
 } mapinfo;
 
diff --git a/include/arch/target_linux-x86/AndroidConfig.h b/include/arch/target_linux-x86/AndroidConfig.h
index b9800dd..2152d6a 100644
--- a/include/arch/target_linux-x86/AndroidConfig.h
+++ b/include/arch/target_linux-x86/AndroidConfig.h
@@ -227,7 +227,7 @@
 /*
  * Define if we have Linux's dbus 
  */
-#define HAVE_DBUS 1
+/* #define HAVE_DBUS 1 */
 
 /*
  * Define if tm struct has tm_gmtoff field
diff --git a/include/cutils/log.h b/include/cutils/log.h
index dd47c35..f602017 100644
--- a/include/cutils/log.h
+++ b/include/cutils/log.h
@@ -291,11 +291,11 @@
  */
 #define LOG_ALWAYS_FATAL_IF(cond, ...) \
     ( (CONDITION(cond)) \
-    ? ((void)android_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \
+    ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \
     : (void)0 )
 
 #define LOG_ALWAYS_FATAL(...) \
-    ( ((void)android_printAssert(NULL, LOG_TAG, __VA_ARGS__)) )
+    ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) )
 
 /*
  * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that
@@ -308,7 +308,7 @@
 
 #else
 
-#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__)
+#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__)
 #define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__)
 
 #endif
@@ -317,7 +317,7 @@
  * Assertion that generates a log message when the assertion fails.
  * Stripped out of release builds.  Uses the current LOG_TAG.
  */
-#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), __VA_ARGS__)
+#define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__)
 //#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond)
 
 // ---------------------------------------------------------------------
@@ -403,8 +403,24 @@
 #define android_vprintLog(prio, cond, tag, fmt...) \
     __android_log_vprint(prio, tag, fmt)
 
+/* XXX Macros to work around syntax errors in places where format string
+ * arg is not passed to LOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF
+ * (happens only in debug builds).
+ */
+
+/* Returns 2nd arg.  Used to substitute default value if caller's vararg list
+ * is empty.
+ */
+#define __android_second(dummy, second, ...)     second
+
+/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
+ * returns nothing.
+ */
+#define __android_rest(first, ...)               , ## __VA_ARGS__
+
 #define android_printAssert(cond, tag, fmt...) \
-    __android_log_assert(cond, tag, fmt)
+    __android_log_assert(cond, tag, \
+        __android_second(0, ## fmt, NULL) __android_rest(fmt))
 
 #define android_writeLog(prio, tag, text) \
     __android_log_write(prio, tag, text)
@@ -413,7 +429,7 @@
     __android_log_bwrite(tag, payload, len)
 #define android_btWriteLog(tag, type, payload, len) \
     __android_log_btwrite(tag, type, payload, len)
-	
+
 // TODO: remove these prototypes and their users
 #define android_testLog(prio, tag) (1)
 #define android_writevLog(vec,num) do{}while(0)
diff --git a/include/netutils/dhcp.h b/include/netutils/dhcp.h
new file mode 100644
index 0000000..96798c5
--- /dev/null
+++ b/include/netutils/dhcp.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#ifndef _NETUTILS_DHCP_H_
+#define _NETUTILS_DHCP_H_
+
+#include <sys/cdefs.h>
+#include <arpa/inet.h>
+
+__BEGIN_DECLS
+
+extern int do_dhcp(char *iname);
+extern int dhcp_do_request(const char *ifname,
+                          in_addr_t *ipaddr,
+                          in_addr_t *gateway,
+                          in_addr_t *mask,
+                          in_addr_t *dns1,
+                          in_addr_t *dns2,
+                          in_addr_t *server,
+                          uint32_t  *lease);
+extern int dhcp_stop(const char *ifname);
+extern int dhcp_release_lease(const char *ifname);
+extern char *dhcp_get_errmsg();
+
+__END_DECLS
+
+#endif /* _NETUTILS_DHCP_H_ */
diff --git a/include/netutils/ifc.h b/include/netutils/ifc.h
new file mode 100644
index 0000000..2e502ab
--- /dev/null
+++ b/include/netutils/ifc.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2008, 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.
+ */
+
+#ifndef _NETUTILS_IFC_H_
+#define _NETUTILS_IFC_H_
+
+#include <sys/cdefs.h>
+#include <arpa/inet.h>
+
+__BEGIN_DECLS
+
+extern int ifc_init(void);
+extern void ifc_close(void);
+
+extern int ifc_get_ifindex(const char *name, int *if_indexp);
+extern int ifc_get_hwaddr(const char *name, void *ptr);
+
+extern int ifc_up(const char *name);
+extern int ifc_down(const char *name);
+
+extern int ifc_enable(const char *ifname);
+extern int ifc_disable(const char *ifname);
+
+extern int ifc_reset_connections(const char *ifname);
+
+extern int ifc_set_addr(const char *name, in_addr_t addr);
+extern int ifc_set_mask(const char *name, in_addr_t mask);
+extern int ifc_set_hwaddr(const char *name, const void *ptr);
+
+extern int ifc_add_host_route(const char *name, in_addr_t addr);
+extern int ifc_remove_host_routes(const char *name);
+extern int ifc_get_default_route(const char *ifname);
+extern int ifc_set_default_route(const char *ifname, in_addr_t gateway);
+extern int ifc_create_default_route(const char *name, in_addr_t addr);
+extern int ifc_remove_default_route(const char *ifname);
+
+extern int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask,
+                        in_addr_t *flags);
+
+extern int ifc_configure(const char *ifname, in_addr_t address,
+                         in_addr_t netmask, in_addr_t gateway,
+                         in_addr_t dns1, in_addr_t dns2);
+
+__END_DECLS
+
+#endif /* _NETUTILS_IFC_H_ */
diff --git a/init/util.c b/init/util.c
old mode 100644
new mode 100755
index 377754b..d8ec88e
--- a/init/util.c
+++ b/init/util.c
@@ -439,8 +439,9 @@
         if (x) {
             x += 2;
             n = 0;
-            while (*x && !isspace(*x)) {
-                hardware[n++] = tolower(*x);
+            while (*x && *x != '\n') {
+                if (!isspace(*x))
+                    hardware[n++] = tolower(*x);
                 x++;
                 if (n == 31) break;
             }
diff --git a/libdiskconfig/Android.mk b/libdiskconfig/Android.mk
index 1d0ecb4..c887955 100644
--- a/libdiskconfig/Android.mk
+++ b/libdiskconfig/Android.mk
@@ -3,17 +3,25 @@
 
 ifneq ($(TARGET_SIMULATOR),true)
 
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
+commonSources := \
 	diskconfig.c \
 	diskutils.c \
 	write_lst.c \
 	config_mbr.c
 
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(commonSources)
 LOCAL_MODULE := libdiskconfig
 LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils liblog libc
-
 include $(BUILD_SHARED_LIBRARY)
 
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(commonSources)
+LOCAL_MODULE := libdiskconfig_host
+LOCAL_SYSTEM_SHARED_LIBRARIES := libcutils
+LOCAL_CFLAGS := -O2 -g -W -Wall -Werror -D_LARGEFILE64_SOURCE
+include $(BUILD_HOST_STATIC_LIBRARY)
+endif # HOST_OS == linux
+
 endif  # ! TARGET_SIMULATOR
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 9923bba..a0a753b 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -56,7 +56,7 @@
  * the simulator rather than a desktop tool and want to use the device.
  */
 static enum {
-    kLogUninitialized, kLogNotAvailable, kLogAvailable 
+    kLogUninitialized, kLogNotAvailable, kLogAvailable
 } g_log_status = kLogUninitialized;
 int __android_log_dev_available(void)
 {
@@ -189,7 +189,7 @@
 
 int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
 {
-    char buf[LOG_BUF_SIZE];    
+    char buf[LOG_BUF_SIZE];
 
     vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
 
@@ -223,12 +223,23 @@
 void __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);
+    if (fmt) {
+        va_list ap;
+        va_start(ap, fmt);
+        vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+        va_end(ap);
+    } else {
+        /* Msg not provided, log condition.  N.B. Do not use cond directly as
+         * format string as it could contain spurious '%' syntax (e.g.
+         * "%d" in "blocks%devs == 0").
+         */
+        if (cond)
+            snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
+        else
+            strcpy(buf, "Unspecified assertion failed");
+    }
 
     __android_log_write(ANDROID_LOG_FATAL, tag, buf);
 
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
index 0f8a6c4..cb0960f 100644
--- a/libnetutils/dhcp_utils.c
+++ b/libnetutils/dhcp_utils.c
@@ -115,6 +115,14 @@
     }
 }
 
+static const char *ipaddr_to_string(in_addr_t addr)
+{
+    struct in_addr in_addr;
+
+    in_addr.s_addr = addr;
+    return inet_ntoa(in_addr);
+}
+
 /*
  * Start the dhcp client daemon, and wait for it to finish
  * configuring the interface.
@@ -165,7 +173,13 @@
         return -1;
     }
     if (strcmp(prop_value, "ok") == 0) {
+        char dns_prop_name[PROPERTY_KEY_MAX];
         fill_ip_info(interface, ipaddr, gateway, mask, dns1, dns2, server, lease);
+        /* copy the dhcp.XXX.dns properties to net.XXX.dns */
+        snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", interface);
+        property_set(dns_prop_name, *dns1 ? ipaddr_to_string(*dns1) : "");
+        snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", interface);
+        property_set(dns_prop_name, *dns2 ? ipaddr_to_string(*dns2) : "");
         return 0;
     } else {
         snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
index 6755ba1..ff00432 100644
--- a/libnetutils/dhcpclient.c
+++ b/libnetutils/dhcpclient.c
@@ -36,8 +36,8 @@
 
 #include <dirent.h>
 
+#include <netutils/ifc.h>
 #include "dhcpmsg.h"
-#include "ifc_utils.h"
 #include "packet.h"
 
 #define VERBOSE 2
@@ -85,16 +85,12 @@
 //    exit(1);
 }
 
-const char *ipaddr(uint32_t addr)
+const char *ipaddr(in_addr_t addr)
 {
-    static char buf[32];
+    struct in_addr in_addr;
 
-    sprintf(buf,"%d.%d.%d.%d",
-            addr & 255,
-            ((addr >> 8) & 255),
-            ((addr >> 16) & 255),
-            (addr >> 24));
-    return buf;
+    in_addr.s_addr = addr;
+    return inet_ntoa(in_addr);
 }
 
 typedef struct dhcp_info dhcp_info;
@@ -128,31 +124,11 @@
     *lease = last_good_info.lease;
 }
 
-static int ifc_configure(const char *ifname, dhcp_info *info)
+static int dhcp_configure(const char *ifname, dhcp_info *info)
 {
-    char dns_prop_name[PROPERTY_KEY_MAX];
-
-    if (ifc_set_addr(ifname, info->ipaddr)) {
-        printerr("failed to set ipaddr %s: %s\n", ipaddr(info->ipaddr), strerror(errno));
-        return -1;
-    }
-    if (ifc_set_mask(ifname, info->netmask)) {
-        printerr("failed to set netmask %s: %s\n", ipaddr(info->netmask), strerror(errno));
-        return -1;
-    }
-    if (ifc_create_default_route(ifname, info->gateway)) {
-        printerr("failed to set default route %s: %s\n", ipaddr(info->gateway), strerror(errno));
-        return -1;
-    }
-
-    snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname);
-    property_set(dns_prop_name, info->dns1 ? ipaddr(info->dns1) : "");
-    snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname);
-    property_set(dns_prop_name, info->dns2 ? ipaddr(info->dns2) : "");
-
     last_good_info = *info;
-
-    return 0;
+    return ifc_configure(ifname, info->ipaddr, info->netmask, info->gateway,
+                         info->dns1, info->dns2);
 }
 
 static const char *dhcp_type_to_name(uint32_t type)
@@ -449,7 +425,7 @@
                 printerr("timed out\n");
                 if ( info.type == DHCPOFFER ) {
                     printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
-                    return ifc_configure(ifname, &info);
+                    return dhcp_configure(ifname, &info);
                 }
                 errno = ETIME;
                 close(s);
@@ -530,7 +506,7 @@
             if (info.type == DHCPACK) {
                 printerr("configuring %s\n", ifname);
                 close(s);
-                return ifc_configure(ifname, &info);
+                return dhcp_configure(ifname, &info);
             } else if (info.type == DHCPNAK) {
                 printerr("configuration request denied\n");
                 close(s);
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index bde336f..296d617 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -27,6 +27,8 @@
 #include <arpa/inet.h>
 
 #include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
 #include <linux/sockios.h>
 #include <linux/route.h>
 #include <linux/wireless.h>
@@ -45,7 +47,7 @@
 static int ifc_ctl_sock = -1;
 void printerr(char *fmt, ...);
 
-static const char *ipaddr_to_string(uint32_t addr)
+static const char *ipaddr_to_string(in_addr_t addr)
 {
     struct in_addr in_addr;
 
@@ -88,7 +90,7 @@
     r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);
     if(r < 0) return -1;
 
-    memcpy(ptr, &ifr.ifr_hwaddr.sa_data, 6);
+    memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
     return 0;    
 }
 
@@ -143,6 +145,17 @@
     return ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr);
 }
 
+int ifc_set_hwaddr(const char *name, const void *ptr)
+{
+    int r;
+    struct ifreq ifr;
+    ifc_init_ifr(name, &ifr);
+
+    ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+    memcpy(&ifr.ifr_hwaddr.sa_data, ptr, ETH_ALEN);
+    return ioctl(ifc_ctl_sock, SIOCSIFHWADDR, &ifr);
+}
+
 int ifc_set_mask(const char *name, in_addr_t mask)
 {
     struct ifreq ifr;
@@ -429,9 +442,9 @@
 
     ifc_close();
 
-    snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns1", ifname);
+    snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname);
     property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : "");
-    snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns2", ifname);
+    snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname);
     property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : "");
 
     return 0;
diff --git a/libnetutils/ifc_utils.h b/libnetutils/ifc_utils.h
deleted file mode 100644
index 49b8747..0000000
--- a/libnetutils/ifc_utils.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2008, 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.
- */
-
-#ifndef _IFC_UTILS_H_
-#define _IFC_UTILS_H_
-
-int ifc_init(void);
-
-int ifc_get_ifindex(const char *name, int *if_indexp);
-int ifc_get_hwaddr(const char *name, void *ptr);
-
-int ifc_up(const char *name);
-int ifc_down(const char *name);
-
-int ifc_set_addr(const char *name, unsigned addr);
-int ifc_set_mask(const char *name, unsigned mask);
-
-int ifc_create_default_route(const char *name, unsigned addr);
-
-int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags);
-
-#endif
diff --git a/libpixelflinger/codeflinger/ARMAssembler.cpp b/libpixelflinger/codeflinger/ARMAssembler.cpp
index d3720c3..fa9f1ad 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.cpp
+++ b/libpixelflinger/codeflinger/ARMAssembler.cpp
@@ -433,6 +433,16 @@
 {
     *mPC++ = (cc<<28) | 0x6CF0070 | (Rd<<12) | ((rotate >> 3) << 10) | Rm;
 }
+#if 0
+#pragma mark -
+#pragma mark Bit manipulation (ARMv7+ only)...
+#endif
+
+// Bit manipulation (ARMv7+ only)...
+void ARMAssembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+{
+    *mPC++ = (cc<<28) | 0x7E00000 | ((width-1)<<16) | (Rd<<12) | (lsb<<7) | 0x50 | Rn;
+}
 
 }; // namespace android
 
diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h
index a667cb5..e7f038a 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.h
+++ b/libpixelflinger/codeflinger/ARMAssembler.h
@@ -124,6 +124,7 @@
     virtual void SMLAW(int cc, int y,
                 int Rd, int Rm, int Rs, int Rn);
     virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+    virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
 
 private:
                 ARMAssembler(const ARMAssembler& rhs);
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
index ff6af2a..796342a 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
@@ -206,6 +206,9 @@
     // byte/half word extract...
     virtual void UXTB16(int cc, int Rd, int Rm, int rotate) = 0;
 
+    // bit manipulation...
+    virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width) = 0;
+
     // -----------------------------------------------------------------------
     // convenience...
     // -----------------------------------------------------------------------
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
index 7c422db..c57d7da 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.cpp
@@ -199,5 +199,9 @@
     mTarget->UXTB16(cc, Rd, Rm, rotate);
 }
 
+void ARMAssemblerProxy::UBFX(int cc, int Rd, int Rn, int lsb, int width) {
+    mTarget->UBFX(cc, Rd, Rn, lsb, width);
+}
+
 }; // namespace android
 
diff --git a/libpixelflinger/codeflinger/ARMAssemblerProxy.h b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
index 9134cce..8c7f270 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerProxy.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerProxy.h
@@ -115,6 +115,7 @@
                 int Rd, int Rm, int Rs, int Rn);
 
     virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+    virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
 
 private:
     ARMAssemblerInterface*  mTarget;
diff --git a/libpixelflinger/codeflinger/disassem.c b/libpixelflinger/codeflinger/disassem.c
index c17f3ec..aeb8034 100644
--- a/libpixelflinger/codeflinger/disassem.c
+++ b/libpixelflinger/codeflinger/disassem.c
@@ -81,6 +81,8 @@
  * g - 2nd fp operand (register) (bits 16-18)
  * h - 3rd fp operand (register/immediate) (bits 0-4)
  * j - xtb rotate literal (bits 10-11)
+ * i - bfx lsb literal (bits 7-11)
+ * w - bfx width literal (bits 16-20)
  * b - branch address
  * t - thumb branch address (bits 24, 0-23)
  * k - breakpoint comment (bits 0-3, 8-19)
@@ -124,6 +126,7 @@
     { 0x0fe000f0, 0x00a00090, "umlal",	"Sdnms" },
     { 0x0fe000f0, 0x00e00090, "smlal",	"Sdnms" },
     { 0x0fff03f0, 0x06cf0070, "uxtb16", "dmj" },
+    { 0x0fe00070, 0x07e00050, "ubfx",   "dmiw" },
     { 0x0d700000, 0x04200000, "strt",	"daW" },
     { 0x0d700000, 0x04300000, "ldrt",	"daW" },
     { 0x0d700000, 0x04600000, "strbt",	"daW" },
@@ -412,6 +415,14 @@
 		case 'j':
 			di->di_printf("ror #%d", ((insn >> 10) & 3) << 3);
 			break;
+        /* i - bfx lsb literal (bits 7-11) */
+        case 'i':
+            di->di_printf("#%d", (insn >> 7) & 31);
+            break;
+        /* w - bfx width literal (bits 16-20) */
+        case 'w':
+            di->di_printf("#%d", 1 + ((insn >> 16) & 31));
+            break;
 		/* b - branch address */
 		case 'b':
 			branch = ((insn << 2) & 0x03ffffff);
diff --git a/libpixelflinger/codeflinger/load_store.cpp b/libpixelflinger/codeflinger/load_store.cpp
index 93c5825..ed20a00 100644
--- a/libpixelflinger/codeflinger/load_store.cpp
+++ b/libpixelflinger/codeflinger/load_store.cpp
@@ -18,9 +18,12 @@
 #include <assert.h>
 #include <stdio.h>
 #include <cutils/log.h>
-
 #include "codeflinger/GGLAssembler.h"
 
+#ifdef __ARM_ARCH__
+#include <machine/cpu-features.h>
+#endif
+
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -110,6 +113,20 @@
     assert(maskLen<=8);
     assert(h);
     
+#if __ARM_ARCH__ >= 7
+    const int mask = (1<<maskLen)-1;
+    if ((h == bits) && !l && (s != d.reg)) {
+        MOV(AL, 0, d.reg, s);                   // component = packed;
+    } else if ((h == bits) && l) {
+        MOV(AL, 0, d.reg, reg_imm(s, LSR, l));  // component = packed >> l;
+    } else if (!l && isValidImmediate(mask)) {
+        AND(AL, 0, d.reg, s, imm(mask));        // component = packed & mask;
+    } else if (!l && isValidImmediate(~mask)) {
+        BIC(AL, 0, d.reg, s, imm(~mask));       // component = packed & mask;
+    } else {
+        UBFX(AL, d.reg, s, l, maskLen);         // component = (packed & mask) >> l;
+    }
+#else
     if (h != bits) {
         const int mask = ((1<<maskLen)-1) << l;
         if (isValidImmediate(mask)) {
@@ -132,6 +149,7 @@
     if (s != d.reg) {
         MOV(AL, 0, d.reg, s);
     }
+#endif
 
     d.s = maskLen;
 }
diff --git a/netcfg/netcfg.c b/netcfg/netcfg.c
index fc9cf48..9cd883a 100644
--- a/netcfg/netcfg.c
+++ b/netcfg/netcfg.c
@@ -19,17 +19,13 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <dirent.h>
+#include <netinet/ether.h>
+
+#include <netutils/ifc.h>
+#include <netutils/dhcp.h>
 
 static int verbose = 0;
 
-int ifc_init();
-void ifc_close();
-int ifc_up(char *iname);
-int ifc_down(char *iname);
-int ifc_remove_host_routes(char *iname);
-int ifc_remove_default_route(char *iname);
-int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags);
-int do_dhcp(char *iname);
 
 void die(const char *reason)
 {
@@ -37,16 +33,12 @@
     exit(1);
 }
 
-const char *ipaddr(unsigned addr)
+const char *ipaddr(in_addr_t addr)
 {
-    static char buf[32];
-    
-    sprintf(buf,"%d.%d.%d.%d", 
-            addr & 255,
-            ((addr >> 8) & 255),
-            ((addr >> 16) & 255), 
-            (addr >> 24));
-    return buf;
+    struct in_addr in_addr;
+
+    in_addr.s_addr = addr;
+    return inet_ntoa(in_addr);
 }
 
 void usage(void)
@@ -86,6 +78,15 @@
     return 0;
 }
 
+int set_hwaddr(const char *name, const char *asc) {
+    struct ether_addr *addr = ether_aton(asc);
+    if (!addr) {
+        printf("Failed to parse '%s'\n", asc);
+        return -1;
+    }
+    return ifc_set_hwaddr(name, addr->ether_addr_octet);
+}
+
 struct 
 {
     const char *name;
@@ -97,6 +98,7 @@
     { "down",   1, ifc_down },
     { "flhosts",  1, ifc_remove_host_routes },
     { "deldefault", 1, ifc_remove_default_route },
+    { "hwaddr", 2, set_hwaddr },
     { 0, 0, 0 },
 };
 
diff --git a/nexus/DhcpClient.cpp b/nexus/DhcpClient.cpp
index a5654d2..713059d 100644
--- a/nexus/DhcpClient.cpp
+++ b/nexus/DhcpClient.cpp
@@ -27,35 +27,15 @@
 
 #include <sysutils/ServiceManager.h>
 
+#include <netutils/ifc.h>
+#include <netutils/dhcp.h>
+
 #include "DhcpClient.h"
 #include "DhcpState.h"
 #include "DhcpListener.h"
 #include "IDhcpEventHandlers.h"
 #include "Controller.h"
 
-extern "C" {
-int ifc_disable(const char *ifname);
-int ifc_add_host_route(const char *ifname, uint32_t addr);
-int ifc_remove_host_routes(const char *ifname);
-int ifc_set_default_route(const char *ifname, uint32_t gateway);
-int ifc_get_default_route(const char *ifname);
-int ifc_remove_default_route(const char *ifname);
-int ifc_reset_connections(const char *ifname);
-int ifc_configure(const char *ifname, in_addr_t ipaddr, in_addr_t netmask, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2);
-
-int dhcp_do_request(const char *ifname,
-                    in_addr_t *ipaddr,
-                    in_addr_t *gateway,
-                    in_addr_t *mask,
-                    in_addr_t *dns1,
-                    in_addr_t *dns2,
-                    in_addr_t *server,
-                    uint32_t  *lease);
-int dhcp_stop(const char *ifname);
-int dhcp_release_lease(const char *ifname);
-char *dhcp_get_errmsg();
-}
-
 DhcpClient::DhcpClient(IDhcpEventHandlers *handlers) :
             mState(DhcpState::INIT), mHandlers(handlers) {
     mServiceManager = new ServiceManager();
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 329be7f..380bb60 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -7,7 +7,7 @@
 	etc/dbus.conf \
 	etc/hosts
 
-ifeq ($(TARGET_PRODUCT),generic)
+ifeq ($(TARGET_PRODUCT),full)
 copy_from += etc/vold.fstab
 endif
 
diff --git a/toolbox/chmod.c b/toolbox/chmod.c
index 31a53bf..2a524e9 100644
--- a/toolbox/chmod.c
+++ b/toolbox/chmod.c
@@ -4,17 +4,74 @@
 #include <sys/types.h>
 #include <dirent.h>
 #include <errno.h>
+#include <sys/limits.h>
+#include <sys/stat.h>
 
 #include <unistd.h>
 #include <time.h>
 
+void recurse_chmod(char* path, int mode)
+{
+    struct dirent *dp;
+    DIR *dir = opendir(path);
+    if (dir == NULL) {
+        // not a directory, carry on
+        return;
+    }
+    char *subpath = malloc(sizeof(char)*PATH_MAX);
+    int pathlen = strlen(path);
+
+    while ((dp = readdir(dir)) != NULL) {
+        if (strcmp(dp->d_name, ".") == 0 ||
+            strcmp(dp->d_name, "..") == 0) continue;
+
+        if (strlen(dp->d_name) + pathlen + 2/*NUL and slash*/ > PATH_MAX) {
+            fprintf(stderr, "Invalid path specified: too long\n");
+            exit(1);
+        }
+
+        strcpy(subpath, path);
+        strcat(subpath, "/");
+        strcat(subpath, dp->d_name);
+
+        if (chmod(subpath, mode) < 0) {
+            fprintf(stderr, "Unable to chmod %s: %s\n", subpath, strerror(errno));
+            exit(1);
+        }
+
+        recurse_chmod(subpath, mode);
+    }
+    free(subpath);
+    closedir(dir);
+}
+
+static int usage()
+{
+    fprintf(stderr, "Usage: chmod [OPTION] <MODE> <FILE>\n");
+    fprintf(stderr, "  -R, --recursive         change files and directories recursively\n");
+    fprintf(stderr, "  --help                  display this help and exit\n");
+
+    return 10;
+}
+
 int chmod_main(int argc, char **argv)
 {
     int i;
 
-    if (argc < 3) {
-        fprintf(stderr, "Usage: chmod <MODE> <FILE>\n");
-        return 10;
+    if (argc < 3 || strcmp(argv[1], "--help") == 0) {
+        return usage();
+    }
+
+    int recursive = (strcmp(argv[1], "-R") == 0 ||
+                     strcmp(argv[1], "--recursive") == 0) ? 1 : 0;
+
+    if (recursive && argc < 4) {
+        return usage();
+    }
+
+    if (recursive) {
+        argc--;
+        argv++;
     }
 
     int mode = 0;
@@ -29,11 +86,15 @@
         }
         s++;
     }
+
     for (i = 2; i < argc; i++) {
         if (chmod(argv[i], mode) < 0) {
             fprintf(stderr, "Unable to chmod %s: %s\n", argv[i], strerror(errno));
             return 10;
         }
+        if (recursive) {
+            recurse_chmod(argv[i], mode);
+        }
     }
     return 0;
 }
diff --git a/toolbox/insmod.c b/toolbox/insmod.c
index 44b9847..756a64b 100644
--- a/toolbox/insmod.c
+++ b/toolbox/insmod.c
@@ -77,7 +77,6 @@
 			memcpy(ptr, argv[i], len);
 			ptr += len;
 			*ptr++ = ' ';
-			*ptr++ = '\0';
 		}
 		*(ptr - 1) = '\0';
 	}
diff --git a/toolbox/ls.c b/toolbox/ls.c
index 8799514..962bf47 100644
--- a/toolbox/ls.c
+++ b/toolbox/ls.c
@@ -13,6 +13,130 @@
 #include <grp.h>
 
 #include <linux/kdev_t.h>
+#include <limits.h>
+
+// dynamic arrays
+typedef struct {
+    int count;
+    int capacity;
+    void** items;
+} dynarray_t;
+
+#define DYNARRAY_INITIALIZER  { 0, 0, NULL }
+
+static void dynarray_init( dynarray_t *a )
+{
+    a->count = a->capacity = 0;
+    a->items = NULL;
+}
+
+static void dynarray_reserve_more( dynarray_t *a, int count )
+{
+    int old_cap = a->capacity;
+    int new_cap = old_cap;
+    const int max_cap = INT_MAX/sizeof(void*);
+    void** new_items;
+    int new_count = a->count + count;
+
+    if (count <= 0)
+        return;
+
+    if (count > max_cap - a->count)
+        abort();
+
+    new_count = a->count + count;
+
+    while (new_cap < new_count) {
+        old_cap = new_cap;
+        new_cap += (new_cap >> 2) + 4;
+        if (new_cap < old_cap || new_cap > max_cap) {
+            new_cap = max_cap;
+        }
+    }
+    new_items = realloc(a->items, new_cap*sizeof(void*));
+    if (new_items == NULL)
+        abort();
+
+    a->items = new_items;
+    a->capacity = new_cap;
+}
+
+static void dynarray_append( dynarray_t *a, void* item )
+{
+    if (a->count >= a->capacity)
+        dynarray_reserve_more(a, 1);
+
+    a->items[a->count++] = item;
+}
+
+static void dynarray_done( dynarray_t *a )
+{
+    free(a->items);
+    a->items = NULL;
+    a->count = a->capacity = 0;
+}
+
+#define DYNARRAY_FOREACH_TYPE(_array,_item_type,_item,_stmnt) \
+    do { \
+        int _nn_##__LINE__ = 0; \
+        for (;_nn_##__LINE__ < (_array)->count; ++ _nn_##__LINE__) { \
+            _item_type _item = (_item_type)(_array)->items[_nn_##__LINE__]; \
+            _stmnt; \
+        } \
+    } while (0)
+
+#define DYNARRAY_FOREACH(_array,_item,_stmnt) \
+    DYNARRAY_FOREACH_TYPE(_array,void *,_item,_stmnt)
+
+// string arrays
+
+typedef dynarray_t  strlist_t;
+
+#define  STRLIST_INITIALIZER  DYNARRAY_INITIALIZER
+
+#define  STRLIST_FOREACH(_list,_string,_stmnt) \
+    DYNARRAY_FOREACH_TYPE(_list,char *,_string,_stmnt)
+
+static void strlist_init( strlist_t *list )
+{
+    dynarray_init(list);
+}
+
+static void strlist_append_b( strlist_t *list, const void* str, size_t  slen )
+{
+    char *copy = malloc(slen+1);
+    memcpy(copy, str, slen);
+    copy[slen] = '\0';
+    dynarray_append(list, copy);
+}
+
+static void strlist_append_dup( strlist_t *list, const char *str)
+{
+    strlist_append_b(list, str, strlen(str));
+}
+
+static void strlist_done( strlist_t *list )
+{
+    STRLIST_FOREACH(list, string, free(string));
+    dynarray_done(list);
+}
+
+static int strlist_compare_strings(const void* a, const void* b)
+{
+    const char *sa = *(const char **)a;
+    const char *sb = *(const char **)b;
+    return strcmp(sa, sb);
+}
+
+static void strlist_sort( strlist_t *list )
+{
+    if (list->count > 0) {
+        qsort(list->items, 
+              (size_t)list->count,
+              sizeof(void*),
+              strlist_compare_strings);
+    }
+}
 
 // bits for flags argument
 #define LIST_LONG           (1 << 0)
@@ -233,7 +357,8 @@
     char tmp[4096];
     DIR *d;
     struct dirent *de;
-
+    strlist_t  files = STRLIST_INITIALIZER;
+    
     d = opendir(name);
     if(d == 0) {
         fprintf(stderr, "opendir failed, %s\n", strerror(errno));
@@ -248,10 +373,16 @@
         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
         if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
 
-        listfile(name, de->d_name, flags);
+        strlist_append_dup(&files, de->d_name);
     }
 
+    strlist_sort(&files);
+    STRLIST_FOREACH(&files, filename, listfile(name, filename, flags));
+    strlist_done(&files);
+
     if (flags & LIST_RECURSIVE) {
+        strlist_t subdirs = STRLIST_INITIALIZER;
+
         rewinddir(d);
 
         while ((de = readdir(d)) != 0) {
@@ -284,10 +415,15 @@
             }
 
             if (S_ISDIR(s.st_mode)) {
-                printf("\n%s:\n", tmp);
-                listdir(tmp, flags);
+                strlist_append_dup(&subdirs, tmp);
             }
         }
+        strlist_sort(&subdirs);
+        STRLIST_FOREACH(&subdirs, path, {
+            printf("\n%s:\n", path);
+            listdir(path, flags);
+        });
+        strlist_done(&subdirs);
     }
 
     closedir(d);
@@ -331,27 +467,40 @@
     if(argc > 1) {
         int i;
         int err = 0;
+        strlist_t  files = STRLIST_INITIALIZER;
 
         for (i = 1; i < argc; i++) {
-            if (!strcmp(argv[i], "-l")) {
-                flags |= LIST_LONG;
-            } else if (!strcmp(argv[i], "-s")) {
-                flags |= LIST_SIZE;
-            } else if (!strcmp(argv[i], "-a")) {
-                flags |= LIST_ALL;
-            } else if (!strcmp(argv[i], "-R")) {
-                flags |= LIST_RECURSIVE;
-            } else if (!strcmp(argv[i], "-d")) {
-                flags |= LIST_DIRECTORIES;
-            } else {
-                listed++;
-                if(listpath(argv[i], flags) != 0) {
-                    err = EXIT_FAILURE;
+            if (argv[i][0] == '-') {
+                /* an option ? */
+                const char *arg = argv[i]+1;
+                while (arg[0]) {
+                    switch (arg[0]) {
+                    case 'l': flags |= LIST_LONG; break;
+                    case 's': flags |= LIST_SIZE; break;
+                    case 'R': flags |= LIST_RECURSIVE; break;
+                    case 'd': flags |= LIST_DIRECTORIES; break;
+                    case 'a': flags |= LIST_ALL; break;
+                    default:
+                        fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]);
+                        exit(1);
+                    }
+                    arg++;
                 }
+            } else {
+                /* not an option ? */
+                strlist_append_dup(&files, argv[i]);
             }
         }
 
-        if (listed  > 0) return err;
+        if (files.count > 0) {
+            STRLIST_FOREACH(&files, path, {
+                if (listpath(path, flags) != 0) {
+                    err = EXIT_FAILURE;
+                }
+            });
+            strlist_done(&files);
+            return err;
+        }
     }
     
     // list working directory if no files or directories were specified    
diff --git a/toolbox/mkdir.c b/toolbox/mkdir.c
index 121adab..656970a 100644
--- a/toolbox/mkdir.c
+++ b/toolbox/mkdir.c
@@ -2,10 +2,14 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <sys/limits.h>
+#include <sys/stat.h>
 
 static int usage()
 {
-    fprintf(stderr,"mkdir <target>\n");
+    fprintf(stderr,"mkdir [OPTION] <target>\n");
+    fprintf(stderr,"    --help           display usage and exit\n");
+    fprintf(stderr,"    -p, --parents    create parent directories as needed\n");
     return -1;
 }
 
@@ -13,15 +17,60 @@
 {
     int symbolic = 0;
     int ret;
-    if(argc < 2) return usage();
+    if(argc < 2 || strcmp(argv[1], "--help") == 0) {
+        return usage();
+    }
+
+    int recursive = (strcmp(argv[1], "-p") == 0 ||
+                     strcmp(argv[1], "--parents") == 0) ? 1 : 0;
+
+    if(recursive && argc < 3) {
+        // -p specified without a path
+        return usage();
+    }
+
+    if(recursive) {
+        argc--;
+        argv++;
+    }
+
+    char currpath[PATH_MAX], *pathpiece;
+    struct stat st;
 
     while(argc > 1) {
         argc--;
         argv++;
-        ret = mkdir(argv[0], 0777);
-        if(ret < 0) {
-            fprintf(stderr, "mkdir failed for %s, %s\n", argv[0], strerror(errno));
-            return ret;
+        if(recursive) {
+            // reset path
+            strcpy(currpath, "");
+            // create the pieces of the path along the way
+            pathpiece = strtok(argv[0], "/");
+            if(argv[0][0] == '/') {
+                // prepend / if needed
+                strcat(currpath, "/");
+            }
+            while(pathpiece != NULL) {
+                if(strlen(currpath) + strlen(pathpiece) + 2/*NUL and slash*/ > PATH_MAX) {
+                    fprintf(stderr, "Invalid path specified: too long\n");
+                    return 1;
+                }
+                strcat(currpath, pathpiece);
+                strcat(currpath, "/");
+                if(stat(currpath, &st) != 0) {
+                    ret = mkdir(currpath, 0777);
+                    if(ret < 0) {
+                        fprintf(stderr, "mkdir failed for %s, %s\n", currpath, strerror(errno));
+                        return ret;
+                    }
+                }
+                pathpiece = strtok(NULL, "/");
+            }
+        } else {
+            ret = mkdir(argv[0], 0777);
+            if(ret < 0) {
+                fprintf(stderr, "mkdir failed for %s, %s\n", argv[0], strerror(errno));
+                return ret;
+            }
         }
     }