special support for PHDR segments and segments which include the elf header

Only elf files with strange GNU_RELRO segments fail the load, save, cycle.

It would maybe a good idea to separate the layout functionality from the
current save. The current coupling of layout and save make it
impossible to build layouts which contain the elf header via the public
API.
This commit is contained in:
Mario Werner 2014-11-15 22:08:52 +01:00
parent 755b92c580
commit b91a43b378
3 changed files with 115 additions and 37 deletions

View File

@ -291,8 +291,22 @@ void checkExeAreEqual( std::string file_name1, std::string file_name2 )
BOOST_REQUIRE_EQUAL( file2.load( file_name2 ), true );
for (int i = 0; i < file1.segments.size(); ++i ) {
BOOST_REQUIRE_NE( file1.segments[i]->get_data(), (const char*)0 );
BOOST_REQUIRE_NE( file2.segments[i]->get_data(), (const char*)0 );
BOOST_CHECK_EQUAL( file1.segments[i]->get_align(),
file2.segments[i]->get_align() );
BOOST_CHECK_EQUAL( file1.segments[i]->get_file_size(),
file2.segments[i]->get_file_size() );
BOOST_CHECK_EQUAL( file1.segments[i]->get_memory_size(),
file2.segments[i]->get_memory_size() );
BOOST_CHECK_EQUAL( file1.segments[i]->get_type(),
file2.segments[i]->get_type() );
// skip data comparisons of the program header and of empty segments
if( file1.segments[i]->get_type() == PT_PHDR || !file1.segments[i]->get_file_size() )
continue;
BOOST_REQUIRE_NE( file1.segments[i]->get_data(), (const char*)0 );
BOOST_REQUIRE_NE( file2.segments[i]->get_data(), (const char*)0 );
std::string pdata1( file1.segments[i]->get_data(),
file1.segments[i]->get_data() +
file1.segments[i]->get_file_size() );
@ -300,10 +314,15 @@ void checkExeAreEqual( std::string file_name1, std::string file_name2 )
file2.segments[i]->get_data() +
file2.segments[i]->get_file_size() );
BOOST_CHECK_EQUAL( file1.segments[i]->get_file_size(),
file2.segments[i]->get_file_size() );
BOOST_CHECK_EQUAL( file1.segments[i]->get_memory_size(),
file2.segments[i]->get_memory_size() );
// truncate the data if the header and the segment table is
// part of the segment
Elf64_Off afterPHDR = file1.get_segments_offset() +
file1.get_segment_entry_size() * file1.segments.size();
if( file1.segments[i]->get_offset() < afterPHDR ) {
pdata1 = pdata1.substr(afterPHDR);
pdata2 = pdata2.substr(afterPHDR);
}
BOOST_CHECK_EQUAL_COLLECTIONS( pdata1.begin(), pdata1.end(),
pdata2.begin(), pdata2.end() );
}
@ -370,35 +389,44 @@ BOOST_AUTO_TEST_CASE( elf_exe_copy_64 )
{
checkExeAreEqual( "../elf_examples/64bitLOAD.elf",
"../elf_examples/64bitLOAD_copy.elf" );
// checkExeAreEqual( "../elf_examples/asm_64",
// "../elf_examples/asm_64_copy" );
// checkExeAreEqual( "../elf_examples/hello_64",
// "../elf_examples/hello_64_copy" );
// checkExeAreEqual( "../elf_examples/main",
// "../elf_examples/main_copy" );
checkExeAreEqual( "../elf_examples/asm64",
"../elf_examples/asm64_copy" );
checkExeAreEqual( "../elf_examples/hello_64",
"../elf_examples/hello_64_copy" );
// The last segment (GNU_RELRO) is bigger than necessary.
// I don't see why but it contains a few bits of the .got.plt section.
// -> load, store, compare cycle fails
// checkExeAreEqual( "../elf_examples/main",
// "../elf_examples/main_copy" );
// checkExeAreEqual( "../elf_examples/ls",
// "../elf_examples/ls_copy" );
}
////////////////////////////////////////////////////////////////////////////////
BOOST_AUTO_TEST_CASE( elf_exe_copy_32 )
{
// checkExeAreEqual( "../elf_examples/asm",
// "../elf_examples/asm_copy" );
checkExeAreEqual( "../elf_examples/asm",
"../elf_examples/asm_copy" );
checkExeAreEqual( "../elf_examples/arm_v7m_test_debug.elf",
"../elf_examples/arm_v7m_test_debug_copy.elf" );
checkExeAreEqual( "../elf_examples/arm_v7m_test_release.elf",
"../elf_examples/arm_v7m_test_release_copy.elf" );
// checkExeAreEqual( "../elf_examples/hello_32",
// "../elf_examples/hello_32_copy" );
checkExeAreEqual( "../elf_examples/hello_32",
"../elf_examples/hello_32_copy" );
checkExeAreEqual( "../elf_examples/hello_arm",
"../elf_examples/hello_arm_copy" );
checkExeAreEqual( "../elf_examples/hello_arm_stripped",
"../elf_examples/hello_arm_stripped_copy" );
// checkExeAreEqual( "../elf_examples/ls",
// "../elf_examples/ls_copy" );
// checkExeAreEqual( "../elf_examples/main32",
// "../elf_examples/main32_copy" );
// The last segment (GNU_RELRO) is bigger than necessary.
// I don't see why but it contains a few bits of the .got.plt section.
// -> load, store, compare cycle fails
// checkExeAreEqual( "../elf_examples/main32",
// "../elf_examples/main32_copy" );
checkExeAreEqual( "../elf_examples/read_write_arm_elf32_input",
"../elf_examples/read_write_arm_elf32_input_copy" );
// checkExeAreEqual( "../elf_examples/test_ppc",
// "../elf_examples/test_ppc_copy" );
checkExeAreEqual( "../elf_examples/test_ppc",
"../elf_examples/test_ppc_copy" );
}

