| Apply additional patch by David Blaikie to update the gold dwp tool |
| to handle DWARF v5. This patch teaches dwp to read DW_FORM_strx (and |
| its variations), so it can read and use the str_offsets_base value to |
| read the .dwo file names out of the compilation unit DIEs. |
| |
| (see https://critique-ng.corp.google.com/cl/348045503 for original patch) |
| (also see https://critique-ng.corp.google.com/cl/350363411) |
| |
| This patch created for Chrome OS by Caroline Tice. |
| Date: 17-Dec-2020 |
| diff --git a/elfcpp/elfcpp_swap.h b/elfcpp/elfcpp_swap.h |
| index 8906029f..898e4852 100644 |
| --- a/elfcpp/elfcpp_swap.h |
| +++ b/elfcpp/elfcpp_swap.h |
| @@ -125,6 +125,13 @@ struct Valtype_base<16> |
| typedef int16_t Signed_valtype; |
| }; |
| |
| +template<> |
| +struct Valtype_base<24> |
| +{ |
| + typedef uint16_t Valtype; |
| + typedef int16_t Signed_valtype; |
| +}; |
| + |
| template<> |
| struct Valtype_base<32> |
| { |
| @@ -324,6 +331,46 @@ struct Swap_unaligned<16, true> |
| } |
| }; |
| |
| +template<> |
| +struct Swap_unaligned<24, false> |
| +{ |
| + typedef Valtype_base<24>::Valtype Valtype; |
| + |
| + static inline Valtype |
| + readval(const unsigned char* wv) |
| + { |
| + return (wv[2] << 16) | (wv[1] << 8) | wv[0]; |
| + } |
| + |
| + static inline void |
| + writeval(unsigned char* wv, Valtype v) |
| + { |
| + wv[0] = v >> 16; |
| + wv[1] = v >> 8; |
| + wv[2] = v; |
| + } |
| +}; |
| + |
| +template<> |
| +struct Swap_unaligned<24, true> |
| +{ |
| + typedef Valtype_base<24>::Valtype Valtype; |
| + |
| + static inline Valtype |
| + readval(const unsigned char* wv) |
| + { |
| + return (wv[0] << 16) | (wv[1] << 8) | wv[2]; |
| + } |
| + |
| + static inline void |
| + writeval(unsigned char* wv, Valtype v) |
| + { |
| + wv[0] = v >> 16; |
| + wv[1] = v >> 8; |
| + wv[2] = v; |
| + } |
| +}; |
| + |
| template<> |
| struct Swap_unaligned<32, false> |
| { |
| diff --git a/gold/dwarf_reader.h b/gold/dwarf_reader.h |
| index 087b88aa..21c4a1dc 100644 |
| --- a/gold/dwarf_reader.h |
| +++ b/gold/dwarf_reader.h |
| @@ -568,6 +568,15 @@ class Dwarf_die |
| return this->abstract_origin_; |
| } |
| |
| + // Return the value of the DW_AT_str_offsets_base attribute. |
| + off_t |
| + string_offsets_base() |
| + { |
| + if (!this->string_offsets_base_) |
| + this->read_attributes(); |
| + return this->string_offsets_base_; |
| + } |
| + |
| // Return the value of attribute ATTR as a string. |
| const char* |
| string_attribute(unsigned int attr); |
| @@ -675,6 +684,8 @@ class Dwarf_die |
| off_t specification_; |
| // The value of a DW_AT_abstract_origin attribute. |
| off_t abstract_origin_; |
| + // The value of DW_AT_str_offsets_base attribute. |
| + off_t string_offsets_base_; |
| }; |
| |
| // This class is used to read the debug info from the .debug_info |
| @@ -698,11 +709,13 @@ class Dwarf_info_reader |
| : is_type_unit_(is_type_unit), 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), |
| - offset_size_(0), address_size_(0), cu_version_(0), |
| + string_offsets_shndx_(0), buffer_(NULL), buffer_end_(NULL), cu_offset_(0), |
| + cu_length_(0), offset_size_(0), address_size_(0), cu_version_(0), |
| abbrev_table_(), ranges_table_(this), |
| reloc_mapper_(NULL), string_buffer_(NULL), string_buffer_end_(NULL), |
| - owns_string_buffer_(false), string_output_section_offset_(0) |
| + owns_string_buffer_(false), string_output_section_offset_(0), |
| + string_offsets_buffer_(NULL), string_offsets_buffer_end_(NULL), |
| + owns_string_offsets_buffer_(false), string_offsets_output_section_offset_(0) |
| { } |
| |
| virtual |
| @@ -712,6 +725,8 @@ class Dwarf_info_reader |
| delete this->reloc_mapper_; |
| if (this->owns_string_buffer_ && this->string_buffer_ != NULL) |
| delete[] this->string_buffer_; |
| + if (this->owns_string_offsets_buffer_ && this->string_offsets_buffer_ != NULL) |
| + delete[] this->string_offsets_buffer_; |
| } |
| |
| // Begin parsing the debug info. This calls visit_compilation_unit() |
| @@ -754,6 +769,10 @@ class Dwarf_info_reader |
| const char* |
| get_string(off_t str_off, unsigned int string_shndx); |
| |
| + // Return a string index from the DWARF string offsets table.. |
| + off_t |
| + get_string_offset(unsigned int index, unsigned int string_offsets_shndx); |
| + |
| // Return the size of a DWARF offset. |
| unsigned int |
| offset_size() const |
| @@ -871,6 +890,22 @@ class Dwarf_info_reader |
| bool |
| do_read_string_table(unsigned int string_shndx); |
| |
| + // Read the DWARF string table. |
| + bool |
| + read_string_offsets_table(unsigned int string_offsets_shndx) |
| + { |
| + // If we've already read this string table, return immediately. |
| + if (this->string_offsets_shndx_ > 0 && |
| + this->string_offsets_shndx_ == string_offsets_shndx) |
| + return true; |
| + if (string_offsets_shndx == 0 && this->string_offsets_shndx_ > 0) |
| + return true; |
| + return this->do_read_string_offsets_table(string_offsets_shndx); |
| + } |
| + |
| + bool |
| + do_read_string_offsets_table(unsigned int string_shndx); |
| + |
| // True if this is a type unit; false for a compilation unit. |
| bool is_type_unit_; |
| // The object containing the .debug_info or .debug_types input section. |
| @@ -889,6 +924,8 @@ class Dwarf_info_reader |
| unsigned int abbrev_shndx_; |
| // Index of the .debug_str section. |
| unsigned int string_shndx_; |
| + // Index of the .debug_str_offsets section. |
| + unsigned int string_offsets_shndx_; |
| // The buffer for the debug info. |
| const unsigned char* buffer_; |
| const unsigned char* buffer_end_; |
| @@ -918,6 +955,16 @@ class Dwarf_info_reader |
| // from relocated data will be relative to the output section, and need |
| // to be corrected before reading data from the input section. |
| uint64_t string_output_section_offset_; |
| + const unsigned char* string_offsets_buffer_; |
| + const unsigned char* string_offsets_buffer_end_; |
| + unsigned int string_offsets_base_; |
| + // True if this object owns the buffer and needs to delete it. |
| + bool owns_string_offsets_buffer_; |
| + // For incremental update links, this will hold the offset of the |
| + // input .debug_str section within the output section. Offsets read |
| + // from relocated data will be relative to the output section, and need |
| + // to be corrected before reading data from the input section. |
| + uint64_t string_offsets_output_section_offset_; |
| }; |
| |
| // We can't do better than to keep the offsets in a sorted vector. |
| diff --git a/gold/dwarf_reader.cc b/gold/dwarf_reader.cc |
| index 74b886d0..c698e652 100644 |
| --- a/gold/dwarf_reader.cc |
| +++ b/gold/dwarf_reader.cc |
| @@ -34,6 +34,7 @@ |
| #include "dwarf_reader.h" |
| #include "int_encoding.h" |
| #include "compressed_output.h" |
| +#include "../elfcpp/dwarf.h" |
| |
| namespace gold { |
| |
| @@ -652,7 +653,7 @@ Dwarf_die::Dwarf_die( |
| child_offset_(0), sibling_offset_(0), abbrev_code_(NULL), attributes_(), |
| attributes_read_(false), name_(NULL), name_off_(-1), linkage_name_(NULL), |
| linkage_name_off_(-1), string_shndx_(0), specification_(0), |
| - abstract_origin_(0) |
| + abstract_origin_(0), string_offsets_base_(0) |
| { |
| size_t len; |
| const unsigned char* pdie = dwinfo->buffer_at_offset(die_offset); |
| @@ -777,6 +778,8 @@ Dwarf_die::read_attributes() |
| break; |
| case elfcpp::DW_FORM_data1: |
| case elfcpp::DW_FORM_flag: |
| + case elfcpp::DW_FORM_strx1: |
| + case elfcpp::DW_FORM_addrx1: |
| attr_value.val.intval = *pattr++; |
| break; |
| case elfcpp::DW_FORM_ref1: |
| @@ -784,6 +787,8 @@ Dwarf_die::read_attributes() |
| ref_form = true; |
| break; |
| case elfcpp::DW_FORM_data2: |
| + case elfcpp::DW_FORM_strx2: |
| + case elfcpp::DW_FORM_addrx2: |
| attr_value.val.intval = |
| this->dwinfo_->read_from_pointer<16>(&pattr); |
| break; |
| @@ -792,7 +797,22 @@ Dwarf_die::read_attributes() |
| this->dwinfo_->read_from_pointer<16>(&pattr); |
| ref_form = true; |
| break; |
| + case elfcpp::DW_FORM_strx3: |
| + case elfcpp::DW_FORM_addrx3: |
| + { |
| + //FIXME: Deal with endianness here - probably have to add |
| + //functionality to Dwarf_info_reader to expose non-power-of-2 |
| + //endian-adjusting reading |
| + //const unsigned char a = *pattr++, b = *pattr++, c = *pattr++; |
| + // attr_value.val.intval = ...; |
| + gold_assert(false); |
| + attr_value.val.intval = |
| + this->dwinfo_->read_from_pointer<24>(&pattr); |
| + break; |
| + } |
| case elfcpp::DW_FORM_data4: |
| + case elfcpp::DW_FORM_strx4: |
| + case elfcpp::DW_FORM_addrx4: |
| { |
| off_t sec_off; |
| sec_off = this->dwinfo_->read_from_pointer<32>(&pattr); |
| @@ -846,6 +866,9 @@ Dwarf_die::read_attributes() |
| case elfcpp::DW_FORM_udata: |
| case elfcpp::DW_FORM_GNU_addr_index: |
| case elfcpp::DW_FORM_GNU_str_index: |
| + case elfcpp::DW_FORM_strx: |
| + case elfcpp::DW_FORM_addrx: |
| + case elfcpp::DW_FORM_rnglistx: |
| attr_value.val.uintval = read_unsigned_LEB_128(pattr, &len); |
| pattr += len; |
| break; |
| @@ -903,6 +925,10 @@ Dwarf_die::read_attributes() |
| if (ref_form) |
| this->abstract_origin_ = attr_value.val.refval; |
| break; |
| + case elfcpp::DW_AT_str_offsets_base: |
| + if (ref_form) |
| + this->string_offsets_base_ = attr_value.val.refval; |
| + break; |
| case elfcpp::DW_AT_sibling: |
| if (ref_form && attr_value.aux.shndx == 0) |
| this->sibling_offset_ = attr_value.val.refval; |
| @@ -985,15 +1011,25 @@ Dwarf_die::skip_attributes() |
| } |
| case elfcpp::DW_FORM_data1: |
| case elfcpp::DW_FORM_ref1: |
| + case elfcpp::DW_FORM_strx1: |
| + case elfcpp::DW_FORM_addrx1: |
| case elfcpp::DW_FORM_flag: |
| 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_strx4: |
| + case elfcpp::DW_FORM_addrx4: |
| pattr += 4; |
| break; |
| case elfcpp::DW_FORM_data8: |
| @@ -1005,6 +1041,8 @@ Dwarf_die::skip_attributes() |
| case elfcpp::DW_FORM_udata: |
| case elfcpp::DW_FORM_GNU_addr_index: |
| case elfcpp::DW_FORM_GNU_str_index: |
| + case elfcpp::DW_FORM_strx: |
| + case elfcpp::DW_FORM_addrx: |
| read_unsigned_LEB_128(pattr, &len); |
| pattr += len; |
| break; |
| @@ -1071,6 +1109,16 @@ Dwarf_die::string_attribute(unsigned int attr) |
| return NULL; |
| switch (attr_val->form) |
| { |
| + case elfcpp::DW_FORM_strx: |
| + case elfcpp::DW_FORM_strx1: |
| + case elfcpp::DW_FORM_strx2: |
| + case elfcpp::DW_FORM_strx3: |
| + case elfcpp::DW_FORM_strx4: |
| + { |
| + unsigned int index = attr_val->val.uintval; |
| + off_t offset = this->dwinfo_->get_string_offset(index, 0); |
| + return this->dwinfo_->get_string(offset, 0); |
| + } |
| case elfcpp::DW_FORM_string: |
| return attr_val->val.stringval; |
| case elfcpp::DW_FORM_strp: |
| @@ -1384,6 +1432,7 @@ Dwarf_info_reader::do_parse() |
| NULL); |
| if (root_die.tag() != 0) |
| { |
| + this->string_offsets_base_ = root_die.string_offsets_base(); |
| // Visit the CU or TU. |
| if (this->is_type_unit_) |
| this->visit_type_unit(section_offset + this->cu_offset_, |
| @@ -1450,6 +1499,48 @@ Dwarf_info_reader::do_read_string_table(unsigned int string_shndx) |
| return true; |
| } |
| |
| + |
| +// Read the string offsets table |
| +bool |
| +Dwarf_info_reader::do_read_string_offsets_table(unsigned int string_offsets_shndx) |
| +{ |
| + Relobj* object = this->object_; |
| + |
| + if (string_offsets_shndx == 0) |
| + { |
| + for (unsigned int i = 1; i < this->object_->shnum(); ++i) |
| + { |
| + std::string name = object->section_name(i); |
| + if (name == ".debug_str_offsets" || name == ".zdebug_str_offsets") |
| + { |
| + string_offsets_shndx = i; |
| + this->string_offsets_output_section_offset_ = |
| + object->output_section_offset(i); |
| + break; |
| + } |
| + } |
| + if (string_offsets_shndx == 0) |
| + return false; |
| + } |
| + |
| + if (this->owns_string_offsets_buffer_ && this->string_offsets_buffer_ != NULL) |
| + { |
| + delete[] this->string_offsets_buffer_; |
| + this->owns_string_offsets_buffer_ = false; |
| + } |
| + |
| + // Get the secton contents and decompress if necessary. |
| + section_size_type buffer_size; |
| + const unsigned char* buffer = |
| + object->decompressed_section_contents(string_offsets_shndx, |
| + &buffer_size, |
| + &this->owns_string_offsets_buffer_); |
| + this->string_offsets_buffer_ = buffer; |
| + this->string_offsets_buffer_end_ = this->string_offsets_buffer_ + buffer_size; |
| + this->string_offsets_shndx_ = string_offsets_shndx; |
| + return true; |
| +} |
| + |
| // Read a possibly unaligned integer of SIZE. |
| template <int valsize> |
| inline typename elfcpp::Valtype_base<valsize>::Valtype |
| @@ -1516,6 +1607,27 @@ Dwarf_info_reader::get_string(off_t str_off, unsigned int string_shndx) |
| return p; |
| } |
| |
| + |
| +off_t // |
| +Dwarf_info_reader::get_string_offset(unsigned int index, |
| + unsigned int string_offsets_shndx) |
| +{ |
| + if (!this->read_string_offsets_table(string_offsets_shndx)) |
| + return -1; |
| + |
| + unsigned int offset_size = this->offset_size(); |
| + |
| + const unsigned char* offsets_start = this->string_offsets_buffer_ + this->string_offsets_base_; |
| + |
| + if ((index + 1) * offset_size > (string_offsets_buffer_end_ - offsets_start)) |
| + return -1; |
| + |
| + const unsigned char* offset = offsets_start + index * offset_size; |
| + |
| + return offset_size == 4 ? this->read_from_pointer<32>(offset) |
| + : this->read_from_pointer<64>(offset); |
| +} |
| + |
| // The following are default, do-nothing, implementations of the |
| // hook methods normally provided by a derived class. We provide |
| // default implementations rather than no implementation so that |
| diff --git a/include/dwarf2.def b/include/dwarf2.def |
| index 69b3ba91..1e97e5b8 100644 |
| --- a/include/dwarf2.def |
| +++ b/include/dwarf2.def |
| @@ -203,7 +203,24 @@ DW_FORM (DW_FORM_exprloc, 0x18) |
| DW_FORM (DW_FORM_flag_present, 0x19) |
| DW_FORM (DW_FORM_ref_sig8, 0x20) |
| /* DWARF 5. */ |
| +DW_FORM (DW_FORM_strx, 0x1a) |
| +DW_FORM (DW_FORM_addrx, 0x1b) |
| +DW_FORM (DW_FORM_ref_sup4, 0x1c) |
| +DW_FORM (DW_FORM_strp_sup, 0x1d) |
| +DW_FORM (DW_FORM_data16, 0x1e) |
| DW_FORM (DW_FORM_line_strp, 0x1f) |
| +DW_FORM (DW_FORM_implicit_const, 0x21) |
| +DW_FORM (DW_FORM_loclistx, 0x22) |
| +DW_FORM (DW_FORM_rnglistx, 0x23) |
| +DW_FORM (DW_FORM_ref_sup8, 0x24) |
| +DW_FORM (DW_FORM_strx1, 0x25) |
| +DW_FORM (DW_FORM_strx2, 0x26) |
| +DW_FORM (DW_FORM_strx3, 0x27) |
| +DW_FORM (DW_FORM_strx4, 0x28) |
| +DW_FORM (DW_FORM_addrx1, 0x29) |
| +DW_FORM (DW_FORM_addrx2, 0x2a) |
| +DW_FORM (DW_FORM_addrx3, 0x2b) |
| +DW_FORM (DW_FORM_addrx4, 0x2c) |
| /* Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. */ |
| DW_FORM (DW_FORM_GNU_addr_index, 0x1f01) |
| DW_FORM (DW_FORM_GNU_str_index, 0x1f02) |
| @@ -311,8 +328,35 @@ DW_AT (DW_AT_const_expr, 0x6c) |
| DW_AT (DW_AT_enum_class, 0x6d) |
| DW_AT (DW_AT_linkage_name, 0x6e) |
| /* DWARF 5. */ |
| +DW_AT (DW_AT_string_length_bit_size, 0x6f) |
| +DW_AT (DW_AT_string_length_byte_size, 0x70) |
| +DW_AT (DW_AT_rank, 0x71) |
| +DW_AT (DW_AT_str_offsets_base, 0x72) |
| +DW_AT (DW_AT_addr_base, 0x73) |
| +DW_AT (DW_AT_rnglists_base, 0x74) |
| DW_AT (DW_AT_dwo_name, 0x76) |
| +DW_AT (DW_AT_reference, 0x77) |
| +DW_AT (DW_AT_rvalue_reference, 0x78) |
| +DW_AT (DW_AT_macros, 0x79) |
| +DW_AT (DW_AT_call_all_calls, 0x7a) |
| +DW_AT (DW_AT_call_all_source_calls, 0x7b) |
| +DW_AT (DW_AT_call_all_tail_calls, 0x7c) |
| +DW_AT (DW_AT_call_return_pc, 0x7d) |
| +DW_AT (DW_AT_call_value, 0x7e) |
| +DW_AT (DW_AT_call_origin, 0x7f) |
| +DW_AT (DW_AT_call_parameter, 0x80) |
| +DW_AT (DW_AT_call_pc, 0x81) |
| +DW_AT (DW_AT_call_tail_call, 0x82) |
| +DW_AT (DW_AT_call_target, 0x83) |
| +DW_AT (DW_AT_call_target_clobbered, 0x84) |
| +DW_AT (DW_AT_call_data_location, 0x85) |
| +DW_AT (DW_AT_call_data_value, 0x86) |
| DW_AT (DW_AT_noreturn, 0x87) |
| +DW_AT (DW_AT_alignment, 0x88) |
| +DW_AT (DW_AT_export_symbols, 0x89) |
| +DW_AT (DW_AT_deleted, 0x8a) |
| +DW_AT (DW_AT_defaulted, 0x8b) |
| +DW_AT (DW_AT_loclists_base, 0x8c) |
| |
| DW_AT_DUP (DW_AT_lo_user, 0x2000) /* Implementation-defined range start. */ |
| DW_AT_DUP (DW_AT_hi_user, 0x3fff) /* Implementation-defined range end. */ |