int find_symbol(char *fn, struct lsym *lsym, unsigned long baseaddr) { struct lsym *ls; int num = 0; for (ls = lsym; ls->name; ls++) num++; elf_version(EV_CURRENT); int fd = open(fn, O_RDONLY); if (fd < 0) return -1; long ret = -1; Elf *elf = elf_begin(fd, ELF_C_READ, NULL); if (elf == NULL) goto out; GElf_Ehdr header; if (!gelf_getehdr(elf, &header)) goto out_elf; Elf_Scn *section = NULL; int found = 0; while (found < num && (section = elf_nextscn(elf, section)) != 0) { GElf_Shdr shdr, *sh; sh = gelf_getshdr(section, &shdr); if (sh->sh_type == SHT_SYMTAB || sh->sh_type == SHT_DYNSYM) { Elf_Data *data = elf_getdata(section, NULL); GElf_Sym *sym, symbol; int j; unsigned numsym = sh->sh_size / sh->sh_entsize; for (j = 0; j < numsym; j++) { sym = gelf_getsymshndx(data, NULL, j, &symbol, NULL); char *symname = elf_strptr(elf, shdr.sh_link, sym->st_name); for (ls = lsym; ls->name; ls++) { if (!strcmp(symname, ls->name)) { Elf_Scn *oscn = elf_getscn(elf, sym->st_shndx); GElf_Shdr oshdr, *osh; osh = gelf_getshdr(oscn, &oshdr); ls->addr = (sym->st_value - osh->sh_addr) + osh->sh_offset + baseaddr; found++; if (found == num) break; } } } } } if (found == num) ret = 0; out_elf: elf_end(elf); out: close(fd); return ret; }
int hash_lookup(Elf *elf, section_info_t *hash_info, section_info_t *symtab_info, const char *symname, GElf_Sym *sym_mem) { Elf32_Word *hash_data = (Elf32_Word *)hash_info->data->d_buf; Elf32_Word index; Elf32_Word nbuckets = *hash_data++; Elf32_Word *buckets = ++hash_data; Elf32_Word *chains = hash_data + nbuckets; GElf_Sym *sym; index = buckets[elf_hash(symname) % nbuckets]; while(index != STN_UNDEF) { sym = gelf_getsymshndx (symtab_info->data, NULL, index, sym_mem, NULL); FAILIF_LIBELF(NULL == sym, gelf_getsymshndx); if (!strcmp(symname, elf_strptr(elf, symtab_info->hdr->sh_link, sym->st_name))) break; index = chains[index]; } return index; }
internal_function __libdwfl_getsym (Dwfl_Module *mod, int ndx, GElf_Sym *sym, GElf_Addr *addr, GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *biasp, bool *resolved, bool adjust_st_value) { if (unlikely (mod == NULL)) return NULL; if (unlikely (mod->symdata == NULL)) { int result = INTUSE(dwfl_module_getsymtab) (mod); if (result < 0) return NULL; } /* All local symbols should come before all global symbols. If we have an auxiliary table make sure all the main locals come first, then all aux locals, then all main globals and finally all aux globals. And skip the auxiliary table zero undefined entry. */ GElf_Word shndx; int tndx = ndx; int skip_aux_zero = (mod->syments > 0 && mod->aux_syments > 0) ? 1 : 0; Elf *elf; Elf_Data *symdata; Elf_Data *symxndxdata; Elf_Data *symstrdata; if (mod->aux_symdata == NULL || ndx < mod->first_global) { /* main symbol table (locals). */ tndx = ndx; elf = mod->symfile->elf; symdata = mod->symdata; symxndxdata = mod->symxndxdata; symstrdata = mod->symstrdata; } else if (ndx < mod->first_global + mod->aux_first_global - skip_aux_zero) { /* aux symbol table (locals). */ tndx = ndx - mod->first_global + skip_aux_zero; elf = mod->aux_sym.elf; symdata = mod->aux_symdata; symxndxdata = mod->aux_symxndxdata; symstrdata = mod->aux_symstrdata; } else if ((size_t) ndx < mod->syments + mod->aux_first_global - skip_aux_zero) { /* main symbol table (globals). */ tndx = ndx - mod->aux_first_global + skip_aux_zero; elf = mod->symfile->elf; symdata = mod->symdata; symxndxdata = mod->symxndxdata; symstrdata = mod->symstrdata; } else { /* aux symbol table (globals). */ tndx = ndx - mod->syments + skip_aux_zero; elf = mod->aux_sym.elf; symdata = mod->aux_symdata; symxndxdata = mod->aux_symxndxdata; symstrdata = mod->aux_symstrdata; } sym = gelf_getsymshndx (symdata, symxndxdata, tndx, sym, &shndx); if (unlikely (sym == NULL)) { __libdwfl_seterrno (DWFL_E_LIBELF); return NULL; } if (sym->st_shndx != SHN_XINDEX) shndx = sym->st_shndx; /* Figure out whether this symbol points into an SHF_ALLOC section. */ bool alloc = true; if ((shndxp != NULL || mod->e_type != ET_REL) && (sym->st_shndx == SHN_XINDEX || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF))) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, shndx), &shdr_mem); alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC); } /* In case of an value in an allocated section the main Elf Ebl might know where the real value is (e.g. for function descriptors). */ char *ident; GElf_Addr st_value = sym->st_value & ebl_func_addr_mask (mod->ebl); *resolved = false; if (! adjust_st_value && mod->e_type != ET_REL && alloc && (GELF_ST_TYPE (sym->st_info) == STT_FUNC || (GELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC && (ident = elf_getident (elf, NULL)) != NULL && ident[EI_OSABI] == ELFOSABI_LINUX))) { if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR)) { if (elf != mod->main.elf) { st_value = dwfl_adjusted_st_value (mod, elf, st_value); st_value = dwfl_deadjust_st_value (mod, mod->main.elf, st_value); } *resolved = ebl_resolve_sym_value (mod->ebl, &st_value); if (! *resolved) st_value = sym->st_value; } } if (shndxp != NULL) /* Yield -1 in case of a non-SHF_ALLOC section. */ *shndxp = alloc ? shndx : (GElf_Word) -1; switch (sym->st_shndx) { case SHN_ABS: /* XXX sometimes should use bias?? */ case SHN_UNDEF: case SHN_COMMON: break; default: if (mod->e_type == ET_REL) { /* In an ET_REL file, the symbol table values are relative to the section, not to the module's load base. */ size_t symshstrndx = SHN_UNDEF; Dwfl_Error result = __libdwfl_relocate_value (mod, elf, &symshstrndx, shndx, &st_value); if (unlikely (result != DWFL_E_NOERROR)) { __libdwfl_seterrno (result); return NULL; } } else if (alloc) /* Apply the bias to the symbol value. */ st_value = dwfl_adjusted_st_value (mod, *resolved ? mod->main.elf : elf, st_value); break; } if (adjust_st_value) sym->st_value = st_value; if (addr != NULL) *addr = st_value; if (unlikely (sym->st_name >= symstrdata->d_size)) { __libdwfl_seterrno (DWFL_E_BADSTROFF); return NULL; } if (elfp) *elfp = elf; if (biasp) *biasp = dwfl_adjusted_st_value (mod, elf, 0); return (const char *) symstrdata->d_buf + sym->st_name; }
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 */ unsigned 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; unsigned 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 %zd 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 (%zd) 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[shdr_info[cnt].shdr.sh_link].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 && (size_t)num_symbols != symdata->d_size / elsize, "Length of dynsym filter (%d) must equal the number" " of dynamic symbols (%zd)!\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 %zd).\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 %zd), " "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); }
/* Handle an undefined symbol. We really only support ET_REL for Linux kernel modules, and offline archives. The behavior of the Linux module loader is very simple and easy to mimic. It only matches magically exported symbols, and we match any defined symbols. But we get the same answer except when the module's symbols are undefined and would prevent it from being loaded. */ static Dwfl_Error resolve_symbol (Dwfl_Module *referer, struct reloc_symtab_cache *symtab, GElf_Sym *sym, GElf_Word shndx) { /* First we need its name. */ if (sym->st_name != 0) { if (symtab->symstrdata == NULL) { /* Cache the strtab for this symtab. */ assert (referer->symfile == NULL || referer->symfile->elf != symtab->symelf); symtab->symstrdata = elf_getdata (elf_getscn (symtab->symelf, symtab->strtabndx), NULL); if (unlikely (symtab->symstrdata == NULL)) return DWFL_E_LIBELF; } if (unlikely (sym->st_name >= symtab->symstrdata->d_size)) return DWFL_E_BADSTROFF; const char *name = symtab->symstrdata->d_buf; name += sym->st_name; for (Dwfl_Module *m = referer->dwfl->modulelist; m != NULL; m = m->next) if (m != referer) { /* Get this module's symtab. If we got a fresh error reading the table, report it. If we just have no symbols in this module, no harm done. */ if (m->symdata == NULL && m->symerr == DWFL_E_NOERROR && INTUSE(dwfl_module_getsymtab) (m) < 0 && m->symerr != DWFL_E_NO_SYMTAB) return m->symerr; for (size_t ndx = 1; ndx < m->syments; ++ndx) { sym = gelf_getsymshndx (m->symdata, m->symxndxdata, ndx, sym, &shndx); if (unlikely (sym == NULL)) return DWFL_E_LIBELF; if (sym->st_shndx != SHN_XINDEX) shndx = sym->st_shndx; /* We are looking for a defined global symbol with a name. */ if (shndx == SHN_UNDEF || shndx == SHN_COMMON || GELF_ST_BIND (sym->st_info) == STB_LOCAL || sym->st_name == 0) continue; /* Get this candidate symbol's name. */ if (unlikely (sym->st_name >= m->symstrdata->d_size)) return DWFL_E_BADSTROFF; const char *n = m->symstrdata->d_buf; n += sym->st_name; /* Does the name match? */ if (strcmp (name, n)) continue; /* We found it! */ if (shndx == SHN_ABS) /* XXX maybe should apply bias? */ return DWFL_E_NOERROR; if (m->e_type != ET_REL) { sym->st_value = dwfl_adjusted_st_value (m, sym->st_value); return DWFL_E_NOERROR; } /* In an ET_REL file, the symbol table values are relative to the section, not to the module's load base. */ size_t symshstrndx = SHN_UNDEF; return __libdwfl_relocate_value (m, m->symfile->elf, &symshstrndx, shndx, &sym->st_value); } } } return DWFL_E_RELUNDEF; }
/* This is just doing dwfl_module_getsym, except that we must always use the symbol table in RELOCATED itself when it has one, not MOD->symfile. */ static Dwfl_Error relocate_getsym (Dwfl_Module *mod, Elf *relocated, struct reloc_symtab_cache *cache, int symndx, GElf_Sym *sym, GElf_Word *shndx) { if (cache->symdata == NULL) { if (mod->symfile == NULL || mod->symfile->elf != relocated) { /* We have to look up the symbol table in the file we are relocating, if it has its own. These reloc sections refer to the symbol table in this file, and a symbol table in the main file might not match. However, some tools did produce ET_REL .debug files with relocs but no symtab of their own. */ Elf_Scn *scn = NULL; while ((scn = elf_nextscn (relocated, scn)) != NULL) { GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem); if (shdr != NULL) switch (shdr->sh_type) { default: continue; case SHT_SYMTAB: cache->symelf = relocated; cache->symdata = elf_getdata (scn, NULL); cache->strtabndx = shdr->sh_link; if (unlikely (cache->symdata == NULL)) return DWFL_E_LIBELF; break; case SHT_SYMTAB_SHNDX: cache->symxndxdata = elf_getdata (scn, NULL); if (unlikely (cache->symxndxdata == NULL)) return DWFL_E_LIBELF; break; } if (cache->symdata != NULL && cache->symxndxdata != NULL) break; } } if (cache->symdata == NULL) { /* We might not have looked for a symbol table file yet, when coming from __libdwfl_relocate_section. */ if (unlikely (mod->symfile == NULL) && unlikely (INTUSE(dwfl_module_getsymtab) (mod) < 0)) return dwfl_errno (); /* The symbol table we have already cached is the one from the file being relocated, so it's what we need. Or else this is an ET_REL .debug file with no .symtab of its own; the symbols refer to the section indices in the main file. */ cache->symelf = mod->symfile->elf; cache->symdata = mod->symdata; cache->symxndxdata = mod->symxndxdata; cache->symstrdata = mod->symstrdata; } } if (unlikely (gelf_getsymshndx (cache->symdata, cache->symxndxdata, symndx, sym, shndx) == NULL)) return DWFL_E_LIBELF; if (sym->st_shndx != SHN_XINDEX) *shndx = sym->st_shndx; switch (sym->st_shndx) { case SHN_ABS: case SHN_UNDEF: return DWFL_E_NOERROR; case SHN_COMMON: sym->st_value = 0; /* Value is size, not helpful. */ return DWFL_E_NOERROR; } return __libdwfl_relocate_value (mod, cache->symelf, &cache->symshstrndx, *shndx, &sym->st_value); }
static void print_symtab(Elf *elf, const char *file) { Elf_Scn *scn = NULL; GElf_Shdr shdr; GElf_Ehdr ehdr; size_t shstrndx; if (gelf_getehdr(elf, &ehdr) == NULL) { (void) fprintf(stderr, "%s: elf_getehdr() failed: %s\n", file, elf_errmsg(0)); return; } if (elf_getshdrstrndx(elf, &shstrndx) == -1) { (void) fprintf(stderr, "%s: elf_getshdrstrndx() failed: %s\n", file, elf_errmsg(0)); return; } while ((scn = elf_nextscn(elf, scn)) != NULL) { uint_t symcnt, ndx, nosymshndx; Elf_Data *symdata, *shndxdata; if (gelf_getshdr(scn, &shdr) == NULL) { (void) fprintf(stderr, "%s: elf_getshdr() failed: %s\n", file, elf_errmsg(0)); return; } if ((shdr.sh_type != SHT_SYMTAB) && (shdr.sh_type != SHT_DYNSYM) && (shdr.sh_type != SHT_SUNW_LDYNSYM)) continue; /* * Get the data associated with the Symbol * section. */ if ((symdata = elf_getdata(scn, NULL)) == NULL) { (void) fprintf(stderr, "%s: elf_getdata() failed: %s\n", file, elf_errmsg(0)); return; } /* * Print symbol table title and header for symbol display */ (void) printf("\nSymTab: %s:%s\n", file, elf_strptr(elf, shstrndx, shdr.sh_name)); (void) printf(" index value size type " "bind oth shndx name\n"); /* * We now iterate over the full symbol table printing * the symbols as we go. */ shndxdata = 0; nosymshndx = 0; symcnt = shdr.sh_size / shdr.sh_entsize; for (ndx = 0; ndx < symcnt; ndx++) { GElf_Sym sym; Elf32_Word shndx; uint_t type, bind, specshndx; char bindbuf[INTSTRLEN]; char typebuf[INTSTRLEN]; char shndxbuf[INTSTRLEN]; const char *bindstr, *typestr, *shndxstr; /* * Get a symbol entry */ if (gelf_getsymshndx(symdata, shndxdata, ndx, &sym, &shndx) == NULL) { (void) fprintf(stderr, "%s: gelf_getsymshndx() failed: %s\n", file, elf_errmsg(0)); return; } /* * Check to see if this symbol's st_shndx is using * the 'Extended SHNDX table' for a SYMTAB. * * If it is - and we haven't searched before, go * find the associated SHT_SYMTAB_SHNDX section. */ if ((sym.st_shndx == SHN_XINDEX) && (shndxdata == 0) && (nosymshndx == 0)) { Elf_Scn *_scn = NULL; GElf_Shdr _shdr; GElf_Word symscnndx; specshndx = 0; symscnndx = elf_ndxscn(scn); while ((_scn = elf_nextscn(elf, _scn)) != NULL) { if (gelf_getshdr(_scn, &_shdr) == NULL) break; /* * We've found the Symtab SHNDX table * if it's of type SHT_SYMTAB_SHNDX * and it's shdr.sh_link points to the * section index for the current symbol * table. */ if ((_shdr.sh_type == SHT_SYMTAB_SHNDX) && (_shdr.sh_link == symscnndx) && ((shndxdata = elf_getdata(_scn, NULL)) != NULL)) break; } /* * Get a symbol entry */ if (shndxdata && (gelf_getsymshndx(symdata, shndxdata, ndx, &sym, &shndx) == NULL)) { (void) fprintf(stderr, "%s: gelf_getsymshndx() " "failed: %s\n", file, elf_errmsg(0)); return; } /* * No Symtab SHNDX table was found. We could * give a fatal error here - instead we'll * just mark that fact and display as much of * the symbol table as we can. Any symbol * displayed with a XINDX section index has * a bogus value. */ if (shndxdata == 0) nosymshndx = 1; } /* * Decode the type & binding information */ type = GELF_ST_TYPE(sym.st_info); bind = GELF_ST_BIND(sym.st_info); if (type < STT_NUM) typestr = symtype[type]; else { (void) snprintf(typebuf, INTSTRLEN, "%d", type); typestr = typebuf; } if (bind < STB_NUM) bindstr = symbind[bind]; else { (void) snprintf(bindbuf, INTSTRLEN, "%d", bind); bindstr = bindbuf; } specshndx = 0; if (sym.st_shndx < SHN_LORESERVE) shndx = sym.st_shndx; else if ((sym.st_shndx != SHN_XINDEX) || (shndxdata == NULL)) { shndx = sym.st_shndx; specshndx = 1; } if (shndx == SHN_UNDEF) { shndxstr = (const char *)"UNDEF"; } else if (specshndx) { if (shndx == SHN_ABS) shndxstr = (const char *)"ABS"; else if (shndx == SHN_COMMON) shndxstr = (const char *)"COMM"; else if (shndx == SHN_XINDEX) shndxstr = (const char *)"XIND"; else { (void) snprintf(shndxbuf, INTSTRLEN, "%ld", shndx); shndxstr = shndxbuf; } } else { (void) snprintf(shndxbuf, INTSTRLEN, "%ld", shndx); shndxstr = shndxbuf; } /* * Display the symbol entry. */ (void) printf("[%3d] 0x%08llx 0x%08llx %-4s " "%-6s %2d %5s %s\n", ndx, sym.st_value, sym.st_size, typestr, bindstr, sym.st_other, shndxstr, elf_strptr(elf, shdr.sh_link, sym.st_name)); } } }
const char * dwfl_module_getsym (Dwfl_Module *mod, int ndx, GElf_Sym *sym, GElf_Word *shndxp) { if (unlikely (mod == NULL)) return NULL; if (unlikely (mod->symdata == NULL)) { int result = INTUSE(dwfl_module_getsymtab) (mod); if (result < 0) return NULL; } GElf_Word shndx; sym = gelf_getsymshndx (mod->symdata, mod->symxndxdata, ndx, sym, &shndx); if (unlikely (sym == NULL)) { __libdwfl_seterrno (DWFL_E_LIBELF); return NULL; } if (sym->st_shndx != SHN_XINDEX) shndx = sym->st_shndx; /* Figure out whether this symbol points into an SHF_ALLOC section. */ bool alloc = true; if ((shndxp != NULL || mod->e_type != ET_REL) && (sym->st_shndx == SHN_XINDEX || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF))) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (elf_getscn (mod->symfile->elf, shndx), &shdr_mem); alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC); } if (shndxp != NULL) /* Yield -1 in case of a non-SHF_ALLOC section. */ *shndxp = alloc ? shndx : (GElf_Word) -1; switch (sym->st_shndx) { case SHN_ABS: /* XXX sometimes should use bias?? */ case SHN_UNDEF: case SHN_COMMON: break; default: if (mod->e_type == ET_REL) { /* In an ET_REL file, the symbol table values are relative to the section, not to the module's load base. */ size_t symshstrndx = SHN_UNDEF; Dwfl_Error result = __libdwfl_relocate_value (mod, mod->symfile->elf, &symshstrndx, shndx, &sym->st_value); if (unlikely (result != DWFL_E_NOERROR)) { __libdwfl_seterrno (result); return NULL; } } else if (alloc) /* Apply the bias to the symbol value. */ sym->st_value += mod->symfile->bias; break; } if (unlikely (sym->st_name >= mod->symstrdata->d_size)) { __libdwfl_seterrno (DWFL_E_BADSTROFF); return NULL; } return (const char *) mod->symstrdata->d_buf + sym->st_name; }