View File

@ -412,15 +412,16 @@ class elfio
// SHF_ALLOC sections are matched based on the virtual address
// otherwise the file offset is matched
if(psec->get_flags() & SHF_ALLOC
? (segVBaseAddr <= psec->get_address()
&& psec->get_address() + psec->get_size()
<= segVEndAddr)
: (segBaseOffset <= psec->get_offset()
&& psec->get_offset() + psec->get_size()
<= segEndOffset)) {
seg->add_section_index( psec->get_index(),
psec->get_addr_align() );
if( psec->get_type() != SHT_NULL
&& (psec->get_flags() & SHF_ALLOC
? (segVBaseAddr <= psec->get_address()
&& psec->get_address() + psec->get_size()
<= segVEndAddr)
: (segBaseOffset <= psec->get_offset()
&& psec->get_offset() + psec->get_size()
<= segEndOffset))) {
seg->add_section_index( psec->get_index(),
psec->get_addr_align() );
}
}
@ -508,6 +509,16 @@ class elfio
std::copy( segments_.begin(), segments_.end(),
std::back_inserter( worklist )) ;
// bring the segments which start at address 0 to the front
size_t nextSlot = 0;
for( size_t i = 0; i < worklist.size(); ++i ) {
if( i != nextSlot && worklist[i]->is_offset_initialized()
&& worklist[i]->get_offset() == 0 ) {
std::swap(worklist[i],worklist[nextSlot]);
++nextSlot;
}
}
while ( !worklist.empty() ) {
segment *seg = worklist.front();
worklist.pop_front();
@ -573,12 +584,26 @@ class elfio
Elf_Xword seg_start_pos = 0;
segment* seg = worklist[i];
// special case: PHDR segments
// this segment contains the program headers but no sections
if( seg->get_type() == PT_PHDR && seg->get_sections_num() == 0 ) {
seg_start_pos = header->get_segments_offset();
segment_memory = segment_filesize =
header->get_segment_entry_size() * header->get_segments_num();
// special case: segments with offset 0
} else if ( seg->is_offset_initialized() && seg->get_offset() == 0 ) {
seg_start_pos = 0;
if( seg->get_sections_num() )
segment_memory = segment_filesize = current_file_pos;
// new segments (no sections or not generated sections)
// have to be aligned
if( !seg->get_sections_num()
} else if( !seg->get_sections_num()
|| ( seg->get_sections_num()
&& !section_generated[seg->get_section_index_at( 0 )] )) {
Elf64_Off error = current_file_pos % seg->get_align();
// this alignment seems to be optional
// many of the input files are not aligned in the elf file...
current_file_pos += ( seg->get_align() - error ) % seg->get_align();
seg_start_pos = current_file_pos;
} else {
@ -593,7 +618,7 @@ class elfio
Elf_Xword secAlign = 0;
// fix up the alignment
if ( j != 0 && sec->is_address_initialized()
if ( !section_generated[index] && sec->is_address_initialized()
&& SHT_NOBITS != sec->get_type()
&& SHT_NULL != sec->get_type() ) {
// align the sections based on the virtual addresses
@ -601,11 +626,14 @@ class elfio
Elf64_Off req_offset = sec->get_address() - seg->get_virtual_address();
Elf64_Off cur_offset = current_file_pos - seg_start_pos;
secAlign = req_offset - cur_offset;
} else if (j != 0) {
} else if (!section_generated[index]) {
// if no address has been specified then only the section
// alignment constraint has to be matched
Elf64_Off error = current_file_pos % sec->get_addr_align();
secAlign = ( sec->get_addr_align() - error ) % sec->get_addr_align();
} else {
// alignment for already generated sections
secAlign = sec->get_offset() - seg_start_pos - segment_filesize;
}
// determine the segment file and memory sizes

View File

@ -42,15 +42,17 @@ class segment
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, physical_address );
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, file_size );
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, memory_size );
ELFIO_GET_ACCESS_DECL( Elf64_Off, offset );
virtual const char* get_data() const = 0;
virtual Elf_Half add_section_index( Elf_Half index, Elf_Xword addr_align ) = 0;
virtual Elf_Half get_sections_num() const = 0;
virtual Elf_Half get_section_index_at( Elf_Half num ) const = 0;
virtual bool is_offset_initialized() const = 0;
protected:
ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, offset );
ELFIO_SET_ACCESS_DECL( Elf64_Off, offset );
ELFIO_SET_ACCESS_DECL( Elf_Half, index );
virtual const std::vector<Elf_Half>& get_sections() const = 0;
@ -69,6 +71,7 @@ class segment_impl : public segment
segment_impl( endianess_convertor* convertor_ ) :
convertor( convertor_ )
{
is_offset_set = false;
std::fill_n( reinterpret_cast<char*>( &ph ), sizeof( ph ), '\0' );
data = 0;
}
@ -88,6 +91,7 @@ class segment_impl : public segment
ELFIO_GET_SET_ACCESS( Elf64_Addr, physical_address, ph.p_paddr );
ELFIO_GET_SET_ACCESS( Elf_Xword, file_size, ph.p_filesz );
ELFIO_GET_SET_ACCESS( Elf_Xword, memory_size, ph.p_memsz );
ELFIO_GET_ACCESS( Elf64_Off, offset, ph.p_offset );
//------------------------------------------------------------------------------
Elf_Half
@ -136,10 +140,26 @@ class segment_impl : public segment
//------------------------------------------------------------------------------
protected:
//------------------------------------------------------------------------------
ELFIO_GET_SET_ACCESS( Elf64_Off, offset, ph.p_offset );
//------------------------------------------------------------------------------
const std::vector<Elf_Half>& get_sections() const
void
set_offset( Elf64_Off value )
{
ph.p_offset = value;
ph.p_offset = (*convertor)( ph.p_offset );
is_offset_set = true;
}
//------------------------------------------------------------------------------
bool
is_offset_initialized() const
{
return is_offset_set;
}
//------------------------------------------------------------------------------
const std::vector<Elf_Half>&
get_sections() const
{
return sections;
}
@ -158,6 +178,7 @@ class segment_impl : public segment
{
stream.seekg( header_offset );
stream.read( reinterpret_cast<char*>( &ph ), sizeof( ph ) );
is_offset_set = true;
if ( PT_NULL != get_type() && 0 != get_file_size() ) {
stream.seekg( (*convertor)( ph.p_offset ) );
@ -187,6 +208,7 @@ class segment_impl : public segment
char* data;
std::vector<Elf_Half> sections;
endianess_convertor* convertor;
bool is_offset_set;
};
} // namespace ELFIO