diff --git a/elfio/elf_types.hpp b/elfio/elf_types.hpp index 6c3e67e..2c3d70b 100644 --- a/elfio/elf_types.hpp +++ b/elfio/elf_types.hpp @@ -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; diff --git a/elfio/elfio_versym.hpp b/elfio/elfio_versym.hpp index 9971caf..003a7e1 100644 --- a/elfio/elfio_versym.hpp +++ b/elfio/elfio_versym.hpp @@ -174,6 +174,88 @@ using versym_r_section_accessor = versym_r_section_accessor_template
; using const_versym_r_section_accessor = versym_r_section_accessor_template; +//------------------------------------------------------------------------------ +template 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
; +using const_versym_d_section_accessor = + versym_d_section_accessor_template; + } // namespace ELFIO #endif // ELFIO_VERSYM_HPP diff --git a/tests/ELFIOTest2.cpp b/tests/ELFIOTest2.cpp index ddf4689..00bab9e 100644 --- a/tests/ELFIOTest2.cpp +++ b/tests/ELFIOTest2.cpp @@ -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 ) // { diff --git a/tests/elf_examples/version_d.cpp b/tests/elf_examples/version_d.cpp new file mode 100644 index 0000000..3412210 --- /dev/null +++ b/tests/elf_examples/version_d.cpp @@ -0,0 +1,5 @@ +#include + +void print_hello_world_v1() { printf( "hello v1" ); } + +void print_hello_world_v2() { printf( "hello v2" ); } \ No newline at end of file diff --git a/tests/elf_examples/version_d.map b/tests/elf_examples/version_d.map new file mode 100644 index 0000000..c5064e8 --- /dev/null +++ b/tests/elf_examples/version_d.map @@ -0,0 +1,9 @@ +HELLO_1.0 { + global: + _Z20print_hello_world_v1v; +}; + +HELLO_2.0 { +global: +_Z20print_hello_world_v2v; +};