diff options
-rw-r--r-- | gold/ChangeLog | 20 | ||||
-rw-r--r-- | gold/gold.cc | 4 | ||||
-rw-r--r-- | gold/layout.cc | 4 | ||||
-rw-r--r-- | gold/object.cc | 32 | ||||
-rw-r--r-- | gold/object.h | 19 | ||||
-rw-r--r-- | gold/powerpc.cc | 251 | ||||
-rw-r--r-- | gold/symtab.cc | 4 | ||||
-rw-r--r-- | gold/symtab.h | 32 | ||||
-rw-r--r-- | gold/target.h | 14 |
9 files changed, 342 insertions, 38 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index bbd4eb3..fcbfed4 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,23 @@ +2013-03-11 Alan Modra <amodra@gmail.com> + + * gold.cc (queue_middle_tasks): Move detect_odr_violations.. + * layout.cc (Layout_task_runner::run): ..to here. + * symtab.h (struct Symbol_location): Extract from.. + (class Symbol_table): ..here. + * symtab.cc (Symbol_table::linenos_from_loc): Invoke function_location. + * target.h (class Target): Add function_location and + do_function_location functions. + (class Sized_target): Add do_function_location. + * object.h (class Sized_relobj_file): Move find_shdr.. + (class Object): ..to here. + * object.cc: Likewise. Update to suit. Instantiate. + (Sized_relobj_file::find_eh_frame): Update find_shdr call. + * powerpc.cc (class Powerpc_dynobj): New. + (Target_powerpc::do_function_location): New function. + (Powerpc_relobj::do_find_special_sections): Update find_shdr call. + (Powerpc_dynobj::do_read_symbols): New function. + (Target_powerpc::do_make_elf_object): Make a Powerpc_dynobj. + 2013-03-08 Ian Lance Taylor <iant@google.com> * options.cc (General_options::string_to_object_format): Accept diff --git a/gold/gold.cc b/gold/gold.cc index 30d623e..d26dc2e 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -653,10 +653,6 @@ queue_middle_tasks(const General_options& options, // dynamic objects that it depends upon. input_objects->check_dynamic_dependencies(); - // See if any of the input definitions violate the One Definition Rule. - // TODO: if this is too slow, do this as a task, rather than inline. - symtab->detect_odr_violations(task, options.output_file_name()); - // Do the --no-undefined-version check. if (!parameters->options().undefined_version()) { diff --git a/gold/layout.cc b/gold/layout.cc index 1eb2cc0..faa8f71 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -317,6 +317,10 @@ Layout::Relaxation_debug_check::verify_sections( void Layout_task_runner::run(Workqueue* workqueue, const Task* task) { + // See if any of the input definitions violate the One Definition Rule. + // TODO: if this is too slow, do this as a task, rather than inline. + this->symtab_->detect_odr_violations(task, this->options_.output_file_name()); + Layout* layout = this->layout_; off_t file_size = layout->finalize(this->input_objects_, this->symtab_, diff --git a/gold/object.cc b/gold/object.cc index 3a4f9f8..3434303 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -517,15 +517,16 @@ Sized_relobj_file<size, big_endian>::check_eh_frame_flags( template<int size, bool big_endian> const unsigned char* -Sized_relobj_file<size, big_endian>::find_shdr( +Object::find_shdr( const unsigned char* pshdrs, const char* name, const char* names, section_size_type names_size, const unsigned char* hdr) const { + const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size; const unsigned int shnum = this->shnum(); - const unsigned char* hdr_end = pshdrs + This::shdr_size * shnum; + const unsigned char* hdr_end = pshdrs + shdr_size * shnum; size_t sh_name = 0; while (1) @@ -533,7 +534,7 @@ Sized_relobj_file<size, big_endian>::find_shdr( if (hdr) { // We found HDR last time we were called, continue looking. - typename This::Shdr shdr(hdr); + typename elfcpp::Shdr<size, big_endian> shdr(hdr); sh_name = shdr.get_sh_name(); } else @@ -557,13 +558,13 @@ Sized_relobj_file<size, big_endian>::find_shdr( return hdr; } - hdr += This::shdr_size; + hdr += shdr_size; while (hdr < hdr_end) { - typename This::Shdr shdr(hdr); + typename elfcpp::Shdr<size, big_endian> shdr(hdr); if (shdr.get_sh_name() == sh_name) return hdr; - hdr += This::shdr_size; + hdr += shdr_size; } hdr = NULL; if (sh_name == 0) @@ -585,7 +586,8 @@ Sized_relobj_file<size, big_endian>::find_eh_frame( while (1) { - s = this->find_shdr(pshdrs, ".eh_frame", names, names_size, s); + s = this->template find_shdr<size, big_endian>(pshdrs, ".eh_frame", + names, names_size, s); if (s == NULL) return false; @@ -3163,6 +3165,10 @@ template void Object::read_section_data<32, false>(elfcpp::Elf_file<32, false, Object>*, Read_symbols_data*); +template +const unsigned char* +Object::find_shdr<32,false>(const unsigned char*, const char*, const char*, + section_size_type, const unsigned char*) const; #endif #ifdef HAVE_TARGET_32_BIG @@ -3170,6 +3176,10 @@ template void Object::read_section_data<32, true>(elfcpp::Elf_file<32, true, Object>*, Read_symbols_data*); +template +const unsigned char* +Object::find_shdr<32,true>(const unsigned char*, const char*, const char*, + section_size_type, const unsigned char*) const; #endif #ifdef HAVE_TARGET_64_LITTLE @@ -3177,6 +3187,10 @@ template void Object::read_section_data<64, false>(elfcpp::Elf_file<64, false, Object>*, Read_symbols_data*); +template +const unsigned char* +Object::find_shdr<64,false>(const unsigned char*, const char*, const char*, + section_size_type, const unsigned char*) const; #endif #ifdef HAVE_TARGET_64_BIG @@ -3184,6 +3198,10 @@ template void Object::read_section_data<64, true>(elfcpp::Elf_file<64, true, Object>*, Read_symbols_data*); +template +const unsigned char* +Object::find_shdr<64,true>(const unsigned char*, const char*, const char*, + section_size_type, const unsigned char*) const; #endif #ifdef HAVE_TARGET_32_LITTLE diff --git a/gold/object.h b/gold/object.h index 01c3c6f..a2baecc 100644 --- a/gold/object.h +++ b/gold/object.h @@ -881,6 +881,16 @@ class Object read_section_data(elfcpp::Elf_file<size, big_endian, Object>*, Read_symbols_data*); + // Find the section header with the given NAME. If HDR is non-NULL + // then it is a section header returned from a previous call to this + // function and the next section header with the same name will be + // returned. + template<int size, bool big_endian> + const unsigned char* + find_shdr(const unsigned char* pshdrs, const char* name, + const char* names, section_size_type names_size, + const unsigned char* hdr) const; + // Let the child class initialize the xindex object directly. void set_xindex(Xindex* xindex) @@ -2161,15 +2171,6 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian> Address map_to_kept_section(unsigned int shndx, bool* found) const; - // Find the section header with the given NAME. If HDR is non-NULL - // then it is a section header returned from a previous call to this - // function and the next section header with the same name will be - // returned. - const unsigned char* - find_shdr(const unsigned char* pshdrs, const char* name, - const char* names, section_size_type names_size, - const unsigned char* hdr) const; - // Compute final local symbol value. R_SYM is the local symbol index. // LV_IN points to a local symbol value containing the input value. // LV_OUT points to a local symbol value storing the final output value, diff --git a/gold/powerpc.cc b/gold/powerpc.cc index a978e7a..b8ff86d 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -23,6 +23,7 @@ #include "gold.h" +#include <set> #include <algorithm> #include "elfcpp.h" #include "dwarf.h" @@ -320,6 +321,109 @@ private: }; template<int size, bool big_endian> +class Powerpc_dynobj : public Sized_dynobj<size, big_endian> +{ +public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + + Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset, + const typename elfcpp::Ehdr<size, big_endian>& ehdr) + : Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr), + opd_shndx_(0), opd_ent_() + { } + + ~Powerpc_dynobj() + { } + + // Call Sized_dynobj::do_read_symbols to read the symbols then + // read .opd from a dynamic object, filling in opd_ent_ vector, + void + do_read_symbols(Read_symbols_data*); + + // The .opd section shndx. + unsigned int + opd_shndx() const + { + return this->opd_shndx_; + } + + // The .opd section address. + Address + opd_address() const + { + return this->opd_address_; + } + + // Init OPD entry arrays. + void + init_opd(size_t opd_size) + { + size_t count = this->opd_ent_ndx(opd_size); + this->opd_ent_.resize(count); + } + + // Return section and offset of function entry for .opd + R_OFF. + unsigned int + get_opd_ent(Address r_off, Address* value = NULL) const + { + size_t ndx = this->opd_ent_ndx(r_off); + gold_assert(ndx < this->opd_ent_.size()); + gold_assert(this->opd_ent_[ndx].shndx != 0); + if (value != NULL) + *value = this->opd_ent_[ndx].off; + return this->opd_ent_[ndx].shndx; + } + + // Set section and offset of function entry for .opd + R_OFF. + void + set_opd_ent(Address r_off, unsigned int shndx, Address value) + { + size_t ndx = this->opd_ent_ndx(r_off); + gold_assert(ndx < this->opd_ent_.size()); + this->opd_ent_[ndx].shndx = shndx; + this->opd_ent_[ndx].off = value; + } + +private: + // Used to specify extent of executable sections. + struct Sec_info + { + Sec_info(Address start_, Address len_, unsigned int shndx_) + : start(start_), len(len_), shndx(shndx_) + { } + + bool + operator<(const Sec_info& that) const + { return this->start < that.start; } + + Address start; + Address len; + unsigned int shndx; + }; + + struct Opd_ent + { + unsigned int shndx; + Address off; + }; + + // Return index into opd_ent_ array for .opd entry at OFF. + size_t + opd_ent_ndx(size_t off) const + { return off >> 4;} + + // For 64-bit the .opd section shndx and address. + unsigned int opd_shndx_; + Address opd_address_; + + // The first 8-byte word of an OPD entry gives the address of the + // entry point of the function. Records the section and offset + // corresponding to the address. Note that in dynamic objects, + // offset is *not* relative to the section. + std::vector<Opd_ent> opd_ent_; +}; + +template<int size, bool big_endian> class Target_powerpc : public Sized_target<size, big_endian> { public: @@ -448,6 +552,9 @@ class Target_powerpc : public Sized_target<size, big_endian> int64_t do_tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const; + void + do_function_location(Symbol_location*) const; + // Relocate a section. void relocate_section(const Relocate_info<size, big_endian>*, @@ -1518,8 +1625,9 @@ Powerpc_relobj<size, big_endian>::do_find_special_sections( section_size_type names_size = sd->section_names_size; const unsigned char* s; - s = this->find_shdr(pshdrs, size == 32 ? ".got2" : ".opd", - names, names_size, NULL); + s = this->template find_shdr<size, big_endian>(pshdrs, + size == 32 ? ".got2" : ".opd", + names, names_size, NULL); if (s != NULL) { unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size; @@ -1634,6 +1742,105 @@ Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd) } } +// Call Sized_dynobj::do_read_symbols to read the symbols then +// read .opd from a dynamic object, filling in opd_ent_ vector, + +template<int size, bool big_endian> +void +Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd) +{ + Sized_dynobj<size, big_endian>::do_read_symbols(sd); + if (size == 64) + { + const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size; + const unsigned char* const pshdrs = sd->section_headers->data(); + const unsigned char* namesu = sd->section_names->data(); + const char* names = reinterpret_cast<const char*>(namesu); + const unsigned char* s = NULL; + const unsigned char* opd; + section_size_type opd_size; + + // Find and read .opd section. + while (1) + { + s = this->template find_shdr<size, big_endian>(pshdrs, ".opd", names, + sd->section_names_size, + s); + if (s == NULL) + return; + + typename elfcpp::Shdr<size, big_endian> shdr(s); + if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS + && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0) + { + this->opd_shndx_ = (s - pshdrs) / shdr_size; + this->opd_address_ = shdr.get_sh_addr(); + opd_size = convert_to_section_size_type(shdr.get_sh_size()); + opd = this->get_view(shdr.get_sh_offset(), opd_size, + true, false); + break; + } + } + + // Build set of executable sections. + // Using a set is probably overkill. There is likely to be only + // a few executable sections, typically .init, .text and .fini, + // and they are generally grouped together. + typedef std::set<Sec_info> Exec_sections; + Exec_sections exec_sections; + s = pshdrs; + for (unsigned int i = 1; i < this->shnum(); ++i, s += shdr_size) + { + typename elfcpp::Shdr<size, big_endian> shdr(s); + if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS + && ((shdr.get_sh_flags() + & (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR)) + == (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR)) + && shdr.get_sh_size() != 0) + { + exec_sections.insert(Sec_info(shdr.get_sh_addr(), + shdr.get_sh_size(), i)); + } + } + if (exec_sections.empty()) + return; + + // Look over the OPD entries. This is complicated by the fact + // that some binaries will use two-word entries while others + // will use the standard three-word entries. In most cases + // the third word (the environment pointer for languages like + // Pascal) is unused and will be zero. If the third word is + // used it should not be pointing into executable sections, + // I think. + this->init_opd(opd_size); + for (const unsigned char* p = opd; p < opd + opd_size; p += 8) + { + typedef typename elfcpp::Swap<64, big_endian>::Valtype Valtype; + const Valtype* valp = reinterpret_cast<const Valtype*>(p); + Valtype val = elfcpp::Swap<64, big_endian>::readval(valp); + if (val == 0) + // Chances are that this is the third word of an OPD entry. + continue; + typename Exec_sections::const_iterator e + = exec_sections.upper_bound(Sec_info(val, 0, 0)); + if (e != exec_sections.begin()) + { + --e; + if (e->start <= val && val < e->start + e->len) + { + // We have an address in an executable section. + // VAL ought to be the function entry, set it up. + this->set_opd_ent(p - opd, e->shndx, val); + // Skip second word of OPD entry, the TOC pointer. + p += 8; + } + } + // If we didn't match any executable sections, we likely + // have a non-zero third word in the OPD entry. + } + } +} + // Set up some symbols. template<int size, bool big_endian> @@ -1706,8 +1913,8 @@ Target_powerpc<size, big_endian>::do_make_elf_object( } else if (et == elfcpp::ET_DYN) { - Sized_dynobj<size, big_endian>* obj = - new Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr); + Powerpc_dynobj<size, big_endian>* obj = + new Powerpc_dynobj<size, big_endian>(name, input_file, offset, ehdr); obj->setup(); return obj; } @@ -5581,6 +5788,42 @@ Target_powerpc<size, big_endian>::do_gc_mark_symbol( } } +// For a symbol location in .opd, set LOC to the location of the +// function entry. + +template<int size, bool big_endian> +void +Target_powerpc<size, big_endian>::do_function_location( + Symbol_location* loc) const +{ + if (size == 64) + { + if (loc->object->is_dynamic()) + { + Powerpc_dynobj<size, big_endian>* ppc_object + = static_cast<Powerpc_dynobj<size, big_endian>*>(loc->object); + if (loc->shndx == ppc_object->opd_shndx()) + { + Address dest_off; + Address off = loc->offset - ppc_object->opd_address(); + loc->shndx = ppc_object->get_opd_ent(off, &dest_off); + loc->offset = dest_off; + } + } + else + { + const Powerpc_relobj<size, big_endian>* ppc_object + = static_cast<const Powerpc_relobj<size, big_endian>*>(loc->object); + if (loc->shndx == ppc_object->opd_shndx()) + { + Address dest_off; + loc->shndx = ppc_object->get_opd_ent(loc->offset, &dest_off); + loc->offset = dest_off; + } + } + } +} + // Scan relocations for a section. template<int size, bool big_endian> diff --git a/gold/symtab.cc b/gold/symtab.cc index 8fadd1d..1fad11e 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -3175,9 +3175,11 @@ Symbol_table::linenos_from_loc(const Task* task, Task_lock_obj<Object> tl(task, loc.object); std::vector<std::string> result; + Symbol_location code_loc = loc; + parameters->target().function_location(&code_loc); // 16 is the size of the object-cache that one_addr2line should use. std::string canonical_result = Dwarf_line_info::one_addr2line( - loc.object, loc.shndx, loc.offset, 16, &result); + code_loc.object, code_loc.shndx, code_loc.offset, 16, &result); if (!canonical_result.empty()) result.push_back(canonical_result); return result; diff --git a/gold/symtab.h b/gold/symtab.h index f26d4b9..040be95 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -1180,6 +1180,25 @@ struct Define_symbol_in_segment bool only_if_ref; }; +// Specify an object/section/offset location. Used by ODR code. + +struct Symbol_location +{ + // Object where the symbol is defined. + Object* object; + // Section-in-object where the symbol is defined. + unsigned int shndx; + // For relocatable objects, offset-in-section where the symbol is defined. + // For dynamic objects, address where the symbol is defined. + off_t offset; + bool operator==(const Symbol_location& that) const + { + return (this->object == that.object + && this->shndx == that.shndx + && this->offset == that.offset); + } +}; + // This class manages warnings. Warnings are a GNU extension. When // we see a section named .gnu.warning.SYM in an object file, and if // we wind using the definition of SYM from that object file, then we @@ -1599,19 +1618,6 @@ class Symbol_table // the locations the symbols is (weakly) defined (and certain other // conditions are met). This map will be used later to detect // possible One Definition Rule (ODR) violations. - struct Symbol_location - { - Object* object; // Object where the symbol is defined. - unsigned int shndx; // Section-in-object where the symbol is defined. - off_t offset; // Offset-in-section where the symbol is defined. - bool operator==(const Symbol_location& that) const - { - return (this->object == that.object - && this->shndx == that.shndx - && this->offset == that.offset); - } - }; - struct Symbol_location_hash { size_t operator()(const Symbol_location& loc) const diff --git a/gold/target.h b/gold/target.h index d75711e..6523c04 100644 --- a/gold/target.h +++ b/gold/target.h @@ -61,6 +61,7 @@ class Output_data_got_base; class Output_section; class Input_objects; class Task; +struct Symbol_location; // The abstract class for target specific handling. @@ -286,6 +287,12 @@ class Target tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const { return do_tls_offset_for_global(gsym, got_indx); } + // For targets that use function descriptors, if LOC is the location + // of a function, modify it to point at the function entry location. + void + function_location(Symbol_location* loc) const + { return do_function_location(loc); } + // Return whether this target can use relocation types to determine // if a function's address is taken. bool @@ -575,6 +582,9 @@ class Target do_tls_offset_for_global(Symbol*, unsigned int) const { gold_unreachable(); } + virtual void + do_function_location(Symbol_location*) const = 0; + // Virtual function which may be overriden by the child class. virtual bool do_can_check_for_function_pointers() const @@ -1009,6 +1019,10 @@ class Sized_target : public Target Object*, unsigned int, typename elfcpp::Elf_types<size>::Elf_Addr) const { } + + virtual void + do_function_location(Symbol_location*) const + { } }; } // End namespace gold. |