Bug 19141 - Libabigail doesn't support common ELF symbols

Libabigail's internal representation of elf symbols fails to account
for common symbols in relocatable files.  There can be several common
symbols of the same name (defined in a section of SHN_COMMON kind).
In that case, Libabigail wrongly considers these multiple instances of
the same common symbol as being alias, and that breaks some
basic assumptions about aliases.  Oops.

This patch adds support for the common symbols (and the fact that
relocatable files can have several instances of the same common
symbol) and amends the ELF reader to make it properly represent those.
	* include/abg-ir.h (elf_symbol::elf_symbol): Take a new flag to
	say if the symbol is common.
	(elf_symbol::{is_common_symbol, has_other_common_instances,
	get_next_common_instance, add_common_instance}): New member functions.
	* src/abg-ir.cc (elf_symbol::priv::{is_common_,
	next_common_instance_): New data members.
	(elf_symbol::priv::priv): Adjust.
	(elf_symbol::{elf_symbol, create}): Take a new flag to say if the
	symbol is common.
	(textually_equals): Adjust to account for symbol common-ness.
	(elf_symbol::{is_common_symbol, has_other_common_instances,
	get_next_common_instance, add_common_instance}): Define new member
	functions.
	(elf_symbol::add_alias): Drive-by fix; compare symbols using
	pointer value.  Value comparison is not necessary.
	* src/abg-dwarf-reader.cc (lookup_symbol_from_sysv_hash_tab)
	(lookup_symbol_from_gnu_hash_tab, lookup_symbol_from_symtab)
	(read_context::lookup_elf_symbol_from_index): Adjust the creation
	of the symbol to account for common-ness.
	(read_context::load_symbol_maps): Recognize instances of a given
	common symbol and represent them as such.  Do not mistake this
	with symbol aliases.
	* src/abg-reader.cc (build_elf_symbol): Adjust the creation of the
	symbol to account for common-ness.
	* src/abg-writer.cc (write_elf_symbol): Adjust symbol
	serialization to account common-ness.
	* tests/data/test-types-stability/pr19141-get5d.o: Add new test
	binary input.
	* tests/data/test-types-stability/pr19142-topo.o: Likewise.
	* tests/data/Makefile.am: Add the new test inputs to source distribution.
	* tests/test-types-stability.cc (elf_paths): The the new test
	inputs into account.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2016-01-20 13:37:52 +01:00
parent c3869ecc7b
commit 640b3a2f59
9 changed files with 205 additions and 20 deletions

View File

@ -577,6 +577,7 @@ private:
type t,
binding b,
bool d,
bool c,
const version& v);
elf_symbol(const elf_symbol&);
@ -596,6 +597,7 @@ public:
type t,
binding b,
bool d,
bool c,
const version& v);
size_t
@ -670,6 +672,18 @@ public:
void
add_alias(elf_symbol_sptr);
bool
is_common_symbol() const;
bool
has_other_common_instances() const;
elf_symbol_sptr
get_next_common_instance() const;
void
add_common_instance(const elf_symbol_sptr&);
const string&
get_id_string() const;

View File

