diff --git a/tools/elf-dynstr-gc/elf-gc-dynstr.c b/tools/elf-dynstr-gc/elf-gc-dynstr.c index 7764b5f14626..b751f75cdab3 100644 --- a/tools/elf-dynstr-gc/elf-gc-dynstr.c +++ b/tools/elf-dynstr-gc/elf-gc-dynstr.c @@ -1,1082 +1,1082 @@ -/* elf_gc_dynst - * - * This is a program that removes unreferenced strings from the .dynstr - * section in ELF shared objects. It also shrinks the .dynstr section and - * relocates all symbols after it. - * - * This program was written and copyrighted by: - * Alexander Larsson - * - * - * - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - * License for the specific language governing rights and limitations - * under the License. - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include - - -Elf32_Ehdr *elf_header = NULL; -#define FILE_OFFSET(offset) ((unsigned char *)(elf_header) + (offset)) - -struct dynamic_symbol { - Elf32_Word old_index; - Elf32_Word new_index; - char *string; -}; - -GHashTable *used_dynamic_symbols = NULL; -/* Data is dynamic_symbols, hashes on old_index */ -Elf32_Word hole_index; -Elf32_Word hole_end; -Elf32_Word hole_len; - -Elf32_Addr hole_addr_start; -Elf32_Addr hole_addr_remap_start; -Elf32_Addr hole_addr_remap_end; - - -Elf32_Shdr * -elf_find_section_num(int section_index) -{ - Elf32_Shdr *section; - Elf32_Word sectionsize; - - section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); - sectionsize = elf_header->e_shentsize; - - section = (Elf32_Shdr *)((char *)section + sectionsize*section_index); - - return section; -} - -Elf32_Shdr * -elf_find_section_named(char *name) -{ - Elf32_Shdr *section; - Elf32_Shdr *strtab_section; - Elf32_Word sectionsize; - int numsections; - char *strtab; - int i = 0; - - section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); - - strtab_section = elf_find_section_num(elf_header->e_shstrndx); - - strtab = (char *)FILE_OFFSET(strtab_section->sh_offset); - - sectionsize = elf_header->e_shentsize; - numsections = elf_header->e_shnum; - - for (i=0;ish_name], name) == 0) { - return section; - } - section = (Elf32_Shdr *)((char *)section + sectionsize); - } - return NULL; -} - - -Elf32_Shdr * -elf_find_section(Elf32_Word sh_type) -{ - Elf32_Shdr *section; - Elf32_Word sectionsize; - int numsections; - int i = 0; - - section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); - sectionsize = elf_header->e_shentsize; - numsections = elf_header->e_shnum; - - for (i=0;ish_type == sh_type) { - return section; - } - section = (Elf32_Shdr *)((char *)section + sectionsize); - } - return NULL; -} - -Elf32_Shdr * -elf_find_next_higher_section(Elf32_Word offset) -{ - Elf32_Shdr *section; - Elf32_Shdr *higher; - Elf32_Word sectionsize; - int numsections; - int i = 0; - - section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); - sectionsize = elf_header->e_shentsize; - numsections = elf_header->e_shnum; - - higher = NULL; - - for (i=0;ish_offset >= offset) { - if (higher == NULL) { - higher = section; - } else if (section->sh_offset < higher->sh_offset) { - higher = section; - } - } - - section = (Elf32_Shdr *)((char *)section + sectionsize); - } - - return higher; -} - -Elf32_Word -vma_to_offset(Elf32_Addr addr) -{ - Elf32_Shdr *section; - Elf32_Shdr *higher; - Elf32_Word sectionsize; - int numsections; - int i = 0; - - section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); - sectionsize = elf_header->e_shentsize; - numsections = elf_header->e_shnum; - - higher = NULL; - - for (i=0;i= section->sh_addr) && - (addr < section->sh_addr + section->sh_size) ) { - return section->sh_offset + (addr-section->sh_addr); - } - - section = (Elf32_Shdr *)((char *)section + sectionsize); - } - - fprintf(stderr, "Warning, unable to convert address %d (0x%x) to file offset\n", - addr, addr); - return 0; -} - - -void -find_segment_addr_min_max(Elf32_Word file_offset, - Elf32_Addr *start, Elf32_Addr *end) -{ - Elf32_Phdr *segment; - Elf32_Word segmentsize; - int numsegments; - int i = 0; - - segment = (Elf32_Phdr *)FILE_OFFSET(elf_header->e_phoff); - segmentsize = elf_header->e_phentsize; - numsegments = elf_header->e_phnum; - - for (i=0;i= segment->p_offset) && - (file_offset < segment->p_offset + segment->p_filesz)) { - *start = segment->p_vaddr; - *end = segment->p_vaddr + segment->p_memsz; - return; - } - - segment = (Elf32_Phdr *)((char *)segment + segmentsize); - } - fprintf(stderr, "Error: Couldn't find segment in find_segment_addr_min_max()\n"); -} - -void * -dynamic_find_tag(Elf32_Shdr *dynamic, Elf32_Sword d_tag) -{ - int i; - Elf32_Dyn *element; - - element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); - for (i=0; element[i].d_tag != DT_NULL; i++) { - if (element[i].d_tag = d_tag) { - return FILE_OFFSET(element[i].d_un.d_ptr); - } - } - - return NULL; -} - -Elf32_Word -fixup_offset(Elf32_Word offset) -{ - if (offset >= hole_index) { - return offset - hole_len; - } - return offset; -} - -Elf32_Word -fixup_size(Elf32_Word offset, Elf32_Word size) -{ - /* Note: Doesn't handle the cases where the hole and the size intersect - partially. */ - - if ( (hole_index >= offset) && - (hole_index < offset + size)){ - return size - hole_len; - } - - return size; -} - -Elf32_Addr -fixup_addr(Elf32_Addr addr) -{ - if (addr == 0) - return 0; - - /* - if ( (addr < hole_addr_remap_start) || - (addr >= hole_addr_remap_end)) - return addr; - */ - - if (addr >= hole_addr_start) { - return addr - hole_len; - } - return addr; -} - -Elf32_Word -fixup_addr_size(Elf32_Addr addr, Elf32_Word size) -{ - /* Note: Doesn't handle the cases where the hole and the size intersect - partially. */ - /* - if ( (addr < hole_addr_remap_start) || - (addr >= hole_addr_remap_end)) - return size; - */ - if ( (hole_addr_start >= addr) && - (hole_addr_start < addr + size)){ - return size - hole_len; - } - - return size; -} - -void -possibly_add_string(int name_idx, const char *name) -{ - struct dynamic_symbol *dynamic_symbol; - if (name_idx != 0) { - dynamic_symbol = g_hash_table_lookup(used_dynamic_symbols, (gpointer) name_idx); - - if (dynamic_symbol == NULL) { - - dynamic_symbol = g_new(struct dynamic_symbol, 1); - - dynamic_symbol->old_index = name_idx; - dynamic_symbol->new_index = 0; - dynamic_symbol->string = g_strdup(name); - - g_hash_table_insert(used_dynamic_symbols, (gpointer)name_idx, dynamic_symbol); - /*printf("added dynamic string: %s (%d)\n", dynamic_symbol->string, name_idx);*/ - } - } -} - -Elf32_Word -fixup_string(Elf32_Word old_idx) -{ - struct dynamic_symbol *dynamic_symbol; - - if (old_idx == 0) - return 0; - - dynamic_symbol = g_hash_table_lookup(used_dynamic_symbols, (gpointer) old_idx); - - if (dynamic_symbol == NULL) { - fprintf(stderr, "AAAAAAAAAAAARGH!? Unknown string found in fixup (index: %d)!\n", old_idx); - return 0; - } - - return dynamic_symbol->new_index; -} - - - -void -add_strings_from_dynsym(Elf32_Shdr *dynsym, char *strtab) -{ - Elf32_Sym *symbol; - Elf32_Sym *symbol_end; - Elf32_Word entry_size; - - - symbol = (Elf32_Sym *)FILE_OFFSET(dynsym->sh_offset); - symbol_end = (Elf32_Sym *)FILE_OFFSET(dynsym->sh_offset + dynsym->sh_size); - entry_size = dynsym->sh_entsize; - - while (symbol < symbol_end) { - int name_idx; - struct dynamic_symbol *dynamic_symbol; - - name_idx = symbol->st_name; - possibly_add_string(name_idx, &strtab[name_idx]); - - - symbol = (Elf32_Sym *)((char *)symbol + entry_size); - } -} - - -void -fixup_strings_in_dynsym(Elf32_Shdr *dynsym) -{ - Elf32_Sym *symbol; - Elf32_Sym *symbol_end; - Elf32_Word entry_size; - - - symbol = (Elf32_Sym *)FILE_OFFSET(dynsym->sh_offset); - symbol_end = (Elf32_Sym *)FILE_OFFSET(dynsym->sh_offset + dynsym->sh_size); - entry_size = dynsym->sh_entsize; - - while (symbol < symbol_end) { - int name_idx; - struct dynamic_symbol *dynamic_symbol; - - symbol->st_name = fixup_string(symbol->st_name); - - symbol = (Elf32_Sym *)((char *)symbol + entry_size); - } -} - - -void -add_strings_from_dynamic(Elf32_Shdr *dynamic, char *strtab) -{ - int i; - int name_idx; - Elf32_Dyn *element; - Elf32_Word entry_size; - - entry_size = dynamic->sh_entsize; - - - element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); - while (element->d_tag != DT_NULL) { - - switch(element->d_tag) { - case DT_NEEDED: - case DT_SONAME: - case DT_RPATH: - name_idx = element->d_un.d_val; - /*if (name_idx) printf("d_tag: %d\n", element->d_tag);*/ - possibly_add_string(name_idx, &strtab[name_idx]); - break; - default: - /*printf("unhandled d_tag: %d (0x%x)\n", element->d_tag, element->d_tag);*/ - } - - element = (Elf32_Dyn *)((char *)element + entry_size); - } - -} - -void -fixup_strings_in_dynamic(Elf32_Shdr *dynamic) -{ - int i; - int name_idx; - Elf32_Dyn *element; - Elf32_Word entry_size; - - entry_size = dynamic->sh_entsize; - - element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); - while (element->d_tag != DT_NULL) { - - switch(element->d_tag) { - case DT_NEEDED: - case DT_SONAME: - case DT_RPATH: - element->d_un.d_val = fixup_string(element->d_un.d_val); - break; - default: - /*printf("unhandled d_tag: %d (0x%x)\n", element->d_tag, element->d_tag);*/ - } - - element = (Elf32_Dyn *)((char *)element + entry_size); - } - -} - - -void -add_strings_from_ver_d(Elf32_Shdr *ver_d, char *strtab) -{ - Elf32_Verdaux *veraux; - Elf32_Verdef *verdef; - int num_aux; - int name_idx; - int i; - int cont; - - verdef = (Elf32_Verdef *)FILE_OFFSET(ver_d->sh_offset); - - do { - num_aux = verdef->vd_cnt; - veraux = (Elf32_Verdaux *)((char *)verdef + verdef->vd_aux); - for (i=0; ivda_name; - possibly_add_string(name_idx, &strtab[name_idx]); - veraux = (Elf32_Verdaux *)((char *)veraux + veraux->vda_next); - } - - cont = verdef->vd_next != 0; - verdef = (Elf32_Verdef *)((char *)verdef + verdef->vd_next); - } while (cont); - -} - -void -fixup_strings_in_ver_d(Elf32_Shdr *ver_d) -{ - Elf32_Verdaux *veraux; - Elf32_Verdef *verdef; - int num_aux; - int name_idx; - int i; - int cont; - - verdef = (Elf32_Verdef *)FILE_OFFSET(ver_d->sh_offset); - - do { - num_aux = verdef->vd_cnt; - veraux = (Elf32_Verdaux *)((char *)verdef + verdef->vd_aux); - for (i=0; ivda_name = fixup_string(veraux->vda_name); - veraux = (Elf32_Verdaux *)((char *)veraux + veraux->vda_next); - } - - cont = verdef->vd_next != 0; - verdef = (Elf32_Verdef *)((char *)verdef + verdef->vd_next); - } while (cont); - -} - -void -add_strings_from_ver_r(Elf32_Shdr *ver_r, char *strtab) -{ - Elf32_Vernaux *veraux; - Elf32_Verneed *verneed; - int num_aux; - int name_idx; - int i; - int cont; - - verneed = (Elf32_Verneed *)FILE_OFFSET(ver_r->sh_offset); - - do { - name_idx = verneed->vn_file; - possibly_add_string(name_idx, &strtab[name_idx]); - num_aux = verneed->vn_cnt; - veraux = (Elf32_Vernaux *)((char *)verneed + verneed->vn_aux); - for (i=0; ivna_name; - possibly_add_string(name_idx, &strtab[name_idx]); - veraux = (Elf32_Vernaux *)((char *)veraux + veraux->vna_next); - } - - cont = verneed->vn_next != 0; - verneed = (Elf32_Verneed *)((char *)verneed + verneed->vn_next); - } while (cont); -} - -void -fixup_strings_in_ver_r(Elf32_Shdr *ver_r) -{ - Elf32_Vernaux *veraux; - Elf32_Verneed *verneed; - int num_aux; - int name_idx; - int i; - int cont; - - verneed = (Elf32_Verneed *)FILE_OFFSET(ver_r->sh_offset); - - do { - verneed->vn_file = fixup_string(verneed->vn_file); - num_aux = verneed->vn_cnt; - veraux = (Elf32_Vernaux *)((char *)verneed + verneed->vn_aux); - for (i=0; ivna_name = fixup_string(veraux->vna_name); - veraux = (Elf32_Vernaux *)((char *)veraux + veraux->vna_next); - } - - cont = verneed->vn_next != 0; - verneed = (Elf32_Verneed *)((char *)verneed + verneed->vn_next); - } while (cont); -} - -gboolean sum_size(gpointer key, - struct dynamic_symbol *sym, - int *size) -{ - *size += strlen(sym->string) + 1; - return 1; -} - -struct index_n_dynstr { - int index; - char *dynstr; -}; - -gboolean output_string(gpointer key, - struct dynamic_symbol *sym, - struct index_n_dynstr *x) -{ - sym->new_index = x->index; - memcpy(x->dynstr + x->index, sym->string, strlen(sym->string) + 1); - x->index += strlen(sym->string) + 1; - return 1; -} - - -char * -generate_new_dynstr(Elf32_Word *size_out) -{ - int size; - char *new_dynstr; - struct index_n_dynstr x; - - size = 1; /* first a zero */ - g_hash_table_foreach (used_dynamic_symbols, - (GHFunc)sum_size, - &size); - - - new_dynstr = g_malloc(size); - - new_dynstr[0] = 0; - x.index = 1; - x.dynstr = new_dynstr; - g_hash_table_foreach (used_dynamic_symbols, - (GHFunc)output_string, - &x); - - *size_out = size; - return new_dynstr; -} - -void -remap_sections(void) -{ - Elf32_Shdr *section; - Elf32_Word sectionsize; - int numsections; - int i = 0; - - section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); - sectionsize = elf_header->e_shentsize; - numsections = elf_header->e_shnum; - - for (i=0;ish_size = fixup_size(section->sh_offset, section->sh_size); - section->sh_offset = fixup_offset(section->sh_offset); - - section->sh_addr = fixup_addr(section->sh_addr); - - section = (Elf32_Shdr *)((char *)section + sectionsize); - } -} - - -void -remap_segments(void) -{ - Elf32_Phdr *segment; - Elf32_Word segmentsize; - int numsegments; - int i = 0; - - segment = (Elf32_Phdr *)FILE_OFFSET(elf_header->e_phoff); - segmentsize = elf_header->e_phentsize; - numsegments = elf_header->e_phnum; - - for (i=0;ip_filesz = fixup_size(segment->p_offset, segment->p_filesz); - segment->p_offset = fixup_offset(segment->p_offset); - - segment->p_memsz = fixup_addr_size(segment->p_vaddr, segment->p_memsz); - segment->p_vaddr = fixup_addr(segment->p_vaddr); - segment->p_paddr = segment->p_vaddr; - - segment = (Elf32_Phdr *)((char *)segment + segmentsize); - } -} - -void -remap_elf_header(void) -{ - elf_header->e_phoff = fixup_offset(elf_header->e_phoff); - elf_header->e_shoff = fixup_offset(elf_header->e_shoff); - - elf_header->e_entry = fixup_addr(elf_header->e_entry); -} - -void -remap_symtab(Elf32_Shdr *symtab) -{ - Elf32_Sym *symbol; - Elf32_Sym *symbol_end; - Elf32_Word entry_size; - - symbol = (Elf32_Sym *)FILE_OFFSET(symtab->sh_offset); - symbol_end = (Elf32_Sym *)FILE_OFFSET(symtab->sh_offset + symtab->sh_size); - entry_size = symtab->sh_entsize; - - while (symbol < symbol_end) { - symbol->st_value = fixup_addr(symbol->st_value); - symbol = (Elf32_Sym *)((char *)symbol + entry_size); - } -} - - -/* Ugly global variables: */ -Elf32_Addr got_data_start = 0; -Elf32_Addr got_data_end = 0; - - -void -remap_rel_section(Elf32_Rel *rel, Elf32_Word size, Elf32_Word entry_size) -{ - Elf32_Rel *rel_end; - Elf32_Word offset; - Elf32_Addr *addr; - - rel_end = (Elf32_Rel *)((char *)rel + size); - - while (rel < rel_end) { - if (ELF32_R_TYPE(rel->r_info) == R_386_RELATIVE) { - /* We need to relocate the data this is pointing to too. */ - offset = vma_to_offset(rel->r_offset); - - if ( (offset >= got_data_start) && - (offset < got_data_end) ) { - /*printf("RELATIVE REL in .rel.got, skipping\n");*/ - } else { - addr = (Elf32_Addr *)FILE_OFFSET(offset); - *addr = fixup_addr(*addr); - } - } - - rel->r_offset = fixup_addr(rel->r_offset); - - rel = (Elf32_Rel *)((char *)rel + entry_size); - } -} - -void -remap_rela_section(Elf32_Rela *rela, Elf32_Word size, Elf32_Word entry_size) -{ - Elf32_Rela *rela_end; - Elf32_Addr *addr; - Elf32_Word offset; - - rela_end = (Elf32_Rela *)((char *)rela + size); - - while (rela < rela_end) { - if (ELF32_R_TYPE(rela->r_info) == R_386_RELATIVE) { - /* We need to relocate the data this is pointing to too. */ - offset = vma_to_offset(rela->r_offset); - - if ( (offset >= got_data_start) && - (offset < got_data_end) ) { - /*printf("RELATIVE RELA in .rel.got, skipping\n");*/ - } else { - addr = (Elf32_Addr *)FILE_OFFSET(offset); - *addr = fixup_addr(*addr); - } - } - - rela->r_offset = fixup_addr(rela->r_offset); - - rela = (Elf32_Rela *)((char *)rela + entry_size); - } -} - - -void -remap_reloc(void) -{ - Elf32_Shdr *section; - Elf32_Word sectionsize; - Elf32_Word offset; - int numsections; - int i = 0; - - /* This is old code. relocations are now handled from remap_dynamic() */ - section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); - sectionsize = elf_header->e_shentsize; - numsections = elf_header->e_shnum; - - for (i=0;ish_type == SHT_REL) { - Elf32_Rel *rel; - - rel = (Elf32_Rel *)FILE_OFFSET(section->sh_offset); - remap_rel_section(rel, section->sh_size, section->sh_entsize); - - } else if (section->sh_type == SHT_RELA) { - Elf32_Rela *rel; - - rel = (Elf32_Rela *)FILE_OFFSET(section->sh_offset); - remap_rela_section(rel, section->sh_size, section->sh_entsize); - } - - section = (Elf32_Shdr *)((char *)section + sectionsize); - } -} - - -void -remap_i386_got(void) -{ - Elf32_Shdr *got_section; - Elf32_Addr *got; - Elf32_Addr *got_end; - Elf32_Word entry_size; - - got_section = elf_find_section_named(".got"); - if (got_section == NULL) { - fprintf(stderr, "Warning, no .got section\n"); - return; - } - - got_data_start = got_section->sh_offset; - got_data_end = got_section->sh_offset + got_section->sh_size; - - got = (Elf32_Addr *)FILE_OFFSET(got_section->sh_offset); - got_end = (Elf32_Addr *)FILE_OFFSET(got_section->sh_offset + got_section->sh_size); - entry_size = got_section->sh_entsize; - - *got= fixup_addr(*got); /* Pointer to .dynamic */ - got = (Elf32_Addr *)((char *)got + 2*entry_size); /* Skip two reserved entries. */ - - - while (got < got_end) { - if (*got != 0) - *got= fixup_addr(*got); - - got = (Elf32_Addr *)((char *)got + entry_size); - } -} - -Elf32_Word -get_dynamic_val(Elf32_Shdr *dynamic, Elf32_Sword tag) -{ - Elf32_Dyn *element; - Elf32_Word entry_size; - - entry_size = dynamic->sh_entsize; - - element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); - while (element->d_tag != DT_NULL) { - if (element->d_tag == tag) { - return element->d_un.d_val; - } - element = (Elf32_Dyn *)((char *)element + entry_size); - } - return 0; -} - -void -remap_dynamic(Elf32_Shdr *dynamic, Elf32_Word new_dynstr_size) -{ - Elf32_Dyn *element; - Elf32_Word entry_size; - Elf32_Word rel_size; - Elf32_Word rel_entry_size; - Elf32_Rel *rel; - Elf32_Rela *rela; - - entry_size = dynamic->sh_entsize; - - element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); - while (element->d_tag != DT_NULL) { - switch(element->d_tag) { - case DT_STRSZ: - element->d_un.d_val = new_dynstr_size; - break; - case DT_PLTGOT: - case DT_HASH: - case DT_STRTAB: - case DT_INIT: - case DT_FINI: - case DT_VERDEF: - case DT_VERNEED: - case DT_VERSYM: - element->d_un.d_ptr = fixup_addr(element->d_un.d_ptr); - break; - case DT_JMPREL: - rel_size = get_dynamic_val(dynamic, DT_PLTRELSZ); - if (get_dynamic_val(dynamic, DT_PLTREL) == DT_REL) { - rel_entry_size = get_dynamic_val(dynamic, DT_RELENT); - rel = (Elf32_Rel *)FILE_OFFSET(vma_to_offset(element->d_un.d_ptr)); - remap_rel_section(rel, rel_size, rel_entry_size); - } else { - rel_entry_size = get_dynamic_val(dynamic, DT_RELAENT); - rela = (Elf32_Rela *)FILE_OFFSET(vma_to_offset(element->d_un.d_ptr)); - remap_rela_section(rela, rel_size, rel_entry_size); - } - - element->d_un.d_ptr = fixup_addr(element->d_un.d_ptr); - break; - case DT_REL: - rel_size = get_dynamic_val(dynamic, DT_RELSZ); - rel_entry_size = get_dynamic_val(dynamic, DT_RELENT); - rel = (Elf32_Rel *)FILE_OFFSET(vma_to_offset(element->d_un.d_ptr)); - remap_rel_section(rel, rel_size, rel_entry_size); - - element->d_un.d_ptr = fixup_addr(element->d_un.d_ptr); - break; - case DT_RELA: - rel_size = get_dynamic_val(dynamic, DT_RELASZ); - rel_entry_size = get_dynamic_val(dynamic, DT_RELAENT); - rela = (Elf32_Rela *)FILE_OFFSET(vma_to_offset(element->d_un.d_ptr)); - remap_rela_section(rela, rel_size, rel_entry_size); - - element->d_un.d_ptr = fixup_addr(element->d_un.d_ptr); - break; - default: - /*printf("unhandled d_tag: %d (0x%x)\n", element->d_tag, element->d_tag);*/ - } - - element = (Elf32_Dyn *)((char *)element + entry_size); - } -} - -align_hole(Elf32_Word *start, Elf32_Word *end) -{ - Elf32_Word len; - Elf32_Word align; - Elf32_Shdr *section; - Elf32_Word sectionsize; - int numsections; - int i = 0; - int unaligned; - - len = *end - *start; - align = 0; - - sectionsize = elf_header->e_shentsize; - numsections = elf_header->e_shnum; - do { - section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); - unaligned = 0; - - for (i=0;ish_addralign > 1) && - ( (section->sh_offset-len + align)%section->sh_addralign != 0) ) { - unaligned = 1; - } - - section = (Elf32_Shdr *)((char *)section + sectionsize); - } - - if (unaligned) { - align++; - } - - } while (unaligned); - - *start += align; -} - -int -main(int argc, char *argv[]) -{ - int fd; - unsigned char *mapping; - Elf32_Word size; - struct stat statbuf; - Elf32_Shdr *dynamic; - Elf32_Shdr *dynsym; - Elf32_Shdr *symtab; - Elf32_Shdr *dynstr; - Elf32_Shdr *hash; - Elf32_Shdr *higher_section; - int dynstr_index; - Elf32_Shdr *ver_r; - Elf32_Shdr *ver_d; - char *dynstr_data; - char *new_dynstr; - Elf32_Word old_dynstr_size; - Elf32_Word new_dynstr_size; - - if (argc != 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - - fd = open(argv[1], O_RDWR); - if (fd == -1) { - fprintf(stderr, "Cannot open file %s\n", argv[1]); - return 1; - } - - if (fstat(fd, &statbuf) == -1) { - fprintf(stderr, "Cannot stat file %s\n", argv[1]); - return 1; - } - - size = statbuf.st_size; - - mapping = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - - if (mapping == (unsigned char *)-1) { - fprintf(stderr, "Cannot mmap file %s\n", argv[1]); - return 1; - } - - used_dynamic_symbols = g_hash_table_new(g_direct_hash, g_direct_equal); - - elf_header = (Elf32_Ehdr *)mapping; - - if (strncmp((void *)elf_header, ELFMAG, SELFMAG)!=0) { - fprintf(stderr, "Not an ELF file\n"); - return 1; - } - - if (elf_header->e_ident[EI_VERSION] != EV_CURRENT) { - fprintf(stderr, "Wrong ELF file version\n"); - return 1; - } - - if (elf_header->e_ident[EI_CLASS] != ELFCLASS32) { - fprintf(stderr, "Only 32bit ELF files supported\n"); - return 1; - } - - if ( (elf_header->e_ident[EI_DATA] != ELFDATA2LSB) || - ((elf_header->e_machine != EM_386 && elf_header->e_machine != EM_486)) ) { - fprintf(stderr, "Only intel LSB binaries are supported right now\n"); - return 1; - } - - if (elf_header->e_type != ET_DYN) { - fprintf(stderr, "Not an ELF shared object\n"); - return 1; - } - - dynamic = elf_find_section(SHT_DYNAMIC); - dynsym = elf_find_section(SHT_DYNSYM); - symtab = elf_find_section(SHT_SYMTAB); - dynstr_index = dynsym->sh_link; - dynstr = elf_find_section_num(dynstr_index); - dynstr_data = FILE_OFFSET(dynstr->sh_offset); - old_dynstr_size = dynstr->sh_size; - ver_d = elf_find_section(SHT_GNU_verdef); - ver_r = elf_find_section(SHT_GNU_verneed); - hash = elf_find_section(SHT_HASH); - - /* Generate hash table with all used strings: */ - - add_strings_from_dynsym(dynsym, dynstr_data); - add_strings_from_dynamic(dynamic, dynstr_data); - if (ver_d && (ver_d->sh_link == dynstr_index)) - add_strings_from_ver_d(ver_d, dynstr_data); - if (ver_r && (ver_r->sh_link == dynstr_index)) - add_strings_from_ver_r(ver_r, dynstr_data); - - /* Generate new dynstr section from the used strings hashtable: */ - - new_dynstr = generate_new_dynstr(&new_dynstr_size); - /* - printf("New dynstr size: %d\n", new_dynstr_size); - printf("Old dynstr size: %d\n", old_dynstr_size); - */ - - if (new_dynstr_size >= old_dynstr_size) { - fprintf(stderr, "Couldn't GC any strings, exiting.\n"); - return 1; - } - - /* Fixup all references: */ - - fixup_strings_in_dynsym(dynsym); - fixup_strings_in_dynamic(dynamic); - if (ver_d && (ver_d->sh_link == dynstr_index)) - fixup_strings_in_ver_d(ver_d); - if (ver_r && (ver_r->sh_link == dynstr_index)) - fixup_strings_in_ver_r(ver_r); - - /* Copy over the new dynstr: */ - memcpy(dynstr_data, new_dynstr, new_dynstr_size); - memset(dynstr_data + new_dynstr_size, ' ', old_dynstr_size-new_dynstr_size); - - /* Compact the dynstr section and the file: */ - - /* 1. Set up the data for the fixup_offset() function: */ - hole_index = dynstr->sh_offset + new_dynstr_size; - higher_section = elf_find_next_higher_section(hole_index); - hole_end = higher_section->sh_offset; - - align_hole(&hole_index, &hole_end); - - hole_len = hole_end - hole_index; - - hole_addr_start = hole_index; /* TODO: Fix this to something better */ - - find_segment_addr_min_max(dynstr->sh_offset, - &hole_addr_remap_start, &hole_addr_remap_end); - - /* - printf("Hole remap: 0x%lx - 0x%lx\n", hole_addr_remap_start, hole_addr_remap_end); - - printf("hole: %lu - %lu (%lu bytes)\n", hole_index, hole_end, hole_len); - printf("hole: 0x%lx - 0x%lx (0x%lx bytes)\n", hole_index, hole_end, hole_len); - */ - - /* 2. Change all section and segment sizes and offsets: */ - - remap_symtab(dynsym); - if (symtab) - remap_symtab(symtab); - - remap_i386_got(); - remap_dynamic(dynamic, new_dynstr_size); - - remap_sections(); /* After this line the section headers are wrong */ - remap_segments(); - remap_elf_header(); - - /* 3. Do the real compacting. */ - - memmove(mapping + hole_index, - mapping + hole_index + hole_len, - size - (hole_index + hole_len)); - - munmap(mapping, size); - - ftruncate(fd, size - hole_len); - close(fd); -} - - - +/* elf_gc_dynst + * + * This is a program that removes unreferenced strings from the .dynstr + * section in ELF shared objects. It also shrinks the .dynstr section and + * relocates all symbols after it. + * + * This program was written and copyrighted by: + * Alexander Larsson + * + * + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include + + +Elf32_Ehdr *elf_header = NULL; +#define FILE_OFFSET(offset) ((unsigned char *)(elf_header) + (offset)) + +struct dynamic_symbol { + Elf32_Word old_index; + Elf32_Word new_index; + char *string; +}; + +GHashTable *used_dynamic_symbols = NULL; +/* Data is dynamic_symbols, hashes on old_index */ +Elf32_Word hole_index; +Elf32_Word hole_end; +Elf32_Word hole_len; + +Elf32_Addr hole_addr_start; +Elf32_Addr hole_addr_remap_start; +Elf32_Addr hole_addr_remap_end; + + +Elf32_Shdr * +elf_find_section_num(int section_index) +{ + Elf32_Shdr *section; + Elf32_Word sectionsize; + + section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); + sectionsize = elf_header->e_shentsize; + + section = (Elf32_Shdr *)((char *)section + sectionsize*section_index); + + return section; +} + +Elf32_Shdr * +elf_find_section_named(char *name) +{ + Elf32_Shdr *section; + Elf32_Shdr *strtab_section; + Elf32_Word sectionsize; + int numsections; + char *strtab; + int i = 0; + + section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); + + strtab_section = elf_find_section_num(elf_header->e_shstrndx); + + strtab = (char *)FILE_OFFSET(strtab_section->sh_offset); + + sectionsize = elf_header->e_shentsize; + numsections = elf_header->e_shnum; + + for (i=0;ish_name], name) == 0) { + return section; + } + section = (Elf32_Shdr *)((char *)section + sectionsize); + } + return NULL; +} + + +Elf32_Shdr * +elf_find_section(Elf32_Word sh_type) +{ + Elf32_Shdr *section; + Elf32_Word sectionsize; + int numsections; + int i = 0; + + section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); + sectionsize = elf_header->e_shentsize; + numsections = elf_header->e_shnum; + + for (i=0;ish_type == sh_type) { + return section; + } + section = (Elf32_Shdr *)((char *)section + sectionsize); + } + return NULL; +} + +Elf32_Shdr * +elf_find_next_higher_section(Elf32_Word offset) +{ + Elf32_Shdr *section; + Elf32_Shdr *higher; + Elf32_Word sectionsize; + int numsections; + int i = 0; + + section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); + sectionsize = elf_header->e_shentsize; + numsections = elf_header->e_shnum; + + higher = NULL; + + for (i=0;ish_offset >= offset) { + if (higher == NULL) { + higher = section; + } else if (section->sh_offset < higher->sh_offset) { + higher = section; + } + } + + section = (Elf32_Shdr *)((char *)section + sectionsize); + } + + return higher; +} + +Elf32_Word +vma_to_offset(Elf32_Addr addr) +{ + Elf32_Shdr *section; + Elf32_Shdr *higher; + Elf32_Word sectionsize; + int numsections; + int i = 0; + + section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); + sectionsize = elf_header->e_shentsize; + numsections = elf_header->e_shnum; + + higher = NULL; + + for (i=0;i= section->sh_addr) && + (addr < section->sh_addr + section->sh_size) ) { + return section->sh_offset + (addr-section->sh_addr); + } + + section = (Elf32_Shdr *)((char *)section + sectionsize); + } + + fprintf(stderr, "Warning, unable to convert address %d (0x%x) to file offset\n", + addr, addr); + return 0; +} + + +void +find_segment_addr_min_max(Elf32_Word file_offset, + Elf32_Addr *start, Elf32_Addr *end) +{ + Elf32_Phdr *segment; + Elf32_Word segmentsize; + int numsegments; + int i = 0; + + segment = (Elf32_Phdr *)FILE_OFFSET(elf_header->e_phoff); + segmentsize = elf_header->e_phentsize; + numsegments = elf_header->e_phnum; + + for (i=0;i= segment->p_offset) && + (file_offset < segment->p_offset + segment->p_filesz)) { + *start = segment->p_vaddr; + *end = segment->p_vaddr + segment->p_memsz; + return; + } + + segment = (Elf32_Phdr *)((char *)segment + segmentsize); + } + fprintf(stderr, "Error: Couldn't find segment in find_segment_addr_min_max()\n"); +} + +void * +dynamic_find_tag(Elf32_Shdr *dynamic, Elf32_Sword d_tag) +{ + int i; + Elf32_Dyn *element; + + element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); + for (i=0; element[i].d_tag != DT_NULL; i++) { + if (element[i].d_tag = d_tag) { + return FILE_OFFSET(element[i].d_un.d_ptr); + } + } + + return NULL; +} + +Elf32_Word +fixup_offset(Elf32_Word offset) +{ + if (offset >= hole_index) { + return offset - hole_len; + } + return offset; +} + +Elf32_Word +fixup_size(Elf32_Word offset, Elf32_Word size) +{ + /* Note: Doesn't handle the cases where the hole and the size intersect + partially. */ + + if ( (hole_index >= offset) && + (hole_index < offset + size)){ + return size - hole_len; + } + + return size; +} + +Elf32_Addr +fixup_addr(Elf32_Addr addr) +{ + if (addr == 0) + return 0; + + /* + if ( (addr < hole_addr_remap_start) || + (addr >= hole_addr_remap_end)) + return addr; + */ + + if (addr >= hole_addr_start) { + return addr - hole_len; + } + return addr; +} + +Elf32_Word +fixup_addr_size(Elf32_Addr addr, Elf32_Word size) +{ + /* Note: Doesn't handle the cases where the hole and the size intersect + partially. */ + /* + if ( (addr < hole_addr_remap_start) || + (addr >= hole_addr_remap_end)) + return size; + */ + if ( (hole_addr_start >= addr) && + (hole_addr_start < addr + size)){ + return size - hole_len; + } + + return size; +} + +void +possibly_add_string(int name_idx, const char *name) +{ + struct dynamic_symbol *dynamic_symbol; + if (name_idx != 0) { + dynamic_symbol = g_hash_table_lookup(used_dynamic_symbols, (gpointer) name_idx); + + if (dynamic_symbol == NULL) { + + dynamic_symbol = g_new(struct dynamic_symbol, 1); + + dynamic_symbol->old_index = name_idx; + dynamic_symbol->new_index = 0; + dynamic_symbol->string = g_strdup(name); + + g_hash_table_insert(used_dynamic_symbols, (gpointer)name_idx, dynamic_symbol); + /*printf("added dynamic string: %s (%d)\n", dynamic_symbol->string, name_idx);*/ + } + } +} + +Elf32_Word +fixup_string(Elf32_Word old_idx) +{ + struct dynamic_symbol *dynamic_symbol; + + if (old_idx == 0) + return 0; + + dynamic_symbol = g_hash_table_lookup(used_dynamic_symbols, (gpointer) old_idx); + + if (dynamic_symbol == NULL) { + fprintf(stderr, "AAAAAAAAAAAARGH!? Unknown string found in fixup (index: %d)!\n", old_idx); + return 0; + } + + return dynamic_symbol->new_index; +} + + + +void +add_strings_from_dynsym(Elf32_Shdr *dynsym, char *strtab) +{ + Elf32_Sym *symbol; + Elf32_Sym *symbol_end; + Elf32_Word entry_size; + + + symbol = (Elf32_Sym *)FILE_OFFSET(dynsym->sh_offset); + symbol_end = (Elf32_Sym *)FILE_OFFSET(dynsym->sh_offset + dynsym->sh_size); + entry_size = dynsym->sh_entsize; + + while (symbol < symbol_end) { + int name_idx; + struct dynamic_symbol *dynamic_symbol; + + name_idx = symbol->st_name; + possibly_add_string(name_idx, &strtab[name_idx]); + + + symbol = (Elf32_Sym *)((char *)symbol + entry_size); + } +} + + +void +fixup_strings_in_dynsym(Elf32_Shdr *dynsym) +{ + Elf32_Sym *symbol; + Elf32_Sym *symbol_end; + Elf32_Word entry_size; + + + symbol = (Elf32_Sym *)FILE_OFFSET(dynsym->sh_offset); + symbol_end = (Elf32_Sym *)FILE_OFFSET(dynsym->sh_offset + dynsym->sh_size); + entry_size = dynsym->sh_entsize; + + while (symbol < symbol_end) { + int name_idx; + struct dynamic_symbol *dynamic_symbol; + + symbol->st_name = fixup_string(symbol->st_name); + + symbol = (Elf32_Sym *)((char *)symbol + entry_size); + } +} + + +void +add_strings_from_dynamic(Elf32_Shdr *dynamic, char *strtab) +{ + int i; + int name_idx; + Elf32_Dyn *element; + Elf32_Word entry_size; + + entry_size = dynamic->sh_entsize; + + + element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); + while (element->d_tag != DT_NULL) { + + switch(element->d_tag) { + case DT_NEEDED: + case DT_SONAME: + case DT_RPATH: + name_idx = element->d_un.d_val; + /*if (name_idx) printf("d_tag: %d\n", element->d_tag);*/ + possibly_add_string(name_idx, &strtab[name_idx]); + break; + default: + /*printf("unhandled d_tag: %d (0x%x)\n", element->d_tag, element->d_tag);*/ + } + + element = (Elf32_Dyn *)((char *)element + entry_size); + } + +} + +void +fixup_strings_in_dynamic(Elf32_Shdr *dynamic) +{ + int i; + int name_idx; + Elf32_Dyn *element; + Elf32_Word entry_size; + + entry_size = dynamic->sh_entsize; + + element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); + while (element->d_tag != DT_NULL) { + + switch(element->d_tag) { + case DT_NEEDED: + case DT_SONAME: + case DT_RPATH: + element->d_un.d_val = fixup_string(element->d_un.d_val); + break; + default: + /*printf("unhandled d_tag: %d (0x%x)\n", element->d_tag, element->d_tag);*/ + } + + element = (Elf32_Dyn *)((char *)element + entry_size); + } + +} + + +void +add_strings_from_ver_d(Elf32_Shdr *ver_d, char *strtab) +{ + Elf32_Verdaux *veraux; + Elf32_Verdef *verdef; + int num_aux; + int name_idx; + int i; + int cont; + + verdef = (Elf32_Verdef *)FILE_OFFSET(ver_d->sh_offset); + + do { + num_aux = verdef->vd_cnt; + veraux = (Elf32_Verdaux *)((char *)verdef + verdef->vd_aux); + for (i=0; ivda_name; + possibly_add_string(name_idx, &strtab[name_idx]); + veraux = (Elf32_Verdaux *)((char *)veraux + veraux->vda_next); + } + + cont = verdef->vd_next != 0; + verdef = (Elf32_Verdef *)((char *)verdef + verdef->vd_next); + } while (cont); + +} + +void +fixup_strings_in_ver_d(Elf32_Shdr *ver_d) +{ + Elf32_Verdaux *veraux; + Elf32_Verdef *verdef; + int num_aux; + int name_idx; + int i; + int cont; + + verdef = (Elf32_Verdef *)FILE_OFFSET(ver_d->sh_offset); + + do { + num_aux = verdef->vd_cnt; + veraux = (Elf32_Verdaux *)((char *)verdef + verdef->vd_aux); + for (i=0; ivda_name = fixup_string(veraux->vda_name); + veraux = (Elf32_Verdaux *)((char *)veraux + veraux->vda_next); + } + + cont = verdef->vd_next != 0; + verdef = (Elf32_Verdef *)((char *)verdef + verdef->vd_next); + } while (cont); + +} + +void +add_strings_from_ver_r(Elf32_Shdr *ver_r, char *strtab) +{ + Elf32_Vernaux *veraux; + Elf32_Verneed *verneed; + int num_aux; + int name_idx; + int i; + int cont; + + verneed = (Elf32_Verneed *)FILE_OFFSET(ver_r->sh_offset); + + do { + name_idx = verneed->vn_file; + possibly_add_string(name_idx, &strtab[name_idx]); + num_aux = verneed->vn_cnt; + veraux = (Elf32_Vernaux *)((char *)verneed + verneed->vn_aux); + for (i=0; ivna_name; + possibly_add_string(name_idx, &strtab[name_idx]); + veraux = (Elf32_Vernaux *)((char *)veraux + veraux->vna_next); + } + + cont = verneed->vn_next != 0; + verneed = (Elf32_Verneed *)((char *)verneed + verneed->vn_next); + } while (cont); +} + +void +fixup_strings_in_ver_r(Elf32_Shdr *ver_r) +{ + Elf32_Vernaux *veraux; + Elf32_Verneed *verneed; + int num_aux; + int name_idx; + int i; + int cont; + + verneed = (Elf32_Verneed *)FILE_OFFSET(ver_r->sh_offset); + + do { + verneed->vn_file = fixup_string(verneed->vn_file); + num_aux = verneed->vn_cnt; + veraux = (Elf32_Vernaux *)((char *)verneed + verneed->vn_aux); + for (i=0; ivna_name = fixup_string(veraux->vna_name); + veraux = (Elf32_Vernaux *)((char *)veraux + veraux->vna_next); + } + + cont = verneed->vn_next != 0; + verneed = (Elf32_Verneed *)((char *)verneed + verneed->vn_next); + } while (cont); +} + +gboolean sum_size(gpointer key, + struct dynamic_symbol *sym, + int *size) +{ + *size += strlen(sym->string) + 1; + return 1; +} + +struct index_n_dynstr { + int index; + char *dynstr; +}; + +gboolean output_string(gpointer key, + struct dynamic_symbol *sym, + struct index_n_dynstr *x) +{ + sym->new_index = x->index; + memcpy(x->dynstr + x->index, sym->string, strlen(sym->string) + 1); + x->index += strlen(sym->string) + 1; + return 1; +} + + +char * +generate_new_dynstr(Elf32_Word *size_out) +{ + int size; + char *new_dynstr; + struct index_n_dynstr x; + + size = 1; /* first a zero */ + g_hash_table_foreach (used_dynamic_symbols, + (GHFunc)sum_size, + &size); + + + new_dynstr = g_malloc(size); + + new_dynstr[0] = 0; + x.index = 1; + x.dynstr = new_dynstr; + g_hash_table_foreach (used_dynamic_symbols, + (GHFunc)output_string, + &x); + + *size_out = size; + return new_dynstr; +} + +void +remap_sections(void) +{ + Elf32_Shdr *section; + Elf32_Word sectionsize; + int numsections; + int i = 0; + + section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); + sectionsize = elf_header->e_shentsize; + numsections = elf_header->e_shnum; + + for (i=0;ish_size = fixup_size(section->sh_offset, section->sh_size); + section->sh_offset = fixup_offset(section->sh_offset); + + section->sh_addr = fixup_addr(section->sh_addr); + + section = (Elf32_Shdr *)((char *)section + sectionsize); + } +} + + +void +remap_segments(void) +{ + Elf32_Phdr *segment; + Elf32_Word segmentsize; + int numsegments; + int i = 0; + + segment = (Elf32_Phdr *)FILE_OFFSET(elf_header->e_phoff); + segmentsize = elf_header->e_phentsize; + numsegments = elf_header->e_phnum; + + for (i=0;ip_filesz = fixup_size(segment->p_offset, segment->p_filesz); + segment->p_offset = fixup_offset(segment->p_offset); + + segment->p_memsz = fixup_addr_size(segment->p_vaddr, segment->p_memsz); + segment->p_vaddr = fixup_addr(segment->p_vaddr); + segment->p_paddr = segment->p_vaddr; + + segment = (Elf32_Phdr *)((char *)segment + segmentsize); + } +} + +void +remap_elf_header(void) +{ + elf_header->e_phoff = fixup_offset(elf_header->e_phoff); + elf_header->e_shoff = fixup_offset(elf_header->e_shoff); + + elf_header->e_entry = fixup_addr(elf_header->e_entry); +} + +void +remap_symtab(Elf32_Shdr *symtab) +{ + Elf32_Sym *symbol; + Elf32_Sym *symbol_end; + Elf32_Word entry_size; + + symbol = (Elf32_Sym *)FILE_OFFSET(symtab->sh_offset); + symbol_end = (Elf32_Sym *)FILE_OFFSET(symtab->sh_offset + symtab->sh_size); + entry_size = symtab->sh_entsize; + + while (symbol < symbol_end) { + symbol->st_value = fixup_addr(symbol->st_value); + symbol = (Elf32_Sym *)((char *)symbol + entry_size); + } +} + + +/* Ugly global variables: */ +Elf32_Addr got_data_start = 0; +Elf32_Addr got_data_end = 0; + + +void +remap_rel_section(Elf32_Rel *rel, Elf32_Word size, Elf32_Word entry_size) +{ + Elf32_Rel *rel_end; + Elf32_Word offset; + Elf32_Addr *addr; + + rel_end = (Elf32_Rel *)((char *)rel + size); + + while (rel < rel_end) { + if (ELF32_R_TYPE(rel->r_info) == R_386_RELATIVE) { + /* We need to relocate the data this is pointing to too. */ + offset = vma_to_offset(rel->r_offset); + + if ( (offset >= got_data_start) && + (offset < got_data_end) ) { + /*printf("RELATIVE REL in .rel.got, skipping\n");*/ + } else { + addr = (Elf32_Addr *)FILE_OFFSET(offset); + *addr = fixup_addr(*addr); + } + } + + rel->r_offset = fixup_addr(rel->r_offset); + + rel = (Elf32_Rel *)((char *)rel + entry_size); + } +} + +void +remap_rela_section(Elf32_Rela *rela, Elf32_Word size, Elf32_Word entry_size) +{ + Elf32_Rela *rela_end; + Elf32_Addr *addr; + Elf32_Word offset; + + rela_end = (Elf32_Rela *)((char *)rela + size); + + while (rela < rela_end) { + if (ELF32_R_TYPE(rela->r_info) == R_386_RELATIVE) { + /* We need to relocate the data this is pointing to too. */ + offset = vma_to_offset(rela->r_offset); + + if ( (offset >= got_data_start) && + (offset < got_data_end) ) { + /*printf("RELATIVE RELA in .rel.got, skipping\n");*/ + } else { + addr = (Elf32_Addr *)FILE_OFFSET(offset); + *addr = fixup_addr(*addr); + } + } + + rela->r_offset = fixup_addr(rela->r_offset); + + rela = (Elf32_Rela *)((char *)rela + entry_size); + } +} + + +void +remap_reloc(void) +{ + Elf32_Shdr *section; + Elf32_Word sectionsize; + Elf32_Word offset; + int numsections; + int i = 0; + + /* This is old code. relocations are now handled from remap_dynamic() */ + section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); + sectionsize = elf_header->e_shentsize; + numsections = elf_header->e_shnum; + + for (i=0;ish_type == SHT_REL) { + Elf32_Rel *rel; + + rel = (Elf32_Rel *)FILE_OFFSET(section->sh_offset); + remap_rel_section(rel, section->sh_size, section->sh_entsize); + + } else if (section->sh_type == SHT_RELA) { + Elf32_Rela *rel; + + rel = (Elf32_Rela *)FILE_OFFSET(section->sh_offset); + remap_rela_section(rel, section->sh_size, section->sh_entsize); + } + + section = (Elf32_Shdr *)((char *)section + sectionsize); + } +} + + +void +remap_i386_got(void) +{ + Elf32_Shdr *got_section; + Elf32_Addr *got; + Elf32_Addr *got_end; + Elf32_Word entry_size; + + got_section = elf_find_section_named(".got"); + if (got_section == NULL) { + fprintf(stderr, "Warning, no .got section\n"); + return; + } + + got_data_start = got_section->sh_offset; + got_data_end = got_section->sh_offset + got_section->sh_size; + + got = (Elf32_Addr *)FILE_OFFSET(got_section->sh_offset); + got_end = (Elf32_Addr *)FILE_OFFSET(got_section->sh_offset + got_section->sh_size); + entry_size = got_section->sh_entsize; + + *got= fixup_addr(*got); /* Pointer to .dynamic */ + got = (Elf32_Addr *)((char *)got + 2*entry_size); /* Skip two reserved entries. */ + + + while (got < got_end) { + if (*got != 0) + *got= fixup_addr(*got); + + got = (Elf32_Addr *)((char *)got + entry_size); + } +} + +Elf32_Word +get_dynamic_val(Elf32_Shdr *dynamic, Elf32_Sword tag) +{ + Elf32_Dyn *element; + Elf32_Word entry_size; + + entry_size = dynamic->sh_entsize; + + element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); + while (element->d_tag != DT_NULL) { + if (element->d_tag == tag) { + return element->d_un.d_val; + } + element = (Elf32_Dyn *)((char *)element + entry_size); + } + return 0; +} + +void +remap_dynamic(Elf32_Shdr *dynamic, Elf32_Word new_dynstr_size) +{ + Elf32_Dyn *element; + Elf32_Word entry_size; + Elf32_Word rel_size; + Elf32_Word rel_entry_size; + Elf32_Rel *rel; + Elf32_Rela *rela; + + entry_size = dynamic->sh_entsize; + + element = (Elf32_Dyn *)FILE_OFFSET(dynamic->sh_offset); + while (element->d_tag != DT_NULL) { + switch(element->d_tag) { + case DT_STRSZ: + element->d_un.d_val = new_dynstr_size; + break; + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_INIT: + case DT_FINI: + case DT_VERDEF: + case DT_VERNEED: + case DT_VERSYM: + element->d_un.d_ptr = fixup_addr(element->d_un.d_ptr); + break; + case DT_JMPREL: + rel_size = get_dynamic_val(dynamic, DT_PLTRELSZ); + if (get_dynamic_val(dynamic, DT_PLTREL) == DT_REL) { + rel_entry_size = get_dynamic_val(dynamic, DT_RELENT); + rel = (Elf32_Rel *)FILE_OFFSET(vma_to_offset(element->d_un.d_ptr)); + remap_rel_section(rel, rel_size, rel_entry_size); + } else { + rel_entry_size = get_dynamic_val(dynamic, DT_RELAENT); + rela = (Elf32_Rela *)FILE_OFFSET(vma_to_offset(element->d_un.d_ptr)); + remap_rela_section(rela, rel_size, rel_entry_size); + } + + element->d_un.d_ptr = fixup_addr(element->d_un.d_ptr); + break; + case DT_REL: + rel_size = get_dynamic_val(dynamic, DT_RELSZ); + rel_entry_size = get_dynamic_val(dynamic, DT_RELENT); + rel = (Elf32_Rel *)FILE_OFFSET(vma_to_offset(element->d_un.d_ptr)); + remap_rel_section(rel, rel_size, rel_entry_size); + + element->d_un.d_ptr = fixup_addr(element->d_un.d_ptr); + break; + case DT_RELA: + rel_size = get_dynamic_val(dynamic, DT_RELASZ); + rel_entry_size = get_dynamic_val(dynamic, DT_RELAENT); + rela = (Elf32_Rela *)FILE_OFFSET(vma_to_offset(element->d_un.d_ptr)); + remap_rela_section(rela, rel_size, rel_entry_size); + + element->d_un.d_ptr = fixup_addr(element->d_un.d_ptr); + break; + default: + /*printf("unhandled d_tag: %d (0x%x)\n", element->d_tag, element->d_tag);*/ + } + + element = (Elf32_Dyn *)((char *)element + entry_size); + } +} + +align_hole(Elf32_Word *start, Elf32_Word *end) +{ + Elf32_Word len; + Elf32_Word align; + Elf32_Shdr *section; + Elf32_Word sectionsize; + int numsections; + int i = 0; + int unaligned; + + len = *end - *start; + align = 0; + + sectionsize = elf_header->e_shentsize; + numsections = elf_header->e_shnum; + do { + section = (Elf32_Shdr *)FILE_OFFSET(elf_header->e_shoff); + unaligned = 0; + + for (i=0;ish_addralign > 1) && + ( (section->sh_offset-len + align)%section->sh_addralign != 0) ) { + unaligned = 1; + } + + section = (Elf32_Shdr *)((char *)section + sectionsize); + } + + if (unaligned) { + align++; + } + + } while (unaligned); + + *start += align; +} + +int +main(int argc, char *argv[]) +{ + int fd; + unsigned char *mapping; + Elf32_Word size; + struct stat statbuf; + Elf32_Shdr *dynamic; + Elf32_Shdr *dynsym; + Elf32_Shdr *symtab; + Elf32_Shdr *dynstr; + Elf32_Shdr *hash; + Elf32_Shdr *higher_section; + int dynstr_index; + Elf32_Shdr *ver_r; + Elf32_Shdr *ver_d; + char *dynstr_data; + char *new_dynstr; + Elf32_Word old_dynstr_size; + Elf32_Word new_dynstr_size; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + fd = open(argv[1], O_RDWR); + if (fd == -1) { + fprintf(stderr, "Cannot open file %s\n", argv[1]); + return 1; + } + + if (fstat(fd, &statbuf) == -1) { + fprintf(stderr, "Cannot stat file %s\n", argv[1]); + return 1; + } + + size = statbuf.st_size; + + mapping = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (mapping == (unsigned char *)-1) { + fprintf(stderr, "Cannot mmap file %s\n", argv[1]); + return 1; + } + + used_dynamic_symbols = g_hash_table_new(g_direct_hash, g_direct_equal); + + elf_header = (Elf32_Ehdr *)mapping; + + if (strncmp((void *)elf_header, ELFMAG, SELFMAG)!=0) { + fprintf(stderr, "Not an ELF file\n"); + return 1; + } + + if (elf_header->e_ident[EI_VERSION] != EV_CURRENT) { + fprintf(stderr, "Wrong ELF file version\n"); + return 1; + } + + if (elf_header->e_ident[EI_CLASS] != ELFCLASS32) { + fprintf(stderr, "Only 32bit ELF files supported\n"); + return 1; + } + + if ( (elf_header->e_ident[EI_DATA] != ELFDATA2LSB) || + ((elf_header->e_machine != EM_386 && elf_header->e_machine != EM_486)) ) { + fprintf(stderr, "Only intel LSB binaries are supported right now\n"); + return 1; + } + + if (elf_header->e_type != ET_DYN) { + fprintf(stderr, "Not an ELF shared object\n"); + return 1; + } + + dynamic = elf_find_section(SHT_DYNAMIC); + dynsym = elf_find_section(SHT_DYNSYM); + symtab = elf_find_section(SHT_SYMTAB); + dynstr_index = dynsym->sh_link; + dynstr = elf_find_section_num(dynstr_index); + dynstr_data = FILE_OFFSET(dynstr->sh_offset); + old_dynstr_size = dynstr->sh_size; + ver_d = elf_find_section(SHT_GNU_verdef); + ver_r = elf_find_section(SHT_GNU_verneed); + hash = elf_find_section(SHT_HASH); + + /* Generate hash table with all used strings: */ + + add_strings_from_dynsym(dynsym, dynstr_data); + add_strings_from_dynamic(dynamic, dynstr_data); + if (ver_d && (ver_d->sh_link == dynstr_index)) + add_strings_from_ver_d(ver_d, dynstr_data); + if (ver_r && (ver_r->sh_link == dynstr_index)) + add_strings_from_ver_r(ver_r, dynstr_data); + + /* Generate new dynstr section from the used strings hashtable: */ + + new_dynstr = generate_new_dynstr(&new_dynstr_size); + /* + printf("New dynstr size: %d\n", new_dynstr_size); + printf("Old dynstr size: %d\n", old_dynstr_size); + */ + + if (new_dynstr_size >= old_dynstr_size) { + fprintf(stderr, "Couldn't GC any strings, exiting.\n"); + return 1; + } + + /* Fixup all references: */ + + fixup_strings_in_dynsym(dynsym); + fixup_strings_in_dynamic(dynamic); + if (ver_d && (ver_d->sh_link == dynstr_index)) + fixup_strings_in_ver_d(ver_d); + if (ver_r && (ver_r->sh_link == dynstr_index)) + fixup_strings_in_ver_r(ver_r); + + /* Copy over the new dynstr: */ + memcpy(dynstr_data, new_dynstr, new_dynstr_size); + memset(dynstr_data + new_dynstr_size, ' ', old_dynstr_size-new_dynstr_size); + + /* Compact the dynstr section and the file: */ + + /* 1. Set up the data for the fixup_offset() function: */ + hole_index = dynstr->sh_offset + new_dynstr_size; + higher_section = elf_find_next_higher_section(hole_index); + hole_end = higher_section->sh_offset; + + align_hole(&hole_index, &hole_end); + + hole_len = hole_end - hole_index; + + hole_addr_start = hole_index; /* TODO: Fix this to something better */ + + find_segment_addr_min_max(dynstr->sh_offset, + &hole_addr_remap_start, &hole_addr_remap_end); + + /* + printf("Hole remap: 0x%lx - 0x%lx\n", hole_addr_remap_start, hole_addr_remap_end); + + printf("hole: %lu - %lu (%lu bytes)\n", hole_index, hole_end, hole_len); + printf("hole: 0x%lx - 0x%lx (0x%lx bytes)\n", hole_index, hole_end, hole_len); + */ + + /* 2. Change all section and segment sizes and offsets: */ + + remap_symtab(dynsym); + if (symtab) + remap_symtab(symtab); + + remap_i386_got(); + remap_dynamic(dynamic, new_dynstr_size); + + remap_sections(); /* After this line the section headers are wrong */ + remap_segments(); + remap_elf_header(); + + /* 3. Do the real compacting. */ + + memmove(mapping + hole_index, + mapping + hole_index + hole_len, + size - (hole_index + hole_len)); + + munmap(mapping, size); + + ftruncate(fd, size - hole_len); + close(fd); +} + + +