From: Ulrich Drepper Date: Fri, 25 Jan 2008 00:18:42 +0000 (+0000) Subject: Remove magic assignment of indeces in the dynsym X-Git-Tag: elfutils-0.133~14 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0e864dd86871c809668c557985ca19344dfff787;p=thirdparty%2Felfutils.git Remove magic assignment of indeces in the dynsym section. Start implementation of --hash-style. --- diff --git a/src/ChangeLog b/src/ChangeLog index 5b2d0c128..e7ed847aa 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,14 @@ +2008-01-24 Ulrich Drepper + + * elflint.c (check_group): Check that signature symbol for section + group is not an empty string. + * ldgeneric.c: Remove magic assignment of indeces in the dynsym + section. Start implementation of --hash-style. + * i386_ld.c: Likewise. + * ld.c: Recognize --hash-style. + * ld.h (struct scninfo): Add comdat_group. + Add additional parameter to finalize_plt callback. + 2008-01-22 Ulrich Drepper * ld.h (struct callbacks): Add initialize_gotplt. diff --git a/src/elflint.c b/src/elflint.c index e4ebac4c1..100328742 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -2352,9 +2352,9 @@ section [%2d] '%s': section groups only allowed in relocatable object files\n"), } /* Check that sh_link is an index of a symbol table. */ + 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) ERROR (gettext ("section [%2d] '%s': cannot get symbol table: %s\n"), idx, section_name (ebl, idx), elf_errmsg (-1)); @@ -2375,6 +2375,19 @@ section [%2d] '%s': invalid symbol index in sh_info\n"), ERROR (gettext ("section [%2d] '%s': sh_flags not zero\n"), idx, section_name (ebl, idx)); + GElf_Sym sym_data; + GElf_Sym *sym = gelf_getsym (elf_getdata (symscn, NULL), shdr->sh_info, + &sym_data); + if (sym == NULL) + ERROR (gettext ("\ +section [%2d] '%s': cannot get symbol for signature\n"), + idx, section_name (ebl, idx)); + else if (strcmp (elf_strptr (ebl->elf, symshdr->sh_link, sym->st_name), + "") == 0) + ERROR (gettext ("\ +section [%2d] '%s': signature symbol canot be empty string\n"), + idx, section_name (ebl, idx)); + if (be_strict && shdr->sh_entsize != elf32_fsize (ELF_T_WORD, 1, EV_CURRENT)) ERROR (gettext ("section [%2d] '%s': sh_flags not set correctly\n"), diff --git a/src/i386_ld.c b/src/i386_ld.c index 029e939d2..a3182442e 100644 --- a/src/i386_ld.c +++ b/src/i386_ld.c @@ -360,25 +360,23 @@ struct plt_entry static void elf_i386_finalize_plt (struct ld_state *statep, size_t nsym, - size_t nsym_dyn __attribute__ ((unused))) + size_t nsym_local, struct symbol **ndxtosym) { + if (unlikely (statep->nplt + statep->ngot == 0)) + /* Nothing to be done. */ + return; + Elf_Scn *scn; XElf_Shdr_vardef (shdr); Elf_Data *data; - Elf_Data *symdata = NULL; - Elf_Data *dynsymdata; - size_t cnt; const bool build_dso = statep->file_type == dso_file_type; - if (unlikely (statep->nplt + statep->ngot == 0)) - /* Nothing to be done. */ - return; - - /* Get the address of the got section. */ + /* Get the address of the .got.plt section. */ scn = elf_getscn (statep->outelf, statep->gotpltscnidx); xelf_getshdr (scn, shdr); data = elf_getdata (scn, NULL); assert (shdr != NULL && data != NULL); + /* The address points to the .got.plt section, not the .got section. */ Elf32_Addr gotaddr = shdr->sh_addr; /* Now create the initial values for the .got.plt section. The @@ -394,13 +392,15 @@ elf_i386_finalize_plt (struct ld_state *statep, size_t nsym, /* The PLT contains code which a user of a function jumps to. The first PLT entry is special, so the first used one has the index 1. */ scn = elf_getscn (statep->outelf, statep->pltscnidx); - xelf_getshdr (scn, shdr); - assert (shdr != NULL); + XElf_Shdr_vardef (pltshdr); + xelf_getshdr (scn, pltshdr); + assert (pltshdr != NULL); - dynsymdata = elf_getdata (elf_getscn (statep->outelf, statep->dynsymscnidx), - NULL); + Elf_Data *dynsymdata = elf_getdata (elf_getscn (statep->outelf, + statep->dynsymscnidx), NULL); assert (dynsymdata != NULL); + Elf_Data *symdata = NULL; if (statep->symscnidx != 0) { symdata = elf_getdata (elf_getscn (statep->outelf, statep->symscnidx), @@ -408,47 +408,40 @@ elf_i386_finalize_plt (struct ld_state *statep, size_t nsym, assert (symdata != NULL); } - for (cnt = 0; cnt < statep->nplt; ++cnt) - { - assert ((4 + cnt) * sizeof (Elf32_Word) <= data->d_size); - - /* Address in the PLT. */ - Elf32_Addr pltentryaddr = shdr->sh_addr + (1 + cnt) * PLT_ENTRY_SIZE; - - /* Point the GOT entry at the PLT entry, after the initial jmp. */ - ((Elf32_Word *) data->d_buf)[3 + cnt] = pltentryaddr + 6; - - /* If the symbol is defined, adjust the address. */ - if (((Elf32_Sym *) dynsymdata->d_buf)[1 + cnt].st_shndx != SHN_UNDEF) - { - /* The value of the symbol is the address of the corresponding PLT - entry. Store the address, also for the normal symbol table if - this is necessary. */ - ((Elf32_Sym *) dynsymdata->d_buf)[1 + cnt].st_value = pltentryaddr; - - if (symdata != NULL) - ((Elf32_Sym *) symdata->d_buf)[nsym - statep->nplt + cnt].st_value - = pltentryaddr; - } - } - /* Create the .plt section. */ scn = elf_getscn (statep->outelf, statep->pltscnidx); - data = elf_getdata (scn, NULL); - assert (data != NULL); + Elf_Data *pltdata = elf_getdata (scn, NULL); + assert (pltdata != NULL); + + /* Also create the .rel.plt section data. It simply means relocations + addressing the corresponding entry in the .got.plt section. The + section name is misleading. */ + scn = elf_getscn (statep->outelf, statep->pltrelscnidx); + xelf_getshdr (scn, shdr); + Elf_Data *reldata = elf_getdata (scn, NULL); + assert (shdr != NULL && reldata != NULL); + + /* Update the sh_link to point to the section being modified. We + point it here (correctly) to the .got.plt section. Some linkers + (e.g., the GNU binutils linker) point to the .plt section. This + is wrong since the .plt section isn't modified even though the + name .rel.plt suggests that this is correct. */ + shdr->sh_link = statep->dynsymscnidx; + shdr->sh_info = statep->gotpltscnidx; + (void) xelf_update_shdr (scn, shdr); - /* Create the first entry. */ - assert (data->d_size >= PLT_ENTRY_SIZE); + /* Create the first entry of the .plt section. */ + assert (pltdata->d_size >= PLT_ENTRY_SIZE); if (build_dso) /* Copy the entry. It's complete, no relocation needed. */ - memcpy (data->d_buf, elf_i386_pic_plt0_entry, PLT_ENTRY_SIZE); + memcpy (pltdata->d_buf, elf_i386_pic_plt0_entry, PLT_ENTRY_SIZE); else { /* Copy the skeleton. */ - memcpy (data->d_buf, elf_i386_plt0_entry, PLT_ENTRY_SIZE); + memcpy (pltdata->d_buf, elf_i386_plt0_entry, PLT_ENTRY_SIZE); /* And fill in the addresses. */ - struct plt0_entry *addr = (struct plt0_entry *) data->d_buf; + struct plt0_entry *addr = (struct plt0_entry *) pltdata->d_buf; addr->gotp4_addr = target_bswap_32 (gotaddr + 4); addr->gotp8_addr = target_bswap_32 (gotaddr + 8); } @@ -460,54 +453,68 @@ elf_i386_finalize_plt (struct ld_state *statep, size_t nsym, const unsigned char *plt_template = build_dso ? elf_i386_pic_plt_entry : elf_i386_plt_entry; - for (cnt = 0; cnt < statep->nplt; ++cnt) + for (size_t idx = nsym_local; idx < nsym; ++idx) { - struct plt_entry *addr; + struct symbol *symbol = ndxtosym[idx]; + if (symbol == NULL || symbol->type != STT_FUNC + || ndxtosym[idx]->outdynsymidx == 0 + // XXX is the following test correct? + || ! ndxtosym[idx]->in_dso) + continue; + + size_t pltidx = symbol->merge.value; + + assert (pltidx > 0); + assert ((3 + pltidx) * sizeof (Elf32_Word) <= data->d_size); + + /* Address in the PLT. */ + Elf32_Addr pltentryaddr = (pltshdr->sh_addr + pltidx * PLT_ENTRY_SIZE); + + /* Point the GOT entry at the PLT entry, after the initial jmp. */ + ((Elf32_Word *) data->d_buf)[2 + pltidx] = pltentryaddr + 6; + + /* If the symbol is defined, adjust the address. */ + if (((Elf32_Sym *) dynsymdata->d_buf)[ndxtosym[idx]->outdynsymidx].st_shndx != SHN_UNDEF) + { + /* The value of the symbol is the address of the corresponding PLT + entry. Store the address, also for the normal symbol table if + this is necessary. */ + ((Elf32_Sym *) dynsymdata->d_buf)[pltidx].st_value = pltentryaddr; + + if (symdata != NULL) + { + assert(nsym - statep->nplt + (pltidx - 1) == idx); + ((Elf32_Sym *) symdata->d_buf)[nsym - statep->nplt + + (pltidx - 1)].st_value + = pltentryaddr; + } + } - /* Copy the template. */ - assert (data->d_size >= (2 + cnt) * PLT_ENTRY_SIZE); - addr = (struct plt_entry *) ((char *) data->d_buf - + (1 + cnt) * PLT_ENTRY_SIZE); + /* Copy the PLT entry template. */ + assert (pltdata->d_size >= (1 + pltidx) * PLT_ENTRY_SIZE); + struct plt_entry *addr = (struct plt_entry *) ((char *) pltdata->d_buf + + (pltidx + * PLT_ENTRY_SIZE)); memcpy (addr, plt_template, PLT_ENTRY_SIZE); /* And once more, fill in the addresses. First the address of this symbol in .got. */ addr->offset_got = target_bswap_32 (gotaddr_off - + (3 + cnt) * sizeof (Elf32_Addr)); + + (2 + pltidx) * sizeof (Elf32_Addr)); /* Offset into relocation table. */ - addr->push_imm = target_bswap_32 (cnt * sizeof (Elf32_Rel)); + addr->push_imm = target_bswap_32 ((pltidx - 1) * sizeof (Elf32_Rel)); /* Offset to start of .plt. */ - addr->plt0_offset = target_bswap_32 (-(2 + cnt) * PLT_ENTRY_SIZE); - } - - /* Create the .rel.plt section data. It simply means relocations - addressing the corresponding entry in the .got.plt section. The - section name is misleading. */ - scn = elf_getscn (statep->outelf, statep->pltrelscnidx); - xelf_getshdr (scn, shdr); - data = elf_getdata (scn, NULL); - assert (shdr != NULL && data != NULL); + addr->plt0_offset = target_bswap_32 (-(1 + pltidx) * PLT_ENTRY_SIZE); - /* Update the sh_link to point to the section being modified. We - point it here (correctly) to the .got.plt section. Some linkers - (e.g., the GNU binutils linker) point to the .plt section. This - is wrong since the .plt section isn't modified even though the - name .rel.plt suggests that this is correct. */ - shdr->sh_link = statep->dynsymscnidx; - shdr->sh_info = statep->gotpltscnidx; - (void) xelf_update_shdr (scn, shdr); - for (cnt = 0; cnt < statep->nplt; ++cnt) - { XElf_Rel_vardef (rel); - - assert ((1 + cnt) * sizeof (Elf32_Rel) <= data->d_size); - xelf_getrel_ptr (data, cnt, rel); - rel->r_offset = gotaddr + (3 + cnt) * sizeof (Elf32_Addr); + assert (pltidx * sizeof (Elf32_Rel) <= reldata->d_size); + xelf_getrel_ptr (reldata, pltidx - 1, rel); + rel->r_offset = gotaddr + (2 + pltidx) * sizeof (Elf32_Addr); /* The symbol table entries for the functions from DSOs are at the beginning of the symbol table. */ - rel->r_info = XELF_R_INFO (1 + cnt, R_386_JMP_SLOT); - (void) xelf_update_rel (data, cnt, rel); + rel->r_info = XELF_R_INFO (ndxtosym[idx]->outdynsymidx, R_386_JMP_SLOT); + (void) xelf_update_rel (reldata, pltidx - 1, rel); } } diff --git a/src/ld.c b/src/ld.c index 4ac3023af..24d5cb3d3 100644 --- a/src/ld.c +++ b/src/ld.c @@ -73,6 +73,7 @@ enum ARGP_as_needed, ARGP_no_as_needed, ARGP_eh_frame_hdr, + ARGP_hash_style, #if YYDEBUG ARGP_yydebug, #endif @@ -167,6 +168,8 @@ Default rules of extracting from archive; weak references are not enough."), 0 }, { "eh-frame-hdr", ARGP_eh_frame_hdr, NULL, 0, N_("Create .eh_frame_hdr section"), 0 }, + { "hash-style", ARGP_hash_style, "STYLE", 0, + N_("Set hash style to sysv, gnu or both."), 0 }, { NULL, 0, NULL, 0, N_("Linker Operation Control:"), 0 }, { "verbose", 'v', NULL, 0, N_("Verbose messages."), 0 }, @@ -328,6 +331,11 @@ main (int argc, char *argv[]) /* Determine which ELF backend to use. */ determine_output_format (); + /* If no hash style was specific default to the oldand slow SysV + method. */ + if (unlikely (ld_state.hash_style == hash_style_none)) + ld_state.hash_style = hash_style_sysv; + /* Prepare state. */ err = ld_prepare_state (emulation); if (err != 0) @@ -611,6 +619,17 @@ parse_opt_1st (int key, char *arg, ld_state.eh_frame_hdr = true; break; + case ARGP_hash_style: + if (strcmp (arg, "gnu") == 0) + ld_state.hash_style = hash_style_gnu; + else if (strcmp (arg, "both") == 0) + ld_state.hash_style = hash_style_gnu | hash_style_sysv; + else if (strcmp (arg, "sysv") == 0) + ld_state.hash_style = hash_style_sysv; + else + error (EXIT_FAILURE, 0, gettext ("invalid hash style '%s'"), arg); + break; + case 's': if (arg == NULL) { diff --git a/src/ld.h b/src/ld.h index 5890e855f..d4ad8f9dc 100644 --- a/src/ld.h +++ b/src/ld.h @@ -176,11 +176,14 @@ struct usedfiles bool used; /* True if section is an unused COMDAT section. */ bool unused_comdat; + /* True if this is a COMDAT group section. */ + bool comdat_group; /* Section group number. This is the index of the SHT_GROUP section. */ Elf32_Word grpid; /* Pointer back to the containing file information structure. */ struct usedfiles *fileinfo; - /* List of symbols in this section (set only for merge-able sections). */ + /* List of symbols in this section (set only for merge-able sections + and group sections). */ struct symbol *symbols; /* Size of relocations in this section. Only used for relocation sections. */ @@ -397,9 +400,10 @@ struct callbacks DL_CALL_FCT ((state)->callbacks.initialize_pltrel, (state, scn)) /* Finalize the .plt section the what belongs to them. */ - void (*finalize_plt) (struct ld_state *, size_t, size_t); -#define FINALIZE_PLT(state, nsym, nsym_dyn) \ - DL_CALL_FCT ((state)->callbacks.finalize_plt, (state, nsym, nsym_dyn)) + void (*finalize_plt) (struct ld_state *, size_t, size_t, struct symbol **); +#define FINALIZE_PLT(state, nsym, nsym_dyn, ndxtosym) \ + DL_CALL_FCT ((state)->callbacks.finalize_plt, (state, nsym, nsym_dyn, \ + ndxtosym)) /* Create the data structures for the .got section and initialize it. */ void (*initialize_got) (struct ld_state *, Elf_Scn *scn); @@ -681,6 +685,7 @@ struct scnhead scn_dot_dynsym, /* Generated .dynsym section. */ scn_dot_dynstr, /* Generated .dynstr section. */ scn_dot_hash, /* Generated .hash section. */ + scn_dot_gnu_hash, /* Generated .gnu.hash section. */ scn_dot_plt, /* Generated .plt section. */ scn_dot_pltrel, /* Generated .rel.plt section. */ scn_dot_version, /* Generated .gnu.version section. */ @@ -931,8 +936,9 @@ struct ld_state Elf32_Word dynsymscnidx; /* Dynamic symbol string table section. */ Elf32_Word dynstrscnidx; - /* Dynamic symbol hash table. */ + /* Dynamic symbol hash tables. */ size_t hashscnidx; + size_t gnuhashscnidx; /* Procedure linkage table section. */ Elf32_Word pltscnidx; @@ -1022,6 +1028,17 @@ struct ld_state /* True if an .eh_frame_hdr section should be generated. */ bool eh_frame_hdr; + /* What hash style to generate. */ + enum + { + hash_style_none = 0, + hash_style_sysv = 1, +#define GENERATE_SYSV_HASH ((ld_state.hash_style & hash_style_sysv) != 0) + hash_style_gnu = 2 +#define GENERATE_GNU_HASH ((ld_state.hash_style & hash_style_gnu) != 0) + } + hash_style; + /* True if in executables all global symbols should be exported in the dynamic symbol table. */ diff --git a/src/ldgeneric.c b/src/ldgeneric.c index 9a8ea8de8..d1eaf41cf 100644 --- a/src/ldgeneric.c +++ b/src/ldgeneric.c @@ -49,6 +49,8 @@ #include "list.h" +unsigned *p1; + /* Header of .eh_frame_hdr section. */ struct unw_eh_frame_hdr { @@ -88,8 +90,11 @@ static void ld_generic_initialize_plt (struct ld_state *statep, Elf_Scn *scn); static void ld_generic_initialize_pltrel (struct ld_state *statep, Elf_Scn *scn); static void ld_generic_initialize_got (struct ld_state *statep, Elf_Scn *scn); +static void ld_generic_initialize_gotplt (struct ld_state *statep, + Elf_Scn *scn); static void ld_generic_finalize_plt (struct ld_state *statep, size_t nsym, - size_t nsym_dyn); + size_t nsym_dyn, + struct symbol **ndxtosymp); static int ld_generic_rel_type (struct ld_state *statep); static void ld_generic_count_relocations (struct ld_state *statep, struct scninfo *scninfo); @@ -220,6 +225,7 @@ ld_prepare_state (const char *emulation) ld_state.callbacks.initialize_plt = ld_generic_initialize_plt; ld_state.callbacks.initialize_pltrel = ld_generic_initialize_pltrel; ld_state.callbacks.initialize_got = ld_generic_initialize_got; + ld_state.callbacks.initialize_gotplt = ld_generic_initialize_gotplt; ld_state.callbacks.finalize_plt = ld_generic_finalize_plt; ld_state.callbacks.rel_type = ld_generic_rel_type; ld_state.callbacks.count_relocations = ld_generic_count_relocations; @@ -514,6 +520,38 @@ open_along_path (struct usedfiles *fileinfo) } +static int +matching_group_comdat_scn (const XElf_Sym *sym, size_t shndx, + struct usedfiles *fileinfo, struct symbol *oldp) +{ + if ((shndx >= SHN_LORESERVE && shndx <= SHN_HIRESERVE) + || (oldp->scndx >= SHN_LORESERVE && oldp->scndx <= SHN_HIRESERVE)) + /* Cannot be a group COMDAT section. */ + return 0; + + size_t newgrpid = fileinfo->scninfo[shndx].grpid; + size_t oldgrpid = oldp->file->scninfo[oldp->scndx].grpid; + if (newgrpid == 0 || oldgrpid == 0) + return 0; + + assert (SCNINFO_SHDR (fileinfo->scninfo[newgrpid].shdr).sh_type + == SHT_GROUP); + assert (SCNINFO_SHDR (oldp->file->scninfo[oldgrpid].shdr).sh_type + == SHT_GROUP); + + if (! fileinfo->scninfo[newgrpid].comdat_group + || ! oldp->file->scninfo[oldgrpid].comdat_group) + return 0; + + if (strcmp (fileinfo->scninfo[newgrpid].symbols->name, + oldp->file->scninfo[oldgrpid].symbols->name) != 0) + return 0; + + /* This is a matching, duplicate COMDAT group section. Ignore it. */ + return 1; +} + + static void check_type_and_size (const XElf_Sym *sym, struct usedfiles *fileinfo, struct symbol *oldp) @@ -551,7 +589,7 @@ Warning: size of `%s' changed from %" PRIu64 " in %s to %" PRIu64 " in %s"), static int -check_definition (const XElf_Sym *sym, size_t symidx, +check_definition (const XElf_Sym *sym, size_t shndx, size_t symidx, struct usedfiles *fileinfo, struct symbol *oldp) { int result = 0; @@ -559,9 +597,9 @@ check_definition (const XElf_Sym *sym, size_t symidx, bool new_in_dso = FILEINFO_EHDR (fileinfo->ehdr).e_type == ET_DYN; bool use_new_def = false; - if (sym->st_shndx != SHN_UNDEF + if (shndx != SHN_UNDEF && (! oldp->defined - || (sym->st_shndx != SHN_COMMON && oldp->common && ! new_in_dso) + || (shndx != SHN_COMMON && oldp->common && ! new_in_dso) || (old_in_dso && ! new_in_dso))) { /* We found a definition for a previously undefined symbol or a @@ -591,10 +629,17 @@ check_definition (const XElf_Sym *sym, size_t symidx, /* Use the values of the definition from now on. */ use_new_def = true; } - else if (sym->st_shndx != SHN_UNDEF + else if (shndx != SHN_UNDEF + && oldp->defined + && matching_group_comdat_scn (sym, shndx, fileinfo, oldp)) + /* The duplicate symbol is in a group COMDAT section with the same + signature as the one containing the original definition. + Just ignore the second definition. */ + /* nothing */; + else if (shndx != SHN_UNDEF && unlikely (! oldp->common) && oldp->defined - && sym->st_shndx != SHN_COMMON + && shndx != SHN_COMMON /* Multiple definitions are no fatal errors if the -z muldefs flag is used. We don't warn about the multiple definition unless we are told to be verbose. */ @@ -607,7 +652,6 @@ check_definition (const XElf_Sym *sym, size_t symidx, struct usedfiles *oldfile; const char *scnname; Elf32_Word xndx; - size_t shndx; size_t shnum; if (elf_getshnum (fileinfo->elf, &shnum) < 0) @@ -616,15 +660,14 @@ check_definition (const XElf_Sym *sym, size_t symidx, elf_errmsg (-1)); /* XXX Use only ebl_section_name. */ - if (sym->st_shndx < SHN_LORESERVE // || sym->st_shndx > SHN_HIRESERVE - && sym->st_shndx < shnum) + if (shndx < SHN_LORESERVE || (shndx > SHN_HIRESERVE && shndx < shnum)) scnname = elf_strptr (fileinfo->elf, fileinfo->shstrndx, - SCNINFO_SHDR (fileinfo->scninfo[sym->st_shndx].shdr).sh_name); + SCNINFO_SHDR (fileinfo->scninfo[shndx].shdr).sh_name); else // XXX extended section - scnname = ebl_section_name (ld_state.ebl, sym->st_shndx, 0, - buf, sizeof (buf), NULL, shnum); + scnname = ebl_section_name (ld_state.ebl, shndx, 0, buf, sizeof (buf), + NULL, shnum); /* XXX Print source file and line number. */ print_file_name (stderr, fileinfo, 1, 0); @@ -639,23 +682,16 @@ check_definition (const XElf_Sym *sym, size_t symidx, oldfile = oldp->file; xelf_getsymshndx (oldfile->symtabdata, oldfile->xndxdata, oldp->symidx, oldsym, xndx); - if (oldsym == NULL) - /* This should never happen since the same call - succeeded before. */ - abort (); - - shndx = oldsym->st_shndx; - if (unlikely (oldsym->st_shndx == SHN_XINDEX)) - shndx = xndx; + assert (oldsym != NULL); /* XXX Use only ebl_section_name. */ - if (shndx < SHN_LORESERVE || shndx > SHN_HIRESERVE) + if (oldp->scndx < SHN_LORESERVE || oldp->scndx > SHN_HIRESERVE) scnname = elf_strptr (oldfile->elf, oldfile->shstrndx, SCNINFO_SHDR (oldfile->scninfo[shndx].shdr).sh_name); else - scnname = ebl_section_name (ld_state.ebl, oldsym->st_shndx, shndx, buf, - sizeof (buf), NULL, shnum); + scnname = ebl_section_name (ld_state.ebl, oldp->scndx, oldp->scndx, + buf, sizeof (buf), NULL, shnum); /* XXX Print source file and line number. */ print_file_name (stderr, oldfile, 1, 0); @@ -666,7 +702,7 @@ check_definition (const XElf_Sym *sym, size_t symidx, result = 1; } else if (old_in_dso && fileinfo->file_type == relocatable_file_type - && sym->st_shndx != SHN_UNDEF) + && shndx != SHN_UNDEF) /* We use the definition from a normal relocatable file over the definition in a DSO. This is what the dynamic linker would do, too. */ @@ -685,7 +721,7 @@ check_definition (const XElf_Sym *sym, size_t symidx, oldp->on_dsolist = 1; } - else if (oldp->common && sym->st_shndx == SHN_COMMON) + else if (oldp->common && shndx == SHN_COMMON) { /* The symbol size is the largest of all common definitions. */ oldp->size = MAX (oldp->size, sym->st_size); @@ -714,12 +750,12 @@ check_definition (const XElf_Sym *sym, size_t symidx, oldp->size = sym->st_size; oldp->type = XELF_ST_TYPE (sym->st_info); oldp->symidx = symidx; - oldp->scndx = sym->st_shndx; + oldp->scndx = shndx; //oldp->symscndx = THESYMSCNDX must be passed; oldp->file = fileinfo; oldp->defined = 1; oldp->in_dso = new_in_dso; - oldp->common = sym->st_shndx == SHN_COMMON; + oldp->common = shndx == SHN_COMMON; if (likely (fileinfo->file_type == relocatable_file_type)) { /* If the definition comes from a DSO we pertain the weak flag @@ -727,11 +763,10 @@ check_definition (const XElf_Sym *sym, size_t symidx, oldp->weak = XELF_ST_BIND (sym->st_info) == STB_WEAK; // XXX Really exclude SHN_ABS? - if (sym->st_shndx != SHN_COMMON && sym->st_shndx != SHN_ABS) + if (shndx != SHN_COMMON && shndx != SHN_ABS) { struct scninfo *ignore; - mark_section_used (&fileinfo->scninfo[sym->st_shndx], - sym->st_shndx, &ignore); + mark_section_used (&fileinfo->scninfo[shndx], shndx, &ignore); } } @@ -750,7 +785,7 @@ check_definition (const XElf_Sym *sym, size_t symidx, oldp->on_dsolist = 1; } - else if (sym->st_shndx == SHN_COMMON) + else if (shndx == SHN_COMMON) { /* Store the alignment. */ oldp->merge.value = sym->st_value; @@ -844,14 +879,12 @@ mark_section_group (struct usedfiles *fileinfo, Elf32_Word shndx, Elf32_Word idx = grpref[--cnt]; XElf_Shdr *shdr = &SCNINFO_SHDR (fileinfo->scninfo[idx].shdr); - if (fileinfo->scninfo[idx].grpid != 0) + if (fileinfo->scninfo[idx].grpid != grpscn->grpid) error (EXIT_FAILURE, 0, gettext ("\ -%s: section [%2d] '%s' is in more than one section group"), +%s: section [%2d] '%s' is not in the correct section group"), fileinfo->fname, (int) idx, elf_strptr (fileinfo->elf, fileinfo->shstrndx, shdr->sh_name)); - fileinfo->scninfo[idx].grpid = grpscn->grpid; - if (ld_state.strip == strip_none /* If we are stripping, remove debug sections. */ || (!ebl_debugscn_p (ld_state.ebl, @@ -1223,6 +1256,7 @@ add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype) symscn = elf_getscn (fileinfo->elf, shdr->sh_link); xelf_getshdr (symscn, symshdr); symdata = elf_getdata (symscn, NULL); + if (symshdr != NULL) { XElf_Sym_vardef (sym); @@ -1234,9 +1268,26 @@ add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype) { struct symbol *symbol = fileinfo->scninfo[cnt].symbols; - symbol->name = elf_strptr (fileinfo->elf, symshdr->sh_link, - sym->st_name); - symbol->symidx = shdr->sh_info; +#ifndef NO_HACKS + if (XELF_ST_TYPE (sym->st_info) == STT_SECTION) + { + XElf_Shdr_vardef (buggyshdr); + xelf_getshdr (elf_getscn (fileinfo->elf, sym->st_shndx), + buggyshdr); + + symbol->name = elf_strptr (fileinfo->elf, + FILEINFO_EHDR (fileinfo->ehdr).e_shstrndx, + buggyshdr->sh_name); + symbol->symidx = -1; + } + else +#endif + { + symbol->name = elf_strptr (fileinfo->elf, + symshdr->sh_link, + sym->st_name); + symbol->symidx = shdr->sh_info; + } symbol->file = fileinfo; } } @@ -1249,6 +1300,35 @@ add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype) shdr->sh_name), elf_errmsg (-1)); + + /* For all the sections which are part of this group, add + the reference. */ + if (data == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +%s: cannot get content of section group [%2zd] '%s': %s'"), + fileinfo->fname, elf_ndxscn (fileinfo->scninfo[cnt].scn), + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name), + elf_errmsg (-1)); + + Elf32_Word *grpdata = (Elf32_Word *) data->d_buf; + if (grpdata[0] & GRP_COMDAT) + fileinfo->scninfo[cnt].comdat_group = true; + for (size_t inner = 1; inner < data->d_size / sizeof (Elf32_Word); + ++inner) + { + if (grpdata[inner] >= scncnt) + error (EXIT_FAILURE, 0, gettext ("\ +%s: group member %zu of section group [%2zd] '%s' has too high index: %" PRIu32), + fileinfo->fname, + inner, elf_ndxscn (fileinfo->scninfo[cnt].scn), + elf_strptr (fileinfo->elf, fileinfo->shstrndx, + shdr->sh_name), + grpdata[inner]); + + fileinfo->scninfo[grpdata[inner]].grpid = cnt; + } + /* The 'used' flag is used to indicate when the information in the section group is used to mark all other sections as used. So it must not be true yet. */ @@ -1512,7 +1592,8 @@ add_relocatable_file (struct usedfiles *fileinfo, GElf_Word secttype) else if (hval == 6672457 && strcmp (newp->name, "_fini") == 0) ld_state.fini_symbol = newp; } - else if (unlikely (check_definition (sym, cnt, fileinfo, oldp) != 0)) + else if (unlikely (check_definition (sym, shndx, cnt, fileinfo, oldp) + != 0)) /* A fatal error (multiple definition of a symbol) occurred, no need to continue. */ return 1; @@ -2392,8 +2473,13 @@ ld_generic_generate_sections (struct ld_state *statep) 0, 1); /* And a hashing table. */ // XXX For Linux/Alpha we need other sizes unless they change... - new_generated_scn (scn_dot_hash, ".hash", SHT_HASH, SHF_ALLOC, - sizeof (Elf32_Word), sizeof (Elf32_Word)); + if (GENERATE_SYSV_HASH) + new_generated_scn (scn_dot_hash, ".hash", SHT_HASH, SHF_ALLOC, + sizeof (Elf32_Word), sizeof (Elf32_Word)); + if (GENERATE_GNU_HASH) + new_generated_scn (scn_dot_gnu_hash, ".gnu.hash", SHT_GNU_HASH, + SHF_ALLOC, sizeof (Elf32_Word), + sizeof (Elf32_Word)); /* Create the section associated with the PLT if necessary. */ if (ld_state.nplt > 0) @@ -3511,6 +3597,17 @@ optimal_bucket_size (Elf32_Word *hashcodes, size_t maxcnt, int optlevel) } +static void +optimal_gnu_hash_size (Elf32_Word *hashcodes, size_t maxcnt, int optlevel, + size_t *bitmask_nwords, size_t *shift, size_t *nbuckets) +{ + // XXX Implement something real + *bitmask_nwords = 256; + *shift = 6; + *nbuckets = 3 * maxcnt / 2; +} + + static XElf_Addr find_entry_point (void) { @@ -3745,6 +3842,18 @@ create_verneed_data (XElf_Off offset, Elf_Data *verneeddata, } +/* Sort the dynamic symbol table. The GNU hash table lookup assumes + that all symbols with the same hash value module the bucket table + size follow one another. This avoids the extra hash chain table. + There is no need (and no way) to perform this operation if we do + not use the new hash table format. */ +static void +sort_dynsym (uint32_t *hashcodes, size_t nbuckets, size_t nsym_dyn) +{ + // XXX +} + + /* Create the output file. For relocatable files what basically has to happen is that all @@ -3798,6 +3907,7 @@ ld_generic_create_outfile (struct ld_state *statep) Elf_Data *dynsymdata = NULL; Elf_Data *dynstrdata = NULL; Elf32_Word *hashcodes = NULL; + Elf32_Word *gnuhashcodes = NULL; size_t nsym_dyn_allocated = 0; Elf_Scn *versymscn = NULL; Elf_Data *versymdata = NULL; @@ -4094,6 +4204,14 @@ ld_generic_create_outfile (struct ld_state *statep) continue; } + if (unlikely (head->kind == scn_dot_gnu_hash)) + { + /* Remember the index of this section. */ + ld_state.gnuhashscnidx = elf_ndxscn (scn); + + continue; + } + if (unlikely (head->kind == scn_dot_plt)) { /* Remember the index of this section. */ @@ -4908,6 +5026,10 @@ section index too large in dynamic symbol table")); if (reduce_symbol_p (sym, symstrent[cnt])) { + // XXX Check whether this is correct... + assert (ndxtosym[cnt]->outdynsymidx != 0); + ndxtosym[cnt]->outdynsymidx = 0; + sym->st_info = XELF_ST_INFO (STB_LOCAL, XELF_ST_TYPE (sym->st_info)); (void) xelf_update_sym (symdata, cnt, sym); @@ -5188,6 +5310,17 @@ section index too large in dynamic symbol table")); } + size_t nbucket = 0; + Elf32_Word *bucket = NULL; + Elf32_Word *chain = NULL; + + size_t gnu_bitmask_nwords = 0; + size_t gnu_shift = 0; + size_t gnu_nbuckets = 0; + Elf32_Word *gnu_bitmask = NULL; + Elf32_Word *gnu_bucket = NULL; + Elf32_Word *gnu_chain = NULL; + /* If we have to construct the dynamic symbol table we must not include the local symbols. If the normal symbol has to be emitted as well we haven't done anything else yet and we can construct it from @@ -5264,15 +5397,16 @@ cannot create dynamic symbol table for output file: %s"), /* We need one more array which contains the hash codes of the symbol names. */ - hashcodes = (Elf32_Word *) xcalloc (nsym_dyn_allocated, + hashcodes = (Elf32_Word *) xcalloc (__builtin_popcount ((int) ld_state.hash_style) + * nsym_dyn_allocated, sizeof (Elf32_Word)); + gnuhashcodes = hashcodes; + if (GENERATE_SYSV_HASH) + gnuhashcodes += nsym_dyn_allocated; /* We have and empty entry at the beginning. */ nsym_dyn = 1; - - /* We don't mix PLT symbols and others. */ - size_t plt_idx = 1; - size_t obj_idx = 1 + ld_state.nplt; + size_t gnu_symbias = 0; /* Populate the table. */ for (cnt = nsym_local; cnt < nsym; ++cnt) @@ -5305,18 +5439,6 @@ section index too large in dynamic symbol table")); continue; } - size_t idx; - if (ndxtosym[cnt]->in_dso && ndxtosym[cnt]->type == STT_FUNC) - { - idx = plt_idx++; - assert (idx < 1 + ld_state.nplt); - } - else - { - idx = obj_idx++; - assert (idx < nsym_dyn_allocated); - } - /* Add the version information. */ if (versymdata != NULL) { @@ -5331,28 +5453,121 @@ section index too large in dynamic symbol table")); gelf_getversym (symp->file->versymdata, symp->symidx, &versym); - (void) gelf_update_versym (versymdata, idx, + (void) gelf_update_versym (versymdata, nsym_dyn, &symp->file->verdefused[versym]); } else { /* XXX Add support for version definitions. */ GElf_Versym global = VER_NDX_GLOBAL; - (void) gelf_update_versym (versymdata, idx, &global); + (void) gelf_update_versym (versymdata, nsym_dyn, &global); } } /* Store the index of the symbol in the dynamic symbol table. */ - ndxtosym[cnt]->outdynsymidx = idx; + ndxtosym[cnt]->outdynsymidx = nsym_dyn; /* Create a new string table entry. */ const char *str = ndxtosym[cnt]->name; symstrent[cnt] = ebl_strtabadd (dynstrtab, str, 0); - hashcodes[idx] = elf_hash (str); + if (GENERATE_SYSV_HASH) + hashcodes[nsym_dyn] = elf_hash (str); + if (GENERATE_GNU_HASH) + gnuhashcodes[nsym_dyn] = elf_gnu_hash (str); ++nsym_dyn; } - assert (nsym_dyn == obj_idx); - assert (ld_state.nplt + 1 == plt_idx); + + if (ld_state.file_type != relocatable_file_type) + { + /* Finalize the dynamic string table. */ + ebl_strtabfinalize (dynstrtab, dynstrdata); + + assert (ld_state.hashscnidx != 0 || ld_state.gnuhashscnidx != 0); + + if (GENERATE_SYSV_HASH) + { + /* Determine the "optimal" bucket size. If we also generate + the new-style hash function there is no need to waste + effort and space on the old one which should not be used. + Make it as small as possible. */ + if (GENERATE_GNU_HASH) + nbucket = 1; + else + nbucket = optimal_bucket_size (hashcodes, nsym_dyn, + ld_state.optlevel); + + /* Create the .hash section data structures. */ + Elf_Scn *hashscn = elf_getscn (ld_state.outelf, + ld_state.hashscnidx); + xelf_getshdr (hashscn, shdr); + Elf_Data *hashdata = elf_newdata (hashscn); + if (shdr == NULL || hashdata == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot create hash table section for output file: %s"), + elf_errmsg (-1)); + + shdr->sh_link = ld_state.dynsymscnidx; + (void) xelf_update_shdr (hashscn, shdr); + + hashdata->d_size = (2 + nsym_dyn + nbucket) * sizeof (Elf32_Word); + hashdata->d_buf = xcalloc (1, hashdata->d_size); + hashdata->d_align = sizeof (Elf32_Word); + hashdata->d_type = ELF_T_WORD; + hashdata->d_off = 0; + + ((Elf32_Word *) hashdata->d_buf)[0] = nbucket; + ((Elf32_Word *) hashdata->d_buf)[1] = nsym_dyn; + bucket = &((Elf32_Word *) hashdata->d_buf)[2]; + chain = &((Elf32_Word *) hashdata->d_buf)[2 + nbucket]; + } + + if (GENERATE_GNU_HASH) + { + /* Determine the "optimal" bucket size. */ + optimal_gnu_hash_size (gnuhashcodes, nsym_dyn, ld_state.optlevel, + &gnu_bitmask_nwords, &gnu_shift, + &gnu_nbuckets); + + /* Sort the symbol table according to the GNU hash table + bucket table size. */ + sort_dynsym (gnuhashcodes, gnu_nbuckets, nsym_dyn); + + /* Create the .gnu.hash section data structures. */ + Elf_Scn *hashscn = elf_getscn (ld_state.outelf, + ld_state.gnuhashscnidx); + xelf_getshdr (hashscn, shdr); + Elf_Data *hashdata = elf_newdata (hashscn); + if (shdr == NULL || hashdata == NULL) + error (EXIT_FAILURE, 0, gettext ("\ +cannot create hash table section for output file: %s"), + elf_errmsg (-1)); + + shdr->sh_link = ld_state.dynsymscnidx; + (void) xelf_update_shdr (hashscn, shdr); + + hashdata->d_size = ((4 + (xelf_fsize (ld_state.outelf, + ELF_T_ADDR, 1) + / sizeof (Elf32_Word) + * gnu_bitmask_nwords) + + gnu_nbuckets + nsym_dyn) + * sizeof (Elf32_Word)); + hashdata->d_buf = xcalloc (1, hashdata->d_size); + hashdata->d_align = sizeof (Elf32_Word); + hashdata->d_type = ELF_T_WORD; + hashdata->d_off = 0; + + ((Elf32_Word *) hashdata->d_buf)[0] = gnu_nbuckets; + ((Elf32_Word *) hashdata->d_buf)[1] = gnu_symbias; + ((Elf32_Word *) hashdata->d_buf)[2] = gnu_bitmask_nwords; + ((Elf32_Word *) hashdata->d_buf)[3] = gnu_shift; + gnu_bitmask = &((Elf32_Word *) hashdata->d_buf)[4]; + gnu_bucket = &gnu_bitmask[xelf_fsize (ld_state.outelf, + ELF_T_ADDR, 1) + / sizeof (Elf32_Word) + * gnu_bitmask_nwords]; + gnu_chain = &gnu_bucket[gnu_nbuckets]; + } + } /* Update the information about the symbol section. */ if (versymdata != NULL) @@ -5374,46 +5589,6 @@ section index too large in dynamic symbol table")); if (ld_state.file_type != relocatable_file_type) { - size_t nbucket; - Elf32_Word *bucket; - Elf32_Word *chain; - size_t nchain; - Elf_Scn *hashscn; - Elf_Data *hashdata; - - /* Finalize the dynamic string table. */ - ebl_strtabfinalize (dynstrtab, dynstrdata); - - /* Determine the "optimal" bucket size. */ - nbucket = optimal_bucket_size (hashcodes, nsym_dyn, ld_state.optlevel); - - /* Create the .hash section data structures. */ - assert (ld_state.hashscnidx != 0); - hashscn = elf_getscn (ld_state.outelf, ld_state.hashscnidx); - xelf_getshdr (hashscn, shdr); - hashdata = elf_newdata (hashscn); - if (shdr == NULL || hashdata == NULL) - error (EXIT_FAILURE, 0, gettext ("\ -cannot create hash table section for output file: %s"), - elf_errmsg (-1)); - - shdr->sh_link = ld_state.dynsymscnidx; - (void) xelf_update_shdr (hashscn, shdr); - - hashdata->d_size = (2 + nsym_dyn + nbucket) * sizeof (Elf32_Word); - hashdata->d_buf = xcalloc (1, hashdata->d_size); - hashdata->d_align = sizeof (Elf32_Word); - hashdata->d_type = ELF_T_WORD; - hashdata->d_off = 0; - - ((Elf32_Word *) hashdata->d_buf)[0] = nbucket; - ((Elf32_Word *) hashdata->d_buf)[1] = nsym_dyn; - bucket = &((Elf32_Word *) hashdata->d_buf)[2]; - chain = &((Elf32_Word *) hashdata->d_buf)[2 + nbucket]; - - /* Haven't yet filled in any chain value. */ - nchain = 0; - /* Now put the names in. */ for (cnt = nsym_local; cnt < nsym; ++cnt) if (symstrent[cnt] != NULL) @@ -5436,25 +5611,24 @@ cannot create hash table section for output file: %s"), (void) xelf_update_sym (dynsymdata, dynidx, sym); /* Add to the hash table. */ - size_t hashidx = hashcodes[dynidx] % nbucket; - if (bucket[hashidx] == 0) - bucket[hashidx] = dynidx; - else + if (GENERATE_SYSV_HASH) { - hashidx = bucket[hashidx]; - while (chain[hashidx] != 0) - hashidx = chain[hashidx]; + size_t hashidx = hashcodes[dynidx] % nbucket; + if (bucket[hashidx] == 0) + bucket[hashidx] = dynidx; + else + { + hashidx = bucket[hashidx]; + while (chain[hashidx] != 0) + hashidx = chain[hashidx]; - chain[hashidx] = dynidx; + chain[hashidx] = dynidx; + } } } free (hashcodes); - /* We don't need the map from the symbol table index to the symbol - structure anymore. */ - free (ndxtosym); - /* Create the required version section. */ if (ld_state.verneedscnidx != 0) { @@ -5527,10 +5701,6 @@ cannot create hash table section for output file: %s"), /* Write the updated info back. */ (void) xelf_update_shdr (dynsymscn, shdr); } - else - /* We don't need the map from the symbol table index to the symbol - structure anymore. */ - free (ndxtosym); /* We don't need the string table anymore. */ free (symstrent); @@ -5993,7 +6163,6 @@ internal error: nobits section follows nobits section")); } } - /* Now is a good time to determine the values of all the symbols we encountered. */ // XXX This loop is very inefficient. The hash tab iterator also @@ -6386,8 +6555,12 @@ internal error: nobits section follows nobits section")); free (ld_state.dblindirect); - /* Finalize the .plt section the what belongs to them. */ - FINALIZE_PLT (statep, nsym, nsym_dyn); + /* Finalize the .plt section and what else belongs to it. */ + FINALIZE_PLT (statep, nsym, nsym_local, ndxtosym); + + /* We don't need the map from the symbol table index to the symbol + structure anymore. */ + free (ndxtosym); return 0; } @@ -6519,7 +6692,19 @@ ld_generic_initialize_got (struct ld_state *statep, Elf_Scn *scn) static void -ld_generic_finalize_plt (struct ld_state *statep, size_t nsym, size_t nsym_dyn) +ld_generic_initialize_gotplt (struct ld_state *statep, Elf_Scn *scn) +{ + /* This cannot be implemented generally. There should have been a + machine dependent implementation and we should never have arrived + here. */ + error (EXIT_FAILURE, 0, gettext ("no machine specific '%s' implementation"), + "initialize_gotplt"); +} + + +static void +ld_generic_finalize_plt (struct ld_state *statep, size_t nsym, size_t nsym_dyn, + struct symbol **ndxtosymp) { /* By default we assume that nothing has to be done. */ }