@ -1278,6 +1278,7 @@ lookup_symbol_from_sysv_hash_tab(Elf* elf_handle,
sym_type,
sym_binding,
symbol.st_shndx != SHN_UNDEF,
symbol.st_shndx == SHN_COMMON,
ver);
syms_found.push_back(symbol_found);
found = true;
@ -1548,7 +1549,9 @@ lookup_symbol_from_gnu_hash_tab(Elf* elf_handle,
elf_symbol_sptr symbol_found =
elf_symbol::create(i, symbol.st_size, sym_name_str,
sym_type, sym_binding,
symbol.st_shndx != SHN_UNDEF, ver);
symbol.st_shndx != SHN_UNDEF,
symbol.st_shndx == SHN_COMMON,
ver);
syms_found.push_back(symbol_found);
found = true;
}
@ -1678,13 +1681,15 @@ lookup_symbol_from_symtab(Elf* elf_handle,
elf_symbol::binding sym_binding =
stb_to_elf_symbol_binding(GELF_ST_BIND(sym->st_info));
bool sym_is_defined = sym->st_shndx != SHN_UNDEF;
bool sym_is_common = sym->st_shndx == SHN_COMMON;
if (get_version_for_symbol(elf_handle, i,
/*get_def_version=*/sym_is_defined,
ver))
assert(!ver.str().empty());
elf_symbol_sptr symbol_found =
elf_symbol::create(i, sym->st_size, name_str, sym_type,
sym_binding, sym_is_defined, ver);
sym_binding, sym_is_defined,
sym_is_common, ver);
syms_found.push_back(symbol_found);
found = true;
}
@ -3092,7 +3097,9 @@ public:
return elf_symbol_sptr();
bool sym_is_defined = s->st_shndx != SHN_UNDEF;
bool sym_is_common = s->st_shndx == SHN_COMMON; // this occurs in
// relocatable
// files.
const char* name_str = elf_strptr(elf_handle(),
symtab_sheader->sh_link,
s->st_name);
@ -3108,7 +3115,7 @@ public:
elf_symbol::create(symbol_index, s->st_size, name_str,
stt_to_elf_symbol_type(GELF_ST_TYPE(s->st_info)),
stb_to_elf_symbol_binding(GELF_ST_BIND(s->st_info)),
sym_is_defined, v);
sym_is_defined, sym_is_common, v);
return sym;
}
@ -3596,14 +3603,33 @@ public:
it->second.push_back(symbol);
}
{
addr_elf_symbol_sptr_map_type::const_iterator it =
var_addr_sym_map_->find(sym->st_value);
if (it == var_addr_sym_map_->end())
(*var_addr_sym_map_)[sym->st_value] = symbol;
else
it->second->get_main_symbol()->add_alias(symbol);
}
if (symbol->is_common_symbol())
{
string_elf_symbols_map_type::iterator it =
var_syms_->find(symbol->get_name());
assert(it != var_syms_->end());
const elf_symbols& common_sym_instances = it->second;
assert(!common_sym_instances.empty());
if (common_sym_instances.size() > 1)
{
elf_symbol_sptr main_common_sym =
common_sym_instances[0];
assert(main_common_sym->get_name()
== symbol->get_name());
assert(main_common_sym->is_common_symbol());
assert(symbol.get() != main_common_sym.get());
main_common_sym->add_common_instance(symbol);
}
}
else
{
addr_elf_symbol_sptr_map_type::const_iterator it =
var_addr_sym_map_->find(sym->st_value);
if (it == var_addr_sym_map_->end())
(*var_addr_sym_map_)[sym->st_value] = symbol;
else
it->second->get_main_symbol()->add_alias(symbol);
}
}
else if (load_undefined_var_map && !symbol->is_defined())
{

View File

@ -732,8 +732,38 @@ struct elf_symbol::priv
elf_symbol::binding binding_;
elf_symbol::version version_;
bool is_defined_;
// This flag below says if the symbol is a common elf symbol. In
// relocatable files, a common symbol is a symbol defined in a
// section of kind SHN_COMMON.
//
// Note that a symbol of kind STT_COMMON is also considered a common
// symbol. Here is what the gABI says about STT_COMMON and
// SHN_COMMON:
//
// Symbols with type STT_COMMON label uninitialized common
// blocks. In relocatable objects, these symbols are not
// allocated and must have the special section index SHN_COMMON
// (see below). In shared objects and executables these symbols
// must be allocated to some section in the defining object.
//
// In relocatable objects, symbols with type STT_COMMON are
// treated just as other symbols with index SHN_COMMON. If the
// link-editor allocates space for the SHN_COMMON symbol in an
// output section of the object it is producing, it must
// preserve the type of the output symbol as STT_COMMON.
//
// When the dynamic linker encounters a reference to a symbol
// that resolves to a definition of type STT_COMMON, it may (but
// is not required to) change its symbol resolution rules as
// follows: instead of binding the reference to the first symbol
// found with the given name, the dynamic linker searches for
// the first symbol with that name with type other than
// STT_COMMON. If no such symbol is found, it looks for the
// STT_COMMON definition of that name that has the largest size.
bool is_common_;
elf_symbol_wptr main_symbol_;
elf_symbol_wptr next_alias_;
elf_symbol_wptr next_common_instance_;
string id_string_;
priv()
@ -741,7 +771,8 @@ struct elf_symbol::priv
size_(),
type_(elf_symbol::NOTYPE_TYPE),
binding_(elf_symbol::GLOBAL_BINDING),
is_defined_(false)
is_defined_(false),
is_common_(false)
{}
priv(size_t i,
@ -750,6 +781,7 @@ struct elf_symbol::priv
elf_symbol::type t,
elf_symbol::binding b,
bool d,
bool c,
const elf_symbol::version& v)
: index_(i),
size_(s),
@ -757,8 +789,12 @@ struct elf_symbol::priv
type_(t),
binding_(b),
version_(v),
is_defined_(d)
{}
is_defined_(d),
is_common_(c)
{
if (!is_common_)
is_common_ = type_ == COMMON_TYPE;
}
}; // end struct elf_symbol::priv
/// Default constructor of the @ref elf_symbol type.
@ -791,6 +827,8 @@ elf_symbol::elf_symbol()
///
/// @param d true if the symbol is defined, false otherwise.
///
/// @param c true if the symbol is a common symbol, false otherwise.
///
/// @param v the version of the symbol.
elf_symbol::elf_symbol(size_t i,
size_t s,
@ -798,8 +836,9 @@ elf_symbol::elf_symbol(size_t i,
type t,
binding b,
bool d,
bool c,
const version& v)
: priv_(new priv(i, s, n, t, b, d, v))
: priv_(new priv(i, s, n, t, b, d, c, v))
{}
/// Factory of instances of @ref elf_symbol.
@ -832,6 +871,8 @@ elf_symbol::create()
///
/// @param d true if the symbol is defined, false otherwise.
///
/// @param c true if the symbol is a common symbol.
///
/// @param v the version of the symbol.
///
/// @return a (smart) pointer to a newly created instance of @ref
@ -843,9 +884,10 @@ elf_symbol::create(size_t i,
type t,
binding b,
bool d,
bool c,
const version& v)
{
elf_symbol_sptr e(new elf_symbol(i, s, n, t, b, d, v));
elf_symbol_sptr e(new elf_symbol(i, s, n, t, b, d, c, v));
e->priv_->main_symbol_ = e;
return e;
}
@ -865,6 +907,7 @@ textually_equals(const elf_symbol&l,
&& l.get_type() == r.get_type()
&& l.is_public() == r.is_public()
&& l.is_defined() == r.is_defined()
&& l.is_common_symbol() == r.is_common_symbol()
&& l.get_version() == r.get_version());
if (equals && l.is_variable())
@ -1115,10 +1158,10 @@ elf_symbol::add_alias(elf_symbol_sptr alias)
{
elf_symbol_sptr last_alias;
for (elf_symbol_sptr a = get_next_alias();
a && (a != get_main_symbol());
a && (a.get() != get_main_symbol().get());
a = a->get_next_alias())
{
if (a->get_next_alias() == get_main_symbol())
if (a->get_next_alias().get() == get_main_symbol().get())
{
assert(last_alias == 0);
last_alias = a;
@ -1135,6 +1178,89 @@ elf_symbol::add_alias(elf_symbol_sptr alias)
alias->priv_->main_symbol_ = get_main_symbol();
}
/// Return true if the symbol is a common one.
///
/// @return true iff the symbol is common.
bool
elf_symbol::is_common_symbol() const
{return priv_->is_common_;}
/// Return true if this common common symbol has other common instances.
///
/// A common instance of a given common symbol is another common
/// symbol with the same name. Those exist in relocatable files. The
/// linker normally allocates all the instances into a common block in
/// the final output file.
///
/// Note that the current object must be a common symbol, otherwise,
/// this function aborts.
///
/// @return true iff the current common symbol has other common
/// instances.
bool
elf_symbol::has_other_common_instances() const
{
assert(is_common_symbol());
return get_next_common_instance();
}
/// Get the next common instance of the current common symbol.
///
/// A common instance of a given common symbol is another common
/// symbol with the same name. Those exist in relocatable files. The
/// linker normally allocates all the instances into a common block in
/// the final output file.
///
/// @return the next common instance, or nil if there is not any.
elf_symbol_sptr
elf_symbol::get_next_common_instance() const
{
if (priv_->next_common_instance_.expired())
return elf_symbol_sptr();
return elf_symbol_sptr(priv_->next_common_instance_);
}
/// Add a common instance to the current common elf symbol.
///
/// Note that this symbol must be the main symbol. Being the main
/// symbol means being the first common symbol to appear in the symbol
/// table.
///
/// @param common the other common instance to add.
void
elf_symbol::add_common_instance(const elf_symbol_sptr& common)
{
if (!common)
return;
assert(!common->has_other_common_instances());
assert(is_common_symbol());
assert(is_main_symbol());
if (has_other_common_instances())
{
elf_symbol_sptr last_common_instance;
for (elf_symbol_sptr c = get_next_common_instance();
c && (c.get() != get_main_symbol().get());
c = c->get_next_common_instance())
{
if (c->get_next_common_instance().get() == get_main_symbol().get())
{
assert(last_common_instance == 0);
last_common_instance = c;
}
}
assert(last_common_instance);
last_common_instance->priv_->next_common_instance_ = common;
}
else
priv_->next_common_instance_ = common;
common->priv_->next_common_instance_ = get_main_symbol();
common->priv_->main_symbol_ = get_main_symbol();
}
/// Get a string that is representative of a given elf_symbol.
///
/// If the symbol has a version, then the ID string is the

View File

@ -2110,6 +2110,17 @@ build_elf_symbol(read_context&, const xmlNodePtr node)
is_defined = false;
}
bool is_common = false;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-common"))
{
string value;
xml::xml_char_sptr_to_string(s, value);
if (value == "true" || value == "yes")
is_common = true;
else
is_common = false;
}
string version_string;
if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "version"))
xml::xml_char_sptr_to_string(s, version_string);
@ -2133,7 +2144,8 @@ build_elf_symbol(read_context&, const xmlNodePtr node)
elf_symbol_sptr e = elf_symbol::create(/*index=*/0, size,
name, type, binding,
is_defined, version);
is_defined, is_common,
version);
return e;
}

View File

@ -1936,6 +1936,9 @@ write_elf_symbol(const shared_ptr<elf_symbol> sym,
o << "no";
o << "'";
if (sym->is_common_symbol())
o << " is-common='yes'";
o << "/>";
return true;

View File

@ -320,6 +320,8 @@ test-types-stability/pr19202-libmpi_gpfs.so.5.0 \
test-types-stability/pr19026-libvtkIOSQL-6.1.so.1 \
test-types-stability/pr19138-elf0 \
test-types-stability/pr19433-custom0 \
test-types-stability/pr19141-get5d.o \
test-types-stability/pr19142-topo.o \
\
test-diff-filter/test0-v0.cc \
test-diff-filter/test0-v1.cc \

Binary file not shown.

Binary file not shown.

View File

@ -59,6 +59,8 @@ const char* elf_paths[] =
"data/test-types-stability/pr19026-libvtkIOSQL-6.1.so.1",
"data/test-types-stability/pr19138-elf0",
"data/test-types-stability/pr19433-custom0",
"data/test-types-stability/pr19141-get5d.o",
"data/test-types-stability/pr19142-topo.o",
// The below should always be the last element of array.
0
};