From 3e55690e6df5d3c8e85d24db5b73334b6d763fc3 Mon Sep 17 00:00:00 2001 From: Martin Bickel Date: Tue, 5 Jan 2021 17:50:23 +0100 Subject: [PATCH] Added new check for section/progHeader consistency --- elfio/elfio.hpp | 77 +++++++++++++++++++++++++++++++++---------- elfio/elfio_utils.hpp | 15 +++++++++ tests/ELFIOTest2.cpp | 10 ++++++ 3 files changed, 85 insertions(+), 17 deletions(-) diff --git a/elfio/elfio.hpp b/elfio/elfio.hpp index f9474d9..13e88bf 100644 --- a/elfio/elfio.hpp +++ b/elfio/elfio.hpp @@ -251,30 +251,72 @@ class elfio ( offset < ( sec->get_offset() + sec->get_size() ) ); } + Elf64_Addr get_virtual_addr( Elf64_Off offset, const section* sec ) const + { + return sec->get_address() + offset - sec->get_offset(); + } + + const section* find_prog_section_for_offset( Elf64_Off offset ) const + { + for ( int i = 0; i < sections.size(); ++i ) { + const section* sec = sections[i]; + if ( sec->get_type() == SHT_PROGBITS ) + if ( is_offset_in_section( offset, sec ) ) + return sec; + } + return NULL; + } + //------------------------------------------------------------------------------ public: //! returns an empty string if no problems are detected, - //! or a string containing an error message if problems are found + //! or a string containing an error message if problems are found, + //! with one error per line. std::string validate() const { + // clang-format off - // check for overlapping sections in the file - for ( int i = 0; i < sections.size(); ++i ) { - for ( int j = i + 1; j < sections.size(); ++j ) { + std::string errors; + // Check for overlapping sections in the file + // This is explicitly forbidden by ELF specification + for ( int i = 0; i < sections.size(); ++i) { + for ( int j = i+1; j < sections.size(); ++j ) { const section* a = sections[i]; const section* b = sections[j]; - if ( !( a->get_type() & SHT_NOBITS ) && - !( b->get_type() & SHT_NOBITS ) && ( a->get_size() > 0 ) && - ( b->get_size() > 0 ) && ( a->get_offset() > 0 ) && - ( b->get_offset() > 0 ) ) { - if ( is_offset_in_section( a->get_offset(), b ) || - is_offset_in_section( - a->get_offset() + a->get_size() - 1, b ) || - is_offset_in_section( b->get_offset(), a ) || - is_offset_in_section( - b->get_offset() + b->get_size() - 1, a ) ) { - return "Sections " + a->get_name() + " and " + - b->get_name() + " overlap in file"; + if ( !(a->get_type() & SHT_NOBITS) + && !(b->get_type() & SHT_NOBITS) + && (a->get_size() > 0) + && (b->get_size() > 0) + && (a->get_offset() > 0) + && (b->get_offset() > 0)) { + if ( is_offset_in_section( a->get_offset(), b ) + || is_offset_in_section( a->get_offset()+a->get_size()-1, b ) + || is_offset_in_section( b->get_offset(), a ) + || is_offset_in_section( b->get_offset()+b->get_size()-1, a )) { + errors += "Sections " + a->get_name() + " and " + b->get_name() + " overlap in file\n"; + } + } + } + } + + // Check for conflicting section / program header tables, where + // the same offset has different vaddresses in section table and + // program header table. + // This doesn't seem to be explicitly forbidden by ELF specification, + // but: + // - it doesn't make any sense + // - ELFIO relies on this being consistent when writing ELF files, + // since offsets are re-calculated from vaddress + for ( int h = 0; h < segments.size(); ++h ) { + const segment* seg = segments[h]; + if ( seg->get_type() == PT_LOAD && seg->get_file_size() > 0 ) { + auto sec = find_prog_section_for_offset( seg->get_offset() ); + if ( sec ) { + auto sec_addr = get_virtual_addr( seg->get_offset(), sec ); + if ( sec_addr != seg->get_virtual_address() ) { + errors += "Virtual address of segment " + std::to_string( h ) + " (" + to_hex_string( seg->get_virtual_address() ) + ")" + + " conflicts with address of section " + sec->get_name() + " (" + to_hex_string( sec_addr ) + ")" + + " at offset " + to_hex_string( seg->get_offset() ) + "\n"; } } } @@ -282,7 +324,8 @@ class elfio // more checks to be added here... - return ""; + return errors; + // clang-format on } //------------------------------------------------------------------------------ diff --git a/elfio/elfio_utils.hpp b/elfio/elfio_utils.hpp index 1efdbfc..bcc126c 100644 --- a/elfio/elfio_utils.hpp +++ b/elfio/elfio_utils.hpp @@ -172,6 +172,21 @@ inline uint32_t elf_hash( const unsigned char* name ) return h; } +inline std::string to_hex_string( Elf64_Addr t ) +{ + std::string s; + while( t ) { + auto d = t & 0xf; + if ( d < 0xa ) + s = char('0' + d) + s; + else + s = char('A' + d - 0xa) + s; + t >>= 4; + } + return "0x" + s; +} + + } // namespace ELFIO #endif // ELFIO_UTILS_HPP diff --git a/tests/ELFIOTest2.cpp b/tests/ELFIOTest2.cpp index 79702b6..ba0aa8f 100644 --- a/tests/ELFIOTest2.cpp +++ b/tests/ELFIOTest2.cpp @@ -232,3 +232,13 @@ BOOST_AUTO_TEST_CASE( init_array_write_64 ) BOOST_CHECK_EQUAL( array.get_entry( 2, addr ), true ); BOOST_CHECK_EQUAL( addr, 0x12345678 ); } + +//////////////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE( test_hex ) +{ + BOOST_CHECK_EQUAL( to_hex_string( 1 ), "0x1"); + BOOST_CHECK_EQUAL( to_hex_string( 10 ), "0xA"); + BOOST_CHECK_EQUAL( to_hex_string( 0x12345678 ), "0x12345678"); + BOOST_CHECK_EQUAL( to_hex_string( 0xFFFFFFFF ), "0xFFFFFFFF"); + BOOST_CHECK_EQUAL( to_hex_string( 0xFFFFFFFFFFFFFFFF ), "0xFFFFFFFFFFFFFFFF"); +}