Fixup virtual member functions with linkage and no underlying symbol

This is from the problem report at
https://sourceware.org/bugzilla/show_bug.cgi?id=19707.

GCC 5.3.1, when invoked with -g -O2 emits some debug info where some
virtual member functions with linkage name have no reference to any
underlying symbol.  When that binary is compared to its counterpart
compiled with -g, the member function in question does have an
underlying symbol and thus the comparison of the enclosing types yield
a spurious change report like:

    method virtual void unity::scopes::ScopeBase::stop() didn't have any
    linkage name, and it now has: '_ZN5unity6scopes9ScopeBase4stopEv'

This patch introduces a pass in the DWARF reader which fixes up
virtual member function with linkage and no underlying symbol.  If
there is a symbol with a name equals to the linkage name, the virtual
member function has its underlying symbol set to that symbol.

This fixes the spurious change report.

	* src/abg-dwarf-reader.cc (die_function_decl_map_type): New
	typedef.
	(read_context::die_function_with_no_symbol_map_): New data member.
	(read_context::die_function_decl_with_no_symbol_map): New
	accessor.
	(read_context::fixup_functions_with_no_symbols): New member
	function.
	(finish_member_function_reading): Take a read_context.  Schedule
	virtual member functions with linkage and no underlying symbol to
	be fixed up after all the debug info is read.
	(build_function_decl): After a virtual member function_decl has
	been updated, if it has its underlying symbol and was scheduled
	for fixup, then de-schedule it.
	(build_class_type_and_add_to_ir, build_ir_node_from_die): Adjust
	call to finish_member_function_reading.
	(read_corpus_from_elf): Move the pure ELF (symbol and other mundane
	information) information reading ...
	(read_debug_info_into_corpus): ...  here.  Make it happen *before*
	actual reading of DWARF information.  We need symbol information
	to be present and fully set before we start reading debug info.
	This is so that we can know when a virtual member function doesn't
	need to be fixed up.  Also, perform the fixup after the debug
	information was read.
	* tests/data/test-read-dwarf/test22-pr19097-libstdc++.so.6.0.17.so.abi: Adjust.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2016-02-25 16:25:17 +01:00
parent d78ad0e349
commit 2bbe75a08a
2 changed files with 141 additions and 19 deletions

View File

