This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[GOLD] powerpc64 ODR violation check
- From: Alan Modra <amodra at gmail dot com>
- To: binutils at sourceware dot org
- Date: Wed, 6 Mar 2013 23:53:33 +1030
- Subject: [GOLD] powerpc64 ODR violation check
This is a first pass at fixing detect_odr_violations for PowerPC64.
PowerPC64 uses function descriptors, with the address of a function
defined to be that of its descriptor in the .opd section. This causes
difficulty with detect_odr_violations which wants to match up function
symbols with their Dwarf line number info. Since the function symbol
isn't defined on function code, no lines match.
So what I need to do for powerpc64 is translate a function symbol
location in .opd to the corresponding location in the function text
section. ie. I need to do an OPD lookup. However, the opd_ent_
vector is only set up for relocatable objects after reading
relocations. That means detect_odr_violations must be run later, or I
need to read .opd relocs early. The latter option is rather more
difficult, and the former seems to work on both powerpc and x86_64,
so that's what I did. Does anyone see a problem with this?
I have yet to implement read_dyn_opd(), and may decide to invoke that
function around the same time symbols are read from dynamic objects,
when section headers are likely to be hot in cache.
* 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 func_desc_lookup.
* target.h (class Target): Add has_func_descriptors,
func_desc_lookup and do_func_desc_lookup functions.
(class Sized_target): Add do_func_desc_lookup function.
(Target::Target_info): Add has_func_descriptors bool.
* powerpc.cc (class Powerpc_dynobj): New.
(Target_powerpc::do_func_desc_loopkup): New function.
(powerpc_info): Update.
(Powerpc_dynobj::read_dyn_opd): New stub.
(Target_powerpc::do_make_elf_object): Make a Powerpc_dynobj.
* arm.cc, * i386.cc, * sparc.cc, * tilegx.cc, * x86_64.cc,
* testsuite/testfile.cc: Update Target_info initialization.
Index: gold/gold.cc
===================================================================
RCS file: /cvs/src/src/gold/gold.cc,v
retrieving revision 1.103
diff -u -p -r1.103 gold.cc
--- gold/gold.cc 17 Oct 2012 11:58:39 -0000 1.103
+++ gold/gold.cc 6 Mar 2013 09:51:11 -0000
@@ -653,10 +653,6 @@ queue_middle_tasks(const General_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())
{
Index: gold/layout.cc
===================================================================
RCS file: /cvs/src/src/gold/layout.cc,v
retrieving revision 1.245
diff -u -p -r1.245 layout.cc
--- gold/layout.cc 24 Jan 2013 18:49:54 -0000 1.245
+++ gold/layout.cc 6 Mar 2013 09:51:11 -0000
@@ -317,6 +317,10 @@ Layout::Relaxation_debug_check::verify_s
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_,
Index: gold/symtab.h
===================================================================
RCS file: /cvs/src/src/gold/symtab.h,v
retrieving revision 1.128
diff -u -p -r1.128 symtab.h
--- gold/symtab.h 18 Aug 2012 11:12:50 -0000 1.128
+++ gold/symtab.h 6 Mar 2013 09:51:12 -0000
@@ -1180,6 +1180,19 @@ struct Define_symbol_in_segment
bool only_if_ref;
};
+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);
+ }
+};
+
// 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 +1612,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
Index: gold/symtab.cc
===================================================================
RCS file: /cvs/src/src/gold/symtab.cc,v
retrieving revision 1.168
diff -u -p -r1.168 symtab.cc
--- gold/symtab.cc 9 Sep 2012 03:43:51 -0000 1.168
+++ gold/symtab.cc 6 Mar 2013 09:51:12 -0000
@@ -3175,9 +3175,16 @@ Symbol_table::linenos_from_loc(const Tas
Task_lock_obj<Object> tl(task, loc.object);
std::vector<std::string> result;
+ const Symbol_location* code_loc = NULL;
+ if (parameters->target().has_func_descriptors())
+ code_loc = parameters->target().func_desc_lookup(&loc);
+ if (code_loc == NULL)
+ code_loc = &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 (code_loc != &loc)
+ delete code_loc;
if (!canonical_result.empty())
result.push_back(canonical_result);
return result;
Index: gold/target.h
===================================================================
RCS file: /cvs/src/src/gold/target.h,v
retrieving revision 1.73
diff -u -p -r1.73 target.h
--- gold/target.h 27 Feb 2013 23:11:56 -0000 1.73
+++ gold/target.h 6 Mar 2013 09:51:12 -0000
@@ -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,14 @@ class Target
tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const
{ return do_tls_offset_for_global(gsym, got_indx); }
+ bool
+ has_func_descriptors() const
+ { return this->pti_->has_func_descriptors; }
+
+ Symbol_location*
+ func_desc_lookup(const Symbol_location* loc) const
+ { return do_func_desc_lookup(loc); }
+
// Return whether this target can use relocation types to determine
// if a function's address is taken.
bool
@@ -460,6 +469,8 @@ class Target
bool has_resolve;
// Whether this target has a specific code fill function.
bool has_code_fill;
+ // Whether this target has function descriptors.
+ bool has_func_descriptors;
// Whether an object file with no .note.GNU-stack sections implies
// that the stack should be executable.
bool is_default_stack_executable;
@@ -575,6 +586,9 @@ class Target
do_tls_offset_for_global(Symbol*, unsigned int) const
{ gold_unreachable(); }
+ virtual Symbol_location*
+ do_func_desc_lookup(const 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 +1023,10 @@ class Sized_target : public Target
Object*, unsigned int,
typename elfcpp::Elf_types<size>::Elf_Addr) const
{ }
+
+ virtual Symbol_location*
+ do_func_desc_lookup(const Symbol_location*) const
+ { return NULL; }
};
} // End namespace gold.
Index: gold/powerpc.cc
===================================================================
RCS file: /cvs/src/src/gold/powerpc.cc,v
retrieving revision 1.85
diff -u -p -r1.85 powerpc.cc
--- gold/powerpc.cc 6 Mar 2013 12:28:47 -0000 1.85
+++ gold/powerpc.cc 6 Mar 2013 12:41:14 -0000
@@ -320,6 +320,89 @@ 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;
+ typedef Unordered_set<Section_id, Section_id_hash> Section_refs;
+ typedef Unordered_map<Address, Section_refs> Access_from;
+
+ 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()
+ { }
+
+ // The .opd section shndx.
+ unsigned int
+ opd_shndx() const
+ {
+ return this->opd_shndx_;
+ }
+
+ // 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;
+ }
+
+ bool
+ opd_valid() const
+ { return this->opd_shndx_ != 0; }
+
+ // Read .opd from a dynamic object, filling in opd_ent_ vector.
+ void
+ read_dyn_opd();
+
+private:
+ 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.
+ unsigned int opd_shndx_;
+
+ // 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.
+ std::vector<Opd_ent> opd_ent_;
+};
+
+template<int size, bool big_endian>
class Target_powerpc : public Sized_target<size, big_endian>
{
public:
@@ -448,6 +531,9 @@ class Target_powerpc : public Sized_targ
int64_t
do_tls_offset_for_global(Symbol* gsym, unsigned int got_indx) const;
+ Symbol_location*
+ do_func_desc_lookup(const Symbol_location*) const;
+
// Relocate a section.
void
relocate_section(const Relocate_info<size, big_endian>*,
@@ -1052,6 +1138,7 @@ Target::Target_info Target_powerpc<32, t
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -1078,6 +1165,7 @@ Target::Target_info Target_powerpc<32, f
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -1104,6 +1192,7 @@ Target::Target_info Target_powerpc<64, t
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ true, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -1130,6 +1219,7 @@ Target::Target_info Target_powerpc<64, f
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ true, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -1634,6 +1724,19 @@ Powerpc_relobj<size, big_endian>::do_rea
}
}
+// Read .opd from a dynamic object, filling in opd_ent_ vector.
+
+template<int size, bool big_endian>
+void
+Powerpc_dynobj<size, big_endian>::read_dyn_opd()
+{
+ if (size == 64)
+ {
+ // Failed. Set opd_shndx_ non-zero so we don't try again.
+ this->opd_shndx_ = (unsigned) -1;
+ }
+}
+
// Set up some symbols.
template<int size, bool big_endian>
@@ -1706,8 +1809,8 @@ Target_powerpc<size, big_endian>::do_mak
}
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 +5684,51 @@ Target_powerpc<size, big_endian>::do_gc_
}
}
+// For a symbol location in .opd, return the location of the function
+// entry. Return NULL for other symbols.
+
+template<int size, bool big_endian>
+Symbol_location*
+Target_powerpc<size, big_endian>::do_func_desc_lookup(
+ const 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 (!ppc_object->opd_valid())
+ ppc_object->read_dyn_opd();
+ if (loc->shndx == ppc_object->opd_shndx())
+ {
+ Symbol_location* code_loc = new Symbol_location;
+ Address dest_off;
+ code_loc->object = loc->object;
+ code_loc->shndx = ppc_object->get_opd_ent(loc->offset, &dest_off);
+ code_loc->offset = dest_off;
+ return code_loc;
+ }
+ }
+ 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())
+ {
+ Symbol_location* code_loc = new Symbol_location;
+ Address dest_off;
+ code_loc->object = loc->object;
+ code_loc->shndx = ppc_object->get_opd_ent(loc->offset, &dest_off);
+ code_loc->offset = dest_off;
+ return code_loc;
+ }
+ }
+
+ }
+ return NULL;
+}
+
// Scan relocations for a section.
template<int size, bool big_endian>
Index: gold/arm.cc
===================================================================
RCS file: /cvs/src/src/gold/arm.cc,v
retrieving revision 1.161
diff -u -p -r1.161 arm.cc
--- gold/arm.cc 9 Jan 2013 15:27:24 -0000 1.161
+++ gold/arm.cc 6 Mar 2013 09:51:11 -0000
@@ -2938,6 +2938,7 @@ const Target::Target_info Target_arm<big
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -12199,6 +12200,7 @@ const Target::Target_info Target_arm_nac
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
Index: gold/i386.cc
===================================================================
RCS file: /cvs/src/src/gold/i386.cc,v
retrieving revision 1.152
diff -u -p -r1.152 i386.cc
--- gold/i386.cc 2 Nov 2012 19:50:36 -0000 1.152
+++ gold/i386.cc 6 Mar 2013 09:51:11 -0000
@@ -843,6 +843,7 @@ const Target::Target_info Target_i386::i
false, // has_make_symbol
false, // has_resolve
true, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
true, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -3947,6 +3948,7 @@ const Target::Target_info Target_i386_na
false, // has_make_symbol
false, // has_resolve
true, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
true, // can_icf_inline_merge_sections
'\0', // wrap_char
Index: gold/sparc.cc
===================================================================
RCS file: /cvs/src/src/gold/sparc.cc,v
retrieving revision 1.64
diff -u -p -r1.64 sparc.cc
--- gold/sparc.cc 1 Nov 2012 23:27:00 -0000 1.64
+++ gold/sparc.cc 6 Mar 2013 09:51:12 -0000
@@ -469,6 +469,7 @@ Target::Target_info Target_sparc<32, tru
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -495,6 +496,7 @@ Target::Target_info Target_sparc<64, tru
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
Index: gold/tilegx.cc
===================================================================
RCS file: /cvs/src/src/gold/tilegx.cc,v
retrieving revision 1.5
diff -u -p -r1.5 tilegx.cc
--- gold/tilegx.cc 1 Nov 2012 23:27:00 -0000 1.5
+++ gold/tilegx.cc 6 Mar 2013 09:51:12 -0000
@@ -667,6 +667,7 @@ const Target::Target_info Target_tilegx<
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -693,6 +694,7 @@ const Target::Target_info Target_tilegx<
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -719,6 +721,7 @@ const Target::Target_info Target_tilegx<
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -745,6 +748,7 @@ const Target::Target_info Target_tilegx<
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
Index: gold/x86_64.cc
===================================================================
RCS file: /cvs/src/src/gold/x86_64.cc,v
retrieving revision 1.161
diff -u -p -r1.161 x86_64.cc
--- gold/x86_64.cc 20 Nov 2012 05:56:06 -0000 1.161
+++ gold/x86_64.cc 6 Mar 2013 09:51:12 -0000
@@ -993,6 +993,7 @@ const Target::Target_info Target_x86_64<
false, // has_make_symbol
false, // has_resolve
true, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
true, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -1019,6 +1020,7 @@ const Target::Target_info Target_x86_64<
false, // has_make_symbol
false, // has_resolve
true, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
true, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -4580,6 +4582,7 @@ const Target::Target_info Target_x86_64_
false, // has_make_symbol
false, // has_resolve
true, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
true, // can_icf_inline_merge_sections
'\0', // wrap_char
@@ -4606,6 +4609,7 @@ const Target::Target_info Target_x86_64_
false, // has_make_symbol
false, // has_resolve
true, // has_code_fill
+ false, // has_func_descriptors
true, // is_default_stack_executable
true, // can_icf_inline_merge_sections
'\0', // wrap_char
Index: gold/testsuite/testfile.cc
===================================================================
RCS file: /cvs/src/src/gold/testsuite/testfile.cc,v
retrieving revision 1.24
diff -u -p -r1.24 testfile.cc
--- gold/testsuite/testfile.cc 1 Nov 2012 23:27:00 -0000 1.24
+++ gold/testsuite/testfile.cc 6 Mar 2013 09:51:12 -0000
@@ -93,6 +93,7 @@ const Target::Target_info Target_test<si
false, // has_make_symbol
false, // has_resolve
false, // has_code_fill
+ false, // has_func_descriptors
false, // is_default_stack_executable
false, // can_icf_inline_merge_sections
'\0', // wrap_char
--
Alan Modra
Australia Development Lab, IBM