Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 52 additions & 34 deletions elfio/elfio.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,12 +508,12 @@ class elfio
if ( auto file_class = header->get_class(); file_class == ELFCLASS64 ) {
segments_.emplace_back(
new ( std::nothrow )
segment_impl<Elf64_Phdr>( convertor, addr_translator ) );
segment_impl<Elf64_Phdr>( convertor, addr_translator, sections_ ) );
}
else if ( file_class == ELFCLASS32 ) {
segments_.emplace_back(
new ( std::nothrow )
segment_impl<Elf32_Phdr>( convertor, addr_translator ) );
segment_impl<Elf32_Phdr>( convertor, addr_translator, sections_ ) );
}
else {
segments_.pop_back();
Expand Down Expand Up @@ -613,6 +613,40 @@ class elfio
// sect_begin=12, sect_size=0 -> shall return false!
}


//------------------------------------------------------------------------------
//! \brief Add sections to the segment (similar to readelfs algorithm)
void match_sections_to_segment(segment* seg) const
{
Elf64_Off segBaseOffset = seg->get_offset();
Elf64_Off segEndOffset = segBaseOffset + seg->get_file_size();
Elf64_Off segVBaseAddr = seg->get_virtual_address();
Elf64_Off segVEndAddr = segVBaseAddr + seg->get_memory_size();
for ( const auto& psec : sections ) {
// SHF_ALLOC sections are matched based on the virtual address
// otherwise the file offset is matched
if ( ( ( psec->get_flags() & SHF_ALLOC ) == SHF_ALLOC )
? is_sect_in_seg( psec->get_address(),
psec->get_size(), segVBaseAddr,
segVEndAddr )
: is_sect_in_seg( psec->get_offset(), psec->get_size(),
segBaseOffset, segEndOffset ) ) {

// If it is a TLS segment, add TLS sections only and vice versa
if ( ( ( seg->get_type() == PT_TLS ) &&
( ( psec->get_flags() & SHF_TLS ) != SHF_TLS ) ) ||
( ( ( psec->get_flags() & SHF_TLS ) == SHF_TLS ) &&
( seg->get_type() != PT_TLS ) ) )
continue;

// Alignment of segment shall not be updated, to preserve original value
// It will be re-calculated on saving.
seg->add_section_index( psec->get_index(), 0 );
}
}
}


