diff options
author | Alan Modra <amodra@gmail.com> | 2011-08-17 00:39:41 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2011-08-17 00:39:41 +0000 |
commit | c77ec7261473e7a12a1af46c585caca4bb597b4f (patch) | |
tree | fe75ecc5e02d7b2bcd737b8ce8bccd8b14259809 | |
parent | 142a8c5dcf3f3949d5d2228b894e169ffec28df4 (diff) | |
download | gdb-c77ec7261473e7a12a1af46c585caca4bb597b4f.zip gdb-c77ec7261473e7a12a1af46c585caca4bb597b4f.tar.gz gdb-c77ec7261473e7a12a1af46c585caca4bb597b4f.tar.bz2 |
PR ld/12762
bfd/
* bfd-in.h (struct bfd_section_already_linked): Forward declare.
(_bfd_handle_already_linked): Declare.
* coff-alpha.c (_bfd_ecoff_section_already_linked): Define as
_bfd_coff_section_already_linked.
* coff-mips.c (_bfd_ecoff_section_already_linked): Likewise.
* coffcode.h (coff_section_already_linked): Likewise.
* cofflink.c (coff_link_add_symbols): Revert 2011-07-09 changes.
* elf-bfd.h: Likewise.
* libbfd-in.h: Likewise.
* targets.c: Likewise.
* linker.c (bfd_section_already_linked): Likewise.
(bfd_section_already_linked_table_lookup): Likewise.
(bfd_section_already_linked_table_insert): Likewise.
(_bfd_generic_section_already_linked): Likewise. Call
_bfd_handle_already_linked.
(_bfd_handle_already_linked): New function, split out from..
* elflink.c (_bfd_elf_section_already_linked): ..here. Revert
2011-07-09 changes. Avoid unnecessary strcmp when matching
already_linked_list entries. Match plugin linkonce section.
(section_signature): Delete.
* coffgen.c (_bfd_coff_section_already_linked): New function.
* libcoff-in.h (_bfd_coff_section_already_linked): Declare.
* libbfd.h: Regenerate.
* libcoff.h: Regenerate.
* bfd-in2.h: Regenerate.
ld/
* ldlang.c (section_already_linked): Revert 2011-07-09 changes.
* plugin.c: Likewise.
(asymbol_from_plugin_symbol): Create linkonce section for syms
with comdat_key.
-rw-r--r-- | bfd/ChangeLog | 29 | ||||
-rw-r--r-- | bfd/bfd-in.h | 15 | ||||
-rw-r--r-- | bfd/bfd-in2.h | 24 | ||||
-rw-r--r-- | bfd/coff-alpha.c | 2 | ||||
-rw-r--r-- | bfd/coff-mips.c | 2 | ||||
-rw-r--r-- | bfd/coffcode.h | 2 | ||||
-rw-r--r-- | bfd/coffgen.c | 67 | ||||
-rw-r--r-- | bfd/cofflink.c | 6 | ||||
-rw-r--r-- | bfd/elf-bfd.h | 3 | ||||
-rw-r--r-- | bfd/elflink.c | 371 | ||||
-rw-r--r-- | bfd/libbfd-in.h | 20 | ||||
-rw-r--r-- | bfd/libbfd.h | 20 | ||||
-rw-r--r-- | bfd/libcoff-in.h | 2 | ||||
-rw-r--r-- | bfd/libcoff.h | 2 | ||||
-rw-r--r-- | bfd/linker.c | 250 | ||||
-rw-r--r-- | bfd/targets.c | 3 | ||||
-rw-r--r-- | ld/ChangeLog | 8 | ||||
-rw-r--r-- | ld/ldlang.c | 7 | ||||
-rw-r--r-- | ld/plugin.c | 32 |
19 files changed, 388 insertions, 477 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index d3ec830..1cd864b 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,32 @@ +2011-08-17 Alan Modra <amodra@gmail.com> + + PR ld/12762 + * bfd-in.h (struct bfd_section_already_linked): Forward declare. + (_bfd_handle_already_linked): Declare. + * coff-alpha.c (_bfd_ecoff_section_already_linked): Define as + _bfd_coff_section_already_linked. + * coff-mips.c (_bfd_ecoff_section_already_linked): Likewise. + * coffcode.h (coff_section_already_linked): Likewise. + * cofflink.c (coff_link_add_symbols): Revert 2011-07-09 changes. + * elf-bfd.h: Likewise. + * libbfd-in.h: Likewise. + * targets.c: Likewise. + * linker.c (bfd_section_already_linked): Likewise. + (bfd_section_already_linked_table_lookup): Likewise. + (bfd_section_already_linked_table_insert): Likewise. + (_bfd_generic_section_already_linked): Likewise. Call + _bfd_handle_already_linked. + (_bfd_handle_already_linked): New function, split out from.. + * elflink.c (_bfd_elf_section_already_linked): ..here. Revert + 2011-07-09 changes. Avoid unnecessary strcmp when matching + already_linked_list entries. Match plugin linkonce section. + (section_signature): Delete. + * coffgen.c (_bfd_coff_section_already_linked): New function. + * libcoff-in.h (_bfd_coff_section_already_linked): Declare. + * libbfd.h: Regenerate. + * libcoff.h: Regenerate. + * bfd-in2.h: Regenerate. + 2011-08-14 Alan Modra <amodra@gmail.com> * elf32-ppc.c (ppc_elf_select_plt_layout): Force bss-plt when diff --git a/bfd/bfd-in.h b/bfd/bfd-in.h index f375d2f..718d72e 100644 --- a/bfd/bfd-in.h +++ b/bfd/bfd-in.h @@ -552,11 +552,6 @@ void bfd_putl16 (bfd_vma, void *); bfd_uint64_t bfd_get_bits (const void *, int, bfd_boolean); void bfd_put_bits (bfd_uint64_t, void *, int, bfd_boolean); -extern bfd_boolean bfd_section_already_linked_table_init (void); -extern void bfd_section_already_linked_table_free (void); - -/* Externally visible ECOFF routines. */ - #if defined(__STDC__) || defined(ALMOST_STDC) struct ecoff_debug_info; struct ecoff_debug_swap; @@ -564,8 +559,18 @@ struct ecoff_extr; struct bfd_symbol; struct bfd_link_info; struct bfd_link_hash_entry; +struct bfd_section_already_linked; struct bfd_elf_version_tree; #endif + +extern bfd_boolean bfd_section_already_linked_table_init (void); +extern void bfd_section_already_linked_table_free (void); +extern bfd_boolean _bfd_handle_already_linked + (struct bfd_section *, struct bfd_section_already_linked *, + struct bfd_link_info *); + +/* Externally visible ECOFF routines. */ + extern bfd_vma bfd_ecoff_get_gp_value (bfd * abfd); extern bfd_boolean bfd_ecoff_set_gp_value diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 63ffd95..7cf3b7e 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -559,11 +559,6 @@ void bfd_putl16 (bfd_vma, void *); bfd_uint64_t bfd_get_bits (const void *, int, bfd_boolean); void bfd_put_bits (bfd_uint64_t, void *, int, bfd_boolean); -extern bfd_boolean bfd_section_already_linked_table_init (void); -extern void bfd_section_already_linked_table_free (void); - -/* Externally visible ECOFF routines. */ - #if defined(__STDC__) || defined(ALMOST_STDC) struct ecoff_debug_info; struct ecoff_debug_swap; @@ -571,8 +566,18 @@ struct ecoff_extr; struct bfd_symbol; struct bfd_link_info; struct bfd_link_hash_entry; +struct bfd_section_already_linked; struct bfd_elf_version_tree; #endif + +extern bfd_boolean bfd_section_already_linked_table_init (void); +extern void bfd_section_already_linked_table_free (void); +extern bfd_boolean _bfd_handle_already_linked + (struct bfd_section *, struct bfd_section_already_linked *, + struct bfd_link_info *); + +/* Externally visible ECOFF routines. */ + extern bfd_vma bfd_ecoff_get_gp_value (bfd * abfd); extern bfd_boolean bfd_ecoff_set_gp_value @@ -5773,7 +5778,6 @@ enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN }; /* Forward declaration. */ typedef struct bfd_link_info _bfd_link_info; -struct already_linked; /* Forward declaration. */ typedef struct flag_info flag_info; @@ -6107,7 +6111,7 @@ typedef struct bfd_target /* Check if SEC has been already linked during a reloceatable or final link. */ - bfd_boolean (*_section_already_linked) (bfd *, struct already_linked *, + bfd_boolean (*_section_already_linked) (bfd *, asection *, struct bfd_link_info *); /* Define a common symbol. */ @@ -6178,11 +6182,11 @@ bfd_boolean bfd_link_split_section (bfd *abfd, asection *sec); BFD_SEND (abfd, _bfd_link_split_section, (abfd, sec)) bfd_boolean bfd_section_already_linked (bfd *abfd, - struct already_linked *data, + asection *sec, struct bfd_link_info *info); -#define bfd_section_already_linked(abfd, data, info) \ - BFD_SEND (abfd, _section_already_linked, (abfd, data, info)) +#define bfd_section_already_linked(abfd, sec, info) \ + BFD_SEND (abfd, _section_already_linked, (abfd, sec, info)) bfd_boolean bfd_generic_define_common_symbol (bfd *output_bfd, struct bfd_link_info *info, diff --git a/bfd/coff-alpha.c b/bfd/coff-alpha.c index 2233a33..4466e4d 100644 --- a/bfd/coff-alpha.c +++ b/bfd/coff-alpha.c @@ -2403,7 +2403,7 @@ static const struct ecoff_backend_data alpha_ecoff_backend_data = #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group #define _bfd_ecoff_section_already_linked \ - _bfd_generic_section_already_linked + _bfd_coff_section_already_linked #define _bfd_ecoff_bfd_define_common_symbol bfd_generic_define_common_symbol const bfd_target ecoffalpha_little_vec = diff --git a/bfd/coff-mips.c b/bfd/coff-mips.c index 9f8b90d..4048ca8 100644 --- a/bfd/coff-mips.c +++ b/bfd/coff-mips.c @@ -1419,7 +1419,7 @@ static const struct ecoff_backend_data mips_ecoff_backend_data = #define _bfd_ecoff_bfd_is_group_section bfd_generic_is_group_section #define _bfd_ecoff_bfd_discard_group bfd_generic_discard_group #define _bfd_ecoff_section_already_linked \ - _bfd_generic_section_already_linked + _bfd_coff_section_already_linked #define _bfd_ecoff_bfd_define_common_symbol bfd_generic_define_common_symbol extern const bfd_target ecoff_big_vec; diff --git a/bfd/coffcode.h b/bfd/coffcode.h index 2313dc5..6f9685b 100644 --- a/bfd/coffcode.h +++ b/bfd/coffcode.h @@ -5670,7 +5670,7 @@ static bfd_coff_backend_data ticoff1_swap_table = #ifndef coff_section_already_linked #define coff_section_already_linked \ - _bfd_generic_section_already_linked + _bfd_coff_section_already_linked #endif #ifndef coff_bfd_define_common_symbol diff --git a/bfd/coffgen.c b/bfd/coffgen.c index b0c2c62..bbb0acc 100644 --- a/bfd/coffgen.c +++ b/bfd/coffgen.c @@ -2399,3 +2399,70 @@ bfd_coff_get_comdat_section (bfd *abfd, struct bfd_section *sec) else return NULL; } + +bfd_boolean +_bfd_coff_section_already_linked (bfd *abfd, + asection *sec, + struct bfd_link_info *info) +{ + flagword flags; + const char *name, *key; + struct bfd_section_already_linked *l; + struct bfd_section_already_linked_hash_entry *already_linked_list; + struct coff_comdat_info *s_comdat; + + flags = sec->flags; + if ((flags & SEC_LINK_ONCE) == 0) + return FALSE; + + /* The COFF backend linker doesn't support group sections. */ + if ((flags & SEC_GROUP) != 0) + return FALSE; + + name = bfd_get_section_name (abfd, sec); + s_comdat = bfd_coff_get_comdat_section (abfd, sec); + + if (s_comdat != NULL) + key = s_comdat->name; + else + { + if (CONST_STRNEQ (name, ".gnu.linkonce.") + && (key = strchr (name + sizeof (".gnu.linkonce.") - 1, '.')) != NULL) + key++; + else + /* FIXME: gcc as of 2011-09 emits sections like .text$<key>, + .xdata$<key> and .pdata$<key> only the first of which has a + comdat key. Should these all match the LTO IR key? */ + key = name; + } + + already_linked_list = bfd_section_already_linked_table_lookup (key); + + for (l = already_linked_list->entry; l != NULL; l = l->next) + { + struct coff_comdat_info *l_comdat; + + l_comdat = bfd_coff_get_comdat_section (l->sec->owner, l->sec); + + /* The section names must match, and both sections must be + comdat and have the same comdat name, or both sections must + be non-comdat. LTO IR plugin sections are an exception. They + are always named .gnu.linkonce.t.<key> (<key> is some string) + and match any comdat section with comdat name of <key>, and + any linkonce section with the same suffix, ie. + .gnu.linkonce.*.<key>. */ + if (((s_comdat != NULL) == (l_comdat != NULL) + && strcmp (name, l->sec->name) == 0) + || (l->sec->owner->flags & BFD_PLUGIN) != 0) + { + /* The section has already been linked. See if we should + issue a warning. */ + return _bfd_handle_already_linked (sec, l, info); + } + } + + /* This is the first section with this name. Record it. */ + if (!bfd_section_already_linked_table_insert (already_linked_list, sec)) + info->callbacks->einfo (_("%F%P: already_linked_table: %E\n")); + return FALSE; +} diff --git a/bfd/cofflink.c b/bfd/cofflink.c index 27257ba..bca1364 100644 --- a/bfd/cofflink.c +++ b/bfd/cofflink.c @@ -392,11 +392,7 @@ coff_link_add_symbols (bfd *abfd, section = coff_section_from_bfd_index (abfd, sym.n_scnum); if (! obj_pe (abfd)) value -= section->vma; - /* Treat a symbol from a discarded section as undefined. */ - if (bfd_is_abs_section (section) - || !bfd_is_abs_section (section->output_section)) - break; - /* Fall thru */ + break; case COFF_SYMBOL_UNDEFINED: flags = 0; diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 2c80f67..d6e2ab2 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -1801,9 +1801,8 @@ extern bfd_boolean _bfd_elf_match_sections_by_type (bfd *, const asection *, bfd *, const asection *); extern bfd_boolean bfd_elf_is_group_section (bfd *, const struct bfd_section *); -struct already_linked; extern bfd_boolean _bfd_elf_section_already_linked - (bfd *, struct already_linked *, struct bfd_link_info *); + (bfd *, asection *, struct bfd_link_info *); extern void bfd_elf_set_group_contents (bfd *, asection *, void *); extern asection *_bfd_elf_check_kept_section diff --git a/bfd/elflink.c b/bfd/elflink.c index 53765b6..528f705 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -12502,208 +12502,84 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info) return ret; } -/* For a SHT_GROUP section, return the group signature. For other - sections, return the normal section name. */ - -static const char * -section_signature (asection *sec) -{ - if ((sec->flags & SEC_GROUP) != 0 - && elf_next_in_group (sec) != NULL - && elf_group_name (elf_next_in_group (sec)) != NULL) - return elf_group_name (elf_next_in_group (sec)); - return sec->name; -} - bfd_boolean _bfd_elf_section_already_linked (bfd *abfd, - struct already_linked *linked, + asection *sec, struct bfd_link_info *info) { flagword flags; - const char *name, *p; + const char *name, *key; struct bfd_section_already_linked *l; struct bfd_section_already_linked_hash_entry *already_linked_list; - asection *sec, *l_sec; - bfd_boolean matched; - - p = name = linked->comdat_key; - if (name) - { - sec = NULL; - flags = SEC_GROUP | SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD; - } - else - { - sec = linked->u.sec; - if (sec->output_section == bfd_abs_section_ptr) - return FALSE; - flags = sec->flags; - - /* Return if it isn't a linkonce section. A comdat group section - also has SEC_LINK_ONCE set. */ - if ((flags & SEC_LINK_ONCE) == 0) - return FALSE; - - /* Don't put group member sections on our list of already linked - sections. They are handled as a group via their group section. - */ - if (elf_sec_group (sec) != NULL) - return FALSE; - - /* FIXME: When doing a relocatable link, we may have trouble - copying relocations in other sections that refer to local symbols - in the section being discarded. Those relocations will have to - be converted somehow; as of this writing I'm not sure that any of - the backends handle that correctly. + if (sec->output_section == bfd_abs_section_ptr) + return FALSE; - It is tempting to instead not discard link once sections when - doing a relocatable link (technically, they should be discarded - whenever we are building constructors). However, that fails, - because the linker winds up combining all the link once sections - into a single large link once section, which defeats the purpose - of having link once sections in the first place. + flags = sec->flags; - Also, not merging link once sections in a relocatable link - causes trouble for MIPS ELF, which relies on link once semantics - to handle the .reginfo section correctly. */ + /* Return if it isn't a linkonce section. A comdat group section + also has SEC_LINK_ONCE set. */ + if ((flags & SEC_LINK_ONCE) == 0) + return FALSE; - name = section_signature (sec); + /* Don't put group member sections on our list of already linked + sections. They are handled as a group via their group section. */ + if (elf_sec_group (sec) != NULL) + return FALSE; + /* For a SHT_GROUP section, use the group signature as the key. */ + name = sec->name; + if ((flags & SEC_GROUP) != 0 + && elf_next_in_group (sec) != NULL + && elf_group_name (elf_next_in_group (sec)) != NULL) + key = elf_group_name (elf_next_in_group (sec)); + else + { + /* Otherwise we should have a .gnu.linkonce.<type>.<key> section. */ if (CONST_STRNEQ (name, ".gnu.linkonce.") - && ((p = strchr (name + sizeof (".gnu.linkonce.") - 1, '.')) - != NULL)) - p++; + && (key = strchr (name + sizeof (".gnu.linkonce.") - 1, '.')) != NULL) + key++; else - p = name; + /* Must be a user linkonce section that doesn't follow gcc's + naming convention. In this case we won't be matching + single member groups. */ + key = name; } - already_linked_list = bfd_section_already_linked_table_lookup (p); + already_linked_list = bfd_section_already_linked_table_lookup (key); for (l = already_linked_list->entry; l != NULL; l = l->next) { - flagword l_flags; - bfd *l_owner; - const char *l_name = l->linked.comdat_key; - if (l_name) - { - l_sec = NULL; - l_owner = l->linked.u.abfd; - l_flags = (SEC_GROUP - | SEC_LINK_ONCE - | SEC_LINK_DUPLICATES_DISCARD); - } - else - { - l_sec = l->linked.u.sec; - l_owner = l_sec->owner; - l_flags = l_sec->flags; - l_name = section_signature (l_sec); - } - /* We may have 2 different types of sections on the list: group - sections and linkonce sections. Match like sections. */ - if ((flags & SEC_GROUP) == (l_flags & SEC_GROUP) - && strcmp (name, l_name) == 0) + sections with a signature of <key> (<key> is some string), + and linkonce sections named .gnu.linkonce.<type>.<key>. + Match like sections. LTO plugin sections are an exception. + They are always named .gnu.linkonce.t.<key> and match either + type of section. */ + if (((flags & SEC_GROUP) == (l->sec->flags & SEC_GROUP) + && ((flags & SEC_GROUP) != 0 + || strcmp (name, l->sec->name) == 0)) + || (l->sec->owner->flags & BFD_PLUGIN) != 0) { /* The section has already been linked. See if we should issue a warning. */ - switch (flags & SEC_LINK_DUPLICATES) - { - default: - abort (); - - case SEC_LINK_DUPLICATES_DISCARD: - /* If we found an LTO IR match for this comdat group on - the first pass, replace it with the LTO output on the - second pass. We can't simply choose real object - files over IR because the first pass may contain a - mix of LTO and normal objects and we must keep the - first match, be it IR or real. */ - if (info->loading_lto_outputs - && (l_owner->flags & BFD_PLUGIN) != 0) - { - l->linked = *linked; - return FALSE; - } - break; - - case SEC_LINK_DUPLICATES_ONE_ONLY: - (*_bfd_error_handler) - (_("%B: ignoring duplicate section `%A'"), - abfd, sec); - break; - - case SEC_LINK_DUPLICATES_SAME_SIZE: - if (!sec || !l_sec) - abort (); - - if (sec->size != l_sec->size) - (*_bfd_error_handler) - (_("%B: duplicate section `%A' has different size"), - abfd, sec); - break; - - case SEC_LINK_DUPLICATES_SAME_CONTENTS: - if (!sec || !l_sec) - abort (); - - if (sec->size != l_sec->size) - (*_bfd_error_handler) - (_("%B: duplicate section `%A' has different size"), - abfd, sec); - else if (sec->size != 0) - { - bfd_byte *sec_contents, *l_sec_contents; - - if (!bfd_malloc_and_get_section (abfd, sec, &sec_contents)) - (*_bfd_error_handler) - (_("%B: warning: could not read contents of section `%A'"), - abfd, sec); - else if (!bfd_malloc_and_get_section (l_sec->owner, l_sec, - &l_sec_contents)) - (*_bfd_error_handler) - (_("%B: warning: could not read contents of section `%A'"), - l_sec->owner, l_sec); - else if (memcmp (sec_contents, l_sec_contents, sec->size) != 0) - (*_bfd_error_handler) - (_("%B: warning: duplicate section `%A' has different contents"), - abfd, sec); - - if (sec_contents) - free (sec_contents); - if (l_sec_contents) - free (l_sec_contents); - } - break; - } + if (!_bfd_handle_already_linked (sec, l, info)) + return FALSE; - if (sec) + if (flags & SEC_GROUP) { - /* Set the output_section field so that lang_add_section - does not create a lang_input_section structure for this - section. Since there might be a symbol in the section - being discarded, we must retain a pointer to the section - which we are really going to use. */ - sec->output_section = bfd_abs_section_ptr; - sec->kept_section = l_sec; + asection *first = elf_next_in_group (sec); + asection *s = first; - if (flags & SEC_GROUP) + while (s != NULL) { - asection *first = elf_next_in_group (sec); - asection *s = first; - - while (s != NULL) - { - s->output_section = bfd_abs_section_ptr; - /* Record which group discards it. */ - s->kept_section = l_sec; - s = elf_next_in_group (s); - /* These lists are circular. */ - if (s == first) - break; - } + s->output_section = bfd_abs_section_ptr; + /* Record which group discards it. */ + s->kept_section = l->sec; + s = elf_next_in_group (s); + /* These lists are circular. */ + if (s == first) + break; } } @@ -12711,108 +12587,67 @@ _bfd_elf_section_already_linked (bfd *abfd, } } - matched = FALSE; - if (sec) + /* A single member comdat group section may be discarded by a + linkonce section and vice versa. */ + if ((flags & SEC_GROUP) != 0) { - /* A single member comdat group section may be discarded by a - linkonce section and vice versa. */ + asection *first = elf_next_in_group (sec); - if ((flags & SEC_GROUP) != 0) + if (first != NULL && elf_next_in_group (first) == first) + /* Check this single member group against linkonce sections. */ + for (l = already_linked_list->entry; l != NULL; l = l->next) + if ((l->sec->flags & SEC_GROUP) == 0 + && bfd_elf_match_symbols_in_sections (l->sec, first, info)) + { + first->output_section = bfd_abs_section_ptr; + first->kept_section = l->sec; + sec->output_section = bfd_abs_section_ptr; + break; + } + } + else + /* Check this linkonce section against single member groups. */ + for (l = already_linked_list->entry; l != NULL; l = l->next) + if (l->sec->flags & SEC_GROUP) { - asection *first = elf_next_in_group (sec); + asection *first = elf_next_in_group (l->sec); - if (first != NULL && elf_next_in_group (first) == first) - /* Check this single member group against linkonce sections. */ - for (l = already_linked_list->entry; l != NULL; l = l->next) - { - if (l->linked.comdat_key == NULL) - { - l_sec = l->linked.u.sec; - - if ((l_sec->flags & SEC_GROUP) == 0 - && bfd_coff_get_comdat_section (l_sec->owner, - l_sec) == NULL - && bfd_elf_match_symbols_in_sections (l_sec, - first, - info)) - { - first->output_section = bfd_abs_section_ptr; - first->kept_section = l_sec; - sec->output_section = bfd_abs_section_ptr; - matched = TRUE; - break; - } - } - } + if (first != NULL + && elf_next_in_group (first) == first + && bfd_elf_match_symbols_in_sections (first, sec, info)) + { + sec->output_section = bfd_abs_section_ptr; + sec->kept_section = first; + break; + } } - else - /* Check this linkonce section against single member groups. */ - for (l = already_linked_list->entry; l != NULL; l = l->next) - { - if (l->linked.comdat_key == NULL) - { - l_sec = l->linked.u.sec; - - if (l_sec->flags & SEC_GROUP) - { - asection *first = elf_next_in_group (l_sec); - - if (first != NULL - && elf_next_in_group (first) == first - && bfd_elf_match_symbols_in_sections (first, - sec, - info)) - { - sec->output_section = bfd_abs_section_ptr; - sec->kept_section = first; - matched = TRUE; - break; - } - } - } - } - /* Do not complain on unresolved relocations in `.gnu.linkonce.r.F' - referencing its discarded `.gnu.linkonce.t.F' counterpart - - g++-3.4 specific as g++-4.x is using COMDAT groups (without the - `.gnu.linkonce' prefix) instead. `.gnu.linkonce.r.*' were the - `.rodata' part of its matching `.gnu.linkonce.t.*'. If - `.gnu.linkonce.r.F' is not discarded but its `.gnu.linkonce.t.F' - is discarded means we chose one-only `.gnu.linkonce.t.F' section - from a different bfd not requiring any `.gnu.linkonce.r.F'. - Thus `.gnu.linkonce.r.F' should be discarded. The reverse order - cannot happen as there is never a bfd with only the - `.gnu.linkonce.r.F' section. The order of sections in a bfd - does not matter as here were are looking only for cross-bfd - sections. */ - - if ((flags & SEC_GROUP) == 0 - && CONST_STRNEQ (name, ".gnu.linkonce.r.")) - for (l = already_linked_list->entry; l != NULL; l = l->next) - { - if (l->linked.comdat_key == NULL) - { - l_sec = l->linked.u.sec; - - if ((l_sec->flags & SEC_GROUP) == 0 - && CONST_STRNEQ (l_sec->name, ".gnu.linkonce.t.")) - { - if (abfd != l_sec->owner) - { - sec->output_section = bfd_abs_section_ptr; - matched = TRUE; - } - break; - } - } - } - } + /* Do not complain on unresolved relocations in `.gnu.linkonce.r.F' + referencing its discarded `.gnu.linkonce.t.F' counterpart - g++-3.4 + specific as g++-4.x is using COMDAT groups (without the `.gnu.linkonce' + prefix) instead. `.gnu.linkonce.r.*' were the `.rodata' part of its + matching `.gnu.linkonce.t.*'. If `.gnu.linkonce.r.F' is not discarded + but its `.gnu.linkonce.t.F' is discarded means we chose one-only + `.gnu.linkonce.t.F' section from a different bfd not requiring any + `.gnu.linkonce.r.F'. Thus `.gnu.linkonce.r.F' should be discarded. + The reverse order cannot happen as there is never a bfd with only the + `.gnu.linkonce.r.F' section. The order of sections in a bfd does not + matter as here were are looking only for cross-bfd sections. */ + + if ((flags & SEC_GROUP) == 0 && CONST_STRNEQ (name, ".gnu.linkonce.r.")) + for (l = already_linked_list->entry; l != NULL; l = l->next) + if ((l->sec->flags & SEC_GROUP) == 0 + && CONST_STRNEQ (l->sec->name, ".gnu.linkonce.t.")) + { + if (abfd != l->sec->owner) + sec->output_section = bfd_abs_section_ptr; + break; + } /* This is the first section with this name. Record it. */ - if (! bfd_section_already_linked_table_insert (already_linked_list, - linked)) + if (!bfd_section_already_linked_table_insert (already_linked_list, sec)) info->callbacks->einfo (_("%F%P: already_linked_table: %E\n")); - return matched; + return sec->output_section == bfd_abs_section_ptr; } bfd_boolean diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h index db39f3c..b6c90d1 100644 --- a/bfd/libbfd-in.h +++ b/bfd/libbfd-in.h @@ -3,7 +3,7 @@ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - 2010 + 2010, 2011 Free Software Foundation, Inc. Written by Cygnus Support. @@ -481,7 +481,7 @@ extern bfd_boolean _bfd_generic_set_section_contents #define _bfd_nolink_bfd_link_split_section \ ((bfd_boolean (*) (bfd *, struct bfd_section *)) bfd_false) #define _bfd_nolink_section_already_linked \ - ((bfd_boolean (*) (bfd *, struct already_linked*, \ + ((bfd_boolean (*) (bfd *, asection *, \ struct bfd_link_info *)) bfd_false) #define _bfd_nolink_bfd_define_common_symbol \ ((bfd_boolean (*) (bfd *, struct bfd_link_info *, \ @@ -603,7 +603,7 @@ extern bfd_boolean _bfd_generic_link_split_section (bfd *, struct bfd_section *); extern bfd_boolean _bfd_generic_section_already_linked - (bfd *, struct already_linked *, struct bfd_link_info *); + (bfd *, asection *, struct bfd_link_info *); /* Generic reloc_link_order processing routine. */ extern bfd_boolean _bfd_generic_reloc_link_order @@ -795,26 +795,16 @@ struct bfd_section_already_linked_hash_entry struct bfd_section_already_linked *entry; }; -struct already_linked -{ - const char *comdat_key; - union - { - asection *sec; - bfd *abfd; - } u; -}; - struct bfd_section_already_linked { struct bfd_section_already_linked *next; - struct already_linked linked; + asection *sec; }; extern struct bfd_section_already_linked_hash_entry * bfd_section_already_linked_table_lookup (const char *); extern bfd_boolean bfd_section_already_linked_table_insert - (struct bfd_section_already_linked_hash_entry *, struct already_linked *); + (struct bfd_section_already_linked_hash_entry *, asection *); extern void bfd_section_already_linked_table_traverse (bfd_boolean (*) (struct bfd_section_already_linked_hash_entry *, void *), void *); diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 71188dc..200a6fa 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -8,7 +8,7 @@ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - 2010 + 2010, 2011 Free Software Foundation, Inc. Written by Cygnus Support. @@ -486,7 +486,7 @@ extern bfd_boolean _bfd_generic_set_section_contents #define _bfd_nolink_bfd_link_split_section \ ((bfd_boolean (*) (bfd *, struct bfd_section *)) bfd_false) #define _bfd_nolink_section_already_linked \ - ((bfd_boolean (*) (bfd *, struct already_linked*, \ + ((bfd_boolean (*) (bfd *, asection *, \ struct bfd_link_info *)) bfd_false) #define _bfd_nolink_bfd_define_common_symbol \ ((bfd_boolean (*) (bfd *, struct bfd_link_info *, \ @@ -608,7 +608,7 @@ extern bfd_boolean _bfd_generic_link_split_section (bfd *, struct bfd_section *); extern bfd_boolean _bfd_generic_section_already_linked - (bfd *, struct already_linked *, struct bfd_link_info *); + (bfd *, asection *, struct bfd_link_info *); /* Generic reloc_link_order processing routine. */ extern bfd_boolean _bfd_generic_reloc_link_order @@ -800,26 +800,16 @@ struct bfd_section_already_linked_hash_entry struct bfd_section_already_linked *entry; }; -struct already_linked -{ - const char *comdat_key; - union - { - asection *sec; - bfd *abfd; - } u; -}; - struct bfd_section_already_linked { struct bfd_section_already_linked *next; - struct already_linked linked; + asection *sec; }; extern struct bfd_section_already_linked_hash_entry * bfd_section_already_linked_table_lookup (const char *); extern bfd_boolean bfd_section_already_linked_table_insert - (struct bfd_section_already_linked_hash_entry *, struct already_linked *); + (struct bfd_section_already_linked_hash_entry *, asection *); extern void bfd_section_already_linked_table_traverse (bfd_boolean (*) (struct bfd_section_already_linked_hash_entry *, void *), void *); diff --git a/bfd/libcoff-in.h b/bfd/libcoff-in.h index 1b65151..00d9ab2 100644 --- a/bfd/libcoff-in.h +++ b/bfd/libcoff-in.h @@ -546,6 +546,8 @@ extern struct bfd_link_hash_table *_bfd_coff_link_hash_table_create (bfd *); extern const char *_bfd_coff_internal_syment_name (bfd *, const struct internal_syment *, char *); +extern bfd_boolean _bfd_coff_section_already_linked + (bfd *, asection *, struct bfd_link_info *); extern bfd_boolean _bfd_coff_link_add_symbols (bfd *, struct bfd_link_info *); extern bfd_boolean _bfd_coff_final_link diff --git a/bfd/libcoff.h b/bfd/libcoff.h index 37b05d1..bd58c82 100644 --- a/bfd/libcoff.h +++ b/bfd/libcoff.h @@ -550,6 +550,8 @@ extern struct bfd_link_hash_table *_bfd_coff_link_hash_table_create (bfd *); extern const char *_bfd_coff_internal_syment_name (bfd *, const struct internal_syment *, char *); +extern bfd_boolean _bfd_coff_section_already_linked + (bfd *, asection *, struct bfd_link_info *); extern bfd_boolean _bfd_coff_link_add_symbols (bfd *, struct bfd_link_info *); extern bfd_boolean _bfd_coff_final_link diff --git a/bfd/linker.c b/bfd/linker.c index b3ccefd..e443862 100644 --- a/bfd/linker.c +++ b/bfd/linker.c @@ -2889,15 +2889,15 @@ FUNCTION SYNOPSIS bfd_boolean bfd_section_already_linked (bfd *abfd, - struct already_linked *data, + asection *sec, struct bfd_link_info *info); DESCRIPTION Check if @var{data} has been already linked during a reloceatable or final link. Return TRUE if it has. -.#define bfd_section_already_linked(abfd, data, info) \ -. BFD_SEND (abfd, _section_already_linked, (abfd, data, info)) +.#define bfd_section_already_linked(abfd, sec, info) \ +. BFD_SEND (abfd, _section_already_linked, (abfd, sec, info)) . */ @@ -2940,7 +2940,7 @@ bfd_section_already_linked_table_lookup (const char *name) bfd_boolean bfd_section_already_linked_table_insert (struct bfd_section_already_linked_hash_entry *already_linked_list, - struct already_linked *data) + asection *sec) { struct bfd_section_already_linked *l; @@ -2950,7 +2950,7 @@ bfd_section_already_linked_table_insert bfd_hash_allocate (&_bfd_section_already_linked_table, sizeof *l); if (l == NULL) return FALSE; - l->linked = *data; + l->sec = sec; l->next = already_linked_list->entry; already_linked_list->entry = l; return TRUE; @@ -2988,159 +2988,137 @@ bfd_section_already_linked_table_free (void) bfd_hash_table_free (&_bfd_section_already_linked_table); } -/* This is used on non-ELF inputs. */ +/* Report warnings as appropriate for duplicate section SEC. + Return FALSE if we decide to keep SEC after all. */ bfd_boolean -_bfd_generic_section_already_linked (bfd *abfd, - struct already_linked *linked, - struct bfd_link_info *info) +_bfd_handle_already_linked (asection *sec, + struct bfd_section_already_linked *l, + struct bfd_link_info *info) { - flagword flags; - const char *name; - struct bfd_section_already_linked *l; - struct bfd_section_already_linked_hash_entry *already_linked_list; - struct coff_comdat_info *s_comdat; - asection *sec; - - name = linked->comdat_key; - if (name) - { - sec = NULL; - flags = SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD; - s_comdat = NULL; - } - else + switch (sec->flags & SEC_LINK_DUPLICATES) { - sec = linked->u.sec; - flags = sec->flags; - if ((flags & SEC_LINK_ONCE) == 0) - return FALSE; + default: + abort (); - s_comdat = bfd_coff_get_comdat_section (abfd, sec); + case SEC_LINK_DUPLICATES_DISCARD: + /* If we found an LTO IR match for this comdat group on + the first pass, replace it with the LTO output on the + second pass. We can't simply choose real object + files over IR because the first pass may contain a + mix of LTO and normal objects and we must keep the + first match, be it IR or real. */ + if (info->loading_lto_outputs + && (l->sec->owner->flags & BFD_PLUGIN) != 0) + { + l->sec = sec; + return FALSE; + } + break; - /* FIXME: When doing a relocatable link, we may have trouble - copying relocations in other sections that refer to local symbols - in the section being discarded. Those relocations will have to - be converted somehow; as of this writing I'm not sure that any of - the backends handle that correctly. + case SEC_LINK_DUPLICATES_ONE_ONLY: + info->callbacks->einfo + (_("%B: ignoring duplicate section `%A'\n"), + sec->owner, sec); + break; - It is tempting to instead not discard link once sections when - doing a relocatable link (technically, they should be discarded - whenever we are building constructors). However, that fails, - because the linker winds up combining all the link once sections - into a single large link once section, which defeats the purpose - of having link once sections in the first place. */ + case SEC_LINK_DUPLICATES_SAME_SIZE: + if ((l->sec->owner->flags & BFD_PLUGIN) != 0) + ; + else if (sec->size != l->sec->size) + info->callbacks->einfo + (_("%B: duplicate section `%A' has different size\n"), + sec->owner, sec); + break; - name = bfd_get_section_name (abfd, sec); + case SEC_LINK_DUPLICATES_SAME_CONTENTS: + if ((l->sec->owner->flags & BFD_PLUGIN) != 0) + ; + else if (sec->size != l->sec->size) + info->callbacks->einfo + (_("%B: duplicate section `%A' has different size\n"), + sec->owner, sec); + else if (sec->size != 0) + { + bfd_byte *sec_contents, *l_sec_contents = NULL; + + if (!bfd_malloc_and_get_section (sec->owner, sec, &sec_contents)) + info->callbacks->einfo + (_("%B: could not read contents of section `%A'\n"), + sec->owner, sec); + else if (!bfd_malloc_and_get_section (l->sec->owner, l->sec, + &l_sec_contents)) + info->callbacks->einfo + (_("%B: could not read contents of section `%A'\n"), + l->sec->owner, l->sec); + else if (memcmp (sec_contents, l_sec_contents, sec->size) != 0) + info->callbacks->einfo + (_("%B: duplicate section `%A' has different contents\n"), + sec->owner, sec); + + if (sec_contents) + free (sec_contents); + if (l_sec_contents) + free (l_sec_contents); + } + break; } - already_linked_list = bfd_section_already_linked_table_lookup (name); + /* Set the output_section field so that lang_add_section + does not create a lang_input_section structure for this + section. Since there might be a symbol in the section + being discarded, we must retain a pointer to the section + which we are really going to use. */ + sec->output_section = bfd_abs_section_ptr; + sec->kept_section = l->sec; + return TRUE; +} - for (l = already_linked_list->entry; l != NULL; l = l->next) - { - bfd_boolean skip = FALSE; - bfd *l_owner; - flagword l_flags; - struct coff_comdat_info *l_comdat; - asection *l_sec; +/* This is used on non-ELF inputs. */ - if (l->linked.comdat_key) - { - l_sec = NULL; - l_owner = l->linked.u.abfd; - l_comdat = NULL; - l_flags = SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD; - } - else - { - l_sec = l->linked.u.sec; - l_owner = l_sec->owner; - l_flags = l_sec->flags; - l_comdat = bfd_coff_get_comdat_section (l_sec->owner, l_sec); - } +bfd_boolean +_bfd_generic_section_already_linked (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + struct bfd_link_info *info) +{ + const char *name; + struct bfd_section_already_linked *l; + struct bfd_section_already_linked_hash_entry *already_linked_list; - /* We may have 3 different sections on the list: group section, - comdat section and linkonce section. SEC may be a linkonce or - comdat section. We always ignore group section. For non-COFF - inputs, we also ignore comdat section. + if ((sec->flags & SEC_LINK_ONCE) == 0) + return FALSE; - FIXME: Is that safe to match a linkonce section with a comdat - section for COFF inputs? */ - if ((l_flags & SEC_GROUP) != 0) - skip = TRUE; - else if (bfd_get_flavour (abfd) == bfd_target_coff_flavour) - { - if (s_comdat != NULL - && l_comdat != NULL - && strcmp (s_comdat->name, l_comdat->name) != 0) - skip = TRUE; - } - else if (l_comdat != NULL) - skip = TRUE; + /* The generic linker doesn't handle section groups. */ + if ((sec->flags & SEC_GROUP) != 0) + return FALSE; - if (!skip) - { - /* The section has already been linked. See if we should - issue a warning. */ - switch (flags & SEC_LINK_DUPLICATES) - { - default: - abort (); - - case SEC_LINK_DUPLICATES_DISCARD: - /* If we found an LTO IR match for this comdat group on - the first pass, replace it with the LTO output on the - second pass. We can't simply choose real object - files over IR because the first pass may contain a - mix of LTO and normal objects and we must keep the - first match, be it IR or real. */ - if (info->loading_lto_outputs - && (l_owner->flags & BFD_PLUGIN) != 0) - { - l->linked = *linked; - return FALSE; - } - break; + /* FIXME: When doing a relocatable link, we may have trouble + copying relocations in other sections that refer to local symbols + in the section being discarded. Those relocations will have to + be converted somehow; as of this writing I'm not sure that any of + the backends handle that correctly. - case SEC_LINK_DUPLICATES_ONE_ONLY: - (*_bfd_error_handler) - (_("%B: warning: ignoring duplicate section `%A'\n"), - abfd, sec); - break; + It is tempting to instead not discard link once sections when + doing a relocatable link (technically, they should be discarded + whenever we are building constructors). However, that fails, + because the linker winds up combining all the link once sections + into a single large link once section, which defeats the purpose + of having link once sections in the first place. */ - case SEC_LINK_DUPLICATES_SAME_CONTENTS: - /* FIXME: We should really dig out the contents of both - sections and memcmp them. The COFF/PE spec says that - the Microsoft linker does not implement this - correctly, so I'm not going to bother doing it - either. */ - /* Fall through. */ - case SEC_LINK_DUPLICATES_SAME_SIZE: - if (sec->size != l_sec->size) - (*_bfd_error_handler) - (_("%B: warning: duplicate section `%A' has different size\n"), - abfd, sec); - break; - } + name = bfd_get_section_name (abfd, sec); - if (sec) - { - /* Set the output_section field so that lang_add_section - does not create a lang_input_section structure for this - section. Since there might be a symbol in the section - being discarded, we must retain a pointer to the section - which we are really going to use. */ - sec->output_section = bfd_abs_section_ptr; - sec->kept_section = l_sec; - } + already_linked_list = bfd_section_already_linked_table_lookup (name); - return TRUE; - } + l = already_linked_list->entry; + if (l != NULL) + { + /* The section has already been linked. See if we should + issue a warning. */ + return _bfd_handle_already_linked (sec, l, info); } /* This is the first section with this name. Record it. */ - if (! bfd_section_already_linked_table_insert (already_linked_list, - linked)) + if (!bfd_section_already_linked_table_insert (already_linked_list, sec)) info->callbacks->einfo (_("%F%P: already_linked_table: %E\n")); return FALSE; } diff --git a/bfd/targets.c b/bfd/targets.c index 3dfa145..46c2c94 100644 --- a/bfd/targets.c +++ b/bfd/targets.c @@ -176,7 +176,6 @@ DESCRIPTION . .{* Forward declaration. *} .typedef struct bfd_link_info _bfd_link_info; -.struct already_linked; . .{* Forward declaration. *} .typedef struct flag_info flag_info; @@ -512,7 +511,7 @@ BFD_JUMP_TABLE macros. . . {* Check if SEC has been already linked during a reloceatable or . final link. *} -. bfd_boolean (*_section_already_linked) (bfd *, struct already_linked *, +. bfd_boolean (*_section_already_linked) (bfd *, asection *, . struct bfd_link_info *); . . {* Define a common symbol. *} diff --git a/ld/ChangeLog b/ld/ChangeLog index f842a82..ce8a034 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,11 @@ +2011-08-17 Alan Modra <amodra@gmail.com> + + PR ld/12762 + * ldlang.c (section_already_linked): Revert 2011-07-09 changes. + * plugin.c: Likewise. + (asymbol_from_plugin_symbol): Create linkonce section for syms + with comdat_key. + 2011-08-09 Matthew Gretton-Dann <matthew.gretton-dann@arm.com> * emultempl/armelf.em (fix_arm1176): New variable. diff --git a/ld/ldlang.c b/ld/ldlang.c index 0ffafb6..5b548d0 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -2240,12 +2240,7 @@ section_already_linked (bfd *abfd, asection *sec, void *data) } if (!(abfd->flags & DYNAMIC)) - { - struct already_linked linked; - linked.comdat_key = NULL; - linked.u.sec = sec; - bfd_section_already_linked (abfd, &linked, &link_info); - } + bfd_section_already_linked (abfd, sec, &link_info); } /* The wild routines. diff --git a/ld/plugin.c b/ld/plugin.c index 9baeb46..0be7fa3a 100644 --- a/ld/plugin.c +++ b/ld/plugin.c @@ -32,7 +32,6 @@ #include "plugin.h" #include "plugin-api.h" #include "elf-bfd.h" -#include "libbfd.h" #if !defined (HAVE_DLFCN_H) && defined (HAVE_WINDOWS_H) #include <windows.h> #endif @@ -240,7 +239,7 @@ plugin_get_ir_dummy_bfd (const char *name, bfd *srctemplate) { flagword flags; - /* Create sections to own the symbols. */ + /* Create section to own the symbols. */ flags = (SEC_CODE | SEC_HAS_CONTENTS | SEC_READONLY | SEC_ALLOC | SEC_LOAD | SEC_KEEP | SEC_EXCLUDE); if (bfd_make_section_anyway_with_flags (abfd, ".text", flags)) @@ -285,7 +284,27 @@ asymbol_from_plugin_symbol (bfd *abfd, asymbol *asym, /* FALLTHRU */ case LDPK_DEF: flags |= BSF_GLOBAL; - section = bfd_get_section_by_name (abfd, ".text"); + if (ldsym->comdat_key) + { + char *name = concat (".gnu.linkonce.t.", ldsym->comdat_key, + (const char *) NULL); + section = bfd_get_section_by_name (abfd, name); + if (section != NULL) + free (name); + else + { + flagword sflags; + + sflags = (SEC_CODE | SEC_HAS_CONTENTS | SEC_READONLY + | SEC_ALLOC | SEC_LOAD | SEC_KEEP | SEC_EXCLUDE + | SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD); + section = bfd_make_section_anyway_with_flags (abfd, name, sflags); + if (section == NULL) + return LDPS_ERR; + } + } + else + section = bfd_get_section_by_name (abfd, ".text"); break; case LDPK_WEAKUNDEF: @@ -389,13 +408,6 @@ add_symbols (void *handle, int nsyms, const struct ld_plugin_symbol *syms) enum ld_plugin_status rv; asymbol *bfdsym; - if (syms[n].comdat_key) - { - struct already_linked linked; - linked.comdat_key = xstrdup (syms[n].comdat_key); - linked.u.abfd = abfd; - bfd_section_already_linked (abfd, &linked, &link_info); - } bfdsym = bfd_make_empty_symbol (abfd); symptrs[n] = bfdsym; rv = asymbol_from_plugin_symbol (abfd, bfdsym, syms + n); |