diff options
-rw-r--r-- | gold/ChangeLog | 42 | ||||
-rw-r--r-- | gold/layout.cc | 31 | ||||
-rw-r--r-- | gold/layout.h | 34 | ||||
-rw-r--r-- | gold/object.cc | 184 | ||||
-rw-r--r-- | gold/object.h | 81 | ||||
-rw-r--r-- | gold/target-reloc.h | 65 |
6 files changed, 397 insertions, 40 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 10823b5..c061293 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,45 @@ +2008-04-30 Cary Coutant <ccoutant@google.com> + + * layout.cc (Layout::include_section): Refactored check for debug + info section. + (Layout::add_comdat): Add new parameters. Change type + of signature parameter. Add object and shndx to signatures table. + (Layout::find_kept_object): New function. + * layout.h: Include <cstring>. + (Layout::is_debug_info_section): New function. + (Layout::add_comdat): Add new parameters. + (Layout::find_kept_object): New function. + (Layout::Kept_section): New struct. + (Layout::Signatures): Change type of map range. + * object.cc (Relobj::output_section_address): New function. + (Sized_relobj::include_section_group): Add new parameters. Change + calls to Layout::add_comdat. Change to build table of kept comdat + groups and table mapping discarded sections to kept sections. + (Sized_relobj::include_linkonce_section): Likewise. Add new parameter. + (Sized_relobj::do_layout): Change calls to include_section_group and + include_linkonce_section. + (Sized_relobj::do_finalize_local_symbols): Do not set local symbol + value to zero when section is discarded. + (Sized_relobj::map_to_kept_section): New function. + * object.h (Relobj::output_section_address): New function. + (Relobj::Comdat_group): New type. + (Relobj::find_comdat_group): New function. + (Relobj::Comdat_group_table): New type. + (Relobj::Kept_comdat_section): New type. + (Relobj::Kept_comdat_section_table): New type. + (Relobj::add_comdat_group): New function. + (Relobj::set_kept_comdat_section): New function. + (Relobj::get_kept_comdat_section): New function. + (Relobj::comdat_groups_): New field. + (Relobj::kept_comdat_sections_): New field. + (Symbol_value::input_value): Update comment. + (Sized_relobj::map_to_kept_section) New function. + (Sized_relobj::include_linkonce_section): Add new parameter. + * target-reloc.h (Comdat_behavior): New type. + (get_comdat_behavior): New function. + (relocate_section): Add code to map a discarded section to the + corresponding kept section when applying a relocation. + 2008-04-30 Craig Silverstein <csilvers@google.com> * dwarf_reader.cc (next_generation_count): New static var. diff --git a/gold/layout.cc b/gold/layout.cc index a3bcf21..8d0d5f7 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -192,11 +192,7 @@ Layout::include_section(Sized_relobj<size, big_endian>*, const char* name, if (parameters->options().strip_debug() && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0) { - // Debugging sections can only be recognized by name. - if (is_prefix_of(".debug", name) - || is_prefix_of(".gnu.linkonce.wi.", name) - || is_prefix_of(".line", name) - || is_prefix_of(".stab", name)) + if (is_debug_info_section(name)) return false; } if (parameters->options().strip_debug_gdb() @@ -2770,11 +2766,12 @@ Layout::output_section_name(const char* name, size_t* plen) // want a linkonce signature to block another linkonce signature. bool -Layout::add_comdat(const char* signature, bool group) +Layout::add_comdat(Relobj* object, unsigned int shndx, + const std::string& signature, bool group) { - std::string sig(signature); + Kept_section kept(object, shndx, group); std::pair<Signatures::iterator, bool> ins( - this->signatures_.insert(std::make_pair(sig, group))); + this->signatures_.insert(std::make_pair(signature, kept))); if (ins.second) { @@ -2782,7 +2779,7 @@ Layout::add_comdat(const char* signature, bool group) return true; } - if (ins.first->second) + if (ins.first->second.group_) { // We've already seen a real section group with this signature. return false; @@ -2792,7 +2789,7 @@ Layout::add_comdat(const char* signature, bool group) // This is a real section group, and we've already seen a // linkonce section with this signature. Record that we've seen // a section group, and don't include this section group. - ins.first->second = true; + ins.first->second.group_ = true; return false; } else @@ -2804,6 +2801,20 @@ Layout::add_comdat(const char* signature, bool group) } } +// Find the given comdat signature, and return the object and section +// index of the kept group. +Relobj* +Layout::find_kept_object(const std::string& signature, + unsigned int* pshndx) const +{ + Signatures::const_iterator p = this->signatures_.find(signature); + if (p == this->signatures_.end()) + return NULL; + if (pshndx != NULL) + *pshndx = p->second.shndx_; + return p->second.object_; +} + // Store the allocated sections into the section list. void diff --git a/gold/layout.h b/gold/layout.h index efa8b61..8584a0c 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -23,6 +23,7 @@ #ifndef GOLD_LAYOUT_H #define GOLD_LAYOUT_H +#include <cstring> #include <list> #include <string> #include <utility> @@ -212,12 +213,29 @@ class Layout is_linkonce(const char* name) { return strncmp(name, ".gnu.linkonce", sizeof(".gnu.linkonce") - 1) == 0; } + // Return true if a section is a debugging section. + static inline bool + is_debug_info_section(const char* name) + { + // Debugging sections can only be recognized by name. + return (strncmp(name, ".debug", sizeof(".debug") - 1) == 0 + || strncmp(name, ".gnu.linkonce.wi.", + sizeof(".gnu.linkonce.wi.") - 1) == 0 + || strncmp(name, ".line", sizeof(".line") - 1) == 0 + || strncmp(name, ".stab", sizeof(".stab") - 1) == 0); + } + // Record the signature of a comdat section, and return whether to // include it in the link. The GROUP parameter is true for a // section group signature, false for a signature derived from a // .gnu.linkonce section. bool - add_comdat(const char*, bool group); + add_comdat(Relobj*, unsigned int, const std::string&, bool group); + + // Find the given comdat signature, and return the object and section + // index of the kept group. + Relobj* + find_kept_object(const std::string&, unsigned int*) const; // Finalize the layout after all the input sections have been added. off_t @@ -550,7 +568,19 @@ class Layout segment_precedes(const Output_segment* seg1, const Output_segment* seg2); // A mapping used for group signatures. - typedef Unordered_map<std::string, bool> Signatures; + struct Kept_section + { + Kept_section() + : object_(NULL), shndx_(0), group_(false) + { } + Kept_section(Relobj* object, unsigned int shndx, bool group) + : object_(object), shndx_(shndx), group_(group) + { } + Relobj* object_; + unsigned int shndx_; + bool group_; + }; + typedef Unordered_map<std::string, Kept_section> Signatures; // Mapping from input section name/type/flags to output section. We // use canonicalized strings here. diff --git a/gold/object.cc b/gold/object.cc index 1672225..8f44411 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -230,6 +230,18 @@ Object::handle_gnu_warning_section(const char* name, unsigned int shndx, return false; } +// Class Relobj. + +// Return the output address of the input section SHNDX. +uint64_t +Relobj::output_section_address(unsigned int shndx) const +{ + section_offset_type offset; + Output_section* os = this->output_section(shndx, &offset); + gold_assert(os != NULL && offset != -1); + return os->address() + offset; +} + // Class Sized_relobj. template<int size, bool big_endian> @@ -510,10 +522,13 @@ Sized_relobj<size, big_endian>::include_section_group( Layout* layout, unsigned int index, const char* name, - const elfcpp::Shdr<size, big_endian>& shdr, + const unsigned char* shdrs, + const char* section_names, + section_size_type section_names_size, std::vector<bool>* omit) { // Read the section contents. + typename This::Shdr shdr(shdrs + index * This::shdr_size); const unsigned char* pcon = this->get_view(shdr.get_sh_offset(), shdr.get_sh_size(), true, false); const elfcpp::Elf_Word* pword = @@ -562,12 +577,11 @@ Sized_relobj<size, big_endian>::include_section_group( return false; } - const char* signature = psymnames + sym.get_st_name(); + std::string signature(psymnames + sym.get_st_name()); // It seems that some versions of gas will create a section group // associated with a section symbol, and then fail to give a name to // the section symbol. In such a case, use the name of the section. - std::string secname; if (signature[0] == '\0' && sym.get_st_type() == elfcpp::STT_SECTION) { bool is_ordinary; @@ -580,24 +594,40 @@ Sized_relobj<size, big_endian>::include_section_group( symndx, sym_shndx); return false; } - secname = this->section_name(sym_shndx); - signature = secname.c_str(); + typename This::Shdr member_shdr(shdrs + sym_shndx * This::shdr_size); + if (member_shdr.get_sh_name() < section_names_size) + signature = section_names + member_shdr.get_sh_name(); } - // Record this section group, and see whether we've already seen one - // with the same signature. + // Record this section group in the layout, and see whether we've already + // seen one with the same signature. + bool include_group = ((flags & elfcpp::GRP_COMDAT) == 0 + || layout->add_comdat(this, index, signature, true)); + + if (include_group && parameters->options().relocatable()) + layout->layout_group(symtab, this, index, name, signature.c_str(), + shdr, pword); + + Relobj* kept_object = NULL; + Comdat_group* kept_group = NULL; - if ((flags & elfcpp::GRP_COMDAT) == 0 - || layout->add_comdat(signature, true)) + if (!include_group) { - if (parameters->options().relocatable()) - layout->layout_group(symtab, this, index, name, signature, shdr, - pword); - return true; + // This group is being discarded. Find the object and group + // that was kept in its place. + unsigned int kept_group_index = 0; + kept_object = layout->find_kept_object(signature, &kept_group_index); + if (kept_object != NULL) + kept_group = kept_object->find_comdat_group(kept_group_index); + } + else if (flags & elfcpp::GRP_COMDAT) + { + // This group is being kept. Create the table to map section names + // to section indexes and add it to the table of groups. + kept_group = new Comdat_group(); + this->add_comdat_group(index, kept_group); } - // This is a duplicate. We want to discard the sections in this - // group. size_t count = shdr.get_sh_size() / sizeof(elfcpp::Elf_Word); for (size_t i = 1; i < count; ++i) { @@ -616,10 +646,42 @@ Sized_relobj<size, big_endian>::include_section_group( this->error(_("invalid section group %u refers to earlier section %u"), index, secnum); - (*omit)[secnum] = true; + // Get the name of the member section. + typename This::Shdr member_shdr(shdrs + secnum * This::shdr_size); + if (member_shdr.get_sh_name() >= section_names_size) + { + // This is an error, but it will be diagnosed eventually + // in do_layout, so we don't need to do anything here but + // ignore it. + continue; + } + std::string mname(section_names + member_shdr.get_sh_name()); + + if (!include_group) + { + (*omit)[secnum] = true; + if (kept_group != NULL) + { + // Find the corresponding kept section, and store that info + // in the discarded section table. + Comdat_group::const_iterator p = kept_group->find(mname); + if (p != kept_group->end()) + { + Kept_comdat_section* kept = + new Kept_comdat_section(kept_object, p->second); + this->set_kept_comdat_section(secnum, kept); + } + } + } + else if (flags & elfcpp::GRP_COMDAT) + { + // Add the section to the kept group table. + gold_assert(kept_group != NULL); + kept_group->insert(std::make_pair(mname, secnum)); + } } - return false; + return include_group; } // Whether to include a linkonce section in the link. NAME is the @@ -641,6 +703,7 @@ template<int size, bool big_endian> bool Sized_relobj<size, big_endian>::include_linkonce_section( Layout* layout, + unsigned int index, const char* name, const elfcpp::Shdr<size, big_endian>&) { @@ -658,8 +721,52 @@ Sized_relobj<size, big_endian>::include_linkonce_section( symname = name + strlen(linkonce_t); else symname = strrchr(name, '.') + 1; - bool include1 = layout->add_comdat(symname, false); - bool include2 = layout->add_comdat(name, true); + std::string sig1(symname); + std::string sig2(name); + bool include1 = layout->add_comdat(this, index, sig1, false); + bool include2 = layout->add_comdat(this, index, sig2, true); + + if (!include2) + { + // The section is being discarded on the basis of its section + // name (i.e., the kept section was also a linkonce section). + // In this case, the section index stored with the layout object + // is the linkonce section that was kept. + unsigned int kept_group_index = 0; + Relobj* kept_object = layout->find_kept_object(sig2, &kept_group_index); + if (kept_object != NULL) + { + Kept_comdat_section* kept = + new Kept_comdat_section(kept_object, kept_group_index); + this->set_kept_comdat_section(index, kept); + } + } + else if (!include1) + { + // The section is being discarded on the basis of its symbol + // name. This means that the corresponding kept section was + // part of a comdat group, and it will be difficult to identify + // the specific section within that group that corresponds to + // this linkonce section. We'll handle the simple case where + // the group has only one member section. Otherwise, it's not + // worth the effort. + unsigned int kept_group_index = 0; + Relobj* kept_object = layout->find_kept_object(sig1, &kept_group_index); + if (kept_object != NULL) + { + Comdat_group* kept_group = + kept_object->find_comdat_group(kept_group_index); + if (kept_group != NULL && kept_group->size() == 1) + { + Comdat_group::const_iterator p = kept_group->begin(); + gold_assert(p != kept_group->end()); + Kept_comdat_section* kept = + new Kept_comdat_section(kept_object, p->second); + this->set_kept_comdat_section(index, kept); + } + } + } + return include1 && include2; } @@ -679,7 +786,8 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, return; // Get the section headers. - const unsigned char* pshdrs = sd->section_headers->data(); + const unsigned char* shdrs = sd->section_headers->data(); + const unsigned char* pshdrs; // Get the section names. const unsigned char* pnamesu = sd->section_names->data(); @@ -691,7 +799,7 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, std::vector<unsigned int> reloc_shndx(shnum, 0); std::vector<unsigned int> reloc_type(shnum, elfcpp::SHT_NULL); // Skip the first, dummy, section. - pshdrs += This::shdr_size; + pshdrs = shdrs + This::shdr_size; for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size) { typename This::Shdr shdr(pshdrs); @@ -749,7 +857,7 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, std::vector<unsigned int> eh_frame_sections; // Skip the first, dummy, section. - pshdrs = sd->section_headers->data() + This::shdr_size; + pshdrs = shdrs + This::shdr_size; for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size) { typename This::Shdr shdr(pshdrs); @@ -784,14 +892,15 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, { if (shdr.get_sh_type() == elfcpp::SHT_GROUP) { - if (!this->include_section_group(symtab, layout, i, name, shdr, + if (!this->include_section_group(symtab, layout, i, name, shdrs, + pnames, sd->section_names_size, &omit)) discard = true; } else if ((shdr.get_sh_flags() & elfcpp::SHF_GROUP) == 0 && Layout::is_linkonce(name)) { - if (!this->include_linkonce_section(layout, name, shdr)) + if (!this->include_linkonce_section(layout, i, name, shdr)) discard = true; } } @@ -1124,7 +1233,10 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index, if (os == NULL) { - lv.set_output_value(0); + // This local symbol belongs to a section we are discarding. + // In some cases when applying relocations later, we will + // attempt to match it to the corresponding kept section, + // so we leave the input value unchanged here. continue; } else if (mo[shndx].offset == -1) @@ -1417,6 +1529,28 @@ Sized_relobj<size, big_endian>::get_symbol_location_info( return false; } +// Look for a kept section corresponding to the given discarded section, +// and return its output address. This is used only for relocations in +// debugging sections. If we can't find the kept section, return 0. + +template<int size, bool big_endian> +typename Sized_relobj<size, big_endian>::Address +Sized_relobj<size, big_endian>::map_to_kept_section( + unsigned int shndx, + bool* found) const +{ + Kept_comdat_section *kept = this->get_kept_comdat_section(shndx); + if (kept != NULL) + { + gold_assert(kept->object_ != NULL); + *found = true; + return (static_cast<Address> + (kept->object_->output_section_address(kept->shndx_))); + } + *found = false; + return 0; +} + // Input_objects methods. // Add a regular relocatable object to the list. Return false if this diff --git a/gold/object.h b/gold/object.h index aaaa48d..52359af 100644 --- a/gold/object.h +++ b/gold/object.h @@ -561,6 +561,8 @@ class Relobj : public Object Relobj(const std::string& name, Input_file* input_file, off_t offset = 0) : Object(name, input_file, false, offset), map_to_output_(), + comdat_groups_(), + kept_comdat_sections_(), map_to_relocatable_relocs_(NULL), object_merge_map_(NULL), relocs_must_follow_section_writes_(false) @@ -647,6 +649,10 @@ class Relobj : public Object this->map_to_output_[shndx].offset = off; } + // Return the output address of the input section SHNDX. + uint64_t + output_section_address(unsigned int shndx) const; + // Return true if we need to wait for output sections to be written // before we can apply relocations. This is true if the object has // any relocations for sections which require special handling, such @@ -684,6 +690,24 @@ class Relobj : public Object return (*this->map_to_relocatable_relocs_)[reloc_shndx]; } + // Information needed to keep track of kept comdat groups. This is + // simply a map from the section name to its section index. This may + // not be a one-to-one mapping, but we ignore that possibility since + // this is used only to attempt to handle stray relocations from + // non-comdat debug sections that refer to comdat loadable sections. + typedef Unordered_map<std::string, unsigned int> Comdat_group; + + // Find a comdat group table given its group section SHNDX. + Comdat_group* + find_comdat_group(unsigned int shndx) const + { + Comdat_group_table::const_iterator p = + this->comdat_groups_.find(shndx); + if (p != this->comdat_groups_.end()) + return p->second; + return NULL; + } + protected: // What we need to know to map an input section to an output // section. We keep an array of these, one for each input section, @@ -698,6 +722,23 @@ class Relobj : public Object section_offset_type offset; }; + // A map from group section index to the table of group members. + typedef std::map<unsigned int, Comdat_group*> Comdat_group_table; + + // To keep track of discarded comdat sections, we need to map a member + // section index to the object and section index of the corresponding + // kept section. + struct Kept_comdat_section + { + Kept_comdat_section(Relobj* object, unsigned int shndx) + : object_(object), shndx_(shndx) + { } + Relobj* object_; + unsigned int shndx_; + }; + typedef std::map<unsigned int, Kept_comdat_section*> + Kept_comdat_section_table; + // Read the relocs--implemented by child class. virtual void do_read_relocs(Read_relocs_data*) = 0; @@ -745,6 +786,30 @@ class Relobj : public Object map_to_output() const { return this->map_to_output_; } + // Record a new comdat group whose group section index is SHNDX. + void + add_comdat_group(unsigned int shndx, Comdat_group* group) + { this->comdat_groups_[shndx] = group; } + + // Record a mapping from discarded section SHNDX to the corresponding + // kept section. + void + set_kept_comdat_section(unsigned int shndx, Kept_comdat_section* kept) + { + this->kept_comdat_sections_[shndx] = kept; + } + + // Find the kept section corresponding to the discarded section SHNDX. + Kept_comdat_section* + get_kept_comdat_section(unsigned int shndx) const + { + Kept_comdat_section_table::const_iterator p = + this->kept_comdat_sections_.find(shndx); + if (p == this->kept_comdat_sections_.end()) + return NULL; + return p->second; + } + // Set the size of the relocatable relocs array. void size_relocatable_relocs() @@ -762,6 +827,10 @@ class Relobj : public Object private: // Mapping from input sections to output section. std::vector<Map_to_output> map_to_output_; + // Table of kept comdat groups. + Comdat_group_table comdat_groups_; + // Table mapping discarded comdat sections to corresponding kept sections. + Kept_comdat_section_table kept_comdat_sections_; // Mapping from input section index to the information recorded for // the relocations. This is only used for a relocatable link. std::vector<Relocatable_relocs*>* map_to_relocatable_relocs_; @@ -927,7 +996,7 @@ class Symbol_value { this->u_.value = value; } // Return the input value. This is only called by - // finalize_local_symbols. + // finalize_local_symbols and (in special cases) relocate_section. Value input_value() const { return this->u_.value; } @@ -1288,6 +1357,12 @@ class Sized_relobj : public Relobj get_symbol_location_info(unsigned int shndx, off_t offset, Symbol_location_info* info); + // Look for a kept section corresponding to the given discarded section, + // and return its output address. This is used only for relocations in + // debugging sections. + Address + map_to_kept_section(unsigned int shndx, bool* found) const; + protected: // Read the symbols. void @@ -1421,12 +1496,12 @@ class Sized_relobj : public Relobj // Whether to include a section group in the link. bool include_section_group(Symbol_table*, Layout*, unsigned int, const char*, - const elfcpp::Shdr<size, big_endian>&, + const unsigned char*, const char *, section_size_type, std::vector<bool>*); // Whether to include a linkonce section in the link. bool - include_linkonce_section(Layout*, const char*, + include_linkonce_section(Layout*, unsigned int, const char*, const elfcpp::Shdr<size, big_endian>&); // Views and sizes when relocating. diff --git a/gold/target-reloc.h b/gold/target-reloc.h index b0d71f5..e1c3cc3 100644 --- a/gold/target-reloc.h +++ b/gold/target-reloc.h @@ -119,6 +119,31 @@ scan_relocs( } } +// Behavior for relocations to discarded comdat sections. + +enum Comdat_behavior +{ + CB_UNDETERMINED, // Not yet determined -- need to look at section name. + CB_PRETEND, // Attempt to map to the corresponding kept section. + CB_IGNORE, // Ignore the relocation. + CB_WARNING // Print a warning. +}; + +// Decide what the linker should do for relocations that refer to discarded +// comdat sections. This decision is based on the name of the section being +// relocated. + +inline Comdat_behavior +get_comdat_behavior(const char* name) +{ + if (Layout::is_debug_info_section(name)) + return CB_PRETEND; + if (strcmp(name, ".eh_frame") == 0 + || strcmp(name, ".gcc_except_table") == 0) + return CB_IGNORE; + return CB_WARNING; +} + // This function implements the generic part of relocation processing. // The template parameter Relocate must be a class type which provides // a single function, relocate(), which implements the machine @@ -159,6 +184,8 @@ relocate_section( Sized_relobj<size, big_endian>* object = relinfo->object; unsigned int local_count = object->local_symbol_count(); + Comdat_behavior comdat_behavior = CB_UNDETERMINED; + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) { Reltype reloc(prelocs); @@ -187,6 +214,44 @@ relocate_section( { sym = NULL; psymval = object->local_symbol(r_sym); + + // If the local symbol belongs to a section we are discarding, + // and that section is a debug section, try to find the + // corresponding kept section and map this symbol to its + // counterpart in the kept section. + bool is_ordinary; + unsigned int shndx = psymval->input_shndx(&is_ordinary); + if (is_ordinary + && shndx != elfcpp::SHN_UNDEF + && !object->is_section_included(shndx)) + { + if (comdat_behavior == CB_UNDETERMINED) + { + const char* name = + object->section_name(relinfo->data_shndx).c_str(); + comdat_behavior = get_comdat_behavior(name); + } + if (comdat_behavior == CB_PRETEND) + { + bool found; + typename elfcpp::Elf_types<size>::Elf_Addr value = + object->map_to_kept_section(shndx, &found); + if (found) + symval.set_output_value(value + psymval->input_value()); + else + symval.set_output_value(0); + } + else + { + if (comdat_behavior == CB_WARNING) + gold_warning_at_location(relinfo, i, offset, + _("Relocation refers to discarded " + "comdat section")); + symval.set_output_value(0); + } + symval.set_no_output_symtab_entry(); + psymval = &symval; + } } else { |