//------------------------------------------------------------------------------
//! \brief Load segments from a stream
//! \param stream The input stream to load from
Expand All @@ -636,12 +670,12 @@ class elfio
if ( file_class == ELFCLASS64 ) {
segments_.emplace_back( new ( std::nothrow )
segment_impl<Elf64_Phdr>(
convertor, addr_translator ) );
convertor, addr_translator, sections_ ) );
}
else if ( file_class == ELFCLASS32 ) {
segments_.emplace_back( new ( std::nothrow )
segment_impl<Elf32_Phdr>(
convertor, addr_translator ) );
convertor, addr_translator, sections_ ) );
}
else {
segments_.pop_back();
Expand All @@ -650,43 +684,27 @@ class elfio

segment* seg = segments_.back().get();

if ( !seg->load( stream,
static_cast<std::streamoff>( offset ) +
static_cast<std::streampos>( i ) * entry_size,
is_lazy ) ||
// Load the segment header
if ( !seg->load_header( stream,
static_cast<std::streamoff>( offset ) +
static_cast<std::streampos>( i ) * entry_size,
is_lazy ) ||
stream.fail() ) {
segments_.pop_back();
return false;
}

seg->set_index( i );

// Add sections to the segments (similar to readelfs algorithm)
Elf64_Off segBaseOffset = seg->get_offset();
Elf64_Off segEndOffset = segBaseOffset + seg->get_file_size();
Elf64_Off segVBaseAddr = seg->get_virtual_address();
Elf64_Off segVEndAddr = segVBaseAddr + seg->get_memory_size();
for ( const auto& psec : sections ) {
// SHF_ALLOC sections are matched based on the virtual address
// otherwise the file offset is matched
if ( ( ( psec->get_flags() & SHF_ALLOC ) == SHF_ALLOC )
? is_sect_in_seg( psec->get_address(),
psec->get_size(), segVBaseAddr,
segVEndAddr )
: is_sect_in_seg( psec->get_offset(), psec->get_size(),
segBaseOffset, segEndOffset ) ) {

// If it is a TLS segment, add TLS sections only and vice versa
if ( ( ( seg->get_type() == PT_TLS ) &&
( ( psec->get_flags() & SHF_TLS ) != SHF_TLS ) ) ||
( ( ( psec->get_flags() & SHF_TLS ) == SHF_TLS ) &&
( seg->get_type() != PT_TLS ) ) )
continue;

// Alignment of segment shall not be updated, to preserve original value
// It will be re-calculated on saving.
seg->add_section_index( psec->get_index(), 0 );
}
// Map the sections to the segments
match_sections_to_segment(seg);

// Now that the sections for this segment have been identified, load the
// segment data without duplication
if ( !seg->load_rest(is_lazy ) ||
stream.fail() ) {
segments_.pop_back();
return false;
}
}

Expand Down
101 changes: 79 additions & 22 deletions elfio/elfio_segment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ THE SOFTWARE.

namespace ELFIO {

class elfio;
//------------------------------------------------------------------------------
//! \class segment
//! \brief Class for accessing segment data
Expand Down Expand Up @@ -128,14 +129,21 @@ class segment
virtual const std::vector<Elf_Half>& get_sections() const = 0;

//------------------------------------------------------------------------------
//! \brief Load the segment from a stream
//! \brief Load the segment header from a stream
//! \param stream Input stream
//! \param header_offset Offset of the segment header
//! \param is_lazy Whether to load the segment lazily
//! \return True if successful, false otherwise
virtual bool load( std::istream& stream,
std::streampos header_offset,
bool is_lazy ) = 0;
virtual bool load_header( std::istream& stream,
std::streampos header_offset,
bool is_lazy ) = 0;

//------------------------------------------------------------------------------
//! \brief Load the segment data only if necessary
//! \param is_lazy Whether to load the segment lazily
//! \return True if successful, false otherwise
virtual bool load_rest( bool is_lazy ) = 0;

//------------------------------------------------------------------------------
//! \brief Save the segment to a stream
//! \param stream Output stream
Expand All @@ -157,8 +165,9 @@ template <class T> class segment_impl : public segment
//! \param convertor Pointer to the endianness convertor
//! \param translator Pointer to the address translator
segment_impl( std::shared_ptr<endianness_convertor> convertor,
std::shared_ptr<address_translator> translator )
: convertor( convertor ), translator( translator )
std::shared_ptr<address_translator> translator,
const std::vector<std::unique_ptr<section>> &all_sections)
: convertor( convertor ), translator( translator ), all_sections( all_sections )
{
}

Expand All @@ -185,6 +194,11 @@ template <class T> class segment_impl : public segment
{
if ( !is_loaded ) {
load_data();
// If data is in the matched sections, create a contiguous
// representation of data by copying over from sections.
if ( get_sections_num() ) {
load_section_data();
}
}
return data.get();
}
Expand Down Expand Up @@ -277,14 +291,14 @@ template <class T> class segment_impl : public segment
void set_index( const Elf_Half& value ) override { index = value; }

//------------------------------------------------------------------------------
//! \brief Load the segment from a stream
//! \brief Load the segment header from a stream
//! \param stream Input stream
//! \param header_offset Offset of the segment header
//! \param is_lazy_ Whether to load the segment lazily
//! \return True if successful, false otherwise
bool load( std::istream& stream,
std::streampos header_offset,
bool is_lazy_ ) override
bool load_header( std::istream& stream,
std::streampos header_offset,
bool is_lazy_ ) override
{
pstream = &stream;
is_lazy = is_lazy_;
Expand All @@ -302,15 +316,23 @@ template <class T> class segment_impl : public segment

is_offset_set = true;

return true;
}

//------------------------------------------------------------------------------
//! \brief Load the segment data only if necessary
//! \param is_lazy Whether to load the segment lazily
//! \return True if successful, false otherwise
bool load_rest( bool is_lazy ) override
{
if ( !( is_lazy || is_loaded ) ) {
return load_data();
}

return true;
}

//------------------------------------------------------------------------------
//! \brief Load the data of the segment
//! \brief Load the data of the segment only if necessary
//! \return True if successful, false otherwise
bool load_data() const
{
Expand Down Expand Up @@ -340,22 +362,55 @@ template <class T> class segment_impl : public segment
return false;
}

// If this segment points to sections then the data is already stored in
// the sections. Load data only if there is no section for this segment
if (!get_sections_num()) {
data.reset( new ( std::nothrow ) char[(size_t)size + 1] );
pstream->seekg( p_offset );
if ( nullptr != data.get() && pstream->read( data.get(), size ) ) {
data.get()[size] = 0;
}
else {
data = nullptr;
return false;
}
is_loaded = true;
}

return true;
}


//------------------------------------------------------------------------------
//! \brief Load the segment data by assembling it from the matched sections
//! \return True if successful, false otherwise
bool load_section_data() const
{
Elf_Xword size = get_file_size();
Elf_Xword offset = 0;

data.reset( new ( std::nothrow ) char[(size_t)size + 1] );

pstream->seekg( p_offset );
if ( nullptr != data.get() && pstream->read( data.get(), size ) ) {
data.get()[size] = 0;
}
else {
data = nullptr;
return false;
for ( auto sec_idx : sections ) {
const auto& sec = all_sections[sec_idx];
if ( SHT_NOBITS == sec->get_type() ) {
// Needs no storage like .bss
continue;
}

Elf_Xword section_align = sec->get_addr_align();
if ( section_align > 1 && offset % section_align != 0 ) {
// Add any holes to meet the alignment requirement
offset += section_align - offset % section_align;
}
char* curr = data.get();
curr += offset;
std::memcpy(curr, sec->get_data(), sec->get_size());
offset += sec->get_size();
}

is_loaded = true;

return true;
}

//------------------------------------------------------------------------------
//! \brief Save the segment to a stream
//! \param stream Output stream
Expand Down Expand Up @@ -398,6 +453,8 @@ template <class T> class segment_impl : public segment
false; //!< Flag indicating if the segment is loaded lazily
mutable bool is_loaded =
false; //!< Flag indicating if the segment is loaded
const std::vector<std::unique_ptr<section>>
&all_sections; //!< Reference to the vector of all sections in this ELF
};

} // namespace ELFIO
Expand Down
Loading