diff options
Diffstat (limited to 'gold/reduced_debug_output.cc')
-rw-r--r-- | gold/reduced_debug_output.cc | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/gold/reduced_debug_output.cc b/gold/reduced_debug_output.cc new file mode 100644 index 0000000..d195cfd --- /dev/null +++ b/gold/reduced_debug_output.cc @@ -0,0 +1,429 @@ +// reduced_debug_output.cc -- output reduced debugging information to save space + +// Copyright 2008 Free Software Foundation, Inc. +// Written by Caleb Howe <cshowe@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" + +#include "parameters.h" +#include "options.h" +#include "dwarf.h" +#include "dwarf_reader.h" +#include "reduced_debug_output.h" + +#include <vector> + +namespace gold +{ + +void +write_unsigned_LEB_128(std::vector<unsigned char>* buffer, uint64_t value) +{ + do + { + unsigned char current_byte = value & 0x7f; + value >>= 7; + if (value != 0) + { + current_byte |= 0x80; + } + buffer->push_back(current_byte); + } + while (value != 0); +} + +size_t +get_length_as_unsigned_LEB_128(uint64_t value) +{ + size_t length = 0; + do + { + unsigned char current_byte = value & 0x7f; + value >>= 7; + if (value != 0) + { + current_byte |= 0x80; + } + length++; + } + while (value != 0); + return length; +} + +template <int valsize> +void Insert_into_vector(std::vector<unsigned char>* destination, + typename elfcpp::Valtype_base<valsize>::Valtype value) +{ + union + { + unsigned char buffer[valsize / 8]; + long long align; + } u; + if (parameters->target().is_big_endian()) + elfcpp::Swap<valsize, true>::writeval(u.buffer, value); + else + elfcpp::Swap<valsize, false>::writeval(u.buffer, value); + destination->insert(destination->end(), u.buffer, u.buffer + valsize / 8); +} + +template <int valsize> +typename elfcpp::Valtype_base<valsize>::Valtype +read_from_pointer(unsigned char** source) +{ + typename elfcpp::Valtype_base<valsize>::Valtype return_value; + if (parameters->target().is_big_endian()) + return_value = elfcpp::Swap_unaligned<valsize, true>::readval(*source); + else + return_value = elfcpp::Swap_unaligned<valsize, false>::readval(*source); + *source += valsize / 8; + return return_value; +} + +// Given a pointer to the beginning of a die and the beginning of the associated +// abbreviation fills in die_end with the end of the information entry. If +// successful returns true. Get_die_end also takes a pointer to the end of the +// buffer containing the die. If die_end would be beyond the end of the +// buffer, or if an unsupported dwarf form is encountered returns false. +bool +Output_reduced_debug_info_section::get_die_end( + unsigned char* die, unsigned char* abbrev, unsigned char** die_end, + unsigned char* buffer_end, int address_size, bool is64) +{ + size_t LEB_size; + uint64_t LEB_decoded; + for(;;) + { + uint64_t attribute = read_unsigned_LEB_128(abbrev, &LEB_size); + abbrev += LEB_size; + elfcpp::DW_FORM form = + static_cast<elfcpp::DW_FORM>(read_unsigned_LEB_128(abbrev, + &LEB_size)); + abbrev += LEB_size; + if (!(attribute || form)) + break; + if (die >= buffer_end) + return false; + switch(form) + { + case elfcpp::DW_FORM_null: + break; + case elfcpp::DW_FORM_strp: + die += is64 ? 8 : 4; + break; + case elfcpp::DW_FORM_addr: + case elfcpp::DW_FORM_ref_addr: + die += address_size; + break; + case elfcpp::DW_FORM_block1: + die += *die; + die += 1; + break; + case elfcpp::DW_FORM_block2: + { + uint16_t block_size; + block_size = read_from_pointer<16>(&die); + die += block_size; + break; + } + case elfcpp::DW_FORM_block4: + { + uint32_t block_size; + block_size = read_from_pointer<32>(&die); + die += block_size; + break; + } + case elfcpp::DW_FORM_block: + LEB_decoded = read_unsigned_LEB_128(die, &LEB_size); + die += (LEB_decoded + LEB_size); + break; + case elfcpp::DW_FORM_data1: + case elfcpp::DW_FORM_ref1: + case elfcpp::DW_FORM_flag: + die += 1; + break; + case elfcpp::DW_FORM_data2: + case elfcpp::DW_FORM_ref2: + die += 2; + break; + case elfcpp::DW_FORM_data4: + case elfcpp::DW_FORM_ref4: + die += 4; + break; + case elfcpp::DW_FORM_data8: + case elfcpp::DW_FORM_ref8: + die += 8; + break; + case elfcpp::DW_FORM_ref_udata: + case elfcpp::DW_FORM_udata: + read_unsigned_LEB_128(die, &LEB_size); + die += LEB_size; + break; + case elfcpp::DW_FORM_string: + { + size_t length = strlen(reinterpret_cast<char*>(die)); + die += length + 1; + break; + } + case elfcpp::DW_FORM_sdata: + case elfcpp::DW_FORM_indirect: + return false; + } + } + *die_end = die; + return true; +} + +void +Output_reduced_debug_abbrev_section::set_final_data_size() +{ + if (this->sized_ || this->failed_) + return; + + uint64_t abbrev_number; + size_t LEB_size; + unsigned char* abbrev_data = this->postprocessing_buffer(); + unsigned char* abbrev_end = this->postprocessing_buffer() + + this->postprocessing_buffer_size(); + this->write_to_postprocessing_buffer(); + while(abbrev_data < abbrev_end) + { + uint64_t abbrev_offset = abbrev_data - this->postprocessing_buffer(); + while((abbrev_number = read_unsigned_LEB_128(abbrev_data, &LEB_size))) + { + if (abbrev_data >= abbrev_end) + { + failed("Debug abbreviations extend beyond .debug_abbrev " + "section; failed to reduce debug abbreviations"); + return; + } + abbrev_data += LEB_size; + + // Together with the abbreviation number these fields make up + // the header for each abbreviation + uint64_t abbrev_type = read_unsigned_LEB_128(abbrev_data, &LEB_size); + abbrev_data += LEB_size; + + // This would ordinarily be the has_children field of the + // abbreviation. But it's going to be false after reducting the + // information, so there's no point in storing it + abbrev_data++; + + // Read to the end of the current abbreviation + // This is indicated by two zero unsigned LEBs in a row. We don't + // need to parse the data yet, so we just scan through the data + // looking for two consecutive 0 bytes indicating the end of the + // abbreviation + unsigned char* current_abbrev; + for (current_abbrev = abbrev_data; + current_abbrev[0] || current_abbrev[1]; + current_abbrev++) + { + if (current_abbrev >= abbrev_end) + { + this->failed(_("Debug abbreviations extend beyond " + ".debug_abbrev section; failed to reduce " + "debug abbreviations")); + return; + } + } + // Account for the two nulls and advance to the start of the + // next abbreviation. + current_abbrev += 2; + + // We're eliminating every entry except for compile units, so we + // only need to store abbreviations that describe them + if (abbrev_type == elfcpp::DW_TAG_compile_unit) + { + write_unsigned_LEB_128(&this->data_, ++this->abbrev_count_); + write_unsigned_LEB_128(&this->data_, abbrev_type); + // has_children is false for all entries + this->data_.push_back(0); + this->abbrev_mapping_[std::make_pair(abbrev_offset, + abbrev_number)] = + std::make_pair(abbrev_count_, this->data_.size()); + this->data_.insert(this->data_.end(), abbrev_data, + current_abbrev); + } + abbrev_data = current_abbrev; + } + gold_assert(LEB_size == 1); + abbrev_data += LEB_size; + } + // Null terminate the list of abbreviations + this->data_.push_back(0); + this->set_data_size(data_.size()); + this->sized_ = true; +} + +void +Output_reduced_debug_abbrev_section::do_write(Output_file* of) +{ + off_t offset = this->offset(); + off_t data_size = this->data_size(); + unsigned char* view = of->get_output_view(offset, data_size); + if (this->failed_) + memcpy(view, this->postprocessing_buffer(), + this->postprocessing_buffer_size()); + else + memcpy(view, &this->data_.front(), data_size); + of->write_output_view(offset, data_size, view); +} + +// Locates the abbreviation with abbreviation_number abbrev_number in the +// abbreviation table at offset abbrev_offset. abbrev_number is updated with +// its new abbreviation number and a pointer to the beginning of the +// abbreviation is returned. +unsigned char* +Output_reduced_debug_abbrev_section::get_new_abbrev( + uint64_t* abbrev_number, uint64_t abbrev_offset) +{ + set_final_data_size(); + std::pair<uint64_t, uint64_t> abbrev_info = + this->abbrev_mapping_[std::make_pair(abbrev_offset, *abbrev_number)]; + *abbrev_number = abbrev_info.first; + return &this->data_[abbrev_info.second]; +} + +void Output_reduced_debug_info_section::set_final_data_size() +{ + if (this->failed_) + return; + unsigned char* debug_info = this->postprocessing_buffer(); + unsigned char* debug_info_end = (this->postprocessing_buffer() + + this->postprocessing_buffer_size()); + unsigned char* next_compile_unit; + this->write_to_postprocessing_buffer(); + + while (debug_info < debug_info_end) + { + uint32_t compile_unit_start = read_from_pointer<32>(&debug_info); + // The first 4 bytes of each compile unit determine whether or + // not we're using dwarf32 or dwarf64. This is not necessarily + // related to whether the binary is 32 or 64 bits. + if (compile_unit_start == 0xFFFFFFFF) + { + // Technically the size can be up to 96 bits. Rather than handle + // 96/128 bit integers we just truncate the size at 64 bits. + if (0 != read_from_pointer<32>(&debug_info)) + { + this->failed(_("Extremely large compile unit in debug info; " + "failed to reduce debug info")); + return; + } + const int dwarf64_header_size = sizeof(uint64_t) + sizeof(uint16_t) + + sizeof(uint64_t) + sizeof(uint8_t); + if (debug_info + dwarf64_header_size >= debug_info_end) + { + this->failed(_("Debug info extends beyond .debug_info section;" + "failed to reduce debug info")); + return; + } + + uint64_t compile_unit_size = read_from_pointer<64>(&debug_info); + next_compile_unit = debug_info + compile_unit_size; + uint16_t version = read_from_pointer<16>(&debug_info); + uint64_t abbrev_offset = read_from_pointer<64>(&debug_info); + uint8_t address_size = read_from_pointer<8>(&debug_info); + size_t LEB_size; + uint64_t abbreviation_number = read_unsigned_LEB_128(debug_info, + &LEB_size); + debug_info += LEB_size; + unsigned char* die_abbrev = this->associated_abbrev_->get_new_abbrev( + &abbreviation_number, abbrev_offset); + unsigned char* die_end; + if (!this->get_die_end(debug_info, die_abbrev, &die_end, + debug_info_end, address_size, true)) + { + this->failed(_("Invalid DIE in debug info; " + "failed to reduce debug info")); + return; + } + + Insert_into_vector<32>(&this->data_, 0xFFFFFFFF); + Insert_into_vector<32>(&this->data_, 0); + Insert_into_vector<64>( + &this->data_, + (11 + get_length_as_unsigned_LEB_128(abbreviation_number) + + die_end - debug_info)); + Insert_into_vector<16>(&this->data_, version); + Insert_into_vector<64>(&this->data_, 0); + Insert_into_vector<8>(&this->data_, address_size); + write_unsigned_LEB_128(&this->data_, abbreviation_number); + this->data_.insert(this->data_.end(), debug_info, die_end); + } + else + { + const int dwarf32_header_size = + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint8_t); + if (debug_info + dwarf32_header_size >= debug_info_end) + { + this->failed(_("Debug info extends beyond .debug_info section; " + "failed to reduce debug info")); + return; + } + uint32_t compile_unit_size = compile_unit_start; + next_compile_unit = debug_info + compile_unit_size; + uint16_t version = read_from_pointer<16>(&debug_info); + uint32_t abbrev_offset = read_from_pointer<32>(&debug_info); + uint8_t address_size = read_from_pointer<8>(&debug_info); + size_t LEB_size; + uint64_t abbreviation_number = read_unsigned_LEB_128(debug_info, + &LEB_size); + debug_info += LEB_size; + unsigned char* die_abbrev = this->associated_abbrev_->get_new_abbrev( + &abbreviation_number, abbrev_offset); + unsigned char* die_end; + if (!this->get_die_end(debug_info, die_abbrev, &die_end, + debug_info_end, address_size, false)) + { + this->failed(_("Invalid DIE in debug info; " + "failed to reduce debug info")); + return; + } + + Insert_into_vector<32>( + &this->data_, + (7 + get_length_as_unsigned_LEB_128(abbreviation_number) + + die_end - debug_info)); + Insert_into_vector<16>(&this->data_, version); + Insert_into_vector<32>(&this->data_, 0); + Insert_into_vector<8>(&this->data_, address_size); + write_unsigned_LEB_128(&this->data_, abbreviation_number); + this->data_.insert(this->data_.end(), debug_info, die_end); + } + debug_info = next_compile_unit; + } + this->set_data_size(data_.size()); +} + +void Output_reduced_debug_info_section::do_write(Output_file* of) +{ + off_t offset = this->offset(); + off_t data_size = this->data_size(); + unsigned char* view = of->get_output_view(offset, data_size); + if (this->failed_) + memcpy(view, this->postprocessing_buffer(), + this->postprocessing_buffer_size()); + else + memcpy(view, &this->data_.front(), data_size); + of->write_output_view(offset, data_size, view); +} + +} // End namespace gold. |