From 29dab7648672342418a9d32767e3b3182d9e6a30 Mon Sep 17 00:00:00 2001 From: Cary Coutant Date: Wed, 17 Mar 2021 21:31:15 -0700 Subject: [PATCH] Add DWARF 5 support in gold. elfcpp/ PR gold/27246 * dwarf.h (enum DW_LNCT): Add line number table content type codes. (enum DW_LINE_OPS): Reformat. (enum DW_LINE_EXTENDED_OPS): Reformat. (enum DW_CHILDREN): Reformat. (enum DW_RLE): Add range list entry types. (enum DW_SECT): Update values for DWARF 5. gold/ PR gold/27246 * dwarf_reader.cc (Dwarf_abbrev_table::do_get_abbrev): Handle DW_FORM_implicit_const. (Dwarf_ranges_table::read_ranges_table): Add version parameter; Adjust all callers. Look for .debug_rnglists section if DWARF 5. (Dwarf_ranges_table::read_range_list_v5): New method. (Dwarf_die::read_attributes): Handle new DWARF 5 DW_FORM codes. (Dwarf_die::skip_attributes): Likewise. (Dwarf_info_reader::do_parse): Support DWARF 5 unit header format. (Dwarf_info_reader::read_3bytes_from_pointer): New method. (Sized_dwarf_line_info::Sized_dwarf_line_info): Initialize str_buffer_, str_buffer_start, reloc_map_, line_number_map_. Look for .debug_line_str section. (Sized_dwarf_line_info::read_header_prolog): Support DWARF 5 prolog. (Sized_dwarf_line_info::read_header_tables): Rename to... (Sized_dwarf_line_info::read_header_tables_v2): ... this. (Sized_dwarf_line_info::read_header_tables_v5): New method. (Sized_dwarf_line_info::process_one_opcode): Insert missing "this->". Change advance_line to signed int64_t. (Sized_dwarf_line_info::read_lines): Add endptr parameter; adjust callers. Insert missing "this->". (Sized_dwarf_line_info::read_line_mappings): Support DWARF 5. (Sized_dwarf_line_info::do_addr2line): Add debug code. * dwarf_reader.h (Dwarf_abbrev_table::Attribute): Add implicit_const field. Adjust constructor. (Dwarf_abbrev_table::add_sttribute): Add implicit_const parameter. (Dwarf_ranges_table::read_ranges_table): Add version parameter. (Dwarf_ranges_table::read_range_list_v5): New method. (Dwarf_die): Remove unused attr_off field. (Dwarf_info_reader::Dwarf_info_reader): Initialize unit_type_ field. (Dwarf_info_reader::is_type_unit): New method. (Dwarf_info_reader::read_3bytes_from_pointer): New method. (Dwarf_info_reader::read_range_list): Call read_range_list_v5 for DWARF 5 range lists. (Dwarf_info_reader::is_type_unit_): Remove. (Dwarf_info_reader::unit_type_): New field. (Sized_dwarf_line_info::~Sized_dwarf_line_info): Delete str_buffer_start_. (Sized_dwarf_line_info::read_header_tables): Rename to... (Sized_dwarf_line_info::read_header_tables_v2): ... this. (Sized_dwarf_line_info::read_header_tables_v5): New method. (Sized_dwarf_line_info::read_lines): Add endptr parameter. (Sized_dwarf_line_info::Dwarf_line_infoHeader): Add address_size field. (Sized_dwarf_line_info::str_buffer_): New field. (Sized_dwarf_line_info::str_buffer_end_): New field. (Sized_dwarf_line_info::str_buffer_start_): New field. (Sized_dwarf_line_info::end_of_header_length_): New field. (Sized_dwarf_line_info::end_of_unit_): New field. Upstream-Status: Backport [5cde809b7b9 Add DWARF 5 support in gold.] Signed-off-by: Martin Jansa --- elfcpp/dwarf.h | 89 +++++-- gold/dwarf_reader.cc | 591 +++++++++++++++++++++++++++++++++++++------ gold/dwarf_reader.h | 103 ++++++-- 3 files changed, 654 insertions(+), 129 deletions(-) diff --git a/elfcpp/dwarf.h b/elfcpp/dwarf.h index e24347f8481..c9a9e02369c 100644 --- a/elfcpp/dwarf.h +++ b/elfcpp/dwarf.h @@ -152,35 +152,48 @@ enum DW_EH_PE DW_EH_PE_indirect = 0x80 }; +// Line number table content type codes. + +enum DW_LNCT +{ + DW_LNCT_path = 0x1, + DW_LNCT_directory_index = 0x2, + DW_LNCT_timestamp = 0x3, + DW_LNCT_size = 0x4, + DW_LNCT_MD5 = 0x5, + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff +}; + // Line number opcodes. enum DW_LINE_OPS { - DW_LNS_extended_op = 0, - DW_LNS_copy = 1, - DW_LNS_advance_pc = 2, - DW_LNS_advance_line = 3, - DW_LNS_set_file = 4, - DW_LNS_set_column = 5, - DW_LNS_negate_stmt = 6, - DW_LNS_set_basic_block = 7, - DW_LNS_const_add_pc = 8, - DW_LNS_fixed_advance_pc = 9, + DW_LNS_extended_op = 0x00, + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, // DWARF 3. - DW_LNS_set_prologue_end = 10, - DW_LNS_set_epilogue_begin = 11, - DW_LNS_set_isa = 12 + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c }; // Line number extended opcodes. enum DW_LINE_EXTENDED_OPS { - DW_LNE_end_sequence = 1, - DW_LNE_set_address = 2, - DW_LNE_define_file = 3, + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + DW_LNE_define_file = 0x03, // DWARF4. - DW_LNE_set_discriminator = 4, + DW_LNE_set_discriminator = 0x04, // HP extensions. DW_LNE_HP_negate_is_UV_update = 0x11, DW_LNE_HP_push_context = 0x12, @@ -191,13 +204,15 @@ enum DW_LINE_EXTENDED_OPS DW_LNE_HP_negate_post_semantics = 0x17, DW_LNE_HP_negate_function_exit = 0x18, DW_LNE_HP_negate_front_end_logical = 0x19, - DW_LNE_HP_define_proc = 0x20 + DW_LNE_HP_define_proc = 0x20, + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff }; enum DW_CHILDREN { - DW_CHILDREN_no =0x00, - DW_CHILDREN_yes =0x01 + DW_CHILDREN_no = 0, + DW_CHILDREN_yes = 1 }; // Source language names and codes. @@ -247,20 +262,38 @@ enum DW_LANG DW_LANG_HP_Assembler = 0x8007 }; +// Range list entry kinds in .debug_rnglists* section. + +enum DW_RLE +{ + DW_RLE_end_of_list = 0x00, + DW_RLE_base_addressx = 0x01, + DW_RLE_startx_endx = 0x02, + DW_RLE_startx_length = 0x03, + DW_RLE_offset_pair = 0x04, + DW_RLE_base_address = 0x05, + DW_RLE_start_end = 0x06, + DW_RLE_start_length = 0x07 +}; + // DWARF section identifiers used in the package format. // Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFissionDWP. +// Added (with changes) in DWARF 5. enum DW_SECT { - DW_SECT_INFO = 1, - DW_SECT_TYPES = 2, - DW_SECT_ABBREV = 3, - DW_SECT_LINE = 4, - DW_SECT_LOC = 5, + DW_SECT_INFO = 1, + DW_SECT_ABBREV = 3, + DW_SECT_LINE = 4, + DW_SECT_LOCLISTS = 5, DW_SECT_STR_OFFSETS = 6, - DW_SECT_MACINFO = 7, - DW_SECT_MACRO = 8, - DW_SECT_MAX = DW_SECT_MACRO, + DW_SECT_MACINFO = 7, + DW_SECT_RNGLISTS = 8, + DW_SECT_MAX = DW_SECT_RNGLISTS, + // These were used only for the experimental Fission support in DWARF 4. + DW_SECT_TYPES = 2, + DW_SECT_LOC = 5, + DW_SECT_MACRO = 8 }; } // End namespace elfcpp. diff --git a/gold/dwarf_reader.cc b/gold/dwarf_reader.cc index f0e6b89bde2..83a0114ed39 100644 --- a/gold/dwarf_reader.cc +++ b/gold/dwarf_reader.cc @@ -26,6 +26,7 @@ #include #include +#include "debug.h" #include "elfcpp_swap.h" #include "dwarf.h" #include "object.h" @@ -275,6 +276,14 @@ Dwarf_abbrev_table::do_get_abbrev(unsigned int code) uint64_t form = read_unsigned_LEB_128(this->buffer_pos_, &len); this->buffer_pos_ += len; + // For DW_FORM_implicit_const, read the constant. + int64_t implicit_const = 0; + if (form == elfcpp::DW_FORM_implicit_const) + { + implicit_const = read_signed_LEB_128(this->buffer_pos_, &len); + this->buffer_pos_ += len; + } + // A (0,0) pair terminates the list. if (attr == 0 && form == 0) break; @@ -282,7 +291,7 @@ Dwarf_abbrev_table::do_get_abbrev(unsigned int code) if (attr == elfcpp::DW_AT_sibling) entry->has_sibling_attribute = true; - entry->add_attribute(attr, form); + entry->add_attribute(attr, form, implicit_const); } this->store_abbrev(nextcode, entry); @@ -302,8 +311,16 @@ Dwarf_ranges_table::read_ranges_table( Relobj* object, const unsigned char* symtab, off_t symtab_size, - unsigned int ranges_shndx) + unsigned int ranges_shndx, + unsigned int version) { + const std::string section_name(version < 5 + ? ".debug_ranges" + : ".debug_rnglists"); + const std::string compressed_section_name(version < 5 + ? ".zdebug_ranges" + : ".zdebug_rnglists"); + // If we've already read this abbrev table, return immediately. if (this->ranges_shndx_ > 0 && this->ranges_shndx_ == ranges_shndx) @@ -318,7 +335,7 @@ Dwarf_ranges_table::read_ranges_table( for (unsigned int i = 1; i < object->shnum(); ++i) { std::string name = object->section_name(i); - if (name == ".debug_ranges" || name == ".zdebug_ranges") + if (name == section_name || name == compressed_section_name) { ranges_shndx = i; this->output_section_offset_ = object->output_section_offset(i); @@ -393,7 +410,7 @@ Dwarf_ranges_table::read_range_list( { Dwarf_range_list* ranges; - if (!this->read_ranges_table(object, symtab, symtab_size, ranges_shndx)) + if (!this->read_ranges_table(object, symtab, symtab_size, ranges_shndx, 4)) return NULL; // Correct the offset. For incremental update links, we have a @@ -459,6 +476,125 @@ Dwarf_ranges_table::read_range_list( return ranges; } +// Read a DWARF 5 range list from section RANGES_SHNDX at offset RANGES_OFFSET. + +Dwarf_range_list* +Dwarf_ranges_table::read_range_list_v5( + Relobj* object, + const unsigned char* symtab, + off_t symtab_size, + unsigned int addr_size, + unsigned int ranges_shndx, + off_t offset) +{ + Dwarf_range_list* ranges; + + if (!this->read_ranges_table(object, symtab, symtab_size, ranges_shndx, 5)) + return NULL; + + ranges = new Dwarf_range_list(); + off_t base = 0; + unsigned int shndx0 = 0; + + // Correct the offset. For incremental update links, we have a + // relocated offset that is relative to the output section, but + // here we need an offset relative to the input section. + offset -= this->output_section_offset_; + + // Read the range list at OFFSET. + const unsigned char* prle = this->ranges_buffer_ + offset; + while (prle < this->ranges_buffer_end_) + { + off_t start; + off_t end; + unsigned int shndx1 = 0; + unsigned int shndx2 = 0; + size_t len; + + // Read the entry type. + unsigned int rle_type = *prle++; + offset += 1; + + if (rle_type == elfcpp::DW_RLE_end_of_list) + break; + + switch (rle_type) + { + case elfcpp::DW_RLE_base_address: + if (addr_size == 4) + base = this->dwinfo_->read_from_pointer<32>(prle); + else + base = this->dwinfo_->read_from_pointer<64>(prle); + if (this->ranges_reloc_mapper_ != NULL) + shndx0 = this->lookup_reloc(offset, &base); + prle += addr_size; + offset += addr_size; + break; + + case elfcpp::DW_RLE_offset_pair: + start = read_unsigned_LEB_128(prle, &len); + prle += len; + offset += len; + end = read_unsigned_LEB_128(prle, &len); + prle += len; + offset += len; + if (shndx0 == 0 || object->is_section_included(shndx0)) + ranges->add(shndx0, base + start, base + end); + break; + + case elfcpp::DW_RLE_start_end: + if (addr_size == 4) + { + start = this->dwinfo_->read_from_pointer<32>(prle); + end = this->dwinfo_->read_from_pointer<32>(prle + 4); + } + else + { + start = this->dwinfo_->read_from_pointer<64>(prle); + end = this->dwinfo_->read_from_pointer<64>(prle + 8); + } + if (this->ranges_reloc_mapper_ != NULL) + { + shndx1 = this->lookup_reloc(offset, &start); + shndx2 = this->lookup_reloc(offset + addr_size, &end); + if (shndx1 != shndx2) + gold_warning(_("%s: DWARF info may be corrupt; offsets in a " + "range list entry are in different sections"), + object->name().c_str()); + } + prle += addr_size * 2; + offset += addr_size * 2; + if (shndx1 == 0 || object->is_section_included(shndx1)) + ranges->add(shndx1, start, end); + break; + + case elfcpp::DW_RLE_start_length: + if (addr_size == 4) + start = this->dwinfo_->read_from_pointer<32>(prle); + else + start = this->dwinfo_->read_from_pointer<64>(prle); + if (this->ranges_reloc_mapper_ != NULL) + shndx1 = this->lookup_reloc(offset, &start); + prle += addr_size; + offset += addr_size; + end = start + read_unsigned_LEB_128(prle, &len); + prle += len; + offset += len; + if (shndx1 == 0 || object->is_section_included(shndx1)) + ranges->add(shndx1, start, end); + break; + + default: + gold_warning(_("%s: DWARF range list contains " + "unsupported entry type (%d)"), + object->name().c_str(), rle_type); + break; + } + } + + return ranges; +} + // Look for a relocation at offset OFF in the range table, // and return the section index and offset of the target. @@ -709,7 +845,13 @@ Dwarf_die::read_attributes() case elfcpp::DW_FORM_flag_present: attr_value.val.intval = 1; break; + case elfcpp::DW_FORM_implicit_const: + attr_value.val.intval = + this->abbrev_code_->attributes[i].implicit_const; + break; case elfcpp::DW_FORM_strp: + case elfcpp::DW_FORM_strp_sup: + case elfcpp::DW_FORM_line_strp: { off_t str_off; if (this->dwinfo_->offset_size() == 4) @@ -722,6 +864,26 @@ Dwarf_die::read_attributes() attr_value.val.refval = str_off; break; } + case elfcpp::DW_FORM_strx: + case elfcpp::DW_FORM_GNU_str_index: + attr_value.val.uintval = read_unsigned_LEB_128(pattr, &len); + pattr += len; + break; + case elfcpp::DW_FORM_strx1: + attr_value.val.uintval = *pattr++; + break; + case elfcpp::DW_FORM_strx2: + attr_value.val.uintval = + this->dwinfo_->read_from_pointer<16>(&pattr); + break; + case elfcpp::DW_FORM_strx3: + attr_value.val.uintval = + this->dwinfo_->read_3bytes_from_pointer(&pattr); + break; + case elfcpp::DW_FORM_strx4: + attr_value.val.uintval = + this->dwinfo_->read_from_pointer<32>(&pattr); + break; case elfcpp::DW_FORM_sec_offset: { off_t sec_off; @@ -747,7 +909,6 @@ Dwarf_die::read_attributes() this->dwinfo_->lookup_reloc(attr_off, &sec_off); attr_value.aux.shndx = shndx; attr_value.val.refval = sec_off; - ref_form = true; break; } case elfcpp::DW_FORM_ref_addr: @@ -815,6 +976,7 @@ Dwarf_die::read_attributes() break; } case elfcpp::DW_FORM_ref4: + case elfcpp::DW_FORM_ref_sup4: { off_t sec_off; sec_off = this->dwinfo_->read_from_pointer<32>(&pattr); @@ -835,11 +997,20 @@ Dwarf_die::read_attributes() attr_value.val.intval = sec_off; break; } + case elfcpp::DW_FORM_data16: + { + // For now, treat this as a 16-byte block. + attr_value.val.blockval = pattr; + attr_value.aux.blocklen = 16; + pattr += 16; + break; + } case elfcpp::DW_FORM_ref_sig8: attr_value.val.uintval = this->dwinfo_->read_from_pointer<64>(&pattr); break; case elfcpp::DW_FORM_ref8: + case elfcpp::DW_FORM_ref_sup8: { off_t sec_off; sec_off = this->dwinfo_->read_from_pointer<64>(&pattr); @@ -856,11 +1027,29 @@ Dwarf_die::read_attributes() pattr += len; break; case elfcpp::DW_FORM_udata: + attr_value.val.uintval = read_unsigned_LEB_128(pattr, &len); + pattr += len; + break; + case elfcpp::DW_FORM_addrx: case elfcpp::DW_FORM_GNU_addr_index: - case elfcpp::DW_FORM_GNU_str_index: attr_value.val.uintval = read_unsigned_LEB_128(pattr, &len); pattr += len; break; + case elfcpp::DW_FORM_addrx1: + attr_value.val.uintval = *pattr++; + break; + case elfcpp::DW_FORM_addrx2: + attr_value.val.uintval = + this->dwinfo_->read_from_pointer<16>(&pattr); + break; + case elfcpp::DW_FORM_addrx3: + attr_value.val.uintval = + this->dwinfo_->read_3bytes_from_pointer(&pattr); + break; + case elfcpp::DW_FORM_addrx4: + attr_value.val.uintval = + this->dwinfo_->read_from_pointer<32>(&pattr); + break; case elfcpp::DW_FORM_sdata: attr_value.val.intval = read_signed_LEB_128(pattr, &len); pattr += len; @@ -870,6 +1059,11 @@ Dwarf_die::read_attributes() len = strlen(attr_value.val.stringval); pattr += len + 1; break; + case elfcpp::DW_FORM_loclistx: + case elfcpp::DW_FORM_rnglistx: + attr_value.val.uintval = read_unsigned_LEB_128(pattr, &len); + pattr += len; + break; default: return false; } @@ -954,9 +1148,12 @@ Dwarf_die::skip_attributes() switch(form) { case elfcpp::DW_FORM_flag_present: + case elfcpp::DW_FORM_implicit_const: break; case elfcpp::DW_FORM_strp: case elfcpp::DW_FORM_sec_offset: + case elfcpp::DW_FORM_strp_sup: + case elfcpp::DW_FORM_line_strp: pattr += this->dwinfo_->offset_size(); break; case elfcpp::DW_FORM_addr: @@ -993,23 +1190,42 @@ Dwarf_die::skip_attributes() case elfcpp::DW_FORM_data1: case elfcpp::DW_FORM_ref1: case elfcpp::DW_FORM_flag: + case elfcpp::DW_FORM_strx1: + case elfcpp::DW_FORM_addrx1: pattr += 1; break; case elfcpp::DW_FORM_data2: case elfcpp::DW_FORM_ref2: + case elfcpp::DW_FORM_strx2: + case elfcpp::DW_FORM_addrx2: pattr += 2; break; + case elfcpp::DW_FORM_strx3: + case elfcpp::DW_FORM_addrx3: + pattr += 3; + break; case elfcpp::DW_FORM_data4: case elfcpp::DW_FORM_ref4: + case elfcpp::DW_FORM_ref_sup4: + case elfcpp::DW_FORM_strx4: + case elfcpp::DW_FORM_addrx4: pattr += 4; break; case elfcpp::DW_FORM_data8: case elfcpp::DW_FORM_ref8: case elfcpp::DW_FORM_ref_sig8: + case elfcpp::DW_FORM_ref_sup8: pattr += 8; break; + case elfcpp::DW_FORM_data16: + pattr += 16; + break; case elfcpp::DW_FORM_ref_udata: case elfcpp::DW_FORM_udata: + case elfcpp::DW_FORM_addrx: + case elfcpp::DW_FORM_strx: + case elfcpp::DW_FORM_loclistx: + case elfcpp::DW_FORM_rnglistx: case elfcpp::DW_FORM_GNU_addr_index: case elfcpp::DW_FORM_GNU_str_index: read_unsigned_LEB_128(pattr, &len); @@ -1313,6 +1529,13 @@ Dwarf_info_reader::do_parse() elfcpp::Swap_unaligned<16, big_endian>::readval(pinfo); pinfo += 2; + // DWARF 5: Read the unit type (1 byte) and address size (1 byte). + if (this->cu_version_ >= 5) + { + this->unit_type_ = *pinfo++; + this->address_size_ = *pinfo++; + } + // Read debug_abbrev_offset (4 or 8 bytes). if (this->offset_size_ == 4) abbrev_offset = elfcpp::Swap_unaligned<32, big_endian>::readval(pinfo); @@ -1333,13 +1556,14 @@ Dwarf_info_reader::do_parse() } pinfo += this->offset_size_; - // Read address_size (1 byte). - this->address_size_ = *pinfo++; + // DWARF 2-4: Read address_size (1 byte). + if (this->cu_version_ < 5) + this->address_size_ = *pinfo++; // For type units, read the two extra fields. uint64_t signature = 0; off_t type_offset = 0; - if (this->is_type_unit_) + if (this->is_type_unit()) { if (!this->check_buffer(pinfo + 8 + this->offset_size_)) break; @@ -1369,7 +1593,7 @@ Dwarf_info_reader::do_parse() if (root_die.tag() != 0) { // Visit the CU or TU. - if (this->is_type_unit_) + if (this->is_type_unit()) this->visit_type_unit(section_offset + this->cu_offset_, cu_end - cu_start, type_offset, signature, &root_die); @@ -1460,6 +1684,19 @@ Dwarf_info_reader::read_from_pointer(const unsigned char** source) return return_value; } +// Read a 3-byte integer. Update SOURCE after read. +inline typename elfcpp::Valtype_base<32>::Valtype +Dwarf_info_reader::read_3bytes_from_pointer(const unsigned char** source) +{ + typename elfcpp::Valtype_base<32>::Valtype return_value; + if (this->object_->is_big_endian()) + return_value = ((*source)[0] << 16) | ((*source)[1] << 8) | (*source)[2]; + else + return_value = ((*source)[2] << 16) | ((*source)[1] << 8) | (*source)[0]; + *source += 3; + return return_value; +} + // Look for a relocation at offset ATTR_OFF in the dwarf info, // and return the section index and offset of the target. @@ -1561,27 +1798,40 @@ Sized_dwarf_line_info::Sized_dwarf_line_info( Object* object, unsigned int read_shndx) : data_valid_(false), buffer_(NULL), buffer_start_(NULL), + str_buffer_(NULL), str_buffer_start_(NULL), reloc_mapper_(NULL), symtab_buffer_(NULL), directories_(), files_(), - current_header_index_(-1) + current_header_index_(-1), reloc_map_(), line_number_map_() { - unsigned int debug_shndx; + unsigned int debug_line_shndx = 0; + unsigned int debug_line_str_shndx = 0; - for (debug_shndx = 1; debug_shndx < object->shnum(); ++debug_shndx) + for (unsigned int i = 1; i < object->shnum(); ++i) { + section_size_type buffer_size; + bool is_new = false; + // FIXME: do this more efficiently: section_name() isn't super-fast - std::string name = object->section_name(debug_shndx); + std::string name = object->section_name(i); if (name == ".debug_line" || name == ".zdebug_line") { - section_size_type buffer_size; - bool is_new = false; - this->buffer_ = object->decompressed_section_contents(debug_shndx, - &buffer_size, - &is_new); + this->buffer_ = + object->decompressed_section_contents(i, &buffer_size, &is_new); if (is_new) this->buffer_start_ = this->buffer_; this->buffer_end_ = this->buffer_ + buffer_size; - break; + debug_line_shndx = i; + } + else if (name == ".debug_line_str" || name == ".zdebug_line_str") + { + this->str_buffer_ = + object->decompressed_section_contents(i, &buffer_size, &is_new); + if (is_new) + this->str_buffer_start_ = this->str_buffer_; + this->str_buffer_end_ = this->str_buffer_ + buffer_size; + debug_line_str_shndx = i; } + if (debug_line_shndx > 0 && debug_line_str_shndx > 0) + break; } if (this->buffer_ == NULL) return; @@ -1594,7 +1844,7 @@ Sized_dwarf_line_info::Sized_dwarf_line_info( unsigned int reloc_sh_type = object->section_type(i); if ((reloc_sh_type == elfcpp::SHT_REL || reloc_sh_type == elfcpp::SHT_RELA) - && object->section_info(i) == debug_shndx) + && object->section_info(i) == debug_line_shndx) { reloc_shndx = i; this->track_relocs_type_ = reloc_sh_type; @@ -1640,65 +1890,80 @@ Sized_dwarf_line_info::read_header_prolog( uint32_t initial_length = elfcpp::Swap_unaligned<32, big_endian>::readval(lineptr); lineptr += 4; - // In DWARF2/3, if the initial length is all 1 bits, then the offset + // In DWARF, if the initial length is all 1 bits, then the offset // size is 8 and we need to read the next 8 bytes for the real length. if (initial_length == 0xffffffff) { - header_.offset_size = 8; + this->header_.offset_size = 8; initial_length = elfcpp::Swap_unaligned<64, big_endian>::readval(lineptr); lineptr += 8; } else - header_.offset_size = 4; + this->header_.offset_size = 4; - header_.total_length = initial_length; + this->header_.total_length = initial_length; - gold_assert(lineptr + header_.total_length <= buffer_end_); + this->end_of_unit_ = lineptr + initial_length; + gold_assert(this->end_of_unit_ <= buffer_end_); - header_.version = elfcpp::Swap_unaligned<16, big_endian>::readval(lineptr); + this->header_.version = + elfcpp::Swap_unaligned<16, big_endian>::readval(lineptr); lineptr += 2; - // Skip address size and segment selector for DWARF5. - if (header_.version >= 5) - lineptr += 2; + // We can only read versions 2-5 of the DWARF line number table. + // For other versions, just skip the entire line number table. + if (this->header_.version < 2 || this->header_.version > 5) + return this->end_of_unit_; - if (header_.offset_size == 4) - header_.prologue_length = elfcpp::Swap_unaligned<32, big_endian>::readval(lineptr); + // DWARF 5 only: address size and segment selector. + if (this->header_.version >= 5) + { + this->header_.address_size = *lineptr; + // We ignore the segment selector. + lineptr += 2; + } + + if (this->header_.offset_size == 4) + this->header_.prologue_length = + elfcpp::Swap_unaligned<32, big_endian>::readval(lineptr); else - header_.prologue_length = elfcpp::Swap_unaligned<64, big_endian>::readval(lineptr); - lineptr += header_.offset_size; + this->header_.prologue_length = + elfcpp::Swap_unaligned<64, big_endian>::readval(lineptr); + lineptr += this->header_.offset_size; - header_.min_insn_length = *lineptr; + this->end_of_header_length_ = lineptr; + + this->header_.min_insn_length = *lineptr; lineptr += 1; - if (header_.version < 4) - header_.max_ops_per_insn = 1; + if (this->header_.version < 4) + this->header_.max_ops_per_insn = 1; else { // DWARF 4 added the maximum_operations_per_instruction field. - header_.max_ops_per_insn = *lineptr; + this->header_.max_ops_per_insn = *lineptr; lineptr += 1; // TODO: Add support for values other than 1. - gold_assert(header_.max_ops_per_insn == 1); + gold_assert(this->header_.max_ops_per_insn == 1); } - header_.default_is_stmt = *lineptr; + this->header_.default_is_stmt = *lineptr; lineptr += 1; - header_.line_base = *reinterpret_cast(lineptr); + this->header_.line_base = *reinterpret_cast(lineptr); lineptr += 1; - header_.line_range = *lineptr; + this->header_.line_range = *lineptr; lineptr += 1; - header_.opcode_base = *lineptr; + this->header_.opcode_base = *lineptr; lineptr += 1; - header_.std_opcode_lengths.resize(header_.opcode_base + 1); - header_.std_opcode_lengths[0] = 0; - for (int i = 1; i < header_.opcode_base; i++) + this->header_.std_opcode_lengths.resize(this->header_.opcode_base + 1); + this->header_.std_opcode_lengths[0] = 0; + for (int i = 1; i < this->header_.opcode_base; i++) { - header_.std_opcode_lengths[i] = *lineptr; + this->header_.std_opcode_lengths[i] = *lineptr; lineptr += 1; } @@ -1707,10 +1972,11 @@ Sized_dwarf_line_info::read_header_prolog( // The header for a debug_line section is mildly complicated, because // the line info is very tightly encoded. +// This routine is for DWARF versions 2, 3, and 4. template const unsigned char* -Sized_dwarf_line_info::read_header_tables( +Sized_dwarf_line_info::read_header_tables_v2( const unsigned char* lineptr) { ++this->current_header_index_; @@ -1775,6 +2041,169 @@ Sized_dwarf_line_info::read_header_tables( return lineptr; } +// This routine is for DWARF version 5. + +template +const unsigned char* +Sized_dwarf_line_info::read_header_tables_v5( + const unsigned char* lineptr) +{ + size_t len; + + ++this->current_header_index_; + + gold_assert(static_cast(this->directories_.size()) + == this->current_header_index_); + gold_assert(static_cast(this->files_.size()) + == this->current_header_index_); + + // Read the directory list. + unsigned int format_count = *lineptr; + lineptr += 1; + + unsigned int *types = new unsigned int[format_count]; + unsigned int *forms = new unsigned int[format_count]; + + for (unsigned int i = 0; i < format_count; i++) + { + types[i] = read_unsigned_LEB_128(lineptr, &len); + lineptr += len; + forms[i] = read_unsigned_LEB_128(lineptr, &len); + lineptr += len; + } + + uint64_t entry_count = read_unsigned_LEB_128(lineptr, &len); + lineptr += len; + this->directories_.push_back(std::vector(0)); + std::vector& dir_list = this->directories_.back(); + + for (unsigned int j = 0; j < entry_count; j++) + { + std::string dirname; + + for (unsigned int i = 0; i < format_count; i++) + { + if (types[i] == elfcpp::DW_LNCT_path) + { + if (forms[i] == elfcpp::DW_FORM_string) + { + dirname = reinterpret_cast(lineptr); + lineptr += dirname.size() + 1; + } + else if (forms[i] == elfcpp::DW_FORM_line_strp) + { + uint64_t offset; + if (this->header_.offset_size == 4) + offset = + elfcpp::Swap_unaligned<32, big_endian>::readval(lineptr); + else + offset = + elfcpp::Swap_unaligned<64, big_endian>::readval(lineptr); + typename Reloc_map::const_iterator it + = this->reloc_map_.find(lineptr - this->buffer_); + if (it != reloc_map_.end()) + { + if (this->track_relocs_type_ == elfcpp::SHT_RELA) + offset = 0; + offset += it->second.second; + } + lineptr += this->header_.offset_size; + dirname = reinterpret_cast(this->str_buffer_ + + offset); + } + else + return lineptr; + } + else + return lineptr; + } + dir_list.push_back(dirname); + } + + delete[] types; + delete[] forms; + + // Read the filenames list. + format_count = *lineptr; + lineptr += 1; + + types = new unsigned int[format_count]; + forms = new unsigned int[format_count]; + + for (unsigned int i = 0; i < format_count; i++) + { + types[i] = read_unsigned_LEB_128(lineptr, &len); + lineptr += len; + forms[i] = read_unsigned_LEB_128(lineptr, &len); + lineptr += len; + } + + entry_count = read_unsigned_LEB_128(lineptr, &len); + lineptr += len; + this->files_.push_back( + std::vector >(0)); + std::vector >& file_list = this->files_.back(); + + for (unsigned int j = 0; j < entry_count; j++) + { + const char* path = NULL; + int dirindex = 0; + + for (unsigned int i = 0; i < format_count; i++) + { + if (types[i] == elfcpp::DW_LNCT_path) + { + if (forms[i] == elfcpp::DW_FORM_string) + { + path = reinterpret_cast(lineptr); + lineptr += strlen(path) + 1; + } + else if (forms[i] == elfcpp::DW_FORM_line_strp) + { + uint64_t offset; + if (this->header_.offset_size == 4) + offset = elfcpp::Swap_unaligned<32, big_endian>::readval(lineptr); + else + offset = elfcpp::Swap_unaligned<64, big_endian>::readval(lineptr); + typename Reloc_map::const_iterator it + = this->reloc_map_.find(lineptr - this->buffer_); + if (it != reloc_map_.end()) + { + if (this->track_relocs_type_ == elfcpp::SHT_RELA) + offset = 0; + offset += it->second.second; + } + lineptr += this->header_.offset_size; + path = reinterpret_cast(this->str_buffer_ + + offset); + } + else + return lineptr; + } + else if (types[i] == elfcpp::DW_LNCT_directory_index) + { + if (forms[i] == elfcpp::DW_FORM_udata) + { + dirindex = read_unsigned_LEB_128(lineptr, &len); + lineptr += len; + } + else + return lineptr; + } + else + return lineptr; + } + gold_debug(DEBUG_LOCATION, "File %3d: %s", + static_cast(file_list.size()), path); + file_list.push_back(std::make_pair(dirindex, path)); + } + + delete[] types; + delete[] forms; + + return lineptr; +} + // Process a single opcode in the .debug.line structure. template @@ -1790,15 +2219,15 @@ Sized_dwarf_line_info::process_one_opcode( // If the opcode is great than the opcode_base, it is a special // opcode. Most line programs consist mainly of special opcodes. - if (opcode >= header_.opcode_base) + if (opcode >= this->header_.opcode_base) { - opcode -= header_.opcode_base; - const int advance_address = ((opcode / header_.line_range) - * header_.min_insn_length); + opcode -= this->header_.opcode_base; + const int advance_address = ((opcode / this->header_.line_range) + * this->header_.min_insn_length); lsm->address += advance_address; - const int advance_line = ((opcode % header_.line_range) - + header_.line_base); + const int advance_line = ((opcode % this->header_.line_range) + + this->header_.line_base); lsm->line_num += advance_line; lsm->basic_block = true; *len = oplen; @@ -1818,13 +2247,13 @@ Sized_dwarf_line_info::process_one_opcode( const uint64_t advance_address = read_unsigned_LEB_128(start, &templen); oplen += templen; - lsm->address += header_.min_insn_length * advance_address; + lsm->address += this->header_.min_insn_length * advance_address; } break; case elfcpp::DW_LNS_advance_line: { - const uint64_t advance_line = read_signed_LEB_128(start, &templen); + const int64_t advance_line = read_signed_LEB_128(start, &templen); oplen += templen; lsm->line_num += advance_line; } @@ -1865,9 +2294,9 @@ Sized_dwarf_line_info::process_one_opcode( case elfcpp::DW_LNS_const_add_pc: { - const int advance_address = (header_.min_insn_length - * ((255 - header_.opcode_base) - / header_.line_range)); + const int advance_address = (this->header_.min_insn_length + * ((255 - this->header_.opcode_base) + / this->header_.line_range)); lsm->address += advance_address; } break; @@ -1950,7 +2379,7 @@ Sized_dwarf_line_info::process_one_opcode( default: { // Ignore unknown opcode silently - for (int i = 0; i < header_.std_opcode_lengths[opcode]; i++) + for (int i = 0; i < this->header_.std_opcode_lengths[opcode]; i++) { size_t templen; read_unsigned_LEB_128(start, &templen); @@ -1970,28 +2399,24 @@ Sized_dwarf_line_info::process_one_opcode( template unsigned const char* Sized_dwarf_line_info::read_lines(unsigned const char* lineptr, + unsigned const char* endptr, unsigned int shndx) { struct LineStateMachine lsm; - // LENGTHSTART is the place the length field is based on. It is the - // point in the header after the initial length field. - const unsigned char* lengthstart = buffer_; - - // In 64 bit dwarf, the initial length is 12 bytes, because of the - // 0xffffffff at the start. - if (header_.offset_size == 8) - lengthstart += 12; - else - lengthstart += 4; - - while (lineptr < lengthstart + header_.total_length) + while (lineptr < endptr) { - ResetLineStateMachine(&lsm, header_.default_is_stmt); + ResetLineStateMachine(&lsm, this->header_.default_is_stmt); while (!lsm.end_sequence) { size_t oplength; + + if (lineptr >= endptr) + break; + bool add_line = this->process_one_opcode(lineptr, &lsm, &oplength); + lineptr += oplength; + if (add_line && (shndx == -1U || lsm.shndx == -1U || shndx == lsm.shndx)) { @@ -2012,11 +2437,10 @@ Sized_dwarf_line_info::read_lines(unsigned const char* lineptr map.back().last_line_for_offset = false; map.push_back(entry); } - lineptr += oplength; } } - return lengthstart + header_.total_length; + return endptr; } // Read the relocations into a Reloc_map. @@ -2057,9 +2481,17 @@ Sized_dwarf_line_info::read_line_mappings(unsigned int shndx) { const unsigned char* lineptr = this->buffer_; lineptr = this->read_header_prolog(lineptr); - lineptr = this->read_header_tables(lineptr); - lineptr = this->read_lines(lineptr, shndx); - this->buffer_ = lineptr; + if (this->header_.version >= 2 && this->header_.version <= 4) + { + lineptr = this->read_header_tables_v2(lineptr); + lineptr = this->read_lines(lineptr, this->end_of_unit_, shndx); + } + else if (this->header_.version == 5) + { + lineptr = this->read_header_tables_v5(lineptr); + lineptr = this->read_lines(lineptr, this->end_of_unit_, shndx); + } + this->buffer_ = this->end_of_unit_; } // Sort the lines numbers, so addr2line can use binary search. @@ -2215,6 +2647,9 @@ Sized_dwarf_line_info::do_addr2line( off_t offset, std::vector* other_lines) { + gold_debug(DEBUG_LOCATION, "do_addr2line: shndx %u offset %08x", + shndx, static_cast(offset)); + if (this->data_valid_ == false) return ""; diff --git a/gold/dwarf_reader.h b/gold/dwarf_reader.h index 80b2231327c..921a1f7c876 100644 --- a/gold/dwarf_reader.h +++ b/gold/dwarf_reader.h @@ -173,11 +173,12 @@ class Dwarf_abbrev_table // An attribute list entry. struct Attribute { - Attribute(unsigned int a, unsigned int f) - : attr(a), form(f) + Attribute(unsigned int a, unsigned int f, int c) + : attr(a), form(f), implicit_const(c) { } unsigned int attr; unsigned int form; + int implicit_const; }; // An abbrev code entry. @@ -190,9 +191,9 @@ class Dwarf_abbrev_table } void - add_attribute(unsigned int attr, unsigned int form) + add_attribute(unsigned int attr, unsigned int form, int implicit_const) { - this->attributes.push_back(Attribute(attr, form)); + this->attributes.push_back(Attribute(attr, form, implicit_const)); } // The DWARF tag. @@ -349,14 +350,15 @@ class Dwarf_ranges_table delete this->ranges_reloc_mapper_; } - // Read the ranges table from an object file. + // Fetch the contents of the ranges table from an object file. bool read_ranges_table(Relobj* object, const unsigned char* symtab, off_t symtab_size, - unsigned int ranges_shndx); + unsigned int ranges_shndx, + unsigned int version); - // Read the range table from an object file. + // Read the DWARF 2/3/4 range table. Dwarf_range_list* read_range_list(Relobj* object, const unsigned char* symtab, @@ -365,6 +367,15 @@ class Dwarf_ranges_table unsigned int ranges_shndx, off_t ranges_offset); + // Read the DWARF 5 rnglists table. + Dwarf_range_list* + read_range_list_v5(Relobj* object, + const unsigned char* symtab, + off_t symtab_size, + unsigned int address_size, + unsigned int ranges_shndx, + off_t ranges_offset); + // Look for a relocation at offset OFF in the range table, // and return the section index and offset of the target. unsigned int @@ -490,8 +501,6 @@ class Dwarf_die unsigned int shndx; // Block length for block forms. unsigned int blocklen; - // Attribute offset for DW_FORM_strp. - unsigned int attr_off; } aux; }; @@ -684,6 +693,10 @@ class Dwarf_die // calls the various visit_xxx() methods for each header. Clients // should derive a new class from this one and implement the // visit_compilation_unit() and visit_type_unit() functions. +// IS_TYPE_UNIT is true if we are reading from a .debug_types section, +// which is used only in DWARF 4. For DWARF 5, it will be false, +// and we will determine whether it's a type init when we parse the +// header. class Dwarf_info_reader { @@ -695,7 +708,7 @@ class Dwarf_info_reader unsigned int shndx, unsigned int reloc_shndx, unsigned int reloc_type) - : is_type_unit_(is_type_unit), object_(object), symtab_(symtab), + : object_(object), symtab_(symtab), symtab_size_(symtab_size), shndx_(shndx), reloc_shndx_(reloc_shndx), reloc_type_(reloc_type), abbrev_shndx_(0), string_shndx_(0), buffer_(NULL), buffer_end_(NULL), cu_offset_(0), cu_length_(0), @@ -703,7 +716,12 @@ class Dwarf_info_reader abbrev_table_(), ranges_table_(this), reloc_mapper_(NULL), string_buffer_(NULL), string_buffer_end_(NULL), owns_string_buffer_(false), string_output_section_offset_(0) - { } + { + // For DWARF 4, we infer the unit type from the section name. + // For DWARF 5, we will read this from the unit header. + this->unit_type_ = + (is_type_unit ? elfcpp::DW_UT_type : elfcpp::DW_UT_compile); + } virtual ~Dwarf_info_reader() @@ -714,6 +732,13 @@ class Dwarf_info_reader delete[] this->string_buffer_; } + bool + is_type_unit() const + { + return (this->unit_type_ == elfcpp::DW_UT_type + || this->unit_type_ == elfcpp::DW_UT_split_type); + } + // Begin parsing the debug info. This calls visit_compilation_unit() // or visit_type_unit() for each compilation or type unit found in the // section, and visit_die() for each top-level DIE. @@ -745,6 +770,9 @@ class Dwarf_info_reader inline typename elfcpp::Valtype_base::Valtype read_from_pointer(const unsigned char** source); + inline typename elfcpp::Valtype_base<32>::Valtype + read_3bytes_from_pointer(const unsigned char** source); + // Look for a relocation at offset ATTR_OFF in the dwarf info, // and return the section index and offset of the target. unsigned int @@ -818,12 +846,20 @@ class Dwarf_info_reader Dwarf_range_list* read_range_list(unsigned int ranges_shndx, off_t ranges_offset) { - return this->ranges_table_.read_range_list(this->object_, - this->symtab_, - this->symtab_size_, - this->address_size_, - ranges_shndx, - ranges_offset); + if (this->cu_version_ < 5) + return this->ranges_table_.read_range_list(this->object_, + this->symtab_, + this->symtab_size_, + this->address_size_, + ranges_shndx, + ranges_offset); + else + return this->ranges_table_.read_range_list_v5(this->object_, + this->symtab_, + this->symtab_size_, + this->address_size_, + ranges_shndx, + ranges_offset); } // Return the object. @@ -873,8 +909,8 @@ class Dwarf_info_reader bool do_read_string_table(unsigned int string_shndx); - // True if this is a type unit; false for a compilation unit. - bool is_type_unit_; + // The unit type (DW_UT_xxx). + unsigned int unit_type_; // The object containing the .debug_info or .debug_types input section. Relobj* object_; // The ELF symbol table. @@ -1008,6 +1044,8 @@ class Sized_dwarf_line_info : public Dwarf_line_info { if (this->buffer_start_ != NULL) delete[] this->buffer_start_; + if (this->str_buffer_start_ != NULL) + delete[] this->str_buffer_start_; } private: @@ -1030,19 +1068,23 @@ class Sized_dwarf_line_info : public Dwarf_line_info void read_relocs(); - // Reads the DWARF2/3 header for this line info. Each takes as input + // Reads the DWARF header for this line info. Each takes as input // a starting buffer position, and returns the ending position. const unsigned char* read_header_prolog(const unsigned char* lineptr); const unsigned char* - read_header_tables(const unsigned char* lineptr); + read_header_tables_v2(const unsigned char* lineptr); + + const unsigned char* + read_header_tables_v5(const unsigned char* lineptr); - // Reads the DWARF2/3 line information. If shndx is non-negative, + // Reads the DWARF line information. If shndx is non-negative, // discard all line information that doesn't pertain to the given // section. const unsigned char* - read_lines(const unsigned char* lineptr, unsigned int shndx); + read_lines(const unsigned char* lineptr, const unsigned char* endptr, + unsigned int shndx); // Process a single line info opcode at START using the state // machine at LSM. Return true if we should define a line using the @@ -1069,6 +1111,7 @@ class Sized_dwarf_line_info : public Dwarf_line_info { off_t total_length; int version; + int address_size; off_t prologue_length; int min_insn_length; // insn stands for instruction int max_ops_per_insn; // Added in DWARF-4. @@ -1089,6 +1132,20 @@ class Sized_dwarf_line_info : public Dwarf_line_info // of the buffer. const unsigned char* buffer_start_; + // str_buffer is the buffer for the line table strings. + const unsigned char* str_buffer_; + const unsigned char* str_buffer_end_; + // If the buffer was allocated temporarily, and therefore must be + // deallocated in the dtor, this contains a pointer to the start + // of the buffer. + const unsigned char* str_buffer_start_; + + // Pointer to the end of the header_length field (aka prologue_length). + const unsigned char* end_of_header_length_; + + // Pointer to the end of the current compilation unit. + const unsigned char* end_of_unit_; + // This has relocations that point into buffer. Sized_elf_reloc_mapper* reloc_mapper_; // The type of the reloc section in track_relocs_--SHT_REL or SHT_RELA.