From 108ba30509ad6169d0f2e06b755fa39a2c28a946 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Wed, 22 Oct 2003 06:58:17 +0000 Subject: [PATCH] bfd/ * elflink.c (_bfd_elf_export_symbol): Adjust for globals and locals field changes. (_bfd_elf_link_assign_sym_version): Likewise. * elflink.h (size_dynamic_sections): Likewise. include/ * bfdlink.h (struct bfd_elf_version_expr): Remove match field. Add wildcard and mask fields. (BFD_ELF_VERSION_C_TYPE): Define. (BFD_ELF_VERSION_CXX_TYPE): Likewise. (BFD_ELF_VERSION_JAVA_TYPE): Likewise. (struct bfd_elf_version_expr_head): New. (struct bfd_elf_version_tree): Add match field. Change type of globals and locals fields to struct bfd_elf_version_expr_head. ld/ * ldlang.c: Include hashtab.h. (lang_vers_match_lang_c, lang_vers_match_lang_cplusplus, lang_vers_match_lang_java): Remove. (lang_vers_match): New function. (lang_new_vers_pattern): Initialize wildcard and mask fields, don't initialize match. (lang_new_vers_node): Use xcalloc. Adjust for globals and locals field type changes. Set match field. (version_expr_head_hash, version_expr_head_eq): New functions. (lang_finalize_version_expr_head): New function. (lang_register_vers_node): Call lang_finalize_version_expr_head. Search in hash table if not wildcard when looking for duplicates. * emultempl/ppc64elf.em (new_vers_pattern): Don't bother with duplicate checking. Initialize all fields of dot_entry from entry with the exception of pattern and next. --- bfd/ChangeLog | 7 + bfd/elflink.c | 111 ++++++--------- bfd/elflink.h | 8 +- include/ChangeLog | 12 ++ include/bfdlink.h | 37 +++-- ld/ChangeLog | 18 +++ ld/emultempl/ppc64elf.em | 33 +---- ld/ldlang.c | 298 ++++++++++++++++++++++++++++++--------- 8 files changed, 347 insertions(+), 177 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 2a002c166c2..2d47b54ed9e 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,10 @@ +2003-10-22 Jakub Jelinek + + * elflink.c (_bfd_elf_export_symbol): Adjust for globals and locals + field changes. + (_bfd_elf_link_assign_sym_version): Likewise. + * elflink.h (size_dynamic_sections): Likewise. + 2003-10-21 Alexandre Oliva , Michael Snyder diff --git a/bfd/elflink.c b/bfd/elflink.c index 0e06903c4be..19f6d4d7b8b 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -1505,22 +1505,18 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data) for (t = eif->verdefs; t != NULL; t = t->next) { - if (t->globals != NULL) + if (t->globals.list != NULL) { - for (d = t->globals; d != NULL; d = d->next) - { - if ((*d->match) (d, h->root.root.string)) - goto doit; - } + d = (*t->match) (&t->globals, NULL, h->root.root.string); + if (d != NULL) + goto doit; } - if (t->locals != NULL) + if (t->locals.list != NULL) { - for (d = t->locals ; d != NULL; d = d->next) - { - if ((*d->match) (d, h->root.root.string)) - return TRUE; - } + d = (*t->match) (&t->locals, NULL, h->root.root.string); + if (d != NULL) + return TRUE; } } @@ -1699,31 +1695,19 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data) t->used = TRUE; d = NULL; - if (t->globals != NULL) - { - for (d = t->globals; d != NULL; d = d->next) - if ((*d->match) (d, alc)) - break; - } + if (t->globals.list != NULL) + d = (*t->match) (&t->globals, NULL, alc); /* See if there is anything to force this symbol to local scope. */ - if (d == NULL && t->locals != NULL) + if (d == NULL && t->locals.list != NULL) { - for (d = t->locals; d != NULL; d = d->next) - { - if ((*d->match) (d, alc)) - { - if (h->dynindx != -1 - && info->shared - && ! info->export_dynamic) - { - (*bed->elf_backend_hide_symbol) (info, h, TRUE); - } - - break; - } - } + d = (*t->match) (&t->locals, NULL, alc); + if (d != NULL + && h->dynindx != -1 + && info->shared + && ! info->export_dynamic) + (*bed->elf_backend_hide_symbol) (info, h, TRUE); } free (alc); @@ -1744,18 +1728,14 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data) return TRUE; amt = sizeof *t; - t = bfd_alloc (sinfo->output_bfd, amt); + t = bfd_zalloc (sinfo->output_bfd, amt); if (t == NULL) { sinfo->failed = TRUE; return FALSE; } - t->next = NULL; t->name = p; - t->globals = NULL; - t->locals = NULL; - t->deps = NULL; t->name_indx = (unsigned int) -1; t->used = TRUE; @@ -1801,30 +1781,26 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data) local_ver = NULL; for (t = sinfo->verdefs; t != NULL; t = t->next) { - if (t->globals != NULL) + if (t->globals.list != NULL) { bfd_boolean matched; matched = FALSE; - for (d = t->globals; d != NULL; d = d->next) - { - if ((*d->match) (d, h->root.root.string)) - { - if (d->symver) - matched = TRUE; - else - { - /* There is a version without definition. Make - the symbol the default definition for this - version. */ - h->verinfo.vertree = t; - local_ver = NULL; - d->script = 1; - break; - } - } - } - + d = NULL; + while ((d = (*t->match) (&t->globals, d, + h->root.root.string)) != NULL) + if (d->symver) + matched = TRUE; + else + { + /* There is a version without definition. Make + the symbol the default definition for this + version. */ + h->verinfo.vertree = t; + local_ver = NULL; + d->script = 1; + break; + } if (d != NULL) break; else if (matched) @@ -1833,19 +1809,18 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data) (*bed->elf_backend_hide_symbol) (info, h, TRUE); } - if (t->locals != NULL) + if (t->locals.list != NULL) { - for (d = t->locals; d != NULL; d = d->next) + d = NULL; + while ((d = (*t->match) (&t->locals, d, + h->root.root.string)) != NULL) { + local_ver = t; /* If the match is "*", keep looking for a more - explicit, perhaps even global, match. */ - if (d->pattern[0] == '*' && d->pattern[1] == '\0') - local_ver = t; - else if ((*d->match) (d, h->root.root.string)) - { - local_ver = t; - break; - } + explicit, perhaps even global, match. + XXX: Shouldn't this be !d->wildcard instead? */ + if (d->pattern[0] != '*' || d->pattern[1] != '\0') + break; } if (d != NULL) diff --git a/bfd/elflink.h b/bfd/elflink.h index 383cb88b068..e53911df855 100644 --- a/bfd/elflink.h +++ b/bfd/elflink.h @@ -2058,7 +2058,9 @@ NAME(bfd_elf,size_dynamic_sections) (bfd *output_bfd, /* Make all global versions with definiton. */ for (t = verdefs; t != NULL; t = t->next) - for (d = t->globals; d != NULL; d = d->next) + for (d = t->globals.list; d != NULL; d = d->next) + /* FIXME: Shouldn't this be !d->symver && d->wildcard == 0 + instead? */ if (!d->symver && strchr (d->pattern, '*') == NULL) { const char *verstr, *name; @@ -2124,7 +2126,7 @@ NAME(bfd_elf,size_dynamic_sections) (bfd *output_bfd, /* Check if all global versions have a definiton. */ all_defined = TRUE; for (t = verdefs; t != NULL; t = t->next) - for (d = t->globals; d != NULL; d = d->next) + for (d = t->globals.list; d != NULL; d = d->next) if (!d->symver && !d->script) { (*_bfd_error_handler) @@ -2372,7 +2374,7 @@ NAME(bfd_elf,size_dynamic_sections) (bfd *output_bfd, def.vd_version = VER_DEF_CURRENT; def.vd_flags = 0; - if (t->globals == NULL && t->locals == NULL && ! t->used) + if (t->globals.list == NULL && t->locals.list == NULL && ! t->used) def.vd_flags |= VER_FLG_WEAK; def.vd_ndx = t->vernum + 1; def.vd_cnt = cdeps + 1; diff --git a/include/ChangeLog b/include/ChangeLog index 34f66f05631..d1e9a1df72b 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,15 @@ +2003-10-22 Jakub Jelinek + + * bfdlink.h (struct bfd_elf_version_expr): Remove match field. + Add wildcard and mask fields. + (BFD_ELF_VERSION_C_TYPE): Define. + (BFD_ELF_VERSION_CXX_TYPE): Likewise. + (BFD_ELF_VERSION_JAVA_TYPE): Likewise. + (struct bfd_elf_version_expr_head): New. + (struct bfd_elf_version_tree): Add match field. + Change type of globals and locals fields + to struct bfd_elf_version_expr_head. + 2003-10-14 Bob Wilson * elf/xtensa.h: Formatting. Fix comments about property section diff --git a/include/bfdlink.h b/include/bfdlink.h index c174dcdc698..fd77c294442 100644 --- a/include/bfdlink.h +++ b/include/bfdlink.h @@ -617,20 +617,37 @@ extern struct bfd_link_order *bfd_new_link_order (bfd *, asection *); BFD, but it would be a pain. Instead, the regular linker sets up these structures, and then passes them into BFD. */ -/* Regular expressions for a version. */ +/* Glob pattern for a version. */ struct bfd_elf_version_expr { - /* Next regular expression for this version. */ + /* Next glob pattern for this version. */ struct bfd_elf_version_expr *next; - /* Regular expression. */ + /* Glob pattern. */ const char *pattern; - /* Matching function. */ - int (*match) (struct bfd_elf_version_expr *, const char *); /* Defined by ".symver". */ - unsigned int symver: 1; + unsigned int symver : 1; /* Defined by version script. */ unsigned int script : 1; + /* Is this a wildcard?. */ + unsigned int wildcard : 1; + /* Pattern type. */ +#define BFD_ELF_VERSION_C_TYPE 1 +#define BFD_ELF_VERSION_CXX_TYPE 2 +#define BFD_ELF_VERSION_JAVA_TYPE 4 + unsigned int mask : 3; +}; + +struct bfd_elf_version_expr_head +{ + /* List of all patterns, both wildcards and non-wildcards. */ + struct bfd_elf_version_expr *list; + /* Hash table for non-wildcards. */ + void *htab; + /* Remaining patterns. */ + struct bfd_elf_version_expr *remaining; + /* What kind of pattern types are present in list (bitmask). */ + unsigned int mask; }; /* Version dependencies. */ @@ -654,15 +671,19 @@ struct bfd_elf_version_tree /* Version number. */ unsigned int vernum; /* Regular expressions for global symbols in this version. */ - struct bfd_elf_version_expr *globals; + struct bfd_elf_version_expr_head globals; /* Regular expressions for local symbols in this version. */ - struct bfd_elf_version_expr *locals; + struct bfd_elf_version_expr_head locals; /* List of versions which this version depends upon. */ struct bfd_elf_version_deps *deps; /* Index of the version name. This is used within BFD. */ unsigned int name_indx; /* Whether this version tree was used. This is used within BFD. */ int used; + /* Matching hook. */ + struct bfd_elf_version_expr *(*match) + (struct bfd_elf_version_expr_head *head, + struct bfd_elf_version_expr *prev, const char *sym); }; #endif diff --git a/ld/ChangeLog b/ld/ChangeLog index a495412727f..42b035f1862 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,21 @@ +2003-10-22 Jakub Jelinek + + * ldlang.c: Include hashtab.h. + (lang_vers_match_lang_c, lang_vers_match_lang_cplusplus, + lang_vers_match_lang_java): Remove. + (lang_vers_match): New function. + (lang_new_vers_pattern): Initialize wildcard and mask + fields, don't initialize match. + (lang_new_vers_node): Use xcalloc. Adjust for globals and + locals field type changes. Set match field. + (version_expr_head_hash, version_expr_head_eq): New functions. + (lang_finalize_version_expr_head): New function. + (lang_register_vers_node): Call lang_finalize_version_expr_head. + Search in hash table if not wildcard when looking for duplicates. + * emultempl/ppc64elf.em (new_vers_pattern): Don't bother with + duplicate checking. Initialize all fields of dot_entry from entry + with the exception of pattern and next. + 2003-10-21 Nick Clifton * ldlang.c (lang_memory_region_lookup): Add second parameter - diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em index b1b3f9ef07a..1ed49e580bd 100644 --- a/ld/emultempl/ppc64elf.em +++ b/ld/emultempl/ppc64elf.em @@ -405,46 +405,17 @@ gld${EMULATION_NAME}_new_vers_pattern (struct bfd_elf_version_expr *entry) unsigned int len; char *dot_pat; - if (!dotsyms || entry->pattern[0] == '*') + if (!dotsyms || entry->pattern[0] == '*' || entry->pattern[0] == '.') return entry; - /* Is the script adding ".foo" explicitly? */ - if (entry->pattern[0] == '.') - { - /* We may have added this pattern automatically. Don't add it - again. Quadratic behaviour here is acceptable as the list - may be traversed for each input bfd symbol. */ - for (next = entry->next; next != NULL; next = next->next) - { - if (strcmp (next->pattern, entry->pattern) == 0 - && next->match == entry->match) - { - next = entry->next; - free ((char *) entry->pattern); - free (entry); - return next; - } - } - return entry; - } - - /* Don't add ".foo" if the script has already done so. */ - for (next = entry->next; next != NULL; next = next->next) - { - if (next->pattern[0] == '.' - && strcmp (next->pattern + 1, entry->pattern) == 0 - && next->match == entry->match) - return entry; - } - dot_entry = xmalloc (sizeof *dot_entry); + *dot_entry = *entry; dot_entry->next = entry; len = strlen (entry->pattern) + 2; dot_pat = xmalloc (len); dot_pat[0] = '.'; memcpy (dot_pat + 1, entry->pattern, len - 1); dot_entry->pattern = dot_pat; - dot_entry->match = entry->match; return dot_entry; } diff --git a/ld/ldlang.c b/ld/ldlang.c index ad75d2648e5..b180edeb34a 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -39,6 +39,7 @@ #include "ldemul.h" #include "fnmatch.h" #include "demangle.h" +#include "hashtab.h" #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER)) @@ -4973,65 +4974,108 @@ lang_leave_overlay (etree_type *lma_expr, struct bfd_elf_version_tree *lang_elf_version_info; -static int -lang_vers_match_lang_c (struct bfd_elf_version_expr *expr, - const char *sym) -{ - if (expr->pattern[0] == '*' && expr->pattern[1] == '\0') - return 1; - return fnmatch (expr->pattern, sym, 0) == 0; -} +/* If PREV is NULL, return first version pattern matching particular symbol. + If PREV is non-NULL, return first version pattern matching particular + symbol after PREV (previously returned by lang_vers_match). */ -static int -lang_vers_match_lang_cplusplus (struct bfd_elf_version_expr *expr, - const char *sym) +static struct bfd_elf_version_expr * +lang_vers_match (struct bfd_elf_version_expr_head *head, + struct bfd_elf_version_expr *prev, + const char *sym) { - char *alt_sym; - int result; - - if (expr->pattern[0] == '*' && expr->pattern[1] == '\0') - return 1; + const char *cxx_sym = sym; + const char *java_sym = sym; + struct bfd_elf_version_expr *expr = NULL; - alt_sym = cplus_demangle (sym, /* DMGL_NO_TPARAMS */ 0); - if (!alt_sym) + if (head->mask & BFD_ELF_VERSION_CXX_TYPE) { - /* cplus_demangle (also) returns NULL when it is not a C++ symbol. - Should we early out FALSE in this case? */ - result = fnmatch (expr->pattern, sym, 0) == 0; + cxx_sym = cplus_demangle (sym, /* DMGL_NO_TPARAMS */ 0); + if (!cxx_sym) + cxx_sym = sym; } - else + if (head->mask & BFD_ELF_VERSION_JAVA_TYPE) { - result = fnmatch (expr->pattern, alt_sym, 0) == 0; - free (alt_sym); + java_sym = cplus_demangle (sym, DMGL_JAVA); + if (!java_sym) + java_sym = sym; } - return result; -} - -static int -lang_vers_match_lang_java (struct bfd_elf_version_expr *expr, - const char *sym) -{ - char *alt_sym; - int result; - - if (expr->pattern[0] == '*' && expr->pattern[1] == '\0') - return 1; - - alt_sym = cplus_demangle (sym, DMGL_JAVA); - if (!alt_sym) + if (head->htab && (prev == NULL || prev->wildcard == 0)) { - /* cplus_demangle (also) returns NULL when it is not a Java symbol. - Should we early out FALSE in this case? */ - result = fnmatch (expr->pattern, sym, 0) == 0; + struct bfd_elf_version_expr e; + + switch (prev ? prev->mask : 0) + { + case 0: + if (head->mask & BFD_ELF_VERSION_C_TYPE) + { + e.pattern = sym; + expr = htab_find (head->htab, &e); + while (expr && strcmp (expr->pattern, sym) == 0) + if (expr->mask == BFD_ELF_VERSION_C_TYPE) + goto out_ret; + else + expr = expr->next; + } + /* Fallthrough */ + case BFD_ELF_VERSION_C_TYPE: + if (head->mask & BFD_ELF_VERSION_CXX_TYPE) + { + e.pattern = cxx_sym; + expr = htab_find (head->htab, &e); + while (expr && strcmp (expr->pattern, sym) == 0) + if (expr->mask == BFD_ELF_VERSION_CXX_TYPE) + goto out_ret; + else + expr = expr->next; + } + /* Fallthrough */ + case BFD_ELF_VERSION_CXX_TYPE: + if (head->mask & BFD_ELF_VERSION_JAVA_TYPE) + { + e.pattern = java_sym; + expr = htab_find (head->htab, &e); + while (expr && strcmp (expr->pattern, sym) == 0) + if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE) + goto out_ret; + else + expr = expr->next; + } + /* Fallthrough */ + default: + break; + } } + + /* Finally, try the wildcards. */ + if (prev == NULL || prev->wildcard == 0) + expr = head->remaining; else + expr = prev->next; + while (expr) { - result = fnmatch (expr->pattern, alt_sym, 0) == 0; - free (alt_sym); + const char *s; + + if (expr->pattern[0] == '*' && expr->pattern[1] == '\0') + break; + + if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE) + s = java_sym; + else if (expr->mask == BFD_ELF_VERSION_CXX_TYPE) + s = cxx_sym; + else + s = sym; + if (fnmatch (expr->pattern, sym, 0) == 0) + break; + expr = expr->next; } - return result; +out_ret: + if (cxx_sym != sym) + free ((char *) cxx_sym); + if (java_sym != sym) + free ((char *) java_sym); + return expr; } /* This is called for each variable name or match expression. */ @@ -5048,18 +5092,19 @@ lang_new_vers_pattern (struct bfd_elf_version_expr *orig, ret->pattern = new; ret->symver = 0; ret->script = 0; + ret->wildcard = wildcardp (new); if (lang == NULL || strcasecmp (lang, "C") == 0) - ret->match = lang_vers_match_lang_c; + ret->mask = BFD_ELF_VERSION_C_TYPE; else if (strcasecmp (lang, "C++") == 0) - ret->match = lang_vers_match_lang_cplusplus; + ret->mask = BFD_ELF_VERSION_CXX_TYPE; else if (strcasecmp (lang, "Java") == 0) - ret->match = lang_vers_match_lang_java; + ret->mask = BFD_ELF_VERSION_JAVA_TYPE; else { einfo (_("%X%P: unknown language `%s' in version information\n"), lang); - ret->match = lang_vers_match_lang_c; + ret->mask = BFD_ELF_VERSION_C_TYPE; } return ldemul_new_vers_pattern (ret); @@ -5074,15 +5119,11 @@ lang_new_vers_node (struct bfd_elf_version_expr *globals, { struct bfd_elf_version_tree *ret; - ret = xmalloc (sizeof *ret); - ret->next = NULL; - ret->name = NULL; - ret->vernum = 0; - ret->globals = globals; - ret->locals = locals; - ret->deps = NULL; + ret = xcalloc (1, sizeof *ret); + ret->globals.list = globals; + ret->locals.list = locals; + ret->match = lang_vers_match; ret->name_indx = (unsigned int) -1; - ret->used = 0; return ret; } @@ -5090,6 +5131,102 @@ lang_new_vers_node (struct bfd_elf_version_expr *globals, static int version_index; +static hashval_t +version_expr_head_hash (const void *p) +{ + const struct bfd_elf_version_expr *e = p; + + return htab_hash_string (e->pattern); +} + +static int +version_expr_head_eq (const void *p1, const void *p2) +{ + const struct bfd_elf_version_expr *e1 = p1; + const struct bfd_elf_version_expr *e2 = p2; + + return strcmp (e1->pattern, e2->pattern) == 0; +} + +static void +lang_finalize_version_expr_head (struct bfd_elf_version_expr_head *head) +{ + size_t count = 0; + struct bfd_elf_version_expr *e, *next; + struct bfd_elf_version_expr **list_loc, **remaining_loc; + + for (e = head->list; e; e = e->next) + { + if (!e->wildcard) + count++; + head->mask |= e->mask; + } + + if (count) + { + head->htab = htab_create (count * 2, version_expr_head_hash, + version_expr_head_eq, NULL); + list_loc = &head->list; + remaining_loc = &head->remaining; + for (e = head->list; e; e = next) + { + next = e->next; + if (e->wildcard) + { + *remaining_loc = e; + remaining_loc = &e->next; + } + else + { + void **loc = htab_find_slot (head->htab, e, INSERT); + + if (*loc) + { + struct bfd_elf_version_expr *e1, *last; + + e1 = *loc; + last = NULL; + do + { + if (e1->mask == e->mask) + { + last = NULL; + break; + } + last = e1; + e1 = e1->next; + } + while (e1 && strcmp (e1->pattern, e->pattern) == 0); + + if (last == NULL) + { + /* This is a duplicate. */ + /* FIXME: Memory leak. Sometimes pattern is not + xmalloced alone, but in larger chunk of memory. */ + /* free (e->pattern); */ + free (e); + } + else + { + e->next = last->next; + last->next = e; + } + } + else + { + *loc = e; + *list_loc = e; + list_loc = &e->next; + } + } + } + *remaining_loc = NULL; + *list_loc = head->remaining; + } + else + head->remaining = head->list; +} + /* This is called when we know the name and dependencies of the version. */ @@ -5117,32 +5254,59 @@ lang_register_vers_node (const char *name, if (strcmp (t->name, name) == 0) einfo (_("%X%P: duplicate version tag `%s'\n"), name); + lang_finalize_version_expr_head (&version->globals); + lang_finalize_version_expr_head (&version->locals); + /* Check the global and local match names, and make sure there aren't any duplicates. */ - for (e1 = version->globals; e1 != NULL; e1 = e1->next) + for (e1 = version->globals.list; e1 != NULL; e1 = e1->next) { for (t = lang_elf_version_info; t != NULL; t = t->next) { struct bfd_elf_version_expr *e2; - for (e2 = t->locals; e2 != NULL; e2 = e2->next) - if (strcmp (e1->pattern, e2->pattern) == 0) - einfo (_("%X%P: duplicate expression `%s' in version information\n"), - e1->pattern); + if (t->locals.htab && e1->wildcard == 0) + { + e2 = htab_find (t->locals.htab, e1); + while (e2 && strcmp (e1->pattern, e2->pattern) == 0) + { + if (e1->mask == e2->mask) + einfo (_("%X%P: duplicate expression `%s' in version information\n"), + e1->pattern); + e2 = e2->next; + } + } + else if (e1->wildcard) + for (e2 = t->locals.remaining; e2 != NULL; e2 = e2->next) + if (strcmp (e1->pattern, e2->pattern) == 0 && e1->mask == e2->mask) + einfo (_("%X%P: duplicate expression `%s' in version information\n"), + e1->pattern); } } - for (e1 = version->locals; e1 != NULL; e1 = e1->next) + for (e1 = version->locals.list; e1 != NULL; e1 = e1->next) { for (t = lang_elf_version_info; t != NULL; t = t->next) { struct bfd_elf_version_expr *e2; - for (e2 = t->globals; e2 != NULL; e2 = e2->next) - if (strcmp (e1->pattern, e2->pattern) == 0) - einfo (_("%X%P: duplicate expression `%s' in version information\n"), - e1->pattern); + if (t->globals.htab && e1->wildcard == 0) + { + e2 = htab_find (t->globals.htab, e1); + while (e2 && strcmp (e1->pattern, e2->pattern) == 0) + { + if (e1->mask == e2->mask) + einfo (_("%X%P: duplicate expression `%s' in version information\n"), + e1->pattern); + e2 = e2->next; + } + } + else if (e1->wildcard) + for (e2 = t->globals.remaining; e2 != NULL; e2 = e2->next) + if (strcmp (e1->pattern, e2->pattern) == 0 && e1->mask == e2->mask) + einfo (_("%X%P: duplicate expression `%s' in version information\n"), + e1->pattern); } } -- 2.30.2