/* 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 * * * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * 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. * * The Original Code is mozilla.org Code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2001 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #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; int need_byteswap; unsigned char machine_type; Elf32_Word read_word(Elf32_Word w) { if (need_byteswap) w = GUINT32_SWAP_LE_BE(w); return w; } Elf32_Sword read_sword(Elf32_Sword w) { if (need_byteswap) w = (Elf32_Sword)GUINT32_SWAP_LE_BE((guint32)w); return w; } void write_word(Elf32_Word *ptr, Elf32_Word w) { if (need_byteswap) w = GUINT32_SWAP_LE_BE(w); *ptr = w; } Elf32_Half read_half(Elf32_Half h) { if (need_byteswap) h = GUINT16_SWAP_LE_BE(h); return h; } void write_half(Elf32_Half *ptr, Elf32_Half h) { if (need_byteswap) h = GUINT16_SWAP_LE_BE(h); *ptr = h; } void setup_byteswapping(unsigned char ei_data) { need_byteswap = 0; #if G_BYTE_ORDER == G_BIG_ENDIAN if (ei_data == ELFDATA2LSB) need_byteswap = 1; #endif #if G_BYTE_ORDER == G_LITTLE_ENDIAN if (ei_data == ELFDATA2MSB) need_byteswap = 1; #endif } Elf32_Shdr * elf_find_section_num(int section_index) { Elf32_Shdr *section; Elf32_Word sectionsize; section = (Elf32_Shdr *)FILE_OFFSET(read_word(elf_header->e_shoff)); sectionsize = read_half(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(read_word(elf_header->e_shoff)); strtab_section = elf_find_section_num(read_half(elf_header->e_shstrndx)); strtab = (char *)FILE_OFFSET(read_word(strtab_section->sh_offset)); sectionsize = read_half(elf_header->e_shentsize); numsections = read_half(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(read_word(elf_header->e_shoff)); sectionsize = read_half(elf_header->e_shentsize); numsections = read_half(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(read_word(elf_header->e_shoff)); sectionsize = read_half(elf_header->e_shentsize); numsections = read_half(elf_header->e_shnum); higher = NULL; for (i=0;ish_offset) >= offset) { if (higher == NULL) { higher = section; } else if (read_word(section->sh_offset) < read_word(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(read_word(elf_header->e_shoff)); sectionsize = read_half(elf_header->e_shentsize); numsections = read_half(elf_header->e_shnum); higher = NULL; for (i=0;i= read_word(section->sh_addr)) && (addr < read_word(section->sh_addr) + read_word(section->sh_size)) ) { return read_word(section->sh_offset) + (addr - read_word(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(read_word(elf_header->e_phoff)); segmentsize = read_half(elf_header->e_phentsize); numsegments = read_half(elf_header->e_phnum); for (i=0;i= read_word(segment->p_offset)) && (file_offset < read_word(segment->p_offset) + read_word(segment->p_filesz))) { *start = read_word(segment->p_vaddr); *end = read_word(segment->p_vaddr) + read_word(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(read_word(dynamic->sh_offset)); for (i=0; read_sword(element[i].d_tag) != DT_NULL; i++) { if (read_sword(element[i].d_tag) == d_tag) { return FILE_OFFSET(read_word(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(read_word(dynsym->sh_offset)); symbol_end = (Elf32_Sym *)FILE_OFFSET(read_word(dynsym->sh_offset) + read_word(dynsym->sh_size)); entry_size = read_word(dynsym->sh_entsize); while (symbol < symbol_end) { int name_idx; struct dynamic_symbol *dynamic_symbol; name_idx = read_word(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(read_word(dynsym->sh_offset)); symbol_end = (Elf32_Sym *)FILE_OFFSET(read_word(dynsym->sh_offset) + read_word(dynsym->sh_size)); entry_size = read_word(dynsym->sh_entsize); while (symbol < symbol_end) { struct dynamic_symbol *dynamic_symbol; write_word(&symbol->st_name, fixup_string(read_word(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 = read_word(dynamic->sh_entsize); element = (Elf32_Dyn *)FILE_OFFSET(read_word(dynamic->sh_offset)); while (read_sword(element->d_tag) != DT_NULL) { switch(read_sword(element->d_tag)) { case DT_NEEDED: case DT_SONAME: case DT_RPATH: name_idx = read_word(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 = read_word(dynamic->sh_entsize); element = (Elf32_Dyn *)FILE_OFFSET(read_word(dynamic->sh_offset)); while (read_sword(element->d_tag) != DT_NULL) { switch(read_sword(element->d_tag)) { case DT_NEEDED: case DT_SONAME: case DT_RPATH: write_word(&element->d_un.d_val, fixup_string(read_word(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(read_word(ver_d->sh_offset)); do { num_aux = read_half(verdef->vd_cnt); veraux = (Elf32_Verdaux *)((char *)verdef + read_word(verdef->vd_aux)); for (i=0; ivda_name); possibly_add_string(name_idx, &strtab[name_idx]); veraux = (Elf32_Verdaux *)((char *)veraux + read_word(veraux->vda_next)); } cont = read_word(verdef->vd_next) != 0; verdef = (Elf32_Verdef *)((char *)verdef + read_word(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(read_word(ver_d->sh_offset)); do { num_aux = read_half(verdef->vd_cnt); veraux = (Elf32_Verdaux *)((char *)verdef + read_word(verdef->vd_aux)); for (i=0; ivda_name, fixup_string(read_word(veraux->vda_name))); veraux = (Elf32_Verdaux *)((char *)veraux + read_word(veraux->vda_next)); } cont = read_word(verdef->vd_next) != 0; verdef = (Elf32_Verdef *)((char *)verdef + read_word(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(read_word(ver_r->sh_offset)); do { name_idx = read_word(verneed->vn_file); possibly_add_string(name_idx, &strtab[name_idx]); num_aux = read_half(verneed->vn_cnt); veraux = (Elf32_Vernaux *)((char *)verneed + read_word(verneed->vn_aux)); for (i=0; ivna_name); possibly_add_string(name_idx, &strtab[name_idx]); veraux = (Elf32_Vernaux *)((char *)veraux + read_word(veraux->vna_next)); } cont = read_word(verneed->vn_next) != 0; verneed = (Elf32_Verneed *)((char *)verneed + read_word(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(read_word(ver_r->sh_offset)); do { write_word(&verneed->vn_file, fixup_string(read_word(verneed->vn_file))); num_aux = read_half(verneed->vn_cnt); veraux = (Elf32_Vernaux *)((char *)verneed + read_word(verneed->vn_aux)); for (i=0; ivna_name, fixup_string(read_word(veraux->vna_name))); veraux = (Elf32_Vernaux *)((char *)veraux + read_word(veraux->vna_next)); } cont = read_word(verneed->vn_next) != 0; verneed = (Elf32_Verneed *)((char *)verneed + read_word(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; unsigned 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; } unsigned char * generate_new_dynstr(Elf32_Word *size_out) { int size; unsigned 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(read_word(elf_header->e_shoff)); sectionsize = read_half(elf_header->e_shentsize); numsections = read_half(elf_header->e_shnum); for (i=0;ish_size, fixup_size(read_word(section->sh_offset), read_word(section->sh_size))); write_word(§ion->sh_offset, fixup_offset(read_word(section->sh_offset))); write_word(§ion->sh_addr, fixup_addr(read_word(section->sh_addr))); section = (Elf32_Shdr *)((char *)section + sectionsize); } } void remap_segments(void) { Elf32_Phdr *segment; Elf32_Word segmentsize; Elf32_Word p_align; int numsegments; int i = 0; segment = (Elf32_Phdr *)FILE_OFFSET(read_word(elf_header->e_phoff)); segmentsize = read_half(elf_header->e_phentsize); numsegments = read_half(elf_header->e_phnum); for (i=0;ip_filesz, fixup_size(read_word(segment->p_offset), read_word(segment->p_filesz))); write_word(&segment->p_offset, fixup_offset(read_word(segment->p_offset))); write_word(&segment->p_memsz, fixup_addr_size(read_word(segment->p_vaddr), read_word(segment->p_memsz))); write_word(&segment->p_vaddr, fixup_addr(read_word(segment->p_vaddr))); write_word(&segment->p_paddr, read_word(segment->p_vaddr)); /* Consistancy checking: */ p_align = read_word(segment->p_align); if (p_align > 1) { if ((read_word(segment->p_vaddr) - read_word(segment->p_offset))%p_align != 0) { fprintf(stderr, "Warning, creating non-aligned segment addr: %x offset: %x allign: %x\n", read_word(segment->p_vaddr), read_word(segment->p_offset), p_align); } } segment = (Elf32_Phdr *)((char *)segment + segmentsize); } } void remap_elf_header(void) { write_word(&elf_header->e_phoff, fixup_offset(read_word(elf_header->e_phoff))); write_word(&elf_header->e_shoff, fixup_offset(read_word(elf_header->e_shoff))); write_word(&elf_header->e_entry, fixup_addr(read_word(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(read_word(symtab->sh_offset)); symbol_end = (Elf32_Sym *)FILE_OFFSET(read_word(symtab->sh_offset) + read_word(symtab->sh_size)); entry_size = read_word(symtab->sh_entsize); while (symbol < symbol_end) { write_word(&symbol->st_value, fixup_addr(read_word(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; Elf32_Word type; rel_end = (Elf32_Rel *)((char *)rel + size); while (rel < rel_end) { type = ELF32_R_TYPE(read_word(rel->r_info)); switch (machine_type) { case EM_386: if ((type == R_386_RELATIVE) || (type == R_386_JMP_SLOT)) { /* We need to relocate the data this is pointing to too. */ offset = vma_to_offset(read_word(rel->r_offset)); addr = (Elf32_Addr *)FILE_OFFSET(offset); write_word(addr, fixup_addr(read_word(*addr))); } write_word(&rel->r_offset, fixup_addr(read_word(rel->r_offset))); break; case EM_PPC: /* The PPC always uses RELA relocations */ break; } 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; Elf32_Word type; Elf32_Word bitmask; rela_end = (Elf32_Rela *)((char *)rela + size); while (rela < rela_end) { type = ELF32_R_TYPE(read_word(rela->r_info)); switch (machine_type) { case EM_386: if ((type == R_386_RELATIVE) || (type == R_386_JMP_SLOT)) { /* We need to relocate the data this is pointing to too. */ offset = vma_to_offset(read_word(rela->r_offset)); addr = (Elf32_Addr *)FILE_OFFSET(offset); write_word(addr, fixup_addr(read_word(*addr))); } write_word(&rela->r_offset, fixup_addr(read_word(rela->r_offset))); break; case EM_PPC: switch (type) { case R_PPC_RELATIVE: write_word((Elf32_Word *)&rela->r_addend, fixup_addr(read_word(rela->r_addend))); /* Fall through for 32bit offset fixup */ case R_PPC_ADDR32: case R_PPC_GLOB_DAT: case R_PPC_JMP_SLOT: write_word(&rela->r_offset, fixup_addr(read_word(rela->r_offset))); break; case R_PPC_NONE: break; default: fprintf(stderr, "Warning, unhandled PPC relocation type %d\n", type); } break; } rela = (Elf32_Rela *)((char *)rela + entry_size); } } 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 = read_word(got_section->sh_offset); got_data_end = got_data_start + read_word(got_section->sh_size); got = (Elf32_Addr *)FILE_OFFSET(got_data_start); got_end = (Elf32_Addr *)FILE_OFFSET(got_data_end); entry_size = read_word(got_section->sh_entsize); write_word(got, fixup_addr(read_word(*got))); /* Pointer to .dynamic */ } void remap_ppc_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 = read_word(got_section->sh_offset); got_data_end = got_data_start + read_word(got_section->sh_size); got = (Elf32_Addr *)FILE_OFFSET(got_data_start); got_end = (Elf32_Addr *)FILE_OFFSET(got_data_end); entry_size = read_word(got_section->sh_entsize); /* Skip reserved part. * Note that this should really be found by finding the * _GLOBAL_OFFSET_TABLE symbol, as it could (according to * the spec) point to the middle of the got. */ got = (Elf32_Addr *)((char *)got + entry_size); /* Skip blrl instruction */ write_word(got, fixup_addr(read_word(*got))); /* Pointer to .dynamic */ } Elf32_Word get_dynamic_val(Elf32_Shdr *dynamic, Elf32_Sword tag) { Elf32_Dyn *element; Elf32_Word entry_size; entry_size = read_word(dynamic->sh_entsize); element = (Elf32_Dyn *)FILE_OFFSET(read_word(dynamic->sh_offset)); while (read_sword(element->d_tag) != DT_NULL) { if (read_sword(element->d_tag) == tag) { return read_word(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; int jmprel_overlaps; Elf32_Word rel_start, rel_end, jmprel_start, jmprel_end; entry_size = read_word(dynamic->sh_entsize); /* Find out if REL/RELA and JMPREL overlaps: */ if (get_dynamic_val(dynamic, DT_PLTREL) == DT_REL) { rel_start = get_dynamic_val(dynamic, DT_REL); rel_end = rel_start + get_dynamic_val(dynamic, DT_RELSZ); } else { rel_start = get_dynamic_val(dynamic, DT_RELA); rel_end = rel_start + get_dynamic_val(dynamic, DT_RELASZ); } jmprel_start = get_dynamic_val(dynamic, DT_JMPREL); jmprel_overlaps = 0; if ((jmprel_start >= rel_start) && (jmprel_start < rel_end)) jmprel_overlaps = 1; element = (Elf32_Dyn *)FILE_OFFSET(read_word(dynamic->sh_offset)); while (read_sword(element->d_tag) != DT_NULL) { switch(read_sword(element->d_tag)) { case DT_STRSZ: write_word(&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: write_word(&element->d_un.d_ptr, fixup_addr(read_word(element->d_un.d_ptr))); break; case DT_JMPREL: rel_size = get_dynamic_val(dynamic, DT_PLTRELSZ); if (!jmprel_overlaps) { 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(read_word(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(read_word(element->d_un.d_ptr))); remap_rela_section(rela, rel_size, rel_entry_size); } } write_word(&element->d_un.d_ptr, fixup_addr(read_word(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(read_word(element->d_un.d_ptr))); remap_rel_section(rel, rel_size, rel_entry_size); write_word(&element->d_un.d_ptr, fixup_addr(read_word(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(read_word(element->d_un.d_ptr))); remap_rela_section(rela, rel_size, rel_entry_size); write_word(&element->d_un.d_ptr, fixup_addr(read_word(element->d_un.d_ptr))); break; default: /*printf("unhandled d_tag: %d (0x%x)\n", read_sword(element->d_tag), read_sword(element->d_tag));*/ break; } element = (Elf32_Dyn *)((char *)element + entry_size); } } void 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 = read_half(elf_header->e_shentsize); numsections = read_half(elf_header->e_shnum); do { section = (Elf32_Shdr *)FILE_OFFSET(read_word(elf_header->e_shoff)); unaligned = 0; for (i=0;ish_addralign) > 1) && ( (read_word(section->sh_offset) - len + align)%read_word(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; Elf32_Word dynstr_index; Elf32_Shdr *ver_r; Elf32_Shdr *ver_d; char *dynstr_data; unsigned 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; } setup_byteswapping(elf_header->e_ident[EI_DATA]); machine_type = read_half(elf_header->e_machine); if ( (machine_type != EM_386) && (machine_type != EM_PPC) ) { fprintf(stderr, "Unsupported architecture. Supported are: x86, ppc\n"); return 1; } if (read_half(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 = read_word(dynsym->sh_link); dynstr = elf_find_section_num(dynstr_index); dynstr_data = (char *)FILE_OFFSET(read_word(dynstr->sh_offset)); old_dynstr_size = read_word(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 && (read_word(ver_d->sh_link) == dynstr_index)) add_strings_from_ver_d(ver_d, dynstr_data); if (ver_r && (read_word(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 0; } /* Fixup all references: */ fixup_strings_in_dynsym(dynsym); fixup_strings_in_dynamic(dynamic); if (ver_d && (read_word(ver_d->sh_link) == dynstr_index)) fixup_strings_in_ver_d(ver_d); if (ver_r && (read_word(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 = read_word(dynstr->sh_offset) + new_dynstr_size; higher_section = elf_find_next_higher_section(hole_index); hole_end = read_word(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(read_word(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); if (machine_type == EM_386) remap_i386_got(); if (machine_type == EM_PPC) remap_ppc_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); return 0; }