summaryrefslogtreecommitdiffstats
path: root/tools/soslim
diff options
context:
space:
mode:
Diffstat (limited to 'tools/soslim')
-rw-r--r--tools/soslim/Android.mk49
-rw-r--r--tools/soslim/cmdline.c141
-rw-r--r--tools/soslim/cmdline.h16
-rw-r--r--tools/soslim/common.c35
-rw-r--r--tools/soslim/common.h49
-rw-r--r--tools/soslim/debug.c40
-rw-r--r--tools/soslim/debug.h88
-rw-r--r--tools/soslim/main.c360
-rw-r--r--tools/soslim/prelink_info.c106
-rw-r--r--tools/soslim/prelink_info.h9
-rw-r--r--tools/soslim/soslim.c528
-rw-r--r--tools/soslim/soslim.h32
-rw-r--r--tools/soslim/symfilter.c242
-rw-r--r--tools/soslim/symfilter.h50
14 files changed, 1745 insertions, 0 deletions
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*/