// reduced_debug_output.cc -- output reduced debugging information to save space // Copyright (C) 2008-2014 Free Software Foundation, Inc. // Written by Caleb Howe . // 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 "int_encoding.h" #include namespace gold { // 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(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_flag_present: break; case elfcpp::DW_FORM_strp: case elfcpp::DW_FORM_sec_offset: 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: case elfcpp::DW_FORM_exprloc: 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: case elfcpp::DW_FORM_ref_sig8: 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_sdata: read_signed_LEB_128(die, &LEB_size); die += LEB_size; break; case elfcpp::DW_FORM_string: { size_t length = strlen(reinterpret_cast(die)); die += length + 1; break; } case elfcpp::DW_FORM_indirect: case elfcpp::DW_FORM_GNU_addr_index: case elfcpp::DW_FORM_GNU_str_index: default: 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 reducing 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 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.