auto import from //depot/cupcake/@135843
diff --git a/tools/soslim/Android.mk b/tools/soslim/Android.mk
new file mode 100644
index 0000000..60a860a
--- /dev/null
+++ b/tools/soslim/Android.mk
@@ -0,0 +1,49 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for soslim
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifeq ($(TARGET_ARCH),arm)
+include $(CLEAR_VARS)
+
+LOCAL_LDLIBS += -ldl
+LOCAL_CFLAGS += -O2 -g
+LOCAL_CFLAGS += -fno-function-sections -fno-data-sections -fno-inline
+LOCAL_CFLAGS += -Wall -Wno-unused-function #-Werror
+LOCAL_CFLAGS += -DBIG_ENDIAN=1
+LOCAL_CFLAGS += -DARM_SPECIFIC_HACKS
+LOCAL_CFLAGS += -DSUPPORT_ANDROID_PRELINK_TAGS
+LOCAL_CFLAGS += -DDEBUG
+LOCAL_CFLAGS += -DSTRIP_STATIC_SYMBOLS
+LOCAL_CFLAGS += -DMOVE_SECTIONS_IN_RANGES
+
+ifeq ($(HOST_OS),windows)
+# Cygwin stat does not support ACCESSPERMS bitmask
+LOCAL_CFLAGS += -DACCESSPERMS=0777
+LOCAL_LDLIBS += -lintl
+endif
+
+LOCAL_SRC_FILES := \
+        cmdline.c \
+        common.c \
+        debug.c \
+        soslim.c \
+        main.c \
+        prelink_info.c \
+        symfilter.c
+
+LOCAL_C_INCLUDES:= \
+	$(LOCAL_PATH)/ \
+	external/elfutils/lib/ \
+	external/elfutils/libelf/ \
+	external/elfutils/libebl/ \
+	external/elfcopy/
+
+LOCAL_STATIC_LIBRARIES := libelfcopy libelf libebl libebl_arm #dl
+
+LOCAL_MODULE := soslim
+
+include $(BUILD_HOST_EXECUTABLE)
+endif #TARGET_ARCH==arm
diff --git a/tools/soslim/cmdline.c b/tools/soslim/cmdline.c
new file mode 100644
index 0000000..c2d5e71
--- /dev/null
+++ b/tools/soslim/cmdline.c
@@ -0,0 +1,141 @@
+#include <debug.h>
+#include <cmdline.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+static struct option long_options[] =
+{
+    {"verbose",  no_argument,       0, 'V'},
+    {"quiet",    no_argument,       0, 'Q'},
+    {"shady",    no_argument,       0, 'S'},
+    {"print",    no_argument,       0, 'p'},
+    {"help",     no_argument,       0, 'h'},
+    {"outfile",  required_argument, 0, 'o'},
+    {"filter",   required_argument, 0, 'f'},
+    {"dry",      no_argument,       0, 'n'},
+    {"strip",    no_argument,       0, 's'},
+    {0, 0, 0, 0},
+};
+
+/* This array must parallel long_options[] */
+static
+const char *descriptions[sizeof(long_options)/sizeof(long_options[0])] = {
+	"print verbose output",
+    "suppress errors and warnings",
+    "patch ABS symbols whose values coincide with section starts and ends",
+    "print the symbol table (if specified, only -V is allowed)",
+    "this help screen",
+    "specify an output file (if not provided, input file is modified)",
+    "specify a symbol-filter file",
+    "dry run (perform all calculations but do not modify the ELF file)",
+    "strip debug sections, if they are present"
+};
+
+void print_help(void)
+{
+    fprintf(stdout,
+			"invokation:\n"
+			"\tsoslim file1 [file2 file3 ... fileN] [-Ldir1 -Ldir2 ... -LdirN] "
+			"[-Vpn]\n"
+			"or\n"
+			"\tsoslim -h\n\n");
+	fprintf(stdout, "options:\n");
+	struct option *opt = long_options;
+	const char **desc = descriptions;
+	while (opt->name) {
+		fprintf(stdout, "\t-%c/--%-15s %s\n",
+				opt->val,
+				opt->name,
+				*desc);
+		opt++;
+		desc++;
+	}
+}
+
+int get_options(int argc, char **argv,
+                char **outfile,
+                char **symsfile,
+                int *print_symtab,
+                int *verbose,
+                int *quiet,
+                int *shady,
+                int *dry_run,
+                int *strip_debug)
+{
+    int c;
+
+    ASSERT(outfile);
+    *outfile = NULL;
+    ASSERT(symsfile);
+    *symsfile = NULL;
+    ASSERT(print_symtab);
+    *print_symtab = 0;
+    ASSERT(verbose);
+    *verbose = 0;
+    ASSERT(quiet);
+    *quiet = 0;
+    ASSERT(shady);
+    *shady = 0;
+    ASSERT(dry_run);
+    *dry_run = 0;
+    ASSERT(strip_debug);
+    *strip_debug = 0;
+
+    while (1) {
+        /* getopt_long stores the option index here. */
+        int option_index = 0;
+
+        c = getopt_long (argc, argv,
+                         "QVSphi:o:y:Y:f:ns",
+                         long_options,
+                         &option_index);
+        /* Detect the end of the options. */
+        if (c == -1) break;
+
+        if (isgraph(c)) {
+            INFO ("option -%c with value `%s'\n", c, (optarg ?: "(null)"));
+        }
+
+#define SET_STRING_OPTION(name) do { \
+    ASSERT(optarg);                  \
+    *name = strdup(optarg);          \
+} while(0)
+
+        switch (c) {
+        case 0:
+            /* If this option set a flag, do nothing else now. */
+            if (long_options[option_index].flag != 0)
+                break;
+            INFO ("option %s", long_options[option_index].name);
+            if (optarg)
+                INFO (" with arg %s", optarg);
+            INFO ("\n");
+            break;
+        case 'p': *print_symtab = 1; break;
+        case 'h': print_help(); exit(1); break;
+        case 'V': *verbose = 1; break;
+        case 'Q': *quiet = 1; break;
+        case 'S': *shady = 1; break;
+        case 'n': *dry_run = 1; break;
+        case 's': *strip_debug = 1; break;
+        case 'o': SET_STRING_OPTION(outfile); break;
+        case 'f': SET_STRING_OPTION(symsfile); break;
+        case '?':
+            /* getopt_long already printed an error message. */
+            break;
+
+#undef SET_STRING_OPTION
+
+        default:
+            FAILIF(1, "Unknown option");
+        }
+    }
+
+    return optind;
+}
diff --git a/tools/soslim/cmdline.h b/tools/soslim/cmdline.h
new file mode 100644
index 0000000..bfc431e
--- /dev/null
+++ b/tools/soslim/cmdline.h
@@ -0,0 +1,16 @@
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+void print_help(void);
+
+int get_options(int argc, char **argv,
+                char **outfile,
+                char **symsfile,
+                int *print_symtab,
+                int *verbose,
+                int *quiet,
+                int *shady,
+                int *dry_run,
+                int *strip_debug);
+
+#endif/*CMDLINE_H*/
diff --git a/tools/soslim/common.c b/tools/soslim/common.c
new file mode 100644
index 0000000..b90cf41
--- /dev/null
+++ b/tools/soslim/common.c
@@ -0,0 +1,35 @@
+#include <stdlib.h>
+#include <common.h>
+#include <debug.h>
+
+void map_over_sections(Elf *elf, 
+                       section_match_fn_t match,
+                       void *user_data)
+{
+    Elf_Scn* section = NULL;
+    while ((section = elf_nextscn(elf, section)) != NULL) {
+        if (match(elf, section, user_data))
+            return;
+    }
+}   
+
+void map_over_segments(Elf *elf, 
+                       segment_match_fn_t match, 
+                       void *user_data)
+{
+    Elf32_Ehdr *ehdr; 
+    Elf32_Phdr *phdr; 
+    int index;
+
+    ehdr = elf32_getehdr(elf);
+    phdr = elf32_getphdr(elf);
+
+    INFO("Scanning over %d program segments...\n", 
+         ehdr->e_phnum);
+
+    for (index = ehdr->e_phnum; index; index--) {
+        if (match(elf, phdr++, user_data))
+            return;
+    }
+}
+
diff --git a/tools/soslim/common.h b/tools/soslim/common.h
new file mode 100644
index 0000000..dacf930
--- /dev/null
+++ b/tools/soslim/common.h
@@ -0,0 +1,49 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <libelf.h>
+#include <elf.h>
+
+#define unlikely(expr) __builtin_expect (expr, 0)
+#define likely(expr)   __builtin_expect (expr, 1)
+
+#define MIN(a,b) ((a)<(b)?(a):(b)) /* no side effects in arguments allowed! */
+
+typedef int (*section_match_fn_t)(Elf *, Elf_Scn *, void *);
+void map_over_sections(Elf *, section_match_fn_t, void *);
+
+typedef int (*segment_match_fn_t)(Elf *, Elf32_Phdr *, void *);
+void map_over_segments(Elf *, segment_match_fn_t, void *);
+
+typedef struct {
+    Elf_Scn *sect;
+    Elf32_Shdr *hdr;
+    Elf_Data *data;
+    size_t index;
+} section_info_t;
+
+static inline void get_section_info(Elf_Scn *sect, section_info_t *info)
+{
+    info->sect = sect;
+    info->data = elf_getdata(sect, 0);
+    info->hdr = elf32_getshdr(sect);
+    info->index = elf_ndxscn(sect);
+}
+
+static inline int is_host_little(void)
+{
+    short val = 0x10;
+    return ((char *)&val)[0] != 0;
+}
+
+static inline long switch_endianness(long val)
+{
+	long newval;
+	((char *)&newval)[3] = ((char *)&val)[0];
+	((char *)&newval)[2] = ((char *)&val)[1];
+	((char *)&newval)[1] = ((char *)&val)[2];
+	((char *)&newval)[0] = ((char *)&val)[3];
+	return newval;
+}
+
+#endif/*COMMON_H*/
diff --git a/tools/soslim/debug.c b/tools/soslim/debug.c
new file mode 100644
index 0000000..b8365af
--- /dev/null
+++ b/tools/soslim/debug.c
@@ -0,0 +1,40 @@
+#include <debug.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#if 0
+
+#define NUM_COLS  (32)
+
+int dump_hex_buffer(FILE *s, void *b, size_t len, size_t elsize) {
+    int num_nonprintable = 0;
+    int i, last;
+    char *pchr = (char *)b;
+    fputc('\n', s);
+    for (i = last = 0; i < len; i++) {
+        if (!elsize) {
+            if (i && !(i % 4)) fprintf(s, " ");
+            if (i && !(i % 8)) fprintf(s, " ");
+        } else {
+            if (i && !(i % elsize)) fprintf(s, " ");
+        }
+
+        if (i && !(i % NUM_COLS)) {
+            while (last < i) {
+                if (isprint(pchr[last]))
+                    fputc(pchr[last], s);
+                else {
+                    fputc('.', s);
+                    num_nonprintable++;
+                }
+                last++;
+            }
+            fprintf(s, " (%d)\n", i);
+        }
+        fprintf(s, "%02x", (unsigned char)pchr[i]);
+    }
+    if (i && (i % NUM_COLS)) fputs("\n", s);
+    return num_nonprintable;
+}
+
+#endif
diff --git a/tools/soslim/debug.h b/tools/soslim/debug.h
new file mode 100644
index 0000000..e7a2f9a
--- /dev/null
+++ b/tools/soslim/debug.h
@@ -0,0 +1,88 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <common.h>
+
+#ifdef DEBUG
+
+    #define FAILIF(cond, msg...) do {                        \
+	if (unlikely(cond)) {                                \
+        fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \
+		fprintf(stderr, ##msg);                          \
+		exit(1);                                         \
+	}                                                    \
+} while(0)
+
+/* Debug enabled */
+    #define ASSERT(x) do {                                \
+	if (unlikely(!(x))) {                             \
+		fprintf(stderr,                               \
+				"ASSERTION FAILURE %s:%d: [%s]\n",    \
+				__FILE__, __LINE__, #x);              \
+		exit(1);                                      \
+	}                                                 \
+} while(0)
+
+#else
+
+    #define FAILIF(cond, msg...) do { \
+	if (unlikely(cond)) {         \
+		fprintf(stderr, ##msg);   \
+		exit(1);                  \
+	}                             \
+} while(0)
+
+/* No debug */
+    #define ASSERT(x)   do { } while(0)
+
+#endif/* DEBUG */
+
+#define FAILIF_LIBELF(cond, function) \
+    FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno()));
+
+static inline void *MALLOC(unsigned int size) {
+    void *m = malloc(size);
+    FAILIF(NULL == m, "malloc(%d) failed!\n", size);
+    return m;
+}
+
+static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size) {
+    void *m = calloc(num_entries, entry_size);
+    FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size);
+    return m;
+}
+
+static inline void *REALLOC(void *ptr, unsigned int size) {
+    void *m = realloc(ptr, size);
+    FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size);
+    return m;
+}
+
+static inline void FREE(void *ptr) {
+    free(ptr);
+}
+
+static inline void FREEIF(void *ptr) {
+    if (ptr) FREE(ptr);
+}
+
+#define PRINT(x...)  do {                             \
+    extern int quiet_flag;                            \
+    if(likely(!quiet_flag))                           \
+        fprintf(stdout, ##x);                         \
+} while(0)
+
+#define ERROR(x...) fprintf(stderr, ##x)
+
+#define INFO(x...)  do {                              \
+    extern int verbose_flag;                          \
+    if(unlikely(verbose_flag))                        \
+        fprintf(stdout, ##x);                         \
+} while(0)
+
+/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */
+int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize);
+
+#endif/*DEBUG_H*/
diff --git a/tools/soslim/main.c b/tools/soslim/main.c
new file mode 100644
index 0000000..fa5a315
--- /dev/null
+++ b/tools/soslim/main.c
@@ -0,0 +1,360 @@
+/* TODO:
+   1. check the ARM EABI version--this works for versions 1 and 2.
+   2. use a more-intelligent approach to finding the symbol table, symbol-string
+      table, and the .dynamic section.
+   3. fix the determination of the host and ELF-file endianness
+   4. write the help screen
+*/
+
+#include <stdio.h>
+#include <common.h>
+#include <debug.h>
+#include <hash.h>
+#include <libelf.h>
+#include <elf.h>
+#include <gelf.h>
+#include <cmdline.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <soslim.h>
+#include <symfilter.h>
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+#include <prelink_info.h>
+#endif
+
+/* Flag set by --verbose.  This variable is global as it is accessed by the
+   macro INFO() in multiple compilation unites. */
+int verbose_flag = 0;
+/* Flag set by --quiet.  This variable is global as it is accessed by the
+   macro PRINT() in multiple compilation unites. */
+int quiet_flag = 0;
+static void print_dynamic_symbols(Elf *elf, const char *symtab_name);
+
+int main(int argc, char **argv)
+{
+    int elf_fd = -1, newelf_fd = -1;
+    Elf *elf = NULL, *newelf = NULL;
+    char *infile = NULL;
+    char *outfile = NULL;
+    char *symsfile_name = NULL;
+    int print_symtab = 0;
+    int shady = 0;
+    int dry_run = 0;
+    int strip_debug = 0;
+
+    /* Do not issue INFO() statements before you call get_options() to set
+       the verbose flag as necessary.
+    */
+
+    int first = get_options(argc, argv,
+                            &outfile,
+                            &symsfile_name,
+                            &print_symtab,
+                            &verbose_flag,
+                            &quiet_flag,
+                            &shady,
+                            &dry_run,
+                            &strip_debug);
+
+    if ((print_symtab && (first == argc)) ||
+        (!print_symtab && first + 1 != argc)) {
+        print_help();
+        FAILIF(1,  "You must specify an input ELF file!\n");
+    }
+    FAILIF(print_symtab && (outfile || symsfile_name || shady),
+           "You cannot provide --print and --outfile, --filter options, or "
+           "--shady simultaneously!\n");
+    FAILIF(dry_run && outfile,
+           "You cannot have a dry run and output a file at the same time.");
+
+    /* Check to see whether the ELF library is current. */
+    FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n");
+
+    if (print_symtab) {
+
+        while (first < argc) {
+            infile = argv[first++];
+
+            INFO("Opening %s...\n", infile);
+            elf_fd = open(infile, O_RDONLY);
+            FAILIF(elf_fd < 0, "open(%s): %s (%d)\n",
+                   infile,
+                   strerror(errno),
+                   errno);
+            INFO("Calling elf_begin(%s)...\n", infile);
+            elf = elf_begin(elf_fd, ELF_C_READ, NULL);
+            FAILIF_LIBELF(elf == NULL, elf_begin);
+
+            /* libelf can recognize COFF and A.OUT formats, but we handle only
+               ELF. */
+            FAILIF(elf_kind(elf) != ELF_K_ELF,
+                   "Input file %s is not in ELF format!\n",
+                   infile);
+
+            /* Make sure this is a shared library or an executable. */
+            {
+                GElf_Ehdr elf_hdr;
+                INFO("Making sure %s is a shared library or an executable.\n",
+                     infile);
+                FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr);
+                FAILIF(elf_hdr.e_type != ET_DYN &&
+                       elf_hdr.e_type != ET_EXEC,
+                       "%s must be a shared library or an executable "
+                       "(elf type is %d).\n",
+                       infile,
+                       elf_hdr.e_type);
+            }
+
+            print_dynamic_symbols(elf, infile);
+
+            FAILIF_LIBELF(elf_end(elf), elf_end);
+            FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
+                   infile, strerror(errno), errno);
+        }
+    }
+    else {
+        int elf_fd = -1;
+        Elf *elf = NULL;
+        infile = argv[first];
+
+        INFO("Opening %s...\n", infile);
+        elf_fd = open(infile, ((outfile == NULL && dry_run == 0) ? O_RDWR : O_RDONLY));
+        FAILIF(elf_fd < 0, "open(%s): %s (%d)\n",
+               infile,
+               strerror(errno),
+               errno);
+        INFO("Calling elf_begin(%s)...\n", infile);
+        elf = elf_begin(elf_fd,
+                        ((outfile == NULL && dry_run == 0) ? ELF_C_RDWR : ELF_C_READ),
+                        NULL);
+        FAILIF_LIBELF(elf == NULL, elf_begin);
+
+        /* libelf can recognize COFF and A.OUT formats, but we handle only ELF. */
+        FAILIF(elf_kind(elf) != ELF_K_ELF,
+               "Input file %s is not in ELF format!\n",
+               infile);
+
+        /* We run a better check in adjust_elf() itself.  It is permissible to call adjust_elf()
+           on an executable if we are only stripping sections from the executable, not rearranging
+           or moving sections.
+        */
+        if (0) {
+            /* Make sure this is a shared library. */
+            GElf_Ehdr elf_hdr;
+            INFO("Making sure %s is a shared library...\n", infile);
+            FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr);
+            FAILIF(elf_hdr.e_type != ET_DYN,
+                   "%s must be a shared library (elf type is %d, expecting %d).\n",
+                   infile,
+                   elf_hdr.e_type,
+                   ET_DYN);
+        }
+
+        if (outfile != NULL) {
+            ASSERT(!dry_run);
+            struct stat st;
+            FAILIF(fstat (elf_fd, &st) != 0,
+                   "Cannot stat input file %s: %s (%d)!\n",
+                   infile, strerror(errno), errno);
+            newelf_fd = open (outfile, O_RDWR | O_CREAT | O_TRUNC,
+                    st.st_mode & ACCESSPERMS);
+            FAILIF(newelf_fd < 0, "Cannot create file %s: %s (%d)!\n",
+                   outfile, strerror(errno), errno);
+            INFO("Output file is [%s].\n", outfile);
+            newelf = elf_begin(newelf_fd, ELF_C_WRITE_MMAP, NULL);
+        } else {
+            INFO("Modifying [%s] in-place.\n", infile);
+            newelf = elf_clone(elf, ELF_C_EMPTY);
+        }
+
+        symfilter_t symfilter;
+
+        symfilter.symbols_to_keep = NULL;
+        symfilter.num_symbols_to_keep = 0;
+        if (symsfile_name) {
+            /* Make sure that the file is not empty. */
+            struct stat s;
+            FAILIF(stat(symsfile_name, &s) < 0,
+                   "Cannot stat file %s.\n", symsfile_name);
+            if (s.st_size) {
+                INFO("Building symbol filter.\n");
+                build_symfilter(symsfile_name, elf, &symfilter, s.st_size);
+            }
+            else INFO("Not building symbol filter, filter file is empty.\n");
+        }
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+        int prelinked = 0;
+        int elf_little; /* valid if prelinked != 0 */
+        long prelink_addr; /* valid if prelinked != 0 */
+#endif
+        clone_elf(elf, newelf,
+                  infile, outfile,
+                  symfilter.symbols_to_keep,
+                  symfilter.num_symbols_to_keep,
+                  shady
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+                  , &prelinked,
+                  &elf_little,
+                  &prelink_addr
+#endif
+                  ,
+                  true, /* rebuild the section-header-strings table */
+                  strip_debug,
+                  dry_run);
+
+        if (symsfile_name && symfilter.symbols_to_keep != NULL) {
+            destroy_symfilter(&symfilter);
+        }
+
+        if (outfile != NULL) INFO("Closing %s...\n", outfile);
+        FAILIF_LIBELF(elf_end (newelf) != 0, elf_end);
+        FAILIF(newelf_fd >= 0 && close(newelf_fd) < 0,
+               "Could not close file %s: %s (%d)!\n",
+               outfile, strerror(errno), errno);
+
+        INFO("Closing %s...\n", infile);
+        FAILIF_LIBELF(elf_end(elf), elf_end);
+        FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
+               infile, strerror(errno), errno);
+
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+        if (prelinked) {
+            INFO("File is prelinked, putting prelink TAG back in place.\n");
+            setup_prelink_info(outfile != NULL ? outfile : infile,
+                               elf_little,
+                               prelink_addr);
+        }
+#endif
+    }
+
+    FREEIF(outfile);
+    return 0;
+}
+
+static void print_dynamic_symbols(Elf *elf, const char *file)
+{
+    Elf_Scn *scn = NULL;
+    GElf_Shdr shdr;
+
+    GElf_Ehdr ehdr;
+    FAILIF_LIBELF(0 == gelf_getehdr(elf, &ehdr), gelf_getehdr);
+    while ((scn = elf_nextscn (elf, scn)) != NULL) {
+        FAILIF_LIBELF(NULL == gelf_getshdr(scn, &shdr), gelf_getshdr);
+        if (SHT_DYNSYM == shdr.sh_type) {
+            /* This failure is too restrictive.  There is no reason why
+               the symbol table couldn't be called something else, but
+               there is a standard name, and chances are that if we don't
+               see it, there's something wrong.
+            */
+            size_t shstrndx;
+            FAILIF_LIBELF(elf_getshstrndx(elf, &shstrndx) < 0,
+                          elf_getshstrndx);
+            /* Now print the symbols. */
+            {
+                Elf_Data *symdata;
+                size_t elsize;
+                symdata = elf_getdata (scn, NULL); /* get the symbol data */
+                FAILIF_LIBELF(NULL == symdata, elf_getdata);
+                /* Get the number of section.  We need to compare agains this
+                   value for symbols that have special info in their section
+                   references */
+                size_t shnum;
+                FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum);
+                /* Retrieve the size of a symbol entry */
+                elsize = gelf_fsize(elf, ELF_T_SYM, 1, ehdr.e_version);
+
+                size_t index;
+                for (index = 0; index < symdata->d_size / elsize; index++) {
+                    GElf_Sym sym_mem;
+                    GElf_Sym *sym;
+                    /* Get the symbol. */
+                    sym = gelf_getsymshndx (symdata, NULL,
+                                            index, &sym_mem, NULL);
+                    FAILIF_LIBELF(sym == NULL, gelf_getsymshndx);
+                    /* Print the symbol. */
+                    char bind = '?';
+                    switch(ELF32_ST_BIND(sym->st_info))
+                    {
+                    case STB_LOCAL: bind = 'l'; break;
+                    case STB_GLOBAL: bind = 'g'; break;
+                    case STB_WEAK: bind = 'w'; break;
+                    default: break;
+                    }
+                    char type = '?';
+                    switch(ELF32_ST_TYPE(sym->st_info))
+                    {
+                    case STT_NOTYPE: /* Symbol type is unspecified */
+                        type = '?';
+                        break;
+                    case STT_OBJECT: /* Symbol is a data object */
+                        type = 'o';
+                        break;
+                    case STT_FUNC: /* Symbol is a code object */
+                        type = 'f';
+                        break;
+                    case STT_SECTION:/* Symbol associated with a section */
+                        type = 's';
+                        break;
+                    case STT_FILE: /* Symbol's name is file name */
+                        type = 'f';
+                        break;
+                    case STT_COMMON: /* Symbol is a common data object */
+                        type = 'c';
+                        break;
+                    case STT_TLS: /* Symbol is thread-local data object*/
+                        type = 't';
+                        break;
+                    }
+                    {
+                        int till_lineno;
+                        int lineno;
+                        const char *section_name = "(unknown)";
+                        FAILIF(sym->st_shndx == SHN_XINDEX,
+                               "Can't handle symbol's st_shndx == SHN_XINDEX!\n");
+                        if (sym->st_shndx != SHN_UNDEF &&
+                            sym->st_shndx < shnum) {
+                            Elf_Scn *symscn = elf_getscn(elf, sym->st_shndx);
+                            FAILIF_LIBELF(NULL == symscn, elf_getscn);
+                            GElf_Shdr symscn_shdr;
+                            FAILIF_LIBELF(NULL == gelf_getshdr(symscn,
+                                                               &symscn_shdr),
+                                          gelf_getshdr);
+                            section_name = elf_strptr(elf, shstrndx,
+                                                      symscn_shdr.sh_name);
+                        }
+                        else if (sym->st_shndx == SHN_ABS) {
+                            section_name = "SHN_ABS";
+                        }
+                        else if (sym->st_shndx == SHN_COMMON) {
+                            section_name = "SHN_COMMON";
+                        }
+                        else if (sym->st_shndx == SHN_UNDEF) {
+                            section_name = "(undefined)";
+                        }
+                        /* value size binding type section symname */
+                        PRINT("%-15s %8d: %08llx %08llx %c%c %5d %n%s%n",
+                              file,
+                              index,
+                              sym->st_value, sym->st_size, bind, type,
+                              sym->st_shndx,
+                              &till_lineno,
+                              section_name,
+                              &lineno);
+                        lineno -= till_lineno;
+                        /* Create padding for section names of 15 chars.
+                           This limit is somewhat arbitratry. */
+                        while (lineno++ < 15) PRINT(" ");
+                        PRINT("(%d) %s\n",
+                              sym->st_name,
+                              elf_strptr(elf, shdr.sh_link, sym->st_name));
+                    }
+                }
+            }
+        } /* if (shdr.sh_type = SHT_DYNSYM) */
+    } /* while ((scn = elf_nextscn (elf, scn)) != NULL) */
+}
diff --git a/tools/soslim/prelink_info.c b/tools/soslim/prelink_info.c
new file mode 100644
index 0000000..36516b1
--- /dev/null
+++ b/tools/soslim/prelink_info.c
@@ -0,0 +1,106 @@
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <prelink_info.h>
+#include <debug.h>
+#include <common.h>
+
+typedef struct {
+	uint32_t mmap_addr;
+	char tag[4]; /* 'P', 'R', 'E', ' ' */
+} prelink_info_t __attribute__((packed));
+
+static inline void set_prelink(long *prelink_addr, 
+							   int elf_little,
+							   prelink_info_t *info)
+{
+    FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+	if (prelink_addr) {
+		if (!(elf_little ^ is_host_little())) {
+			/* Same endianness */
+			*prelink_addr = info->mmap_addr;
+		}
+		else {
+			/* Different endianness */
+			*prelink_addr = switch_endianness(info->mmap_addr);
+		}
+	}
+}
+
+int check_prelinked(const char *fname, int elf_little, long *prelink_addr)
+{
+    FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+	int fd = open(fname, O_RDONLY);
+	FAILIF(fd < 0, "open(%s, O_RDONLY): %s (%d)!\n",
+		   fname, strerror(errno), errno);
+	off_t end = lseek(fd, 0, SEEK_END);
+
+    int nr = sizeof(prelink_info_t);
+
+    off_t sz = lseek(fd, -nr, SEEK_CUR);
+	ASSERT((long)(end - sz) == (long)nr);
+	FAILIF(sz == (off_t)-1, 
+		   "lseek(%d, 0, SEEK_END): %s (%d)!\n", 
+		   fd, strerror(errno), errno);
+
+	prelink_info_t info;
+	int num_read = read(fd, &info, nr);
+	FAILIF(num_read < 0, 
+		   "read(%d, &info, sizeof(prelink_info_t)): %s (%d)!\n",
+		   fd, strerror(errno), errno);
+	FAILIF(num_read != sizeof(info),
+		   "read(%d, &info, sizeof(prelink_info_t)): did not read %d bytes as "
+		   "expected (read %d)!\n",
+		   fd, sizeof(info), num_read);
+
+	int prelinked = 0;
+	if (!strncmp(info.tag, "PRE ", 4)) {
+		set_prelink(prelink_addr, elf_little, &info);
+		prelinked = 1;
+	}
+	FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+	return prelinked;
+}
+
+void setup_prelink_info(const char *fname, int elf_little, long base)
+{
+    FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+    int fd = open(fname, O_WRONLY);
+    FAILIF(fd < 0, 
+           "open(%s, O_WRONLY): %s (%d)\n" ,
+           fname, strerror(errno), errno);
+    prelink_info_t info;
+    off_t sz = lseek(fd, 0, SEEK_END);
+    FAILIF(sz == (off_t)-1, 
+           "lseek(%d, 0, SEEK_END): %s (%d)!\n", 
+           fd, strerror(errno), errno);
+
+    if (!(elf_little ^ is_host_little())) {
+        /* Same endianness */
+        INFO("Host and ELF file [%s] have same endianness.\n", fname);
+        info.mmap_addr = base;
+    }
+    else {
+        /* Different endianness */
+        INFO("Host and ELF file [%s] have different endianness.\n", fname);
+		info.mmap_addr = switch_endianness(base);
+    }
+    strncpy(info.tag, "PRE ", 4);
+
+    int num_written = write(fd, &info, sizeof(info));
+    FAILIF(num_written < 0, 
+           "write(%d, &info, sizeof(info)): %s (%d)\n",
+           fd, strerror(errno), errno);
+    FAILIF(sizeof(info) != num_written, 
+           "Could not write %d bytes (wrote only %d bytes) as expected!\n",
+           sizeof(info), num_written);
+    FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+}
+
+#endif /*SUPPORT_ANDROID_PRELINK_TAGS*/
diff --git a/tools/soslim/prelink_info.h b/tools/soslim/prelink_info.h
new file mode 100644
index 0000000..e2787cb
--- /dev/null
+++ b/tools/soslim/prelink_info.h
@@ -0,0 +1,9 @@
+#ifndef PRELINK_INFO_H
+#define PRELINK_INFO_H
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+
+int check_prelinked(const char *fname, int elf_little, long *prelink_addr);
+void setup_prelink_info(const char *fname, int elf_little, long base);
+
+#endif
+#endif/*PRELINK_INFO_H*/
diff --git a/tools/soslim/soslim.c b/tools/soslim/soslim.c
new file mode 100644
index 0000000..4e59c24
--- /dev/null
+++ b/tools/soslim/soslim.c
@@ -0,0 +1,528 @@
+#include <stdio.h>
+//#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <libebl.h>
+#include <libebl_arm.h>
+#include <elf.h>
+#include <gelf.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+#include <prelink_info.h>
+#endif
+
+#include <elfcopy.h>
+
+void clone_elf(Elf *elf, Elf *newelf,
+               const char *elf_name,
+               const char *newelf_name,
+               bool *sym_filter, int num_symbols,
+               int shady
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+               , int *prelinked,
+               int *elf_little,
+               long *prelink_addr
+#endif
+               , bool rebuild_shstrtab,
+               bool strip_debug,
+               bool dry_run)
+{
+	GElf_Ehdr ehdr_mem, *ehdr; /* store ELF header of original library */
+	size_t shstrndx; /* section-strings-section index */
+	size_t shnum; /* number of sections in the original file */
+	/* string table for section headers in new file */
+	struct Ebl_Strtab *shst = NULL;
+    int dynamic_idx = -1; /* index in shdr_info[] of .dynamic section */
+    int dynsym_idx = -1; /* index in shdr_info[] of dynamic symbol table
+                            section */
+
+    int cnt;	  /* general-purpose counter */
+    /* This flag is true when at least one section is dropped or when the
+       relative order of sections has changed, so that section indices in
+       the resulting file will be different from those in the original. */
+    bool sections_dropped_or_rearranged;
+	Elf_Scn *scn; /* general-purpose section */
+	size_t idx;	  /* general-purporse section index */
+
+	shdr_info_t *shdr_info = NULL;
+    int shdr_info_len = 0;
+    GElf_Phdr *phdr_info = NULL;
+
+	/* Get the information from the old file. */
+	ehdr = gelf_getehdr (elf, &ehdr_mem);
+	FAILIF_LIBELF(NULL == ehdr, gelf_getehdr);
+
+	/* Create new program header for the elf file */
+	FAILIF(gelf_newehdr (newelf, gelf_getclass (elf)) == 0 ||
+		   (ehdr->e_type != ET_REL && gelf_newphdr (newelf,
+													ehdr->e_phnum) == 0),
+		   "Cannot create new file: %s", elf_errmsg (-1));
+
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+    ASSERT(prelinked);
+    ASSERT(prelink_addr);
+    ASSERT(elf_little);
+    *elf_little = (ehdr->e_ident[EI_DATA] == ELFDATA2LSB);
+    *prelinked = check_prelinked(elf_name, *elf_little, prelink_addr);
+#endif
+
+    INFO("\n\nCALCULATING MODIFICATIONS\n\n");
+
+	/* Copy out the old program header: notice that if the ELF file does not
+	   have a program header, this loop won't execute.
+	*/
+	INFO("Copying ELF program header...\n");
+    phdr_info = (GElf_Phdr *)CALLOC(ehdr->e_phnum, sizeof(GElf_Phdr));
+	for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) {
+		INFO("\tRetrieving entry %d\n", cnt);
+		FAILIF_LIBELF(NULL == gelf_getphdr(elf, cnt, phdr_info + cnt),
+                      gelf_getphdr);
+        /* -- we update the header at the end
+        FAILIF_LIBELF(gelf_update_phdr (newelf, cnt, phdr_info + cnt) == 0,
+                      gelf_update_phdr);
+        */
+	}
+
+    /* Get the section-header strings section.  This section contains the
+	   strings used to name the other sections. */
+	FAILIF_LIBELF(elf_getshstrndx(elf, &shstrndx) < 0, elf_getshstrndx);
+
+	/* Get the number of sections. */
+	FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum);
+	INFO("Original ELF file has %d sections.\n", shnum);
+
+	/* Allocate the section-header-info buffer.  We allocate one more entry
+       for the section-strings section because we regenerate that one and
+       place it at the very end of the file.  Note that just because we create
+       an extra entry in the shdr_info array, it does not mean that we create
+       one more section the header.  We just mark the old section for removal
+       and create one as the last section.
+    */
+	INFO("Allocating section-header info structure (%d) bytes...\n",
+		 shnum*sizeof (shdr_info_t));
+    shdr_info_len = rebuild_shstrtab ? shnum + 1 : shnum;
+	shdr_info = (shdr_info_t *)CALLOC(shdr_info_len, sizeof (shdr_info_t));
+
+	/* Iterate over all the sections and initialize the internal section-info
+	   array...
+	*/
+	INFO("Initializing section-header info structure...\n");
+	/* Gather information about the sections in this file. */
+	scn = NULL;
+	cnt = 1;
+	while ((scn = elf_nextscn (elf, scn)) != NULL) {
+		ASSERT(elf_ndxscn(scn) == cnt);
+		shdr_info[cnt].scn = scn;
+		FAILIF_LIBELF(NULL == gelf_getshdr(scn, &shdr_info[cnt].shdr),
+					  gelf_getshdr);
+
+		/* Get the name of the section. */
+		shdr_info[cnt].name = elf_strptr (elf, shstrndx,
+										  shdr_info[cnt].shdr.sh_name);
+
+		INFO("\tname: %s\n", shdr_info[cnt].name);
+		FAILIF(shdr_info[cnt].name == NULL,
+			   "Malformed file: section %d name is null\n",
+			   cnt);
+
+		/* Mark them as present but not yet investigated.  By "investigating"
+		   sections, we mean that we check to see if by stripping other
+		   sections, the sections under investigation will be compromised.  For
+		   example, if we are removing a section of code, then we want to make
+		   sure that the symbol table does not contain symbols that refer to
+		   this code, so we investigate the symbol table.  If we do find such
+		   symbols, we will not strip the code section.
+		*/
+		shdr_info[cnt].idx = 1;
+
+		/* Remember the shdr.sh_link value.  We need to remember this value
+		   for those sections that refer to other sections.  For example,
+		   we need to remember it for relocation-entry sections, because if
+		   we modify the symbol table that a relocation-entry section is
+		   relative to, then we need to patch the relocation section.  By the
+		   time we get to deciding whether we need to patch the relocation
+		   section, we will have overwritten its header's sh_link field with
+		   a new value.
+		*/
+		shdr_info[cnt].old_shdr = shdr_info[cnt].shdr;
+        INFO("\t\toriginal sh_link: %08d\n", shdr_info[cnt].old_shdr.sh_link);
+        INFO("\t\toriginal sh_addr: %lld\n", shdr_info[cnt].old_shdr.sh_addr);
+        INFO("\t\toriginal sh_offset: %lld\n",
+             shdr_info[cnt].old_shdr.sh_offset);
+        INFO("\t\toriginal sh_size: %lld\n", shdr_info[cnt].old_shdr.sh_size);
+
+        if (shdr_info[cnt].shdr.sh_type == SHT_DYNAMIC) {
+            INFO("\t\tthis is the SHT_DYNAMIC section [%s] at index %d\n",
+                 shdr_info[cnt].name,
+                 cnt);
+            dynamic_idx = cnt;
+        }
+        else if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM) {
+            INFO("\t\tthis is the SHT_DYNSYM section [%s] at index %d\n",
+                 shdr_info[cnt].name,
+                 cnt);
+            dynsym_idx = cnt;
+        }
+
+		FAILIF(shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX,
+			   "Cannot handle sh_type SHT_SYMTAB_SHNDX!\n");
+		FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GROUP,
+			   "Cannot handle sh_type SHT_GROUP!\n");
+		FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GNU_versym,
+			   "Cannot handle sh_type SHT_GNU_versym!\n");
+
+		/* Increment the counter. */
+		++cnt;
+	} /* while */
+
+	/* Get the EBL handling. */
+	Ebl *ebl = ebl_openbackend (elf);
+	FAILIF_LIBELF(NULL == ebl, ebl_openbackend);
+    FAILIF_LIBELF(0 != arm_init(elf, ehdr->e_machine, ebl, sizeof(Ebl)),
+                  arm_init);
+
+    if (strip_debug) {
+
+      /* This will actually strip more than just sections.  It will strip
+         anything not essential to running the image.
+      */
+
+      INFO("Finding debug sections to strip.\n");
+
+      /* Now determine which sections can go away.  The general rule is that
+         all sections which are not used at runtime are stripped out.  But
+         there are a few exceptions:
+
+         - special sections named ".comment" and ".note" are kept
+         - OS or architecture specific sections are kept since we might not
+		 know how to handle them
+         - if a section is referred to from a section which is not removed
+		 in the sh_link or sh_info element it cannot be removed either
+      */
+      for (cnt = 1; cnt < shnum; ++cnt) {
+		/* Check whether the section can be removed.  */
+		if (SECTION_STRIP_P (ebl, elf, ehdr, &shdr_info[cnt].shdr,
+							 shdr_info[cnt].name,
+							 1,	 /* remove .comment sections */
+							 1	 /* remove all debug sections */) ||
+            /* The macro above is broken--check for .comment explicitly */
+            !strcmp(".comment", shdr_info[cnt].name)
+#ifdef ARM_SPECIFIC_HACKS
+            ||
+            /* We ignore this section, that's why we can remove it. */
+            !strcmp(".stack", shdr_info[cnt].name)
+#endif
+            )
+        {
+          /* For now assume this section will be removed.  */
+          INFO("Section [%s] will be stripped from image.\n",
+               shdr_info[cnt].name);
+          shdr_info[cnt].idx = 0;
+		}
+#ifdef STRIP_STATIC_SYMBOLS
+		else if (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) {
+          /* Mark the static symbol table for removal */
+          INFO("Section [%s] (static symbol table) will be stripped from image.\n",
+               shdr_info[cnt].name);
+          shdr_info[cnt].idx = 0;
+          if (shdr_info[shdr_info[cnt].shdr.sh_link].shdr.sh_type ==
+              SHT_STRTAB)
+          {
+            /* Mark the symbol table's string table for removal. */
+            INFO("Section [%s] (static symbol-string table) will be stripped from image.\n",
+                 shdr_info[cnt].name);
+            shdr_info[shdr_info[cnt].shdr.sh_link].idx = 0;
+          }
+          else {
+            ERROR("Expecting the sh_link field of a symbol table to point to"
+                  " associated symbol-strings table!  This is not mandated by"
+                  " the standard, but is a common practice and the only way "
+                  " to know for sure which strings table corresponds to which"
+                  " symbol table!\n");
+          }
+		}
+#endif
+      }
+
+      /* Mark the SHT_NULL section as handled. */
+      shdr_info[0].idx = 2;
+
+      /* Handle exceptions: section groups and cross-references.  We might have
+         to repeat this a few times since the resetting of the flag might
+         propagate.
+      */
+      int exceptions_pass = 0;
+      bool changes;
+      do {
+        changes = false;
+		INFO("\nHandling exceptions, pass %d\n\n", exceptions_pass++);
+		for (cnt = 1; cnt < shnum; ++cnt) {
+          if (shdr_info[cnt].idx == 0) {
+            /* If a relocation section is marked as being removed but the
+               section it is relocating is not, then do not remove the
+               relocation section.
+            */
+            if ((shdr_info[cnt].shdr.sh_type == SHT_REL
+                 || shdr_info[cnt].shdr.sh_type == SHT_RELA)
+                && shdr_info[shdr_info[cnt].shdr.sh_info].idx != 0) {
+              PRINT("\tSection [%s] will not be removed because the "
+                    "section it is relocating (%s) stays.\n",
+                    shdr_info[cnt].name,
+                    shdr_info[shdr_info[cnt].shdr.sh_info].name);
+            }
+          }
+          if (shdr_info[cnt].idx == 1) {
+            INFO("Processing section [%s]...\n", shdr_info[cnt].name);
+
+            /* The content of symbol tables we don't remove must not
+               reference any section which we do remove.  Otherwise
+               we cannot remove the referred section.
+            */
+            if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM ||
+                shdr_info[cnt].shdr.sh_type == SHT_SYMTAB)
+            {
+              Elf_Data *symdata;
+              size_t elsize;
+
+              INFO("\tSection [%s] is a symbol table that's not being"
+                   " removed.\n\tChecking to make sure that no symbols"
+                   " refer to sections that are being removed.\n",
+                   shdr_info[cnt].name);
+
+              /* Make sure the data is loaded.  */
+              symdata = elf_getdata (shdr_info[cnt].scn, NULL);
+              FAILIF_LIBELF(NULL == symdata, elf_getdata);
+
+              /* Go through all symbols and make sure the section they
+                 reference is not removed.  */
+              elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
+
+              /* Check the length of the dynamic-symbol filter. */
+              FAILIF(sym_filter != NULL &&
+                     num_symbols != symdata->d_size / elsize,
+                     "Length of dynsym filter (%d) must equal the number"
+                     " of dynamic symbols (%d)!\n",
+                     num_symbols,
+                     symdata->d_size / elsize);
+
+              size_t inner;
+              for (inner = 0;
+                   inner < symdata->d_size / elsize;
+                   ++inner)
+              {
+                GElf_Sym sym_mem;
+                GElf_Sym *sym;
+                size_t scnidx;
+
+                sym = gelf_getsymshndx (symdata, NULL,
+                                        inner, &sym_mem, NULL);
+                FAILIF_LIBELF(sym == NULL, gelf_getsymshndx);
+
+                scnidx = sym->st_shndx;
+                FAILIF(scnidx == SHN_XINDEX,
+                       "Can't handle SHN_XINDEX!\n");
+                if (scnidx == SHN_UNDEF ||
+                    scnidx >= shnum ||
+                    (scnidx >= SHN_LORESERVE &&
+                     scnidx <= SHN_HIRESERVE) ||
+                    GELF_ST_TYPE (sym->st_info) == STT_SECTION)
+                {
+                  continue;
+                }
+
+                /* If the symbol is going to be thrown and it is a
+                   global or weak symbol that is defined (not imported),
+                   then continue.  Since the symbol is going away, we
+                   do not care  whether it refers to a section that is
+                   also going away.
+                */
+                if (sym_filter && !sym_filter[inner])
+                {
+                  bool global_or_weak =
+                      ELF32_ST_BIND(sym->st_info) == STB_GLOBAL ||
+                      ELF32_ST_BIND(sym->st_info) == STB_WEAK;
+                  if (!global_or_weak && sym->st_shndx != SHN_UNDEF)
+                    continue;
+                }
+
+                /* -- far too much output
+                   INFO("\t\t\tSymbol [%s] (%d)\n",
+                   elf_strptr(elf,
+                   shdr_info[cnt].shdr.sh_link,
+                   sym->st_name),
+                   shdr_info[cnt].shdr.sh_info);
+                */
+
+                if (shdr_info[scnidx].idx == 0)
+                {
+                  PRINT("\t\t\tSymbol [%s] refers to section [%s], "
+                        "which is being removed.  Will keep that "
+                        "section.\n",
+                        elf_strptr(elf,
+                                   shdr_info[cnt].shdr.sh_link,
+                                   sym->st_name),
+                        shdr_info[scnidx].name);
+                  /* Mark this section as used.  */
+                  shdr_info[scnidx].idx = 1;
+                  changes |= scnidx < cnt;
+                }
+              } /* for each symbol */
+            } /* section type is SHT_DYNSYM or SHT_SYMTAB */
+            /* Cross referencing happens:
+			   - for the cases the ELF specification says.  That are
+			   + SHT_DYNAMIC in sh_link to string table
+			   + SHT_HASH in sh_link to symbol table
+			   + SHT_REL and SHT_RELA in sh_link to symbol table
+			   + SHT_SYMTAB and SHT_DYNSYM in sh_link to string table
+			   + SHT_GROUP in sh_link to symbol table
+			   + SHT_SYMTAB_SHNDX in sh_link to symbol table
+			   Other (OS or architecture-specific) sections might as
+			   well use this field so we process it unconditionally.
+			   - references inside section groups
+			   - specially marked references in sh_info if the SHF_INFO_LINK
+			   flag is set
+            */
+
+            if (shdr_info[shdr_info[cnt].shdr.sh_link].idx == 0) {
+              shdr_info[shdr_info[cnt].shdr.sh_link].idx = 1;
+              changes |= shdr_info[cnt].shdr.sh_link < cnt;
+            }
+
+            /* Handle references through sh_info.  */
+            if (SH_INFO_LINK_P (&shdr_info[cnt].shdr) &&
+                shdr_info[shdr_info[cnt].shdr.sh_info].idx == 0) {
+              PRINT("\tSection [%s] links to section [%s], which was "
+                    "marked for removal--it will not be removed.\n",
+                    shdr_info[cnt].name,
+                    shdr_info[shdr_info[cnt].shdr.sh_info].name);
+
+              shdr_info[shdr_info[cnt].shdr.sh_info].idx = 1;
+              changes |= shdr_info[cnt].shdr.sh_info < cnt;
+            }
+
+            /* Mark the section as investigated.  */
+            shdr_info[cnt].idx = 2;
+          } /* if (shdr_info[cnt].idx == 1) */
+		} /* for (cnt = 1; cnt < shnum; ++cnt) */
+      } while (changes);
+    }
+    else {
+      INFO("Not stripping sections.\n");
+      /* Mark the SHT_NULL section as handled. */
+      shdr_info[0].idx = 2;
+    }
+
+	/* Mark the section header string table as unused, we will create
+	   a new one as the very last section in the new ELF file.
+	*/
+	shdr_info[shstrndx].idx = rebuild_shstrtab ? 0 : 2;
+
+	/* We need a string table for the section headers. */
+	FAILIF_LIBELF((shst = ebl_strtabinit (1	/* null-terminated */)) == NULL,
+				  ebl_strtabinit);
+
+	/* Assign new section numbers. */
+	INFO("Creating new sections...\n");
+	//shdr_info[0].idx = 0;
+	for (cnt = idx = 1; cnt < shnum; ++cnt) {
+		if (shdr_info[cnt].idx > 0) {
+			shdr_info[cnt].idx = idx++;
+
+			/* Create a new section. */
+			FAILIF_LIBELF((shdr_info[cnt].newscn =
+						   elf_newscn(newelf)) == NULL, elf_newscn);
+			ASSERT(elf_ndxscn (shdr_info[cnt].newscn) == shdr_info[cnt].idx);
+
+			/* Add this name to the section header string table. */
+			shdr_info[cnt].se = ebl_strtabadd (shst, shdr_info[cnt].name, 0);
+
+			INFO("\tsection [%s]  (old offset %lld, old size %lld) will have index %d "
+				 "(was %d).\n",
+				 shdr_info[cnt].name,
+				 shdr_info[cnt].old_shdr.sh_offset,
+				 shdr_info[cnt].old_shdr.sh_size,
+				 shdr_info[cnt].idx,
+				 elf_ndxscn(shdr_info[cnt].scn));
+		} else {
+			INFO("\tIgnoring section [%s] (offset %lld, size %lld, index %d), "
+				 "it will be discarded.\n",
+				 shdr_info[cnt].name,
+				 shdr_info[cnt].shdr.sh_offset,
+				 shdr_info[cnt].shdr.sh_size,
+				 elf_ndxscn(shdr_info[cnt].scn));
+		}
+	} /* for */
+
+    sections_dropped_or_rearranged = idx != cnt;
+
+    Elf_Data *shstrtab_data = NULL;
+
+#if 0
+    /* Fail if sections are being dropped or rearranged (except for moving shstrtab) or the
+       symbol filter is not empty, AND the file is an executable.
+    */
+    FAILIF(((idx != cnt && !(cnt - idx == 1 && rebuild_shstrtab)) || sym_filter != NULL) &&
+           ehdr->e_type != ET_DYN,
+           "You may not rearrange sections or strip symbols on an executable file!\n");
+#endif
+
+    INFO("\n\nADJUSTING ELF FILE\n\n");
+
+    adjust_elf(elf, elf_name,
+               newelf, newelf_name,
+               ebl,
+               ehdr, /* store ELF header of original library */
+               sym_filter, num_symbols,
+               shdr_info, shdr_info_len,
+               phdr_info,
+               idx, /* highest_scn_num */
+               shnum,
+               shstrndx,
+               shst,
+               sections_dropped_or_rearranged,
+               dynamic_idx, /* index in shdr_info[] of .dynamic section */
+               dynsym_idx, /* index in shdr_info[] of dynamic symbol table */
+               shady,
+               &shstrtab_data,
+               ehdr->e_type == ET_DYN, /* adjust section ofsets only when the file is a shared library */
+               rebuild_shstrtab);
+
+    /* We have everything from the old file. */
+	FAILIF_LIBELF(elf_cntl(elf, ELF_C_FDDONE) != 0, elf_cntl);
+
+	/* The ELF library better follows our layout when this is not a
+	   relocatable object file. */
+	elf_flagelf (newelf,
+				 ELF_C_SET,
+				 (ehdr->e_type != ET_REL ? ELF_F_LAYOUT : 0));
+
+	/* Finally write the file. */
+    FAILIF_LIBELF(!dry_run && elf_update(newelf, ELF_C_WRITE) == -1, elf_update);
+
+	if (shdr_info != NULL) {
+		/* For some sections we might have created an table to map symbol
+           table indices. */
+       for (cnt = 1; cnt < shdr_info_len; ++cnt) {
+            FREEIF(shdr_info[cnt].newsymidx);
+            FREEIF(shdr_info[cnt].symse);
+            if(shdr_info[cnt].dynsymst != NULL)
+                ebl_strtabfree (shdr_info[cnt].dynsymst);
+        }
+		/* Free the memory. */
+		FREE (shdr_info);
+	}
+    FREEIF(phdr_info);
+
+    ebl_closebackend(ebl);
+
+	/* Free other resources. */
+	if (shst != NULL) ebl_strtabfree (shst);
+    if (shstrtab_data != NULL)
+        FREEIF(shstrtab_data->d_buf);
+}
diff --git a/tools/soslim/soslim.h b/tools/soslim/soslim.h
new file mode 100644
index 0000000..dfcb085
--- /dev/null
+++ b/tools/soslim/soslim.h
@@ -0,0 +1,32 @@
+#ifndef ELFCOPY_H
+#define ELFCOPY_H
+
+#include <libelf.h>
+#include <libebl.h>
+#include <elf.h>
+#include <gelf.h>
+
+/*
+symbol_filter:
+	On input: symbol_filter[i] indicates whether to keep a symbol (1) or to
+	          remove it from the symbol table.
+    On output: symbol_filter[i] indicates whether a symbol was removed (0) or
+	           kept (1) in the symbol table.
+*/
+
+void clone_elf(Elf *elf, Elf *newelf,
+			   const char *elf_name,
+			   const char *newelf_name,
+			   bool *symbol_filter,
+			   int num_symbols,
+               int shady
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+			   , int *prelinked,
+			   int *elf_little,
+			   long *prelink_addr
+#endif
+               , bool rebuild_shstrtab,
+               bool strip_debug,
+               bool dry_run);
+
+#endif/*ELFCOPY_H*/
diff --git a/tools/soslim/symfilter.c b/tools/soslim/symfilter.c
new file mode 100644
index 0000000..c21ab2e
--- /dev/null
+++ b/tools/soslim/symfilter.c
@@ -0,0 +1,242 @@
+#include <debug.h>
+#include <common.h>
+#include <symfilter.h>
+#include <hash.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <ctype.h>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static int match_hash_table_section(Elf *elf, Elf_Scn *sect, void *data);
+static int match_dynsym_section(Elf *elf, Elf_Scn *sect, void *data);
+
+void build_symfilter(const char *name, Elf *elf, symfilter_t *filter, 
+                     off_t fsize)
+{
+    char *line = NULL;
+    symfilter_list_t *symbol;
+
+    FAILIF(NULL == name,
+           "You must provide a list of symbols to filter on!\n");
+
+    filter->num_symbols = 0;
+    filter->total_name_length = 0;
+
+    /* Open the file. */
+    INFO("Opening symbol-filter file %s...\n", name);
+    filter->fd = open(name, O_RDONLY);
+    FAILIF(filter->fd < 0, "open(%s): %s (%d)\n",
+           name, 
+           strerror(errno),
+           errno);
+
+    INFO("Symbol-filter file %s is %ld bytes long...\n", 
+         name,
+         fsize);
+    filter->fsize = fsize;
+
+    /* mmap the symbols file */
+    filter->mmap = mmap(NULL, fsize, 
+                        PROT_READ | PROT_WRITE, MAP_PRIVATE, 
+                        filter->fd, 0);
+    FAILIF(MAP_FAILED == filter->mmap, 
+           "mmap(NULL, %ld, PROT_READ, MAP_PRIVATE, %d, 0): %s (%d)\n",
+           fsize,
+           filter->fd,
+           strerror(errno),
+           errno);
+    INFO("Memory-mapped symbol-filter file at %p\n", filter->mmap);
+
+    /* Make sure that the ELF file has a hash table.  We will use the hash 
+       table to look up symbols quickly.  If the library does not have a hash-
+       table section, we can still do a linear scan, but the code for that is
+       not written, as practically every shared library has a hash table.
+    */
+
+    filter->symtab.sect = NULL;
+    map_over_sections(elf, match_dynsym_section, filter);
+    FAILIF(NULL == filter->symtab.sect, 
+           "There is no dynamic-symbol table in this library.\n");
+    filter->hash.sect = NULL;
+    map_over_sections(elf, match_hash_table_section, filter);
+    FAILIF(NULL == filter->hash.sect, 
+           "There is no hash table in this library.\n");
+    INFO("Hash table size 0x%lx, data size 0x%lx.\n",
+         (unsigned long)filter->hash.hdr->sh_size,
+         (unsigned long)filter->hash.data->d_size);
+
+    INFO("Hash table file offset: 0x%x\n", filter->hash.hdr->sh_offset);
+
+    GElf_Ehdr *ehdr, ehdr_mem;
+    ehdr = gelf_getehdr(elf, &ehdr_mem);
+    size_t symsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
+    ASSERT(symsize);
+    filter->num_symbols_to_keep = filter->symtab.data->d_size / symsize;
+    filter->symbols_to_keep = (bool *)CALLOC(filter->num_symbols_to_keep, 
+                                             sizeof(bool));
+
+    /* Build the symbol-name chain. */
+    INFO("Building symbol list...\n");
+    
+    line = (char *)filter->mmap;
+
+    filter->symbols = NULL;
+#define NOT_DONE ((off_t)(line - (char *)filter->mmap) < fsize)
+    do {
+        char *name = line;
+
+        /* Advance to the next line.  We seek out spaces or new lines.  At the
+           first space or newline character we find, we place a '\0', and 
+           continue till we've consumed the line.  For new lines, we scan both
+           '\r' and '\n'.  For spaces, we look for ' ', '\t', and '\f'
+        */
+
+        while (NOT_DONE && !isspace(*line)) line++;
+        if (likely(NOT_DONE)) {
+            *line++ = '\0';
+            if (line - name > 1) {
+                /* Add the entry to the symbol-filter list */
+                symbol = (symfilter_list_t *)MALLOC(sizeof(symfilter_list_t));
+                symbol->next = filter->symbols;
+                symbol->name = name;
+                filter->symbols = symbol;
+
+#if 0 
+                /* SLOW!  For debugging only! */
+                {
+                    size_t idx;
+                    size_t elsize = gelf_fsize(elf, ELF_T_SYM, 1, 
+                                               ehdr->e_version);
+                    symbol->index = SHN_UNDEF;
+                    for (idx = 0; idx < filter->symtab.data->d_size / elsize;
+                         idx++) {
+                        GElf_Sym sym_mem;
+                        GElf_Sym *sym;
+                        const char *symname;
+                        sym = gelf_getsymshndx (filter->symtab.data, NULL, 
+                                                idx, &sym_mem, NULL);
+                        ASSERT(sym);
+
+                        symname = elf_strptr(elf, 
+                                             filter->symtab.hdr->sh_link,
+                                             sym->st_name);
+                        if(!strcmp(symname, symbol->name)) {
+                            symbol->index = idx;
+                            break;
+                        }
+                    }
+                }
+#else
+                /* Look up the symbol in the ELF file and associate it with the
+                   entry in the filter. */
+                symbol->index = hash_lookup(elf,
+                                            &filter->hash,
+                                            &filter->symtab,
+                                            symbol->name,
+                                            &symbol->symbol);
+#endif                                            
+                symbol->len = line - name - 1;
+                ASSERT(symbol->len == strlen(symbol->name));
+
+                /* If we didn't find the symbol, then it's not in the library. 
+                 */
+
+                if(STN_UNDEF == symbol->index) {
+                    PRINT("%s: symbol was not found!\n", symbol->name);
+                }
+                else {
+                    /* If we found the symbol but it's an undefined symbol, then
+                       it's not in the library as well. */
+                    GElf_Sym sym_mem;
+                    GElf_Sym *sym;
+                    sym = gelf_getsymshndx (filter->symtab.data, NULL, 
+                                            symbol->index, &sym_mem, NULL);
+                    FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
+                    /* Make sure the hash lookup worked. */
+                    ASSERT(!strcmp(elf_strptr(elf, 
+                                              filter->symtab.hdr->sh_link,
+                                              sym->st_name),
+                                   symbol->name));
+                    if (sym->st_shndx == SHN_UNDEF) {
+                        PRINT("%s: symbol was not found (undefined)!\n", symbol->name);
+                    }
+                    else {
+                        filter->num_symbols++;
+                        /* Total count includes null terminators */
+                        filter->total_name_length += symbol->len + 1; 
+
+                        /* Set the flag in the symbols_to_keep[] array.  This indicates
+                           to function copy_elf() that we want to keep the symbol.
+                        */
+                        filter->symbols_to_keep[symbol->index] = true;
+                        INFO("FILTER-SYMBOL: [%s] [%d bytes]\n", 
+                             symbol->name, 
+                             symbol->len);
+                    }
+                }
+            }
+        }
+    } while (NOT_DONE);
+#undef NOT_DONE
+}
+
+void destroy_symfilter(symfilter_t *filter)
+{
+    symfilter_list_t *old;
+    INFO("Destroying symbol list...\n");
+    while ((old = filter->symbols)) {
+        filter->symbols = old->next;
+        FREE(old);
+    }
+    munmap(filter->mmap, filter->fsize);
+    close(filter->fd);
+}
+
+static int match_hash_table_section(Elf *elf, Elf_Scn *sect, void *data)
+{
+    symfilter_t *filter = (symfilter_t *)data;
+    Elf32_Shdr *shdr;
+
+    ASSERT(filter);
+    ASSERT(sect);
+    shdr = elf32_getshdr(sect);
+
+    /* The section must be marked both as a SHT_HASH, and it's sh_link field 
+       must contain the index of our symbol table (per ELF-file spec).
+    */
+    if (shdr->sh_type == SHT_HASH)
+    {
+        FAILIF(filter->hash.sect != NULL, 
+               "There is more than one hash table!\n");
+        get_section_info(sect, &filter->hash);
+    }
+
+    return 0; /* keep looking */
+}
+
+static int match_dynsym_section(Elf *elf, Elf_Scn *sect, void *data)
+{
+    symfilter_t *filter = (symfilter_t *)data;
+    Elf32_Shdr *shdr;
+
+    ASSERT(filter);
+    ASSERT(sect);
+    shdr = elf32_getshdr(sect);
+
+    if (shdr->sh_type == SHT_DYNSYM)
+    {
+        FAILIF(filter->symtab.sect != NULL, 
+               "There is more than one dynamic symbol table!\n");
+        get_section_info(sect, &filter->symtab);
+    }
+
+    return 0; /* keep looking */
+}
diff --git a/tools/soslim/symfilter.h b/tools/soslim/symfilter.h
new file mode 100644
index 0000000..f73fd50
--- /dev/null
+++ b/tools/soslim/symfilter.h
@@ -0,0 +1,50 @@
+#ifndef SYMFILTER_H
+#define SYMFILTER_H
+
+/* This file describes the interface for parsing the list of symbols. Currently,
+   this is just a text file with each symbol on a separate line.  We build an
+   in-memory linked list of symbols out of this image.
+*/
+
+#include <stdio.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libebl.h> /* defines bool */
+
+typedef struct symfilter_list_t symfilter_list_t;
+struct symfilter_list_t {
+    symfilter_list_t *next;
+    const char *name;
+    unsigned int len; /* strlen(name) */
+    Elf32_Word index;
+    GElf_Sym symbol;
+};
+
+typedef struct symfilter_t {
+
+    int fd; /* symbol-filter-file descriptor */
+    off_t fsize; /* size of file */
+    void *mmap; /* symbol-fiter-file memory mapping */
+
+    section_info_t symtab;
+    section_info_t hash;
+    symfilter_list_t *symbols;
+
+    /* The total number of symbols in the symfilter. */
+    unsigned int num_symbols;
+    /* The total number of bytes occupied by the names of the symbols, including
+       the terminating null characters.
+    */
+    unsigned int total_name_length;
+
+    bool *symbols_to_keep;
+    /* must be the same as the number of symbols in the dynamic table! */
+    int num_symbols_to_keep;
+} symfilter_t;
+
+void build_symfilter(const char *name, Elf *elf, symfilter_t *filter, off_t);
+void destroy_symfilter(symfilter_t *);
+
+#endif/*SYMFILTER_H*/