summaryrefslogtreecommitdiffstats
path: root/tools/soslim/soslim.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/soslim/soslim.c')
-rw-r--r--tools/soslim/soslim.c528
1 files changed, 528 insertions, 0 deletions
diff --git a/tools/soslim/soslim.c b/tools/soslim/soslim.c
new file mode 100644
index 000000000..4e59c248c
--- /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);
+}