@ -92,6 +92,10 @@ typedef unordered_map<Dwarf_Off, type_base_sptr> die_type_map_type;
/// corresponding class_decl.
typedef unordered_map<Dwarf_Off, class_decl_sptr> die_class_map_type;
/// Convenience typedef for a map which key the offset of a dwarf die
/// and which value is the corresponding function_decl.
typedef unordered_map<Dwarf_Off, function_decl_sptr> die_function_decl_map_type;
/// Convenience typedef for a map which key is the offset of a dwarf
/// die and which value is the corresponding function_type.
typedef unordered_map<Dwarf_Off, function_type_sptr> die_function_type_map_type;
@ -1929,6 +1933,7 @@ class read_context
die_type_map_type alternate_die_type_map_;
die_class_map_type die_wip_classes_map_;
die_function_type_map_type die_wip_function_types_map_;
die_function_decl_map_type die_function_with_no_symbol_map_;
vector<Dwarf_Off> types_to_canonicalize_;
vector<Dwarf_Off> alt_types_to_canonicalize_;
string_classes_map decl_only_classes_map_;
@ -2483,6 +2488,17 @@ public:
die_wip_function_types_map()
{return die_wip_function_types_map_;}
/// Getter for a map that associates a die with a function decl
/// which has a linkage name but no elf symbol yet.
///
/// This is to fixup function decls with linkage names, but with no
/// link to their underlying elf symbol. There are some DIEs like
/// that in DWARF sometimes, especially when the compiler optimizes
/// stuff aggressively.
die_function_decl_map_type&
die_function_decl_with_no_symbol_map()
{return die_function_with_no_symbol_map_;}
/// Return true iff a given offset is for the DIE of a class that is
/// being currently built.
///
@ -2648,6 +2664,53 @@ public:
}
}
/// Some functions described by DWARF may have their linkage name
/// set, but no link to their actual underlying elf symbol. When
/// these are virtual member functions, comparing the enclosing type
/// against another one which has its underlying symbol properly set
/// might lead to spurious type changes.
///
/// If the corpus contains a symbol with the same name as the
/// linkage name of the function, then set up the link between the
/// function and its underlying symbol.
///
/// Note that for the moment, only virtual member functions are
/// fixed up like this. This is because they really are the only
/// fuctions of functions that can affect types (in spurious ways).
void
fixup_functions_with_no_symbols()
{
corpus_sptr corp = current_corpus();
if (!corp)
return;
die_function_decl_map_type &fns_with_no_symbol =
die_function_decl_with_no_symbol_map();
if (do_log())
cerr << fns_with_no_symbol.size()
<< " functions to fixup, potentially\n";
for (die_function_decl_map_type::iterator i = fns_with_no_symbol.begin();
i != fns_with_no_symbol.end();
++i)
if (elf_symbol_sptr sym =
corp->lookup_function_symbol(i->second->get_linkage_name()))
{
assert(is_member_function(i->second));
assert(get_member_function_is_virtual(i->second));
i->second->set_symbol(sym);
if (do_log())
cerr << "fixed up '"
<< i->second->get_pretty_representation()
<< "' with symbol '"
<< sym->get_id_string()
<< "'\n";
}
fns_with_no_symbol.clear();
}
/// Return a reference to the vector containing the offsets of the
/// types that need late canonicalizing.
///
@ -4198,7 +4261,8 @@ build_function_decl(read_context& ctxt,
static void
finish_member_function_reading(Dwarf_Die* die,
function_decl_sptr f,
class_decl_sptr klass);
class_decl_sptr klass,
read_context& ctxt);
/// Setter of the debug info root path for a dwarf reader context.
///
@ -6968,10 +7032,13 @@ build_enum_type(read_context& ctxt,
/// @param f the function_decl that has just been built from @p die.
///
/// @param klass the class_decl that @p f belongs to.
///
/// @param ctxt the context used to read the ELF/DWARF information.
static void
finish_member_function_reading(Dwarf_Die* die,
function_decl_sptr f,
class_decl_sptr klass)
class_decl_sptr klass,
read_context& ctxt)
{
assert(klass);
@ -7035,6 +7102,34 @@ finish_member_function_reading(Dwarf_Die* die,
if (is_virtual)
klass->sort_virtual_mem_fns();
if (is_virtual && !f->get_linkage_name().empty() && !f->get_symbol())
{
// This is a virtual member function which has a linkage name
// but has no underlying symbol set.
//
// The underlying elf symbol to set to this function can show up
// later in the DWARF input or it can be that, because of some
// compiler optimization, the relation between this function and
// its underlying elf symbol is simply not emitted in the DWARF.
//
// Let's thus schedule this function for a later fixup pass
// (performed by
// read_context::fixup_functions_with_no_symbols()) that will
// set its underlying symbol.
//
// Note that if the underying symbol is encountered later in the
// DWARF input, then the part of build_function_decl() that
// updates the function to set its underlying symbol will
// de-schedule this function wrt fixup pass.
Dwarf_Off die_offset = dwarf_dieoffset(die);
die_function_decl_map_type &fns_with_no_symbol =
ctxt.die_function_decl_with_no_symbol_map();
die_function_decl_map_type::const_iterator i =
fns_with_no_symbol.find(die_offset);
if (i == fns_with_no_symbol.end())
fns_with_no_symbol[die_offset] = f;
}
}
/// Build a an IR node for class type from a DW_TAG_structure_type or
@ -7285,7 +7380,7 @@ build_class_type_and_add_to_ir(read_context& ctxt,
function_decl_sptr f = dynamic_pointer_cast<function_decl>(r);
assert(f);
finish_member_function_reading(&child, f, result);
finish_member_function_reading(&child, f, result, ctxt);
ctxt.associate_die_to_decl(dwarf_dieoffset(&child),
is_in_alt_di, f);
@ -8140,6 +8235,7 @@ build_function_decl(read_context& ctxt,
// Check if a function symbol with this name is exported by the elf
// binary.
bool symbol_updated = false;
Dwarf_Addr fn_addr;
if (ctxt.get_function_address(die, fn_addr))
{
@ -8147,6 +8243,7 @@ build_function_decl(read_context& ctxt,
if (sym->is_function() && sym->is_public())
{
result->set_symbol(sym);
symbol_updated = true;
// If the linkage name is not set or is wrong, set it to
// the name of the underlying symbol.
string linkage_name = result->get_linkage_name();
@ -8156,10 +8253,20 @@ build_function_decl(read_context& ctxt,
}
}
ctxt.associate_die_to_type(dwarf_dieoffset(die),
is_in_alt_di,
result->get_type());
Dwarf_Off die_offset = dwarf_dieoffset(die);
ctxt.associate_die_to_type(die_offset, is_in_alt_di, result->get_type());
if (symbol_updated
&& fn
&& is_member_function(fn)
&& get_member_function_is_virtual(fn)
&& !result->get_linkage_name().empty())
// This function is a virtual member function which has its
// linkage name *and* and has its underlying symbol correctly set.
// It thus doesn't need any fixup related to elf symbol. So
// remove it from the set of virtual member functions with linkage
// names and no elf symbol that need to be fixed up.
ctxt.die_function_decl_with_no_symbol_map().erase(die_offset);
return result;
}
@ -8184,12 +8291,30 @@ read_debug_info_into_corpus(read_context& ctxt)
ctxt.env(corp->get_environment());
}
// First set some mundane properties of the corpus gathered from
// ELF.
ctxt.current_corpus()->set_path(ctxt.elf_path());
ctxt.current_corpus()->set_origin(corpus::DWARF_ORIGIN);
ctxt.current_corpus()->set_soname(ctxt.dt_soname());
ctxt.current_corpus()->set_needed(ctxt.dt_needed());
ctxt.current_corpus()->set_architecture_name(ctxt.elf_architecture());
// Set symbols information to the corpus.
ctxt.current_corpus()->set_fun_symbol_map(ctxt.fun_syms_sptr());
ctxt.current_corpus()->set_undefined_fun_symbol_map
(ctxt.undefined_fun_syms_sptr());
ctxt.current_corpus()->set_var_symbol_map(ctxt.var_syms_sptr());
ctxt.current_corpus()->set_undefined_var_symbol_map
(ctxt.undefined_var_syms_sptr());
// Get out now if no debug info is found.
if (!ctxt.dwarf())
return ctxt.current_corpus();
uint8_t address_size = 0;
size_t header_size = 0;
// Set the set of exported declaration that are defined.
ctxt.exported_decls_builder
(ctxt.current_corpus()->get_exported_decls_builder().get());
@ -8240,6 +8365,13 @@ read_debug_info_into_corpus(read_context& ctxt)
if (ctxt.do_log())
cerr << " DONE@" << ctxt.current_corpus()->get_path() <<"\n";
if (ctxt.do_log())
cerr << "fixing up functions with linkage name but "
<< "no advertised underlying symbols ....";
ctxt.fixup_functions_with_no_symbols();
if (ctxt.do_log())
cerr << " DONE@" << ctxt.current_corpus()->get_path() <<"\n";
/// Now, look at the types that needs to be canonicalized after the
/// translation has been constructed (which is just now) and
/// canonicalize them.
@ -8773,7 +8905,7 @@ build_ir_node_from_die(read_context& ctxt,
class_decl_sptr klass(static_cast<class_decl*>(scope),
sptr_utils::noop_deleter());
assert(klass);
finish_member_function_reading(die, fn, klass);
finish_member_function_reading(die, fn, klass, ctxt);
}
if (fn)
@ -9018,16 +9150,6 @@ read_corpus_from_elf(read_context& ctxt, status& status)
// Read the variable and function descriptions from the debug info
// we have, through the dwfl handle.
corpus_sptr corp = read_debug_info_into_corpus(ctxt);
corp->set_path(ctxt.elf_path());
corp->set_origin(corpus::DWARF_ORIGIN);
corp->set_soname(ctxt.dt_soname());
corp->set_needed(ctxt.dt_needed());
corp->set_architecture_name(ctxt.elf_architecture());
corp->set_fun_symbol_map(ctxt.fun_syms_sptr());
corp->set_undefined_fun_symbol_map(ctxt.undefined_fun_syms_sptr());
corp->set_var_symbol_map(ctxt.var_syms_sptr());
corp->set_undefined_var_symbol_map(ctxt.undefined_var_syms_sptr());
status |= STATUS_OK;

View File

@ -45544,7 +45544,7 @@
</function-decl>
</member-function>
<member-function access='protected' vtable-offset='10'>
<function-decl name='uflow' mangled-name='_ZNSt15basic_streambufIwSt11char_traitsIwEE5uflowEv' filepath='/tmp/legendre/spack-stage/spack-stage-wfh0ig/gcc-4.7.4/x86_64-unknown-linux-gnu/libstdc++-v3/include/streambuf' line='697' column='1' visibility='default' binding='global' size-in-bits='64'>
<function-decl name='uflow' mangled-name='_ZNSt15basic_streambufIwSt11char_traitsIwEE5uflowEv' filepath='/tmp/legendre/spack-stage/spack-stage-wfh0ig/gcc-4.7.4/x86_64-unknown-linux-gnu/libstdc++-v3/include/streambuf' line='697' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_ZNSt15basic_streambufIwSt11char_traitsIwEE5uflowEv@@GLIBCXX_3.4'>
<parameter type-id='type-id-47' is-artificial='yes'/>
<return type-id='type-id-52'/>
</function-decl>
@ -58775,7 +58775,7 @@
</function-decl>
</member-function>
<member-function access='protected' vtable-offset='10'>
<function-decl name='uflow' mangled-name='_ZNSt15basic_streambufIwSt11char_traitsIwEE5uflowEv' filepath='/tmp/legendre/spack-stage/spack-stage-wfh0ig/gcc-4.7.4/x86_64-unknown-linux-gnu/libstdc++-v3/include/streambuf' line='697' column='1' visibility='default' binding='global' size-in-bits='64'>
<function-decl name='uflow' mangled-name='_ZNSt15basic_streambufIwSt11char_traitsIwEE5uflowEv' filepath='/tmp/legendre/spack-stage/spack-stage-wfh0ig/gcc-4.7.4/x86_64-unknown-linux-gnu/libstdc++-v3/include/streambuf' line='697' column='1' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='_ZNSt15basic_streambufIwSt11char_traitsIwEE5uflowEv@@GLIBCXX_3.4'>
<parameter type-id='type-id-47' is-artificial='yes'/>
<return type-id='type-id-52'/>
</function-decl>