From 96803768f1845a2c5af362ea2dc6b39a147d3930 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sat, 1 Dec 2007 06:34:12 +0000 Subject: [PATCH] Compress all debug sections. --- gold/compressed_output.cc | 177 ++++++-------------------------------- gold/compressed_output.h | 93 ++++---------------- gold/layout.cc | 51 +++++++---- gold/layout.h | 6 -- gold/merge.cc | 17 ++++ gold/merge.h | 10 ++- gold/object.cc | 3 +- gold/object.h | 1 + gold/output.cc | 164 +++++++++++++++++++---------------- gold/output.h | 142 +++++++++++++++++++----------- gold/parameters.cc | 27 +++--- gold/parameters.h | 27 ++++-- gold/reloc.cc | 79 ++++++++++++++--- gold/stringpool.cc | 4 +- gold/stringpool.h | 2 +- 15 files changed, 386 insertions(+), 417 deletions(-) diff --git a/gold/compressed_output.cc b/gold/compressed_output.cc index bf5f6407d3..d12bfda03c 100644 --- a/gold/compressed_output.cc +++ b/gold/compressed_output.cc @@ -26,8 +26,8 @@ #include #endif -#include "compressed_output.h" #include "parameters.h" +#include "compressed_output.h" namespace gold { @@ -89,187 +89,60 @@ zlib_compressed_suffix(unsigned long 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; -} +// Class Output_compressed_section. // 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() +Output_compressed_section::set_final_data_size() { - // FIXME: assert that relocations have already been applied. - - off_t uncompressed_size = 0; - for (std::vector::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; - } + off_t uncompressed_size = this->postprocessing_buffer_size(); // (Try to) compress the data. unsigned long compressed_size; - char* uncompressed_data = new char[uncompressed_size]; - off_t pos = 0; - for (std::vector::const_iterator it = this->objects_.begin(); - it != this->objects_.end(); - ++it) - { - memcpy(uncompressed_data + pos, - reinterpret_cast(it->contents), - it->length); - pos += it->length; - } + unsigned char* u_uncompressed_data = this->postprocessing_buffer(); + char* uncompressed_data = reinterpret_cast(u_uncompressed_data); + + // At this point the contents of all regular input sections will + // have been copied into the postprocessing buffer, and relocations + // will have been applied. Now we need to copy in the contents of + // anything other than a regular input section. + this->write_to_postprocessing_buffer(); bool success = false; - if (options_.zlib_compress_debug_sections()) + if (this->options_->zlib_compress_debug_sections()) success = zlib_compress(uncompressed_data, uncompressed_size, &this->data_, &compressed_size); if (success) { - delete[] uncompressed_data; + std::string suffix(zlib_compressed_suffix(uncompressed_size)); + this->new_section_name_ = std::string(this->name()) + suffix; + this->set_name(this->new_section_name_.c_str()); 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_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(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 -bool -Output_compressed_string::do_add_input_section(Relobj* object, - unsigned int shndx) -{ - return Output_merge_string::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 -void -Output_compressed_string::set_final_data_size() +Output_compressed_section::do_write(Output_file* of) { - // 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); - } + off_t offset = this->offset(); + off_t data_size = this->data_size(); + unsigned char* view = of->get_output_view(offset, data_size); + if (this->data_ == NULL) + memcpy(view, this->postprocessing_buffer(), data_size); else - { - this->compressed_data_ = NULL; - this->set_data_size(uncompressed_size); - } - - delete[] uncompressed_data; + memcpy(view, this->data_, data_size); + of->write_output_view(offset, data_size, view); } -// Change the name of the output section to reflect it's compressed. -// The layout routines call into this right before finalizing the -// shstrtab. - -template -const char* -Output_compressed_string::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 -void -Output_compressed_string::do_write(Output_file* of) -{ - if (this->compressed_data_ == NULL) - Output_merge_string::do_write(of); - else - { - unsigned char* uview = of->get_output_view(this->offset(), - this->data_size()); - char* view = reinterpret_cast(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; - -template -class Output_compressed_string; - -template -class Output_compressed_string; - } // End namespace gold. diff --git a/gold/compressed_output.h b/gold/compressed_output.h index 589dacc372..d9bb0c4c46 100644 --- a/gold/compressed_output.h +++ b/gold/compressed_output.h @@ -29,106 +29,43 @@ #define GOLD_COMPRESSED_OUTPUT_H #include -#include #include "output.h" -#include "merge.h" namespace gold { class General_options; -// This is used for compressing a section before emitting it in the -// output file. This only works for unloaded sections, since it -// assumes the final section contents are available at -// set_final_data_size() time. For loaded sections (those that end up -// in segments), this is not true; relocations are applied after -// set_final_data_size() is called. However, for unloaded sections, -// we can -- and do -- postpone calling finalize_data_size() until -// after relocations are applies. +// This is used for a section whose data should be compressed. It is +// a regular Output_section which computes its contents into a buffer +// and then postprocesses it. -class Output_compressed_section_data : public Output_section_data +class Output_compressed_section : public Output_section { public: - Output_compressed_section_data(uint64_t addralign, - const General_options& options) - : Output_section_data(addralign), options_(options), data_(NULL) - { } + Output_compressed_section(const General_options* options, + const char* name, elfcpp::Elf_Word flags, + elfcpp::Elf_Xword type) + : Output_section(name, flags, type), + options_(options) + { this->set_requires_postprocessing(); } protected: - // Add an input section. - bool - do_add_input_section(Relobj* object, unsigned int shndx); - // Set the final data size. void set_final_data_size(); - // Change the name of the output section to reflect it's compressed. - const char* - do_modified_output_section_name(const char* name); - - // Write the data to the file. + // Write out the compressed contents. void do_write(Output_file*); private: - struct Object_entry - { - Relobj* object; - unsigned int shndx; - const unsigned char* contents; - off_t length; - - Object_entry(Relobj* o, unsigned int s) - : object(o), shndx(s), contents(NULL), length(0) - { } - }; - - const General_options& options_; - std::vector objects_; + // The options--this includes the compression type. + const General_options* options_; + // The compressed data. char* data_; - std::string new_section_name_; -}; - -// This is a special case for when the output section is a string -// section and does not have any relocations to apply to it. - -template -class Output_compressed_string : public Output_merge_string -{ - public: - Output_compressed_string(uint64_t addralign, - const General_options& options) - : Output_merge_string(addralign), - options_(options), compressed_data_(NULL) - { } - - ~Output_compressed_string() - { delete[] compressed_data_; } - - protected: - // Add an input section. - bool - do_add_input_section(Relobj* object, unsigned int shndx); - - // Set the final data size. Also compresses the buffer. - void - set_final_data_size(); - - // Change the name of the output section to reflect it's compressed. - const char* - do_modified_output_section_name(const char* name); - - // Write the data to the file. - void - do_write(Output_file*); - - private: - const General_options& options_; - char* compressed_data_; - // This is just a buffer to store the section name in "permanent" storage. + // The new section name if we do compress. std::string new_section_name_; }; diff --git a/gold/layout.cc b/gold/layout.cc index f7c1e40be3..39008cd46f 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -32,6 +32,7 @@ #include "symtab.h" #include "dynobj.h" #include "ehframe.h" +#include "compressed_output.h" #include "layout.h" namespace gold @@ -386,6 +387,22 @@ Layout::section_flags_to_segment(elfcpp::Elf_Xword flags) return ret; } +// Sometimes we compress sections. This is typically done for +// sections that are not part of normal program execution (such as +// .debug_* sections), and where the readers of these sections know +// how to deal with compressed sections. (To make it easier for them, +// we will rename the ouput section in such cases from .foo to +// .foo.zlib.nnnn, where nnnn is the uncompressed size.) This routine +// doesn't say for certain whether we'll compress -- it depends on +// commandline options as well -- just whether this section is a +// candidate for compression. + +static bool +is_compressible_debug_section(const char* secname) +{ + return (strncmp(secname, ".debug", sizeof(".debug") - 1) == 0); +} + // Make a new Output_section, and attach it to segments as // appropriate. @@ -393,7 +410,14 @@ Output_section* Layout::make_output_section(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags) { - Output_section* os = new Output_section(this->options_, name, type, flags); + Output_section* os; + if ((flags & elfcpp::SHF_ALLOC) == 0 + && this->options_.compress_debug_sections() + && is_compressible_debug_section(name)) + os = new Output_compressed_section(&this->options_, name, type, flags); + else + os = new Output_section(name, type, flags); + this->section_list_.push_back(os); if ((flags & elfcpp::SHF_ALLOC) == 0) @@ -1069,6 +1093,10 @@ Layout::set_section_offsets(off_t off, Layout::Section_offset_pass pass) if (*p == this->symtab_section_) continue; + if (pass == BEFORE_INPUT_SECTIONS_PASS + && (*p)->requires_postprocessing()) + (*p)->create_postprocessing_buffer(); + if (pass == BEFORE_INPUT_SECTIONS_PASS && (*p)->after_input_sections()) continue; @@ -1085,23 +1113,14 @@ Layout::set_section_offsets(off_t off, Layout::Section_offset_pass pass) (*p)->set_file_offset(off); (*p)->finalize_data_size(); off += (*p)->data_size(); + + // At this point the name must be set. + if (pass != STRTAB_AFTER_INPUT_SECTIONS_PASS) + this->namepool_.add((*p)->name(), false, NULL); } return off; } -// Allow any section not associated with a segment to change its -// output section name at the last minute. - -void -Layout::modify_section_names() -{ - for (Section_list::iterator p = this->unattached_section_list_.begin(); - p != this->unattached_section_list_.end(); - ++p) - if ((*p)->maybe_modify_output_section_name()) - this->namepool_.add((*p)->name(), true, NULL); -} - // Set the section indexes of all the sections not associated with a // segment. @@ -1911,10 +1930,6 @@ Layout::write_sections_after_input_sections(Output_file* of) off_t off = this->output_file_size_; off = this->set_section_offsets(off, AFTER_INPUT_SECTIONS_PASS); - // Determine the final section names as well (at least, for sections - // that we haven't written yet). - this->modify_section_names(); - // Now that we've finalized the names, we can finalize the shstrab. off = this->set_section_offsets(off, STRTAB_AFTER_INPUT_SECTIONS_PASS); diff --git a/gold/layout.h b/gold/layout.h index cff29ed22a..d909acec94 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -356,12 +356,6 @@ class Layout off_t set_section_offsets(off_t, Section_offset_pass pass); - // We also allow any section not associated with a segment to change - // its output section name at the last minute. Compressed sections - // use this to embed compression info in their name. - void - modify_section_names(); - // Set the final section indexes of all the sections not associated // with a segment. Returns the next unused index. unsigned int diff --git a/gold/merge.cc b/gold/merge.cc index 215bc341c0..98ea89e134 100644 --- a/gold/merge.cc +++ b/gold/merge.cc @@ -445,6 +445,14 @@ Output_merge_data::do_write(Output_file* of) of->write(this->offset(), this->p_, this->len_); } +// Write the data to a buffer. + +void +Output_merge_data::do_write_to_buffer(unsigned char* buffer) +{ + memcpy(buffer, this->p_, this->len_); +} + // Class Output_merge_string. // Add an input section to a merged string section. @@ -535,6 +543,15 @@ Output_merge_string::do_write(Output_file* of) this->stringpool_.write(of, this->offset()); } +// Write a merged string section to a buffer. + +template +void +Output_merge_string::do_write_to_buffer(unsigned char* buffer) +{ + this->stringpool_.write_to_buffer(buffer, this->data_size()); +} + // Instantiate the templates we need. template diff --git a/gold/merge.h b/gold/merge.h index a28ff425cf..adafb0bc38 100644 --- a/gold/merge.h +++ b/gold/merge.h @@ -120,6 +120,10 @@ class Output_merge_data : public Output_merge_base void do_write(Output_file*); + // Write the data to a buffer. + void + do_write_to_buffer(unsigned char*); + private: // We build a hash table of the fixed-size constants. Each constant // is stored as a pointer into the section data we are accumulating. @@ -227,9 +231,13 @@ class Output_merge_string : public Output_merge_base void do_write(Output_file*); + // Write the data to a buffer. + void + do_write_to_buffer(unsigned char*); + // Writes the stringpool to a buffer. void - stringpool_to_buffer(char* buffer, size_t buffer_size) + stringpool_to_buffer(unsigned char* buffer, size_t buffer_size) { this->stringpool_.write_to_buffer(buffer, buffer_size); } // Clears all the data in the stringpool, to save on memory. diff --git a/gold/object.cc b/gold/object.cc index ecefa8bf39..fee249fbc5 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -1103,8 +1103,7 @@ Input_objects::add_object(Object* obj) } } - set_parameters_size_and_endianness(target->get_size(), - target->is_big_endian()); + set_parameters_target(target); return true; } diff --git a/gold/object.h b/gold/object.h index 6991d26470..40839bc7f4 100644 --- a/gold/object.h +++ b/gold/object.h @@ -960,6 +960,7 @@ class Sized_relobj : public Relobj off_t offset; off_t view_size; bool is_input_output_view; + bool is_postprocessing_view; }; typedef std::vector Views; diff --git a/gold/output.cc b/gold/output.cc index 351c21dee3..59658c7994 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -32,7 +32,6 @@ #include "libiberty.h" // for unlink_if_ordinary() #include "parameters.h" -#include "compressed_output.h" #include "object.h" #include "symtab.h" #include "reloc.h" @@ -987,13 +986,25 @@ Output_section::Input_section::data_size() const // Set the address and file offset. void -Output_section::Input_section::set_address(uint64_t addr, off_t off, - off_t secoff) +Output_section::Input_section::set_address_and_file_offset( + uint64_t address, + off_t file_offset, + off_t section_file_offset) { if (this->is_input_section()) - this->u2_.object->set_section_offset(this->shndx_, off - secoff); + this->u2_.object->set_section_offset(this->shndx_, + file_offset - section_file_offset); else - this->u2_.posd->set_address_and_file_offset(addr, off); + this->u2_.posd->set_address_and_file_offset(address, file_offset); +} + +// Finalize the data size. + +void +Output_section::Input_section::finalize_data_size() +{ + if (!this->is_input_section()) + this->u2_.posd->finalize_data_size(); } // Try to turn an input offset into an output offset. @@ -1030,15 +1041,23 @@ Output_section::Input_section::write(Output_file* of) this->u2_.posd->write(of); } +// Write the data to a buffer. As for write(), we don't have to do +// anything for an input section. + +void +Output_section::Input_section::write_to_buffer(unsigned char* buffer) +{ + if (!this->is_input_section()) + this->u2_.posd->write_to_buffer(buffer); +} + // Output_section methods. // Construct an Output_section. NAME will point into a Stringpool. -Output_section::Output_section(const General_options& options, - const char* name, elfcpp::Elf_Word type, +Output_section::Output_section(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags) - : options_(options), - name_(name), + : name_(name), addralign_(0), entsize_(0), link_section_(NULL), @@ -1053,6 +1072,7 @@ Output_section::Output_section(const General_options& options, input_sections_(), first_input_offset_(0), fills_(), + postprocessing_buffer_(NULL), needs_symtab_index_(false), needs_dynsym_index_(false), should_link_to_symtab_(false), @@ -1082,22 +1102,6 @@ Output_section::set_entsize(uint64_t v) gold_assert(this->entsize_ == v); } -// Sometimes we compress sections. This is typically done for -// sections that are not part of normal program execution (such as -// .debug_* sections), and where the readers of these sections know -// how to deal with compressed sections. (To make it easier for them, -// we will rename the ouput section in such cases from .foo to -// .foo.zlib.nnnn, where nnnn is the uncompressed size.) This routine -// doesn't say for certain whether we'll compress -- it depends on -// commandline options as well -- just whether this section is a -// candidate for compression. - -static bool -is_compressible_section(const char* secname) -{ - return (strncmp(secname, ".debug", sizeof(".debug") - 1) == 0); -} - // Add the input section SHNDX, with header SHDR, named SECNAME, in // OBJECT, to the Output_section. RELOC_SHNDX is the index of a // relocation section which applies to this section, or 0 if none, or @@ -1145,8 +1149,7 @@ Output_section::add_input_section(Sized_relobj* object, && reloc_shndx == 0) { if (this->add_merge_input_section(object, shndx, sh_flags, - entsize, addralign, - is_compressible_section(secname))) + entsize, addralign)) { // Tell the relocation routines that they need to call the // output_offset method to determine the final address. @@ -1233,8 +1236,7 @@ Output_section::add_output_merge_section(Output_section_data* posd, bool Output_section::add_merge_input_section(Relobj* object, unsigned int shndx, uint64_t flags, uint64_t entsize, - uint64_t addralign, - bool is_compressible_section) + uint64_t addralign) { bool is_string = (flags & elfcpp::SHF_STRINGS) != 0; @@ -1258,25 +1260,6 @@ Output_section::add_merge_input_section(Relobj* object, unsigned int shndx, Output_section_data* posd; if (!is_string) posd = new Output_merge_data(entsize, addralign); - else if (is_compressible_section && options_.compress_debug_sections()) - { - switch (entsize) - { - case 1: - posd = new Output_compressed_string(addralign, this->options_); - break; - case 2: - posd = new Output_compressed_string(addralign, - this->options_); - break; - case 4: - posd = new Output_compressed_string(addralign, - this->options_); - break; - default: - return false; - } - } else { switch (entsize) @@ -1407,36 +1390,14 @@ Output_section::set_final_data_size() ++p) { off = align_address(off, p->addralign()); - p->set_address(address + (off - startoff), off, startoff); + p->set_address_and_file_offset(address + (off - startoff), off, + startoff); off += p->data_size(); } this->set_data_size(off - startoff); } -// Ask each output_section_data member if it wants to change the name -// of the output section. If any of them says yes, use this to set -// the new name. This should be called after all processing of this -// output section is done, but before the name is finally committed to -// the output-section's header. - -bool -Output_section::maybe_modify_output_section_name() -{ - for (Input_section_list::const_iterator it = input_sections_.begin(); - it != input_sections_.end(); - ++it) - { - const char* newname = it->modified_output_section_name(this->name()); - if (newname != NULL) - { - this->set_name(newname); - return true; - } - } - return false; -} - // Write the section header to *OSHDR. template @@ -1474,6 +1435,8 @@ Output_section::write_header(const Layout* layout, void Output_section::do_write(Output_file* of) { + gold_assert(!this->requires_postprocessing()); + off_t output_section_file_offset = this->offset(); for (Fill_list::iterator p = this->fills_.begin(); p != this->fills_.end(); @@ -1490,6 +1453,63 @@ Output_section::do_write(Output_file* of) p->write(of); } +// If a section requires postprocessing, create the buffer to use. + +void +Output_section::create_postprocessing_buffer() +{ + gold_assert(this->requires_postprocessing()); + gold_assert(this->postprocessing_buffer_ == NULL); + + if (!this->input_sections_.empty()) + { + off_t off = this->first_input_offset_; + for (Input_section_list::iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + { + off = align_address(off, p->addralign()); + p->finalize_data_size(); + off += p->data_size(); + } + this->set_current_data_size_for_child(off); + } + + off_t buffer_size = this->current_data_size_for_child(); + this->postprocessing_buffer_ = new unsigned char[buffer_size]; +} + +// Write all the data of an Output_section into the postprocessing +// buffer. This is used for sections which require postprocessing, +// such as compression. Input sections are handled by +// Object::Relocate. + +void +Output_section::write_to_postprocessing_buffer() +{ + gold_assert(this->requires_postprocessing()); + + Target* target = parameters->target(); + unsigned char* buffer = this->postprocessing_buffer(); + for (Fill_list::iterator p = this->fills_.begin(); + p != this->fills_.end(); + ++p) + { + std::string fill_data(target->code_fill(p->length())); + memcpy(buffer + p->section_offset(), fill_data.data(), fill_data.size()); + } + + off_t off = this->first_input_offset_; + for (Input_section_list::iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + { + off = align_address(off, p->addralign()); + p->write_to_buffer(buffer + off); + off += p->data_size(); + } +} + // Output segment methods. Output_segment::Output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags) diff --git a/gold/output.h b/gold/output.h index 95d95f6558..fafd62b1fa 100644 --- a/gold/output.h +++ b/gold/output.h @@ -432,15 +432,6 @@ class Output_section_data : public Output_data add_input_section(Relobj* object, unsigned int shndx) { return this->do_add_input_section(object, shndx); } - // This class may change the output section name. This is called - // right before shstrtab is written, so after all input-section - // layout processing is done. The input is the old name, and the - // output should be a new name (which will be copied into permanent - // storage) to change the name, or NULL to keep the name as-is. - virtual const char* - do_modified_output_section_name(const char*) - { return NULL; } - // Given an input OBJECT, an input section index SHNDX within that // object, and an OFFSET relative to the start of that input // section, return whether or not the corresponding offset within @@ -452,6 +443,12 @@ class Output_section_data : public Output_data off_t *poutput) const { return this->do_output_offset(object, shndx, offset, poutput); } + // Write the contents to a buffer. This is used for sections which + // require postprocessing, such as compression. + void + write_to_buffer(unsigned char* buffer) + { this->do_write_to_buffer(buffer); } + protected: // The child class must implement do_write. @@ -472,6 +469,13 @@ class Output_section_data : public Output_data do_output_offset(const Relobj*, unsigned int, off_t, off_t*) const { return false; } + // The child class may implement write_to_buffer. Most child + // classes can not appear in a compressed section, and they do not + // implement this. + virtual void + do_write_to_buffer(unsigned char*) + { gold_unreachable(); } + // Return the required alignment. uint64_t do_addralign() const @@ -545,6 +549,11 @@ class Output_data_const : public Output_section_data void do_write(Output_file*); + // Write the data to a buffer. + void + do_write_to_buffer(unsigned char* buffer) + { memcpy(buffer, this->data_.data(), this->data_.size()); } + private: std::string data_; }; @@ -565,6 +574,11 @@ class Output_data_const_buffer : public Output_section_data void do_write(Output_file*); + // Write the data to a buffer. + void + do_write_to_buffer(unsigned char* buffer) + { memcpy(buffer, this->p_, this->data_size()); } + private: const unsigned char* p_; }; @@ -629,6 +643,11 @@ class Output_data_strtab : public Output_section_data void do_write(Output_file*); + // Write the data to a buffer. + void + do_write_to_buffer(unsigned char* buffer) + { this->strtab_->write_to_buffer(buffer, this->data_size()); } + private: Stringpool* strtab_; }; @@ -1317,8 +1336,7 @@ class Output_section : public Output_data { public: // Create an output section, giving the name, type, and flags. - Output_section(const General_options& options, - const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword); + Output_section(const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword); virtual ~Output_section(); // Add a new input section SHNDX, named NAME, with header SHDR, from @@ -1341,13 +1359,6 @@ class Output_section : public Output_data name() const { return this->name_; } - // Modify the section name. This should be called only after this - // section is done being constructed. The input should be a pointer - // into layout's namepool_. - void - set_name(const char* newname) - { this->name_ = newname; } - // Return the section type. elfcpp::Elf_Word type() const @@ -1506,11 +1517,23 @@ class Output_section : public Output_data requires_postprocessing() const { return this->requires_postprocessing_; } - // Record that this section requires postprocessing after all - // relocations have been applied. + // If a section requires postprocessing, return the buffer to use. + unsigned char* + postprocessing_buffer() const + { + gold_assert(this->postprocessing_buffer_ != NULL); + return this->postprocessing_buffer_; + } + + // If a section requires postprocessing, create the buffer to use. void - set_requires_postprocessing() - { this->requires_postprocessing_ = true; } + create_postprocessing_buffer(); + + // If a section requires postprocessing, this is the size of the + // buffer to which relocations should be applied. + off_t + postprocessing_buffer_size() const + { return this->current_data_size_for_child(); } // Return whether the offset OFFSET in the input section SHNDX in // object OBJECT is being included in the link. @@ -1535,16 +1558,6 @@ class Output_section : public Output_data write_header(const Layout*, const Stringpool*, elfcpp::Shdr_write*) const; - // This class may change the output section name. This is called - // right before shstrtab is written, so after all input-section - // layout processing is done. This calls - // do_modified_output_section_name() on all its output_section_data - // members, and changes the name if any member so suggests. If - // several members would suggest, this takes the first, arbitrarily. - // Return true if the name was modified, false else. - bool - maybe_modify_output_section_name(); - protected: // Return the section index in the output file. unsigned int @@ -1566,14 +1579,14 @@ class Output_section : public Output_data // Output_section, there is nothing to do, but if there are any // Output_section_data objects we need to set their final addresses // here. - void + virtual void set_final_data_size(); // Write the data to the file. For a typical Output_section, this // does nothing: the data is written out by calling Object::Relocate // on each input object. But if there are any Output_section_data // objects we do need to write them out here. - void + virtual void do_write(Output_file*); // Return the address alignment--function required by parent class. @@ -1596,6 +1609,36 @@ class Output_section : public Output_data do_is_section_flag_set(elfcpp::Elf_Xword flag) const { return (this->flags_ & flag) != 0; } + // Modify the section name. This is only permitted for an + // unallocated section, and only before the size has been finalized. + // Otherwise the name will not get into Layout::namepool_. + void + set_name(const char* newname) + { + gold_assert((this->flags_ & elfcpp::SHF_ALLOC) == 0); + gold_assert(!this->is_data_size_valid()); + this->name_ = newname; + } + + // This may be implemented by a child class. + virtual void + do_finalize_name(Layout*) + { } + + // Record that this section requires postprocessing after all + // relocations have been applied. This is called by a child class. + void + set_requires_postprocessing() + { + this->requires_postprocessing_ = true; + this->after_input_sections_ = true; + } + + // Write all the data of an Output_section into the postprocessing + // buffer. + void + write_to_postprocessing_buffer(); + private: // In some cases we need to keep a list of the input sections // associated with this output section. We only need the list if we @@ -1685,19 +1728,15 @@ class Output_section : public Output_data } // Set the address and file offset. This is called during - // Layout::finalize. SECOFF is the file offset of the enclosing - // section. + // Layout::finalize. SECTION_FILE_OFFSET is the file offset of + // the enclosing section. void - set_address(uint64_t addr, off_t off, off_t secoff); + set_address_and_file_offset(uint64_t address, off_t file_offset, + off_t section_file_offset); - // Call modified_output_section_name on the output-section-data object. - const char* - modified_output_section_name(const char* name) const - { - if (this->is_input_section()) - return NULL; - return this->u2_.posd->do_modified_output_section_name(name); - } + // Finalize the data size. + void + finalize_data_size(); // Add an input section, for SHF_MERGE sections. bool @@ -1721,6 +1760,11 @@ class Output_section : public Output_data void write(Output_file*); + // Write the data to a buffer. This does nothing for an input + // section. + void + write_to_buffer(unsigned char*); + private: // Code values which appear in shndx_. If the value is not one of // these codes, it is the input section index in the object file. @@ -1813,8 +1857,7 @@ class Output_section : public Output_data // handled. bool add_merge_input_section(Relobj* object, unsigned int shndx, uint64_t flags, - uint64_t entsize, uint64_t addralign, - bool can_compress_section); + uint64_t entsize, uint64_t addralign); // Add an output SHF_MERGE section POSD to this output section. // IS_STRING indicates whether it is a SHF_STRINGS section, and @@ -1826,8 +1869,6 @@ class Output_section : public Output_data // Most of these fields are only valid after layout. - // General options. - const General_options& options_; // The name of the section. This will point into a Stringpool. const char* name_; // The section address is in the parent class. @@ -1869,6 +1910,9 @@ class Output_section : public Output_data // often will need fill sections without needing to keep track of // input sections. Fill_list fills_; + // If the section requires postprocessing, this buffer holds the + // section contents during relocation. + unsigned char* postprocessing_buffer_; // Whether this output section needs a STT_SECTION symbol in the // normal symbol table. This will be true if there is a relocation // which needs it. diff --git a/gold/parameters.cc b/gold/parameters.cc index cd05ffee8d..e6b9682535 100644 --- a/gold/parameters.cc +++ b/gold/parameters.cc @@ -23,6 +23,7 @@ #include "gold.h" #include "options.h" +#include "target.h" #include "parameters.h" namespace gold @@ -37,7 +38,7 @@ Parameters::Parameters(Errors* errors) symbolic_(false), demangle_(false), detect_odr_violations_(false), optimization_level_(0), export_dynamic_(false), debug_(0), is_doing_static_link_valid_(false), doing_static_link_(false), - is_size_and_endian_valid_(false), size_(0), is_big_endian_(false) + is_target_valid_(false), target_(NULL) { } @@ -85,22 +86,20 @@ Parameters::set_doing_static_link(bool doing_static_link) this->is_doing_static_link_valid_ = true; } -// Set the size and endianness. +// Set the target. void -Parameters::set_size_and_endianness(int size, bool is_big_endian) +Parameters::set_target(Target* target) { - if (!this->is_size_and_endian_valid_) + if (!this->is_target_valid_) { - this->size_ = size; - this->is_big_endian_ = is_big_endian; - this->is_size_and_endian_valid_ = true; + this->target_ = target; + this->size_ = target->get_size(); + this->is_big_endian_ = target->is_big_endian(); + this->is_target_valid_ = true; } else - { - gold_assert(size == this->size_); - gold_assert(is_big_endian == this->is_big_endian_); - } + gold_assert(target == this->target_); } // Our local version of the variable, which is not const. @@ -135,12 +134,12 @@ set_parameters_doing_static_link(bool doing_static_link) static_parameters->set_doing_static_link(doing_static_link); } -// Set the size and endianness. +// Set the target. void -set_parameters_size_and_endianness(int size, bool is_big_endian) +set_parameters_target(Target* target) { - static_parameters->set_size_and_endianness(size, is_big_endian); + static_parameters->set_target(target); } } // End namespace gold. diff --git a/gold/parameters.h b/gold/parameters.h index 4b764e3214..2467295c85 100644 --- a/gold/parameters.h +++ b/gold/parameters.h @@ -28,6 +28,7 @@ namespace gold class General_options; class Errors; +class Target; // Here we define the Parameters class which simply holds simple // general parameters which apply to the entire link. We use a global @@ -199,12 +200,20 @@ class Parameters return this->doing_static_link_; } + // The target of the output file we are generating. + Target* + target() const + { + gold_assert(this->is_target_valid_); + return this->target_; + } + // The size of the output file we are generating. This should // return 32 or 64. int get_size() const { - gold_assert(this->is_size_and_endian_valid_); + gold_assert(this->is_target_valid_); return this->size_; } @@ -212,7 +221,7 @@ class Parameters bool is_big_endian() const { - gold_assert(this->is_size_and_endian_valid_); + gold_assert(this->is_target_valid_); return this->is_big_endian_; } @@ -224,9 +233,9 @@ class Parameters void set_doing_static_link(bool doing_static_link); - // Set the size and endianness. + // Set the target. void - set_size_and_endianness(int size, bool is_big_endian); + set_target(Target* target); private: // The types of output files. @@ -291,8 +300,10 @@ class Parameters bool is_doing_static_link_valid_; // Whether we are doing a static link. bool doing_static_link_; - // Whether the size_ and is_big_endian_ fields are valid. - bool is_size_and_endian_valid_; + // Whether the target_ field is valid. + bool is_target_valid_; + // The target. + Target* target_; // The size of the output file--32 or 64. int size_; // Whether the output file is big endian. @@ -308,8 +319,8 @@ extern void initialize_parameters(Errors*); // Set the options. extern void set_parameters_from_options(const General_options*); -// Set the size and endianness of the global parameters variable. -extern void set_parameters_size_and_endianness(int size, bool is_big_endian); +// Set the target recorded in the global parameters variable. +extern void set_parameters_target(Target* target); // Set whether we are doing a static link. extern void set_parameters_doing_static_link(bool doing_static_link); diff --git a/gold/reloc.cc b/gold/reloc.cc index d50674f6d7..2763be5ba4 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -376,12 +376,16 @@ Sized_relobj::do_relocate(const General_options& options, { if (views[i].view != NULL) { - if (views[i].is_input_output_view) - of->write_input_output_view(views[i].offset, views[i].view_size, - views[i].view); - else - of->write_output_view(views[i].offset, views[i].view_size, - views[i].view); + if (!views[i].is_postprocessing_view) + { + if (views[i].is_input_output_view) + of->write_input_output_view(views[i].offset, + views[i].view_size, + views[i].view); + else + of->write_output_view(views[i].offset, views[i].view_size, + views[i].view); + } } } @@ -419,17 +423,50 @@ Sized_relobj::write_sections(const unsigned char* pshdrs, if (shdr.get_sh_type() == elfcpp::SHT_NOBITS) continue; + // In the normal case, this input section is simply mapped to + // the output section at offset OUTPUT_OFFSET. + + // However, if OUTPUT_OFFSET == -1, then input data is handled + // specially--e.g., a .eh_frame section. The relocation + // routines need to check for each reloc where it should be + // applied. For this case, we need an input/output view for the + // entire contents of the section in the output file. We don't + // want to copy the contents of the input section to the output + // section; the output section contents were already written, + // and we waited for them in Relocate_task::is_runnable because + // relocs_must_follow_section_writes is set for the object. + + // Regardless of which of the above cases is true, we have to + // check requires_postprocessing of the output section. If that + // is false, then we work with views of the output file + // directly. If it is true, then we work with a separate + // buffer, and the output section is responsible for writing the + // final data to the output file. + + off_t output_section_offset; + off_t output_section_size; + if (!os->requires_postprocessing()) + { + output_section_offset = os->offset(); + output_section_size = os->data_size(); + } + else + { + output_section_offset = 0; + output_section_size = os->postprocessing_buffer_size(); + } + off_t view_start; off_t view_size; if (output_offset != -1) { - view_start = os->offset() + output_offset; + view_start = output_section_offset + output_offset; view_size = shdr.get_sh_size(); } else { - view_start = os->offset(); - view_size = os->data_size(); + view_start = output_section_offset; + view_size = output_section_size; } if (view_size == 0) @@ -437,15 +474,25 @@ Sized_relobj::write_sections(const unsigned char* pshdrs, gold_assert(output_offset == -1 || (output_offset >= 0 - && output_offset + view_size <= os->data_size())); + && output_offset + view_size <= output_section_size)); unsigned char* view; - if (output_offset == -1) - view = of->get_input_output_view(view_start, view_size); + if (os->requires_postprocessing()) + { + unsigned char* buffer = os->postprocessing_buffer(); + view = buffer + view_start; + if (output_offset != -1) + this->read(shdr.get_sh_offset(), view_size, view); + } else { - view = of->get_output_view(view_start, view_size); - this->read(shdr.get_sh_offset(), view_size, view); + if (output_offset == -1) + view = of->get_input_output_view(view_start, view_size); + else + { + view = of->get_output_view(view_start, view_size); + this->read(shdr.get_sh_offset(), view_size, view); + } } pvs->view = view; @@ -455,6 +502,7 @@ Sized_relobj::write_sections(const unsigned char* pshdrs, pvs->offset = view_start; pvs->view_size = view_size; pvs->is_input_output_view = output_offset == -1; + pvs->is_postprocessing_view = os->requires_postprocessing(); } } @@ -543,6 +591,9 @@ Sized_relobj::relocate_sections( continue; } + gold_assert(output_offset != -1 + || this->relocs_must_follow_section_writes()); + relinfo.reloc_shndx = i; relinfo.data_shndx = index; target->relocate_section(&relinfo, diff --git a/gold/stringpool.cc b/gold/stringpool.cc index 42a2fdabfd..6d3ecfa0e1 100644 --- a/gold/stringpool.cc +++ b/gold/stringpool.cc @@ -426,7 +426,7 @@ Stringpool_template::get_offset(const Stringpool_char* s) template void -Stringpool_template::write_to_buffer(char* buffer, +Stringpool_template::write_to_buffer(unsigned char* buffer, size_t bufsize) { gold_assert(this->strtab_size_ != 0); @@ -452,7 +452,7 @@ Stringpool_template::write(Output_file* of, off_t offset) { gold_assert(this->strtab_size_ != 0); unsigned char* view = of->get_output_view(offset, this->strtab_size_); - this->write_to_buffer(reinterpret_cast(view), this->strtab_size_); + this->write_to_buffer(view, this->strtab_size_); of->write_output_view(offset, this->strtab_size_, view); } diff --git a/gold/stringpool.h b/gold/stringpool.h index 57725a5c59..e902b8e4aa 100644 --- a/gold/stringpool.h +++ b/gold/stringpool.h @@ -147,7 +147,7 @@ class Stringpool_template // specified size. buffer_size should be at least // get_strtab_size(). void - write_to_buffer(char* buffer, size_t buffer_size); + write_to_buffer(unsigned char* buffer, size_t buffer_size); private: Stringpool_template(const Stringpool_template&); -- 2.34.1