+ // We need to check if this is dropped.
+ section_offset_type ref = p->first;
+ section_offset_type mapped_ref = p->second;
+
+ if (mapped_ref != Arm_exidx_input_section::invalid_offset)
+ // Offset is present in output.
+ *poutput = mapped_ref + (offset - ref);
+ else
+ // Offset is discarded owing to EXIDX entry merging.
+ *poutput = -1;
+ }
+
+ return true;
+}
+
+// Write this to output file OF.
+
+void
+Arm_exidx_merged_section::do_write(Output_file* of)
+{
+ off_t offset = this->offset();
+ const section_size_type oview_size = this->data_size();
+ unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+ Output_section* os = this->relobj()->output_section(this->shndx());
+ gold_assert(os != NULL);
+
+ memcpy(oview, this->section_contents_, oview_size);
+ of->write_output_view(this->offset(), oview_size, oview);
+}
+
+// Arm_exidx_fixup methods.
+
+// Append an EXIDX_CANTUNWIND in the current output section if the last entry
+// is not an EXIDX_CANTUNWIND entry already. The new EXIDX_CANTUNWIND entry
+// points to the end of the last seen EXIDX section.
+
+void
+Arm_exidx_fixup::add_exidx_cantunwind_as_needed()
+{
+ if (this->last_unwind_type_ != UT_EXIDX_CANTUNWIND
+ && this->last_input_section_ != NULL)
+ {
+ Relobj* relobj = this->last_input_section_->relobj();
+ unsigned int text_shndx = this->last_input_section_->link();
+ Arm_exidx_cantunwind* cantunwind =
+ new Arm_exidx_cantunwind(relobj, text_shndx);
+ this->exidx_output_section_->add_output_section_data(cantunwind);
+ this->last_unwind_type_ = UT_EXIDX_CANTUNWIND;
+ }
+}
+
+// Process an EXIDX section entry in input. Return whether this entry
+// can be deleted in the output. SECOND_WORD in the second word of the
+// EXIDX entry.
+
+bool
+Arm_exidx_fixup::process_exidx_entry(uint32_t second_word)
+{
+ bool delete_entry;
+ if (second_word == elfcpp::EXIDX_CANTUNWIND)
+ {
+ // Merge if previous entry is also an EXIDX_CANTUNWIND.
+ delete_entry = this->last_unwind_type_ == UT_EXIDX_CANTUNWIND;
+ this->last_unwind_type_ = UT_EXIDX_CANTUNWIND;
+ }
+ else if ((second_word & 0x80000000) != 0)
+ {
+ // Inlined unwinding data. Merge if equal to previous.
+ delete_entry = (merge_exidx_entries_
+ && this->last_unwind_type_ == UT_INLINED_ENTRY
+ && this->last_inlined_entry_ == second_word);
+ this->last_unwind_type_ = UT_INLINED_ENTRY;
+ this->last_inlined_entry_ = second_word;
+ }
+ else
+ {
+ // Normal table entry. In theory we could merge these too,
+ // but duplicate entries are likely to be much less common.
+ delete_entry = false;
+ this->last_unwind_type_ = UT_NORMAL_ENTRY;
+ }
+ return delete_entry;
+}
+
+// Update the current section offset map during EXIDX section fix-up.
+// If there is no map, create one. INPUT_OFFSET is the offset of a
+// reference point, DELETED_BYTES is the number of deleted by in the
+// section so far. If DELETE_ENTRY is true, the reference point and
+// all offsets after the previous reference point are discarded.
+
+void
+Arm_exidx_fixup::update_offset_map(
+ section_offset_type input_offset,
+ section_size_type deleted_bytes,
+ bool delete_entry)
+{
+ if (this->section_offset_map_ == NULL)
+ this->section_offset_map_ = new Arm_exidx_section_offset_map();
+ section_offset_type output_offset;
+ if (delete_entry)
+ output_offset = Arm_exidx_input_section::invalid_offset;
+ else
+ output_offset = input_offset - deleted_bytes;
+ (*this->section_offset_map_)[input_offset] = output_offset;
+}
+
+// Process EXIDX_INPUT_SECTION for EXIDX entry merging. Return the number of
+// bytes deleted. SECTION_CONTENTS points to the contents of the EXIDX
+// section and SECTION_SIZE is the number of bytes pointed by SECTION_CONTENTS.
+// If some entries are merged, also store a pointer to a newly created
+// Arm_exidx_section_offset_map object in *PSECTION_OFFSET_MAP. The caller
+// owns the map and is responsible for releasing it after use.
+
+template<bool big_endian>
+uint32_t
+Arm_exidx_fixup::process_exidx_section(
+ const Arm_exidx_input_section* exidx_input_section,
+ const unsigned char* section_contents,
+ section_size_type section_size,
+ Arm_exidx_section_offset_map** psection_offset_map)
+{
+ Relobj* relobj = exidx_input_section->relobj();
+ unsigned shndx = exidx_input_section->shndx();
+
+ if ((section_size % 8) != 0)
+ {
+ // Something is wrong with this section. Better not touch it.
+ gold_error(_("uneven .ARM.exidx section size in %s section %u"),
+ relobj->name().c_str(), shndx);
+ this->last_input_section_ = exidx_input_section;
+ this->last_unwind_type_ = UT_NONE;
+ return 0;
+ }
+
+ uint32_t deleted_bytes = 0;
+ bool prev_delete_entry = false;
+ gold_assert(this->section_offset_map_ == NULL);
+
+ for (section_size_type i = 0; i < section_size; i += 8)
+ {
+ typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+ const Valtype* wv =
+ reinterpret_cast<const Valtype*>(section_contents + i + 4);
+ uint32_t second_word = elfcpp::Swap<32, big_endian>::readval(wv);
+
+ bool delete_entry = this->process_exidx_entry(second_word);
+
+ // Entry deletion causes changes in output offsets. We use a std::map
+ // to record these. And entry (x, y) means input offset x
+ // is mapped to output offset y. If y is invalid_offset, then x is
+ // dropped in the output. Because of the way std::map::lower_bound
+ // works, we record the last offset in a region w.r.t to keeping or
+ // dropping. If there is no entry (x0, y0) for an input offset x0,
+ // the output offset y0 of it is determined by the output offset y1 of
+ // the smallest input offset x1 > x0 that there is an (x1, y1) entry
+ // in the map. If y1 is not -1, then y0 = y1 + x0 - x1. Otherwise, y1
+ // y0 is also -1.
+ if (delete_entry != prev_delete_entry && i != 0)
+ this->update_offset_map(i - 1, deleted_bytes, prev_delete_entry);
+
+ // Update total deleted bytes for this entry.
+ if (delete_entry)
+ deleted_bytes += 8;
+
+ prev_delete_entry = delete_entry;
+ }
+
+ // If section offset map is not NULL, make an entry for the end of
+ // section.
+ if (this->section_offset_map_ != NULL)
+ update_offset_map(section_size - 1, deleted_bytes, prev_delete_entry);
+
+ *psection_offset_map = this->section_offset_map_;
+ this->section_offset_map_ = NULL;
+ this->last_input_section_ = exidx_input_section;
+
+ // Set the first output text section so that we can link the EXIDX output
+ // section to it. Ignore any EXIDX input section that is completely merged.
+ if (this->first_output_text_section_ == NULL
+ && deleted_bytes != section_size)
+ {
+ unsigned int link = exidx_input_section->link();
+ Output_section* os = relobj->output_section(link);
+ gold_assert(os != NULL);
+ this->first_output_text_section_ = os;
+ }
+
+ return deleted_bytes;
+}
+
+// Arm_output_section methods.
+
+// Create a stub group for input sections from BEGIN to END. OWNER
+// points to the input section to be the owner a new stub table.
+
+template<bool big_endian>
+void
+Arm_output_section<big_endian>::create_stub_group(
+ Input_section_list::const_iterator begin,
+ Input_section_list::const_iterator end,
+ Input_section_list::const_iterator owner,
+ Target_arm<big_endian>* target,
+ std::vector<Output_relaxed_input_section*>* new_relaxed_sections,
+ const Task* task)
+{
+ // We use a different kind of relaxed section in an EXIDX section.
+ // The static casting from Output_relaxed_input_section to
+ // Arm_input_section is invalid in an EXIDX section. We are okay
+ // because we should not be calling this for an EXIDX section.
+ gold_assert(this->type() != elfcpp::SHT_ARM_EXIDX);
+
+ // Currently we convert ordinary input sections into relaxed sections only
+ // at this point but we may want to support creating relaxed input section
+ // very early. So we check here to see if owner is already a relaxed
+ // section.
+
+ Arm_input_section<big_endian>* arm_input_section;
+ if (owner->is_relaxed_input_section())
+ {
+ arm_input_section =
+ Arm_input_section<big_endian>::as_arm_input_section(
+ owner->relaxed_input_section());
+ }
+ else
+ {
+ gold_assert(owner->is_input_section());
+ // Create a new relaxed input section. We need to lock the original
+ // file.
+ Task_lock_obj<Object> tl(task, owner->relobj());
+ arm_input_section =
+ target->new_arm_input_section(owner->relobj(), owner->shndx());
+ new_relaxed_sections->push_back(arm_input_section);
+ }
+
+ // Create a stub table.
+ Stub_table<big_endian>* stub_table =
+ target->new_stub_table(arm_input_section);
+
+ arm_input_section->set_stub_table(stub_table);
+
+ Input_section_list::const_iterator p = begin;
+ Input_section_list::const_iterator prev_p;
+
+ // Look for input sections or relaxed input sections in [begin ... end].
+ do
+ {
+ if (p->is_input_section() || p->is_relaxed_input_section())
+ {
+ // The stub table information for input sections live
+ // in their objects.
+ Arm_relobj<big_endian>* arm_relobj =
+ Arm_relobj<big_endian>::as_arm_relobj(p->relobj());
+ arm_relobj->set_stub_table(p->shndx(), stub_table);
+ }
+ prev_p = p++;
+ }
+ while (prev_p != end);
+}
+
+// Group input sections for stub generation. GROUP_SIZE is roughly the limit
+// of stub groups. We grow a stub group by adding input section until the
+// size is just below GROUP_SIZE. The last input section will be converted
+// into a stub table. If STUB_ALWAYS_AFTER_BRANCH is false, we also add
+// input section after the stub table, effectively double the group size.
+//
+// This is similar to the group_sections() function in elf32-arm.c but is
+// implemented differently.
+
+template<bool big_endian>
+void
+Arm_output_section<big_endian>::group_sections(
+ section_size_type group_size,
+ bool stubs_always_after_branch,
+ Target_arm<big_endian>* target,
+ const Task* task)
+{
+ // We only care about sections containing code.
+ if ((this->flags() & elfcpp::SHF_EXECINSTR) == 0)
+ return;
+
+ // States for grouping.
+ typedef enum
+ {
+ // No group is being built.
+ NO_GROUP,
+ // A group is being built but the stub table is not found yet.
+ // We keep group a stub group until the size is just under GROUP_SIZE.
+ // The last input section in the group will be used as the stub table.
+ FINDING_STUB_SECTION,
+ // A group is being built and we have already found a stub table.
+ // We enter this state to grow a stub group by adding input section
+ // after the stub table. This effectively doubles the group size.
+ HAS_STUB_SECTION
+ } State;
+
+ // Any newly created relaxed sections are stored here.
+ std::vector<Output_relaxed_input_section*> new_relaxed_sections;
+
+ State state = NO_GROUP;
+ section_size_type off = 0;
+ section_size_type group_begin_offset = 0;
+ section_size_type group_end_offset = 0;
+ section_size_type stub_table_end_offset = 0;
+ Input_section_list::const_iterator group_begin =