Skip to content

Commit

Permalink
Added support for ".gnu.version_d"
Browse files Browse the repository at this point in the history
  • Loading branch information
strike-kokhotnikov authored and serge1 committed Oct 23, 2024
1 parent 41f4012 commit 647aba7
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 0 deletions.
17 changes: 17 additions & 0 deletions elfio/elf_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
82 changes: 82 additions & 0 deletions elfio/elfio_versym.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
72 changes: 72 additions & 0 deletions tests/ELFIOTest2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
// {
Expand Down
5 changes: 5 additions & 0 deletions tests/elf_examples/version_d.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include <stdio.h>

void print_hello_world_v1() { printf( "hello v1" ); }

void print_hello_world_v2() { printf( "hello v2" ); }
9 changes: 9 additions & 0 deletions tests/elf_examples/version_d.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
HELLO_1.0 {
global:
_Z20print_hello_world_v1v;
};

HELLO_2.0 {
global:
_Z20print_hello_world_v2v;
};

0 comments on commit 647aba7

Please sign in to comment.