Added support for ".gnu.version_d"

This commit is contained in:
Kirill Okhotnikov 2024-10-22 10:34:47 -04:00 committed by Serge Lamikhov-Center
parent 41f4012d49
commit 647aba7a29
5 changed files with 185 additions and 0 deletions

View File

@ -1349,6 +1349,23 @@ struct Elf64_Dyn
} d_un;
};
struct Elfxx_Verdef
{
Elf_Half vd_version;
Elf_Half vd_flags;
Elf_Half vd_ndx;
Elf_Half vd_cnt;
Elf_Word vd_hash;
Elf_Word vd_aux;
Elf_Word vd_next;
};
struct Elfxx_Verdaux
{
Elf_Word vda_name;
Elf_Word vda_next;
};
struct Elfxx_Verneed
{
Elf_Half vn_version;

View File

@ -174,6 +174,88 @@ using versym_r_section_accessor = versym_r_section_accessor_template<section>;
using const_versym_r_section_accessor =
versym_r_section_accessor_template<const section>;
//------------------------------------------------------------------------------
template <class S> class versym_d_section_accessor_template
{
public:
//------------------------------------------------------------------------------
versym_d_section_accessor_template( const elfio& elf_file,
S* versym_d_section )
: elf_file( elf_file ), versym_d_section( versym_d_section ),
entries_num( 0 )
{
// Find .dynamic section
const section* dynamic_section = elf_file.sections[".dynamic"];
if ( dynamic_section == nullptr ) {
return;
}
const_dynamic_section_accessor dynamic_section_acc( elf_file,
dynamic_section );
Elf_Xword dyn_sec_num = dynamic_section_acc.get_entries_num();
for ( Elf_Xword i = 0; i < dyn_sec_num; ++i ) {
Elf_Xword tag;
Elf_Xword value;
std::string str;
if ( dynamic_section_acc.get_entry( i, tag, value, str ) &&
tag == DT_VERDEFNUM ) {
entries_num = (Elf_Word)value;
break;
}
}
}
//------------------------------------------------------------------------------
Elf_Word get_entries_num() const { return entries_num; }
//------------------------------------------------------------------------------
bool get_entry( Elf_Word no,
Elf_Half& flags,
Elf_Half& version_index,
Elf_Word& hash,
std::string& dep_name ) const
{
if ( versym_d_section == nullptr || ( no >= get_entries_num() ) ) {
return false;
}
const_string_section_accessor string_section_acc(
elf_file.sections[versym_d_section->get_link()] );
Elfxx_Verdef* verdef = (Elfxx_Verdef*)versym_d_section->get_data();
Elfxx_Verdaux* verdaux =
(Elfxx_Verdaux*)( (char*)verdef + verdef->vd_aux );
for ( Elf_Word i = 0; i < no; ++i ) {
verdef = (Elfxx_Verdef*)( (char*)verdef + verdef->vd_next );
verdaux = (Elfxx_Verdaux*)( (char*)verdef + verdef->vd_aux );
}
// verdef->vd_version should always be 1
// see https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA.junk/symversion.html#VERDEFENTRIES
// verdef->vd_cnt should always be 1.
// see https://maskray.me/blog/2020-11-26-all-about-symbol-versioning
flags = verdef->vd_flags;
version_index = verdef->vd_ndx;
hash = verdef->vd_hash;
dep_name = string_section_acc.get_string( verdaux->vda_name );
return true;
}
//------------------------------------------------------------------------------
private:
const elfio& elf_file;
S* versym_d_section = nullptr;
Elf_Word entries_num = 0;
};
using versym_d_section_accessor = versym_d_section_accessor_template<section>;
using const_versym_d_section_accessor =
versym_d_section_accessor_template<const section>;
} // namespace ELFIO
#endif // ELFIO_VERSYM_HPP

View File

@ -416,6 +416,78 @@ TEST( ELFIOTest, gnu_version_64_le )
EXPECT_EQ( dep_name, "GLIBC_2.2.5" );
}
////////////////////////////////////////////////////////////////////////////////
TEST( ELFIOTest, gnu_version_d_64_le )
{
elfio reader;
// Load ELF data
ASSERT_EQ( reader.load( "elf_examples/libversion_d.so" ), true );
section* dynsym = reader.sections[".dynsym"];
const_symbol_section_accessor dynsym_acc( reader, dynsym );
section* gnu_version = reader.sections[".gnu.version"];
const_versym_section_accessor gnu_version_arr( gnu_version );
const section* gnu_version_d = reader.sections[".gnu.version_d"];
const_versym_d_section_accessor gnu_version_d_arr( reader, gnu_version_d );
section* dynstr = reader.sections[".dynstr"];
EXPECT_EQ( gnu_version_d->get_link(), dynstr->get_index() );
EXPECT_EQ( dynsym_acc.get_symbols_num(),
gnu_version_arr.get_entries_num() );
EXPECT_EQ( dynsym_acc.get_symbols_num(), 10 );
EXPECT_EQ( gnu_version_d_arr.get_entries_num(), 3 );
auto v_check = [&]( const std::string& symbol,
const std::string& vername ) -> void {
std::string name;
Elf64_Addr value;
Elf_Xword size;
unsigned char bind;
unsigned char type;
Elf_Half section_index;
unsigned char other;
Elf64_Half verindex;
for ( Elf64_Word i = 0; i < dynsym_acc.get_symbols_num(); i++ ) {
ASSERT_EQ( dynsym_acc.get_symbol( i, name, value, size, bind, type,
section_index, other ),
true );
Elf64_Half vi;
ASSERT_EQ( gnu_version_arr.get_entry( i, vi ), true );
if ( name == symbol ) {
verindex = vi;
}
}
ASSERT_NE( verindex, 0 );
for ( Elf64_Word i = 0; i < gnu_version_d_arr.get_entries_num(); i++ ) {
Elf_Half flags;
Elf_Half version_index;
Elf_Word hash;
std::string dep_name;
ASSERT_EQ( gnu_version_d_arr.get_entry( i, flags, version_index,
hash, dep_name ),
true );
if ( version_index == verindex ) {
EXPECT_EQ( flags, 0 );
EXPECT_EQ( dep_name, vername );
return;
}
}
FAIL() << "version entry is not found";
};
v_check( "_Z20print_hello_world_v1v", "HELLO_1.0" );
v_check( "_Z20print_hello_world_v2v", "HELLO_2.0" );
}
////////////////////////////////////////////////////////////////////////////////
// TEST( ELFIOTest, gnu_version_64_le_modify )
// {

View File

@ -0,0 +1,5 @@
#include <stdio.h>
void print_hello_world_v1() { printf( "hello v1" ); }
void print_hello_world_v2() { printf( "hello v2" ); }

View File

@ -0,0 +1,9 @@
HELLO_1.0 {
global:
_Z20print_hello_world_v1v;
};
HELLO_2.0 {
global:
_Z20print_hello_world_v2v;
};