blob: bf5f6407d3b7cefd2ffae5432e8e6f799fa1528e [file] [log] [blame]
// compressed_output.cc -- manage compressed output sections for gold
// Copyright 2007 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "gold.h"
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
#include "compressed_output.h"
#include "parameters.h"
namespace gold
{
// Compress UNCOMPRESSED_DATA of size UNCOMPRESSED_SIZE. Returns true
// if it successfully compressed, false if it failed for any reason
// (including not having zlib support in the library). If it returns
// true, it allocates memory for the compressed data using new, and
// sets *COMPRESSED_DATA and *COMPRESSED_SIZE to appropriate values.
#ifdef HAVE_ZLIB_H
static bool
zlib_compress(const char* uncompressed_data, unsigned long uncompressed_size,
char** compressed_data, unsigned long* compressed_size)
{
*compressed_size = uncompressed_size + uncompressed_size / 1000 + 128;
*compressed_data = new char[*compressed_size];
int compress_level;
if (parameters->optimization_level() >= 1)
compress_level = 9;
else
compress_level = 1;
int rc = compress2(reinterpret_cast<Bytef*>(*compressed_data),
compressed_size,
reinterpret_cast<const Bytef*>(uncompressed_data),
uncompressed_size,
compress_level);
if (rc == Z_OK)
return true;
else
{
delete[] *compressed_data;
*compressed_data = NULL;
return false;
}
}
#else // !defined(HAVE_ZLIB_H)
static bool
zlib_compress(const char*, unsigned long, char**, unsigned long*)
{
return false;
}
#endif // !defined(HAVE_ZLIB_H)
// After compressing an output section, we rename it from foo to
// foo.zlib.nnnn, where nnnn is the uncompressed size of the section.
static std::string
zlib_compressed_suffix(unsigned long uncompressed_size)
{
char size_string[64];
snprintf(size_string, sizeof(size_string), "%lu", uncompressed_size);
return std::string(".zlib.") + size_string;
}
// Class Output_compressed_section_data.
// Add an input section. In this case, we just keep track of the sections.
bool
Output_compressed_section_data::do_add_input_section(Relobj* obj,
unsigned int shndx)
{
this->objects_.push_back(Object_entry(obj, shndx));
return true;
}
// Set the final data size of a compressed section. This is where
// we actually compress the section data.
void
Output_compressed_section_data::set_final_data_size()
{
// FIXME: assert that relocations have already been applied.
off_t uncompressed_size = 0;
for (std::vector<Object_entry>::iterator it = this->objects_.begin();
it != this->objects_.end();
++it)
{
it->contents
= it->object->section_contents(it->shndx, &it->length, false);
uncompressed_size += it->length;
}
// (Try to) compress the data.
unsigned long compressed_size;
char* uncompressed_data = new char[uncompressed_size];
off_t pos = 0;
for (std::vector<Object_entry>::const_iterator it = this->objects_.begin();
it != this->objects_.end();
++it)
{
memcpy(uncompressed_data + pos,
reinterpret_cast<const char*>(it->contents),
it->length);
pos += it->length;
}
bool success = false;
if (options_.zlib_compress_debug_sections())
success = zlib_compress(uncompressed_data, uncompressed_size,
&this->data_, &compressed_size);
if (success)
{
delete[] uncompressed_data;
this->set_data_size(compressed_size);
this->new_section_name_ = zlib_compressed_suffix(uncompressed_size);
}
else
{
gold_warning(_("Not compressing section data: zlib error"));
gold_assert(this->data_ == NULL);
this->data_ = uncompressed_data;
this->set_data_size(uncompressed_size);
}
}
// Change the name of the output section to reflect it's compressed.
// The layout routines call into this right before finalizing the
// shstrtab.
const char*
Output_compressed_section_data::do_modified_output_section_name(
const char* name)
{
// This mean we never compressed the data.
if (this->new_section_name_.empty())
return NULL;
this->new_section_name_ = std::string(name) + this->new_section_name_;
return this->new_section_name_.c_str();
}
// Write out a compressed section. If we couldn't compress, we just
// write it out as normal, uncompressed data.
void
Output_compressed_section_data::do_write(Output_file* of)
{
unsigned char* uview = of->get_output_view(this->offset(),
this->data_size());
char* view = reinterpret_cast<char*>(uview);
memcpy(view, this->data_, this->data_size());
of->write_output_view(this->offset(), this->data_size(), uview);
}
// Class Output_compressed_string.
// Add an input section. We don't do anything special here.
template<typename Char_type>
bool
Output_compressed_string<Char_type>::do_add_input_section(Relobj* object,
unsigned int shndx)
{
return Output_merge_string<Char_type>::do_add_input_section(object, shndx);
}
// Set the final data size of a compressed section. This is where
// we actually compress the section data.
template<typename Char_type>
void
Output_compressed_string<Char_type>::set_final_data_size()
{
// First let the superclass finalize all its data, then write it to
// a buffer.
unsigned long uncompressed_size = this->finalize_merged_data();
char* uncompressed_data = new char[uncompressed_size];
this->stringpool_to_buffer(uncompressed_data, uncompressed_size);
// (Try to) compress the data.
unsigned long compressed_size;
if (options_.zlib_compress_debug_sections()
&& zlib_compress(uncompressed_data, uncompressed_size,
&this->compressed_data_, &compressed_size))
{
this->set_data_size(compressed_size);
// Save some memory.
this->clear_stringpool();
// We will be renaming the section to name.zlib.uncompressed_size.
this->new_section_name_ = zlib_compressed_suffix(uncompressed_size);
}
else
{
this->compressed_data_ = NULL;
this->set_data_size(uncompressed_size);
}
delete[] uncompressed_data;
}
// Change the name of the output section to reflect it's compressed.
// The layout routines call into this right before finalizing the
// shstrtab.
template<typename Char_type>
const char*
Output_compressed_string<Char_type>::do_modified_output_section_name(
const char* name)
{
// This mean we never compressed the data
if (this->new_section_name_.empty())
return NULL;
this->new_section_name_ = std::string(name) + this->new_section_name_;
return this->new_section_name_.c_str();
}
// Write out a compressed string section. If we couldn't compress,
// we just write out the normal string section.
template<typename Char_type>
void
Output_compressed_string<Char_type>::do_write(Output_file* of)
{
if (this->compressed_data_ == NULL)
Output_merge_string<Char_type>::do_write(of);
else
{
unsigned char* uview = of->get_output_view(this->offset(),
this->data_size());
char* view = reinterpret_cast<char*>(uview);
memcpy(view, this->compressed_data_, this->data_size());
of->write_output_view(this->offset(), this->data_size(), uview);
}
}
// Instantiate the templates we need.
template
class Output_compressed_string<char>;
template
class Output_compressed_string<uint16_t>;
template
class Output_compressed_string<uint32_t>;
} // End namespace gold.