aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gold/ChangeLog20
-rw-r--r--gold/gold.cc4
-rw-r--r--gold/layout.cc4
-rw-r--r--gold/object.cc32
-rw-r--r--gold/object.h19
-rw-r--r--gold/powerpc.cc251
-rw-r--r--gold/symtab.cc4
-rw-r--r--gold/symtab.h32
-rw-r--r--gold/target.h14
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.