From dbace2389b6f89cd4f8f2713aac50a16b16964a2 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sat, 6 Aug 2005 01:37:23 +0000 Subject: [PATCH] Many improvements in elflint, mostly symbol versioning related. Cosmetic changes in readelf. --- NEWS | 2 +- src/ChangeLog | 7 + src/elflint.c | 564 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/readelf.c | 113 +++++------- 4 files changed, 590 insertions(+), 96 deletions(-) diff --git a/NEWS b/NEWS index 5e206a1..264742f 100644 --- a/NEWS +++ b/NEWS @@ -2,7 +2,7 @@ Version 0.112: elfcmp: some more relaxation. -elflint: some more tests. +elflint: many more tests, especially regarding to symbol versioning. libelf: Add elfXX_offscn and gelf_offscn. diff --git a/src/ChangeLog b/src/ChangeLog index f6b5284..6533861 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,10 @@ +2005-08-05 Ulrich Drepper + + * elflint.c: Many, many more tests. Mostly related to symbol + versioning. Those sections should now be completely checked. + + * readelf.c (print_dynamic): Use gelf_offscn. + 2005-08-04 Ulrich Drepper * elflint.c: Add lots more tests: more extension symbol table sanity, diff --git a/src/elflint.c b/src/elflint.c index f97f7a9..0896496 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -540,20 +540,11 @@ section [%2d] '%s': section group [%2zu] '%s' does not preceed group member\n"), static void -check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int idx) +check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { bool no_xndx_warned = false; int no_pt_tls = 0; - - Elf_Scn *scn = elf_getscn (ebl->elf, idx); - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - GElf_Shdr strshdr_mem; - GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), - &strshdr_mem); - if (shdr == NULL || strshdr == NULL) - return; - Elf_Data *data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); if (data == NULL) { ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), @@ -561,6 +552,12 @@ check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, int idx) return; } + GElf_Shdr strshdr_mem; + GElf_Shdr *strshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), + &strshdr_mem); + if (strshdr == NULL) + return; + if (strshdr->sh_type != SHT_STRTAB) ERROR (gettext ("section [%2d] '%s': referenced as string table for section [%2d] '%s' but type is not SHT_STRTAB\n"), shdr->sh_link, section_name (ebl, shdr->sh_link), @@ -1863,9 +1860,9 @@ static const struct { ".text", 6, SHT_PROGBITS, exact, SHF_ALLOC | SHF_EXECINSTR, 0 }, /* The following are GNU extensions. */ - { ".gnu.version", 12, SHT_GNU_versym, exact, SHF_ALLOC, 0 }, - { ".gnu.version_d", 14, SHT_GNU_verdef, exact, SHF_ALLOC, 0 }, - { ".gnu.version_r", 14, SHT_GNU_verneed, exact, SHF_ALLOC, 0 } + { ".gnu.version", 13, SHT_GNU_versym, exact, SHF_ALLOC, 0 }, + { ".gnu.version_d", 15, SHT_GNU_verdef, exact, SHF_ALLOC, 0 }, + { ".gnu.version_r", 15, SHT_GNU_verneed, exact, SHF_ALLOC, 0 } }; #define nspecial_sections \ (sizeof (special_sections) / sizeof (special_sections[0])) @@ -1922,14 +1919,117 @@ section_flags_string (GElf_Word flags, char *buf, size_t len) } +static int +has_copy_reloc (Ebl *ebl, unsigned int symscnndx, unsigned int symndx) +{ + /* First find the relocation section for the symbol table. */ + Elf_Scn *scn = NULL; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL + && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + && shdr->sh_link == symscnndx) + /* Found the section. */ + break; + } + + if (scn == NULL) + return 0; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + return 0; + + if (shdr->sh_type == SHT_REL) + for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i) + { + GElf_Rel rel_mem; + GElf_Rel *rel = gelf_getrel (data, i, &rel_mem); + if (rel == NULL) + continue; + + if (GELF_R_SYM (rel->r_info) == symndx + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info))) + return 1; + } + else + for (int i = 0; (size_t) i < shdr->sh_size / shdr->sh_entsize; ++i) + { + GElf_Rela rela_mem; + GElf_Rela *rela = gelf_getrela (data, i, &rela_mem); + if (rela == NULL) + continue; + + if (GELF_R_SYM (rela->r_info) == symndx + && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info))) + return 1; + } + + return 0; +} + + +static struct version_namelist +{ + const char *objname; + const char *name; + GElf_Word ndx; + enum { ver_def, ver_need } type; + struct version_namelist *next; +} *version_namelist; + + +static int +add_version (const char *objname, const char *name, GElf_Word ndx, int type) +{ + /* Check that there are no duplications. */ + struct version_namelist *nlp = version_namelist; + while (nlp != NULL) + { + if (((nlp->objname == NULL && objname == NULL) + || (nlp->objname != NULL && objname != NULL + && strcmp (nlp->objname, objname) == 0)) + && strcmp (nlp->name, name) == 0) + return nlp->type == ver_def ? 1 : -1; + nlp = nlp->next; + } + + nlp = xmalloc (sizeof (*nlp)); + nlp->objname = objname; + nlp->name = name; + nlp->ndx = ndx; + nlp->type = type; + nlp->next = version_namelist; + version_namelist = nlp; + + return 0; +} + + static void -check_versym (Ebl *ebl, GElf_Shdr *shdr, int idx) +check_versym (Ebl *ebl, int idx) { - /* The number of elements in the version symbol table must be the - same as the number of symbols. */ + Elf_Scn *scn = elf_getscn (ebl->elf, idx); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + /* The error has already been reported. */ + return; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + Elf_Scn *symscn = elf_getscn (ebl->elf, shdr->sh_link); GElf_Shdr symshdr_mem; - GElf_Shdr *symshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), - &symshdr_mem); + GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem); if (symshdr == NULL) /* The error has already been reported. */ return; @@ -1943,6 +2043,8 @@ section [%2d] '%s' refers in sh_link to section [%2d] '%s' which is no dynamic s return; } + /* The number of elements in the version symbol table must be the + same as the number of symbols. */ if (shdr->sh_size / shdr->sh_entsize != symshdr->sh_size / symshdr->sh_entsize) ERROR (gettext ("\ @@ -1950,17 +2052,123 @@ section [%2d] '%s' has different number of entries than symbol table [%2d] '%s'\ idx, section_name (ebl, idx), shdr->sh_link, section_name (ebl, shdr->sh_link)); - // XXX TODO A lot more tests - // check value of the fields. local symbols must have zero entries. - // nonlocal symbols refer to valid version. Check that version index - // in bound. + Elf_Data *symdata = elf_getdata (symscn, NULL); + if (symdata == NULL) + /* The error has already been reported. */ + return; + + for (int cnt = 1; (size_t) cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + { + GElf_Versym versym_mem; + GElf_Versym *versym = gelf_getversym (data, cnt, &versym_mem); + if (versym == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: cannot read version data\n"), + idx, section_name (ebl, idx), cnt); + break; + } + + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsym (symdata, cnt, &sym_mem); + if (sym == NULL) + /* Already reported elsewhere. */ + continue; + + if (*versym == VER_NDX_LOCAL) + { + /* Local symbol. Make sure it is defined unless the + reference is weak. */ + if (sym->st_shndx == SHN_UNDEF + && GELF_ST_BIND (sym->st_info) != STB_WEAK) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: undefined symbol with local scope\n"), + idx, section_name (ebl, idx), cnt); + } + else if (*versym == VER_NDX_GLOBAL) + { + /* Global symbol. Make sure it is not defined as local. */ + if (GELF_ST_BIND (sym->st_info) == STB_LOCAL) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: local symbol with global scope\n"), + idx, section_name (ebl, idx), cnt); + } + else + { + /* Look through the list of defined versions and locate the + index we need for this symbol. */ + struct version_namelist *runp = version_namelist; + while (runp != NULL) + if (runp->ndx == *versym) + break; + else + runp = runp->next; + + if (runp == NULL) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: invalid version index %d\n"), + idx, section_name (ebl, idx), cnt, (int) *versym); + else if (sym->st_shndx == SHN_UNDEF + && runp->type == ver_def) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: version index %d is for defined version\n"), + idx, section_name (ebl, idx), cnt, (int) *versym); + else if (sym->st_shndx != SHN_UNDEF + && runp->type == ver_need) + { + /* Unless this symbol has a copy relocation associated + this must not happen. */ + if (!has_copy_reloc (ebl, shdr->sh_link, cnt)) + ERROR (gettext ("\ +section [%2d] '%s': symbol %d: version index %d is for requested version\n"), + idx, section_name (ebl, idx), cnt, (int) *versym); + } + } + } +} + + +static int +unknown_dependency_p (Elf *elf, GElf_Ehdr *ehdr, const char *fname) +{ + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = NULL; + + int i; + for (i = 0; i < ehdr->e_phnum; ++i) + if ((phdr = gelf_getphdr (elf, i, &phdr_mem)) != NULL + && phdr->p_type == PT_DYNAMIC) + break; + + if (i == ehdr->e_phnum) + return 1; + assert (phdr != NULL); + Elf_Scn *scn = gelf_offscn (elf, phdr->p_offset); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + Elf_Data *data = elf_getdata (scn, NULL); + if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC && data != NULL) + for (size_t j = 0; j < shdr->sh_size / shdr->sh_entsize; ++j) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem); + if (dyn != NULL && dyn->d_tag == DT_NEEDED) + { + const char *str = elf_strptr (elf, shdr->sh_link, dyn->d_un.d_val); + if (str != NULL && strcmp (str, fname) == 0) + /* Found it. */ + return 0; + } + } + + return 1; } static unsigned int nverneed; static void -check_verneed (Ebl *ebl, GElf_Shdr *shdr, int idx) +check_verneed (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { if (++nverneed == 2) ERROR (gettext ("more than one version reference section present\n")); @@ -1974,6 +2182,115 @@ check_verneed (Ebl *ebl, GElf_Shdr *shdr, int idx) ERROR (gettext ("\ section [%2d] '%s': sh_link does not link to string table\n"), idx, section_name (ebl, idx)); + + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); + if (data == NULL) + { + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + unsigned int offset = 0; + for (int cnt = shdr->sh_info; --cnt >= 0; ) + { + /* Get the data at the next offset. */ + GElf_Verneed needmem; + GElf_Verneed *need = gelf_getverneed (data, offset, &needmem); + if (need == NULL) + break; + + unsigned int auxoffset = offset + need->vn_aux; + + if (need->vn_version != EV_CURRENT) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong version %d\n"), + idx, section_name (ebl, idx), cnt, (int) need->vn_version); + + if (need->vn_cnt > 0 && need->vn_aux < gelf_fsize (ebl->elf, ELF_T_VNEED, + 1, EV_CURRENT)) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"), + idx, section_name (ebl, idx), cnt); + + const char *libname = elf_strptr (ebl->elf, shdr->sh_link, + need->vn_file); + if (libname == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': entry %d has invalid file reference\n"), + idx, section_name (ebl, idx), cnt); + goto next_need; + } + + /* Check that there is a DT_NEEDED entry for the referenced library. */ + if (unknown_dependency_p (ebl->elf, ehdr, libname)) + ERROR (gettext ("\ +section [%2d] '%s': entry %d references unknown dependency\n"), + idx, section_name (ebl, idx), cnt); + + for (int cnt2 = need->vn_cnt; --cnt2 >= 0; ) + { + GElf_Vernaux auxmem; + GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem); + if (aux == NULL) + break; + + if ((aux->vna_flags & ~VER_FLG_WEAK) != 0) + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has unknown flag\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt); + + const char *verstr = elf_strptr (ebl->elf, shdr->sh_link, + aux->vna_name); + if (verstr == NULL) + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has invalid name reference\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt); + else + { + GElf_Word hashval = elf_hash (verstr); + if (hashval != aux->vna_hash) + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has wrong hash value: %#x, expected %#x\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, + cnt, (int) hashval, (int) aux->vna_hash); + + int res = add_version (libname, verstr, aux->vna_other, + ver_need); + if (unlikely (res !=0)) + { + assert (res > 0); + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has duplicate version name '%s'\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, + cnt, verstr); + } + } + + if ((aux->vna_next != 0 || cnt2 > 0) + && aux->vna_next < gelf_fsize (ebl->elf, ELF_T_VNAUX, 1, + EV_CURRENT)) + { + ERROR (gettext ("\ +section [%2d] '%s': auxiliary entry %d of entry %d has wrong next field\n"), + idx, section_name (ebl, idx), need->vn_cnt - cnt2, cnt); + break; + } + + auxoffset += MAX (aux->vna_next, + gelf_fsize (ebl->elf, ELF_T_VNAUX, 1, EV_CURRENT)); + } + + /* Find the next offset. */ + next_need: + offset += need->vn_next; + + if ((need->vn_next != 0 || cnt > 0) + && offset < auxoffset) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has invalid offset to next entry\n"), + idx, section_name (ebl, idx), cnt); + } } @@ -1994,6 +2311,160 @@ check_verdef (Ebl *ebl, GElf_Shdr *shdr, int idx) ERROR (gettext ("\ section [%2d] '%s': sh_link does not link to string table\n"), idx, section_name (ebl, idx)); + + Elf_Data *data = elf_getdata (elf_getscn (ebl->elf, idx), NULL); + if (data == NULL) + { + no_data: + ERROR (gettext ("section [%2d] '%s': cannot get section data\n"), + idx, section_name (ebl, idx)); + return; + } + + /* Iterate over all version definition entries. We check that there + is a BASE entry and that each index is unique. To do the later + we collection the information in a list which is later + examined. */ + struct namelist + { + const char *name; + struct namelist *next; + } *namelist = NULL; + struct namelist *refnamelist = NULL; + + bool has_base = false; + unsigned int offset = 0; + for (int cnt = shdr->sh_info; --cnt >= 0; ) + { + /* Get the data at the next offset. */ + GElf_Verdef defmem; + GElf_Verdef *def = gelf_getverdef (data, offset, &defmem); + if (def == NULL) + goto no_data; + + if ((def->vd_flags & VER_FLG_BASE) != 0) + { + if (has_base) + ERROR (gettext ("\ +section [%2d] '%s': more than one BASE definition\n"), + idx, section_name (ebl, idx)); + if (def->vd_ndx != VER_NDX_GLOBAL) + ERROR (gettext ("\ +section [%2d] '%s': BASE definition must have index VER_NDX_GLOBAL\n"), + idx, section_name (ebl, idx)); + has_base = true; + } + if ((def->vd_flags & ~(VER_FLG_BASE|VER_FLG_WEAK)) != 0) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has unknown flag\n"), + idx, section_name (ebl, idx), cnt); + + if (def->vd_version != EV_CURRENT) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong version %d\n"), + idx, section_name (ebl, idx), cnt, (int) def->vd_version); + + if (def->vd_cnt > 0 && def->vd_aux < gelf_fsize (ebl->elf, ELF_T_VDEF, + 1, EV_CURRENT)) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong offset of auxiliary data\n"), + idx, section_name (ebl, idx), cnt); + + unsigned int auxoffset = offset + def->vd_aux; + GElf_Verdaux auxmem; + GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem); + if (aux == NULL) + goto no_data; + + const char *name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name); + if (name == NULL) + { + ERROR (gettext ("\ +section [%2d] '%s': entry %d has invalid name reference\n"), + idx, section_name (ebl, idx), cnt); + goto next_def; + } + GElf_Word hashval = elf_hash (name); + if (def->vd_hash != hashval) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong hash value: %#x, expected %#x\n"), + idx, section_name (ebl, idx), cnt, (int) hashval, + (int) def->vd_hash); + + int res = add_version (NULL, name, def->vd_ndx, ver_def); + if (unlikely (res !=0)) + { + assert (res > 0); + ERROR (gettext ("\ +section [%2d] '%s': entry %d has duplicate version name '%s'\n"), + idx, section_name (ebl, idx), cnt, name); + } + + struct namelist *newname = alloca (sizeof (*newname)); + newname->name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name); + newname->next = namelist; + namelist = newname; + + auxoffset += aux->vda_next; + for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) + { + aux = gelf_getverdaux (data, auxoffset, &auxmem); + if (aux == NULL) + goto no_data; + + newname = alloca (sizeof (*newname)); + newname->name = elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name); + newname->next = refnamelist; + refnamelist = newname; + + if ((aux->vda_next != 0 || cnt2 + 1 < def->vd_cnt) + && aux->vda_next < gelf_fsize (ebl->elf, ELF_T_VDAUX, 1, + EV_CURRENT)) + { + ERROR (gettext ("\ +section [%2d] '%s': entry %d has wrong next field in auxiliary data\n"), + idx, section_name (ebl, idx), cnt); + break; + } + + auxoffset += MAX (aux->vda_next, + gelf_fsize (ebl->elf, ELF_T_VDAUX, 1, EV_CURRENT)); + } + + /* Find the next offset. */ + next_def: + offset += def->vd_next; + + if ((def->vd_next != 0 || cnt > 0) + && offset < auxoffset) + ERROR (gettext ("\ +section [%2d] '%s': entry %d has invalid offset to next entry\n"), + idx, section_name (ebl, idx), cnt); + } + + if (!has_base) + ERROR (gettext ("section [%2d] '%s': no BASE definition\n"), + idx, section_name (ebl, idx)); + + /* Check whether the referenced names are available. */ + while (namelist != NULL) + { + struct version_namelist *runp = version_namelist; + while (runp != NULL) + { + if (runp->type == ver_def + && strcmp (runp->name, namelist->name) == 0) + break; + runp = runp->next; + } + + if (runp == NULL) + ERROR (gettext ("\ +section [%2d] '%s': unknown parent version '%s'\n"), + idx, section_name (ebl, idx), namelist->name); + + namelist = namelist->next; + } } @@ -2044,6 +2515,7 @@ zeroth section has nonzero link value while ELF header does not signal overflow bool dot_interp_section = false; + size_t versym_scnndx = 0; for (size_t cnt = 1; cnt < shnum; ++cnt) { shdr = gelf_getshdr (elf_getscn (ebl->elf, cnt), &shdr_mem); @@ -2298,7 +2770,7 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"), cnt, section_name (ebl, cnt)); /* FALLTHROUGH */ case SHT_SYMTAB: - check_symtab (ebl, ehdr, cnt); + check_symtab (ebl, ehdr, shdr, cnt); break; case SHT_RELA: @@ -2330,11 +2802,16 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"), break; case SHT_GNU_versym: - check_versym (ebl, shdr, cnt); + /* We cannot process this section now since we have no guarantee + that the verneed and verdef sections have already been read. + Just remember the section index. */ + if (versym_scnndx != 0) + ERROR (gettext ("more than one version symbol table present\n")); + versym_scnndx = cnt; break; case SHT_GNU_verneed: - check_verneed (ebl, shdr, cnt); + check_verneed (ebl, ehdr, shdr, cnt); break; case SHT_GNU_verdef: @@ -2350,6 +2827,39 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"), if (has_interp_segment && !dot_interp_section) ERROR (gettext ("INTERP program header entry but no .interp section\n")); + if (version_namelist != NULL) + { + if (versym_scnndx == 0) + ERROR (gettext ("\ +no .gnu.versym section present but .gnu.versym_d or .gnu.versym_r section exist\n")); + else + check_versym (ebl, versym_scnndx); + + /* Check for duplicate index numbers. */ + do + { + struct version_namelist *runp = version_namelist->next; + while (runp != NULL) + { + if (version_namelist->ndx == runp->ndx) + { + ERROR (gettext ("duplicate version index %d\n"), + (int) version_namelist->ndx); + break; + } + runp = runp->next; + } + + struct version_namelist *old = version_namelist; + version_namelist = version_namelist->next; + free (old); + } + while (version_namelist != NULL); + } + else if (versym_scnndx != 0) + ERROR (gettext ("\ +.gnu.versym section present without .gnu.versym_d or .gnu.versym_r\n")); + free (scnref); } diff --git a/src/readelf.c b/src/readelf.c index 95532fa..7100b8e 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -159,7 +159,7 @@ static void print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr); static void print_shdr (Ebl *ebl, GElf_Ehdr *ehdr); static void print_phdr (Ebl *ebl, GElf_Ehdr *ehdr); static void print_scngrp (Ebl *ebl); -static void print_dynamic (Ebl *ebl); +static void print_dynamic (Ebl *ebl, GElf_Ehdr *ehdr); static void print_relocs (Ebl *ebl); static void handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); static void handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr); @@ -479,7 +479,7 @@ process_elf_file (Elf *elf, const char *prefix, const char *fname, if (print_section_groups) print_scngrp (ebl); if (print_dynamic_table) - print_dynamic (ebl); + print_dynamic (ebl, ehdr); if (print_relocations) print_relocs (ebl); if (print_histogram) @@ -1264,20 +1264,20 @@ handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Print the dynamic segment. */ static void -print_dynamic (Ebl *ebl) +print_dynamic (Ebl *ebl, GElf_Ehdr *ehdr) { - /* Find all relocation sections and handle them. */ - Elf_Scn *scn = NULL; - - while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + for (int i = 0; i < ehdr->e_phnum; ++i) { - /* Handle the section if it is a symbol table. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, i, &phdr_mem); - if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC) + if (phdr != NULL && phdr->p_type == PT_DYNAMIC) { - handle_dynamic (ebl, scn, shdr); + Elf_Scn *scn = gelf_offscn (ebl->elf, phdr->p_offset); + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr != NULL && shdr->sh_type == SHT_DYNAMIC) + handle_dynamic (ebl, scn, shdr); break; } } @@ -1943,23 +1943,20 @@ get_ver_flags (unsigned int flags) static void handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { - Elf_Data *data; int class = gelf_getclass (ebl->elf); - GElf_Shdr glink; - int cnt; - unsigned int offset; - size_t shstrndx; /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL) return; /* Get the section header string table index. */ + size_t shstrndx; if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); + GElf_Shdr glink; printf (ngettext ("\ \nVersion needs section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", "\ @@ -1974,16 +1971,12 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &glink)->sh_name)); - offset = 0; - for (cnt = shdr->sh_info; --cnt >= 0; ) + unsigned int offset = 0; + for (int cnt = shdr->sh_info; --cnt >= 0; ) { - GElf_Verneed needmem; - GElf_Verneed *need; - unsigned int auxoffset; - int cnt2; - /* Get the data at the next offset. */ - need = gelf_getverneed (data, offset, &needmem); + GElf_Verneed needmem; + GElf_Verneed *need = gelf_getverneed (data, offset, &needmem); if (need == NULL) break; @@ -1992,13 +1985,11 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) elf_strptr (ebl->elf, shdr->sh_link, need->vn_file), (unsigned short int) need->vn_cnt); - auxoffset = offset + need->vn_aux; - for (cnt2 = need->vn_cnt; --cnt2 >= 0; ) + unsigned int auxoffset = offset + need->vn_aux; + for (int cnt2 = need->vn_cnt; --cnt2 >= 0; ) { GElf_Vernaux auxmem; - GElf_Vernaux *aux; - - aux = gelf_getvernaux (data, auxoffset, &auxmem); + GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem); if (aux == NULL) break; @@ -2020,23 +2011,19 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) static void handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { - Elf_Data *data; - int class = gelf_getclass (ebl->elf); - GElf_Shdr glink; - int cnt; - unsigned int offset; - size_t shstrndx; - /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL) return; /* Get the section header string table index. */ + size_t shstrndx; if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); + int class = gelf_getclass (ebl->elf); + GElf_Shdr glink; printf (ngettext ("\ \nVersion definition section [%2u] '%s' contains %d entry:\n Addr: %#0*" PRIx64 " Offset: %#08" PRIx64 " Link to section: [%2u] '%s'\n", "\ @@ -2052,23 +2039,18 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_link), &glink)->sh_name)); - offset = 0; - for (cnt = shdr->sh_info; --cnt >= 0; ) + unsigned int offset = 0; + for (int cnt = shdr->sh_info; --cnt >= 0; ) { - GElf_Verdef defmem; - GElf_Verdef *def; - GElf_Verdaux auxmem; - GElf_Verdaux *aux; - unsigned int auxoffset; - int cnt2; - /* Get the data at the next offset. */ - def = gelf_getverdef (data, offset, &defmem); + GElf_Verdef defmem; + GElf_Verdef *def = gelf_getverdef (data, offset, &defmem); if (def == NULL) break; - auxoffset = offset + def->vd_aux; - aux = gelf_getverdaux (data, auxoffset, &auxmem); + unsigned int auxoffset = offset + def->vd_aux; + GElf_Verdaux auxmem; + GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem); if (aux == NULL) break; @@ -2081,7 +2063,7 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) elf_strptr (ebl->elf, shdr->sh_link, aux->vda_name)); auxoffset += aux->vda_next; - for (cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) + for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) { aux = gelf_getverdaux (data, auxoffset, &auxmem); if (aux == NULL) @@ -2103,34 +2085,28 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) static void handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { - Elf_Data *data; int class = gelf_getclass (ebl->elf); - Elf_Scn *verscn; GElf_Shdr glink; - Elf_Scn *defscn; - Elf_Scn *needscn; const char **vername; const char **filename; - size_t nvername; - unsigned int cnt; - size_t shstrndx; /* Get the data of the section. */ - data = elf_getdata (scn, NULL); + Elf_Data *data = elf_getdata (scn, NULL); if (data == NULL) return; /* Get the section header string table index. */ + size_t shstrndx; if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); /* We have to find the version definition section and extract the version names. */ - defscn = NULL; - needscn = NULL; + Elf_Scn *defscn = NULL; + Elf_Scn *needscn = NULL; - verscn = NULL; + Elf_Scn *verscn = NULL; while ((verscn = elf_nextscn (ebl->elf, verscn)) != NULL) { GElf_Shdr vershdr_mem; @@ -2145,6 +2121,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) } } + size_t nvername; if (defscn != NULL || needscn != NULL) { /* We have a version information (better should have). Now get @@ -2167,7 +2144,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) if (defshdr == NULL) return; - for (cnt = 0; cnt < defshdr->sh_info; ++cnt) + for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt) { GElf_Verdef defmem; GElf_Verdef *def; @@ -2197,7 +2174,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) if (needshdr == NULL) return; - for (cnt = 0; cnt < needshdr->sh_info; ++cnt) + for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt) { GElf_Verneed needmem; GElf_Verneed *need; @@ -2255,7 +2232,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) if (defshdr == NULL) return; - for (cnt = 0; cnt < defshdr->sh_info; ++cnt) + for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt) { GElf_Verdef defmem; GElf_Verdef *def; @@ -2293,7 +2270,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) if (needshdr == NULL) return; - for (cnt = 0; cnt < needshdr->sh_info; ++cnt) + for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt) { GElf_Verneed needmem; GElf_Verneed *need; @@ -2352,7 +2329,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) &glink)->sh_name)); /* Now we can finally look at the actual contents of this section. */ - for (cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + for (unsigned int cnt = 0; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) { GElf_Versym symmem; GElf_Versym *sym; -- 2.7.4