mirror of
https://github.com/serge1/ELFIO.git
synced 2025-04-17 08:44:25 +00:00
Externalize zlib dependency
== DETAILS - define an interface/abstract class for zlib implementation - update the code to reference the implementation, remove zlib dependency - use constructor injection to provide implementations to elfio - add a test to validate the injected object gets used properly `
This commit is contained in:
parent
04985836db
commit
7608269069
@ -63,11 +63,16 @@ class elfio
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
elfio() noexcept : sections( this ), segments( this )
|
||||
elfio() noexcept : sections( this ), segments( this ), zlib( nullptr )
|
||||
{
|
||||
create( ELFCLASS32, ELFDATA2LSB );
|
||||
}
|
||||
|
||||
elfio( wiiu_zlib_interface *zlib ) noexcept : sections( this ), segments( this ), zlib( std::shared_ptr<wiiu_zlib_interface>(zlib) )
|
||||
{
|
||||
elfio();
|
||||
}
|
||||
|
||||
elfio( elfio&& other ) noexcept
|
||||
: sections( this ), segments( this ),
|
||||
current_file_pos( other.current_file_pos )
|
||||
@ -77,10 +82,12 @@ class elfio
|
||||
segments_ = std::move( other.segments_ );
|
||||
convertor = std::move( other.convertor );
|
||||
addr_translator = std::move( other.addr_translator );
|
||||
zlib = std::move( other.zlib );
|
||||
|
||||
other.header = nullptr;
|
||||
other.sections_.clear();
|
||||
other.segments_.clear();
|
||||
other.zlib = nullptr;
|
||||
}
|
||||
|
||||
elfio& operator=( elfio&& other ) noexcept
|
||||
@ -92,9 +99,11 @@ class elfio
|
||||
convertor = std::move( other.convertor );
|
||||
addr_translator = std::move( other.addr_translator );
|
||||
current_file_pos = other.current_file_pos;
|
||||
zlib = std::move( other.zlib );
|
||||
|
||||
other.current_file_pos = 0;
|
||||
other.header = nullptr;
|
||||
other.zlib = nullptr;
|
||||
other.sections_.clear();
|
||||
other.segments_.clear();
|
||||
}
|
||||
@ -405,11 +414,11 @@ class elfio
|
||||
|
||||
if ( file_class == ELFCLASS64 ) {
|
||||
sections_.emplace_back(
|
||||
new section_impl<Elf64_Shdr>( &convertor, &addr_translator ) );
|
||||
new section_impl<Elf64_Shdr>( &convertor, &addr_translator, zlib ) );
|
||||
}
|
||||
else if ( file_class == ELFCLASS32 ) {
|
||||
sections_.emplace_back(
|
||||
new section_impl<Elf32_Shdr>( &convertor, &addr_translator ) );
|
||||
new section_impl<Elf32_Shdr>( &convertor, &addr_translator, zlib ) );
|
||||
}
|
||||
else {
|
||||
sections_.pop_back();
|
||||
@ -1054,6 +1063,7 @@ class elfio
|
||||
std::vector<std::unique_ptr<segment>> segments_;
|
||||
endianess_convertor convertor;
|
||||
address_translator addr_translator;
|
||||
std::shared_ptr<wiiu_zlib_interface> zlib = nullptr;
|
||||
|
||||
Elf_Xword current_file_pos = 0;
|
||||
};
|
||||
|
@ -28,8 +28,6 @@ THE SOFTWARE.
|
||||
#include <new>
|
||||
#include <limits>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
namespace ELFIO {
|
||||
|
||||
class section
|
||||
@ -76,8 +74,9 @@ template <class T> class section_impl : public section
|
||||
public:
|
||||
//------------------------------------------------------------------------------
|
||||
section_impl( const endianess_convertor* convertor,
|
||||
const address_translator* translator )
|
||||
: convertor( convertor ), translator( translator )
|
||||
const address_translator* translator,
|
||||
const std::shared_ptr<wiiu_zlib_interface> &zlib )
|
||||
: convertor( convertor ), translator( translator ), zlib(zlib)
|
||||
{
|
||||
}
|
||||
|
||||
@ -216,55 +215,30 @@ template <class T> class section_impl : public section
|
||||
if ( ( 0 != size ) && ( nullptr != data ) ) {
|
||||
stream.seekg(
|
||||
( *translator )[( *convertor )( header.sh_offset )] );
|
||||
if(get_flags() & SHF_RPX_DEFLATE) {
|
||||
Elf_Xword uncompressed_size = 0;
|
||||
fixup_size(stream, size, uncompressed_size);
|
||||
stream.read( data.get(), size );
|
||||
if ( static_cast<Elf_Xword>( stream.gcount() ) != size ) {
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// reallocate data to be the correct size
|
||||
data.reset( new (std::nothrow) char[size_t(uncompressed_size) + 1]);
|
||||
// create a buffer to hold the compressed bits
|
||||
auto compressed_data = std::unique_ptr<char[]>(new char[size_t(size)]);
|
||||
if( data == nullptr || compressed_data == nullptr) {
|
||||
std::cerr << "failed to allocate memory buffers for decompression" << std::endl;
|
||||
if(get_flags() & SHF_RPX_DEFLATE) {
|
||||
if(zlib == nullptr) {
|
||||
std::cerr << "WARN: compressed section found but no zlib implementation provided. Skipping." << std::endl;
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
// at this point, data holds the whole compressed stream
|
||||
Elf_Xword uncompressed_size = 0;
|
||||
auto decompressed_data = zlib->inflate(data.get(), convertor, size, uncompressed_size);
|
||||
if(decompressed_data == nullptr) {
|
||||
std::cerr << "Failed to decompress section data." << std::endl;
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
set_size(uncompressed_size);
|
||||
// read rest of data into data buffer
|
||||
stream.read( compressed_data.get(), size);
|
||||
|
||||
z_stream s = { 0 };
|
||||
int z_result = 0;
|
||||
|
||||
s.zalloc = Z_NULL;
|
||||
s.zfree = Z_NULL;
|
||||
s.opaque = Z_NULL;
|
||||
|
||||
if(Z_OK != (z_result = inflateInit_(&s, ZLIB_VERSION, sizeof(s)))) {
|
||||
std::cerr << "error initializing zlib: " << z_result << std::endl;
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
s.avail_in = size;
|
||||
s.next_in = (Bytef *)compressed_data.get();
|
||||
|
||||
s.avail_out = uncompressed_size;
|
||||
s.next_out = (Bytef *)data.get();
|
||||
|
||||
z_result = inflate(&s, Z_FINISH);
|
||||
inflateEnd(&s);
|
||||
if (z_result != Z_OK && z_result != Z_STREAM_END) {
|
||||
std::cerr << "error decompressing section: " << z_result << std::endl;
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
stream.read( data.get(), size );
|
||||
if ( static_cast<Elf_Xword>( stream.gcount() ) != size ) {
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
data = std::move(decompressed_data);
|
||||
}
|
||||
// refresh size because it may have changed if we had to decompress data
|
||||
size = get_size();
|
||||
@ -298,14 +272,6 @@ template <class T> class section_impl : public section
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
void fixup_size(std::istream& stream, Elf_Xword& compressed_size, Elf_Xword& uncompressed_size) {
|
||||
uint32_t tmp = 0;
|
||||
stream.read((char *)&tmp, 4);
|
||||
tmp = (*convertor)(tmp);
|
||||
|
||||
uncompressed_size = static_cast<Elf_Xword>(tmp);
|
||||
compressed_size = get_size() - 4;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
void save_header( std::ostream& stream, std::streampos header_offset ) const
|
||||
{
|
||||
@ -319,9 +285,10 @@ template <class T> class section_impl : public section
|
||||
{
|
||||
adjust_stream_size( stream, data_offset );
|
||||
|
||||
if(get_flags() & SHF_RPX_DEFLATE) {
|
||||
Elf_Xword compressed_size = get_size();
|
||||
auto compressed_ptr = compress_data(compressed_size);
|
||||
if( (get_flags() & SHF_RPX_DEFLATE) && zlib != nullptr) {
|
||||
Elf_Xword decompressed_size = get_size();
|
||||
Elf_Xword compressed_size = 0;
|
||||
auto compressed_ptr = zlib->deflate(data.get(), convertor, decompressed_size, compressed_size);
|
||||
stream.write( compressed_ptr.get(), compressed_size);
|
||||
} else {
|
||||
stream.write( get_data(), get_size() );
|
||||
@ -330,44 +297,16 @@ template <class T> class section_impl : public section
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
private:
|
||||
std::unique_ptr<char[]> compress_data(Elf_Xword &size) const {
|
||||
auto compressed = std::unique_ptr<char[]>(new char[size]);
|
||||
int z_result = 0;
|
||||
z_stream s = { 0 };
|
||||
s.zalloc = Z_NULL;
|
||||
s.zfree = Z_NULL;
|
||||
s.opaque = Z_NULL;
|
||||
if(Z_OK != (z_result = deflateInit(&s, Z_DEFAULT_COMPRESSION))) {
|
||||
std::cerr << "failed to init zlib for compression: " << z_result << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s.avail_in = size;
|
||||
s.next_in = (Bytef *)data.get();
|
||||
s.avail_out = size;
|
||||
s.next_out = (Bytef *)compressed.get();
|
||||
|
||||
z_result = deflate(&s, Z_FINISH);
|
||||
if(z_result != Z_OK && z_result != Z_STREAM_END) {
|
||||
std::cerr << "deflate failed: " << z_result << std::endl;
|
||||
deflateEnd(&s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size = size - s.avail_out;
|
||||
deflateEnd(&s);
|
||||
return compressed;
|
||||
}
|
||||
|
||||
T header = { 0 };
|
||||
Elf_Half index = 0;
|
||||
std::string name;
|
||||
std::unique_ptr<char[]> data;
|
||||
Elf_Word data_size = 0;
|
||||
const endianess_convertor* convertor = nullptr;
|
||||
const address_translator* translator = nullptr;
|
||||
bool is_address_set = false;
|
||||
size_t stream_size = 0;
|
||||
Elf_Word data_size = 0;
|
||||
const endianess_convertor* convertor = nullptr;
|
||||
const address_translator* translator = nullptr;
|
||||
const std::shared_ptr<wiiu_zlib_interface> zlib = nullptr;
|
||||
bool is_address_set = false;
|
||||
size_t stream_size = 0;
|
||||
};
|
||||
|
||||
} // namespace ELFIO
|
||||
|
@ -260,6 +260,37 @@ inline void adjust_stream_size( std::ostream& stream, std::streamsize offset )
|
||||
stream.seekp( offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumers should write an implementation of this class and pass an instance of it to the ELFIO::elfio constructor.
|
||||
*/
|
||||
class wiiu_zlib_interface
|
||||
{
|
||||
public:
|
||||
virtual ~wiiu_zlib_interface() = default;
|
||||
/**
|
||||
* decompresses a RPX/RPL zlib-compressed section.
|
||||
*
|
||||
* @param data the buffer of compressed data
|
||||
* @param endianness_convertor pointer to an endianness_convertor instance, used to convert numbers to/from the target endianness.
|
||||
* @param compressed_size the size of the data buffer, in bytes
|
||||
* @param decompressed_size a reference to a variable where the decompressed buffer size will be stored.
|
||||
* @returns a smart pointer to the decompressed data.
|
||||
*/
|
||||
virtual std::unique_ptr<char[]> inflate(const char *data, const endianess_convertor *convertor, Elf_Xword compressed_size, Elf_Xword &uncompressed_size) const = 0;
|
||||
|
||||
/**
|
||||
* compresses a RPX/RPL zlib-compressed section.
|
||||
*
|
||||
* @param data the buffer of uncompressed data
|
||||
* @param endianness_convertor pointer to an endianness_convertor instance, used to convert numbers to/from the target endianness.
|
||||
* @param decompressed_size the size of the data buffer, in bytes
|
||||
* @param compressed_size a reference to a variable where the compressed buffer size will be stored.
|
||||
* @returns a smart pointer to the compressed data.
|
||||
*/
|
||||
virtual std::unique_ptr<char[]> deflate(const char *data, const endianess_convertor *convertor, Elf_Xword decompressed_size, Elf_Xword &compressed_size) const = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_UTILS_HPP
|
||||
|
@ -27,6 +27,7 @@ THE SOFTWARE.
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <elfio/elfio.hpp>
|
||||
#include <elfio/elfio_utils.hpp>
|
||||
|
||||
using namespace ELFIO;
|
||||
|
||||
@ -970,6 +971,42 @@ TEST( ELFIOTest, test_dynamic_64_2 )
|
||||
EXPECT_EQ( value, 0 );
|
||||
}
|
||||
|
||||
class mock_wiiu_zlib : public wiiu_zlib_interface {
|
||||
public:
|
||||
std::unique_ptr<char[]> inflate(const char *data, const endianess_convertor *convertor, Elf_Xword compressed_size, Elf_Xword &uncompressed_size) const {
|
||||
uncompressed_size = 2 * compressed_size;
|
||||
return std::unique_ptr<char[]>(new char[uncompressed_size+1]);
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]> deflate(const char *data, const endianess_convertor *convertor, Elf_Xword decompressed_size, Elf_Xword &compressed_size) const {
|
||||
compressed_size = decompressed_size / 2;
|
||||
return std::unique_ptr<char[]>(new char[compressed_size+1]);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Given: a valid RPX file
|
||||
// When: we load it with no zlib implementation
|
||||
// Then: the size returns the raw section size (compressed size)
|
||||
// When: we load it with a mock zlib implementation
|
||||
// Then: the size changes to reflect the mock zlib implementation is being called
|
||||
//
|
||||
// This test does not do any further validation because doing so would require providing
|
||||
// a real zlib implementation
|
||||
TEST( ELFIOTest, test_rpx )
|
||||
{
|
||||
elfio reader(new mock_wiiu_zlib());
|
||||
elfio reader_no_zlib;
|
||||
|
||||
ASSERT_EQ( reader_no_zlib.load( "elf_examples/helloworld.rpx" ), true );
|
||||
section *text1 = reader_no_zlib.sections[1];
|
||||
EXPECT_EQ( text1->get_size(), 36744);
|
||||
|
||||
ASSERT_EQ( reader.load( "elf_examples/helloworld.rpx" ), true );
|
||||
section *text2 = reader.sections[1];
|
||||
EXPECT_EQ(text2->get_size(), text1->get_size() * 2);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
TEST( ELFIOTest, test_dynamic_64_3 )
|
||||
{
|
||||
|
BIN
tests/elf_examples/helloworld.rpx
Normal file
BIN
tests/elf_examples/helloworld.rpx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user