// layout.cc -- lay out output file sections for gold
-// Copyright 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
namespace gold
{
+// Layout::Relaxation_debug_check methods.
+
+// Check that sections and special data are in reset states.
+// We do not save states for Output_sections and special Output_data.
+// So we check that they have not assigned any addresses or offsets.
+// clean_up_after_relaxation simply resets their addresses and offsets.
+void
+Layout::Relaxation_debug_check::check_output_data_for_reset_values(
+ const Layout::Section_list& sections,
+ const Layout::Data_list& special_outputs)
+{
+ for(Layout::Section_list::const_iterator p = sections.begin();
+ p != sections.end();
+ ++p)
+ gold_assert((*p)->address_and_file_offset_have_reset_values());
+
+ for(Layout::Data_list::const_iterator p = special_outputs.begin();
+ p != special_outputs.end();
+ ++p)
+ gold_assert((*p)->address_and_file_offset_have_reset_values());
+}
+
+// Save information of SECTIONS for checking later.
+
+void
+Layout::Relaxation_debug_check::read_sections(
+ const Layout::Section_list& sections)
+{
+ for(Layout::Section_list::const_iterator p = sections.begin();
+ p != sections.end();
+ ++p)
+ {
+ Output_section* os = *p;
+ Section_info info;
+ info.output_section = os;
+ info.address = os->is_address_valid() ? os->address() : 0;
+ info.data_size = os->is_data_size_valid() ? os->data_size() : -1;
+ info.offset = os->is_offset_valid()? os->offset() : -1 ;
+ this->section_infos_.push_back(info);
+ }
+}
+
+// Verify SECTIONS using previously recorded information.
+
+void
+Layout::Relaxation_debug_check::verify_sections(
+ const Layout::Section_list& sections)
+{
+ size_t i = 0;
+ for(Layout::Section_list::const_iterator p = sections.begin();
+ p != sections.end();
+ ++p, ++i)
+ {
+ Output_section* os = *p;
+ uint64_t address = os->is_address_valid() ? os->address() : 0;
+ off_t data_size = os->is_data_size_valid() ? os->data_size() : -1;
+ off_t offset = os->is_offset_valid()? os->offset() : -1 ;
+
+ if (i >= this->section_infos_.size())
+ {
+ gold_fatal("Section_info of %s missing.\n", os->name());
+ }
+ const Section_info& info = this->section_infos_[i];
+ if (os != info.output_section)
+ gold_fatal("Section order changed. Expecting %s but see %s\n",
+ info.output_section->name(), os->name());
+ if (address != info.address
+ || data_size != info.data_size
+ || offset != info.offset)
+ gold_fatal("Section %s changed.\n", os->name());
+ }
+}
+
// Layout_task_runner methods.
// Lay out the sections. This is called after all the input objects
section_headers_(NULL),
tls_segment_(NULL),
relro_segment_(NULL),
+ increase_relro_(0),
symtab_section_(NULL),
symtab_xindex_(NULL),
dynsym_section_(NULL),
dynsym_xindex_(NULL),
dynamic_section_(NULL),
+ dynamic_symbol_(NULL),
dynamic_data_(NULL),
eh_frame_section_(NULL),
eh_frame_data_(NULL),
debug_info_(NULL),
group_signatures_(),
output_file_size_(-1),
+ have_added_input_section_(false),
sections_are_attached_(false),
input_requires_executable_stack_(false),
input_with_gnu_stack_note_(false),
any_postprocessing_sections_(false),
resized_signatures_(false),
have_stabstr_section_(false),
- incremental_inputs_(NULL)
+ incremental_inputs_(NULL),
+ record_output_section_data_from_script_(false),
+ script_output_section_data_list_(),
+ segment_states_(NULL),
+ relaxation_debug_check_(NULL)
{
// Make space for more than enough segments for a typical file.
// This is just for efficiency--it's OK if we wind up needing more.
if (is_prefix_of(".gnu.lto_", name))
return false;
}
+ // The GNU linker strips .gnu_debuglink sections, so we do too.
+ // This is a feature used to keep debugging information in
+ // separate files.
+ if (strcmp(name, ".gnu_debuglink") == 0)
+ return false;
return true;
default:
// Return the output section to use for section NAME with type TYPE
// and section flags FLAGS. NAME must be canonicalized in the string
-// pool, and NAME_KEY is the key.
+// pool, and NAME_KEY is the key. IS_INTERP is true if this is the
+// .interp section. IS_DYNAMIC_LINKER_SECTION is true if this section
+// is used by the dynamic linker. IS_RELRO is true for a relro
+// section. IS_LAST_RELRO is true for the last relro section.
+// IS_FIRST_NON_RELRO is true for the first non-relro section.
Output_section*
Layout::get_output_section(const char* name, Stringpool::Key name_key,
- elfcpp::Elf_Word type, elfcpp::Elf_Xword flags)
+ elfcpp::Elf_Word type, elfcpp::Elf_Xword flags,
+ bool is_interp, bool is_dynamic_linker_section,
+ bool is_relro, bool is_last_relro,
+ bool is_first_non_relro)
{
elfcpp::Elf_Xword lookup_flags = flags;
}
if (os == NULL)
- os = this->make_output_section(name, type, flags);
+ os = this->make_output_section(name, type, flags, is_interp,
+ is_dynamic_linker_section, is_relro,
+ is_last_relro, is_first_non_relro);
ins.first->second = os;
return os;
}
// RELOBJ, with type TYPE and flags FLAGS. RELOBJ may be NULL for a
// linker created section. IS_INPUT_SECTION is true if we are
// choosing an output section for an input section found in a input
-// file. This will return NULL if the input section should be
-// discarded.
+// file. IS_INTERP is true if this is the .interp section.
+// IS_DYNAMIC_LINKER_SECTION is true if this section is used by the
+// dynamic linker. IS_RELRO is true for a relro section.
+// IS_LAST_RELRO is true for the last relro section.
+// IS_FIRST_NON_RELRO is true for the first non-relro section. This
+// will return NULL if the input section should be discarded.
Output_section*
Layout::choose_output_section(const Relobj* relobj, const char* name,
elfcpp::Elf_Word type, elfcpp::Elf_Xword flags,
- bool is_input_section)
+ bool is_input_section, bool is_interp,
+ bool is_dynamic_linker_section, bool is_relro,
+ bool is_last_relro, bool is_first_non_relro)
{
// We should not see any input sections after we have attached
// sections to segments.
Script_sections* ss = this->script_options_->script_sections();
const char* file_name = relobj == NULL ? NULL : relobj->name().c_str();
Output_section** output_section_slot;
- name = ss->output_section_name(file_name, name, &output_section_slot);
+ Script_sections::Section_type script_section_type;
+ name = ss->output_section_name(file_name, name, &output_section_slot,
+ &script_section_type);
if (name == NULL)
{
// The SECTIONS clause says to discard this input section.
return NULL;
}
+ // We can only handle script section types ST_NONE and ST_NOLOAD.
+ switch (script_section_type)
+ {
+ case Script_sections::ST_NONE:
+ break;
+ case Script_sections::ST_NOLOAD:
+ flags &= elfcpp::SHF_ALLOC;
+ break;
+ default:
+ gold_unreachable();
+ }
+
// If this is an orphan section--one not mentioned in the linker
// script--then OUTPUT_SECTION_SLOT will be NULL, and we do the
// default processing below.
name = this->namepool_.add(name, false, NULL);
- Output_section* os = this->make_output_section(name, type, flags);
+ Output_section* os =
+ this->make_output_section(name, type, flags, is_interp,
+ is_dynamic_linker_section, is_relro,
+ is_last_relro, is_first_non_relro);
os->set_found_in_sections_clause();
+
+ // Special handling for NOLOAD sections.
+ if (script_section_type == Script_sections::ST_NOLOAD)
+ {
+ os->set_is_noload();
+
+ // The constructor of Output_section sets addresses of non-ALLOC
+ // sections to 0 by default. We don't want that for NOLOAD
+ // sections even if they have no SHF_ALLOC flag.
+ if ((os->flags() & elfcpp::SHF_ALLOC) == 0
+ && os->is_address_valid())
+ {
+ gold_assert(os->address() == 0
+ && !os->is_offset_valid()
+ && !os->is_data_size_valid());
+ os->reset_address_and_file_offset();
+ }
+ }
+
*output_section_slot = os;
return os;
}
// Find or make the output section. The output section is selected
// based on the section name, type, and flags.
- return this->get_output_section(name, name_key, type, flags);
+ return this->get_output_section(name, name_key, type, flags, is_interp,
+ is_dynamic_linker_section, is_relro,
+ is_last_relro, is_first_non_relro);
}
// Return the output section to use for input section SHNDX, with name
Output_section* os;
+ // Sometimes .init_array*, .preinit_array* and .fini_array* do not have
+ // correct section types. Force them here.
+ elfcpp::Elf_Word sh_type = shdr.get_sh_type();
+ if (sh_type == elfcpp::SHT_PROGBITS)
+ {
+ static const char init_array_prefix[] = ".init_array";
+ static const char preinit_array_prefix[] = ".preinit_array";
+ static const char fini_array_prefix[] = ".fini_array";
+ static size_t init_array_prefix_size = sizeof(init_array_prefix) - 1;
+ static size_t preinit_array_prefix_size =
+ sizeof(preinit_array_prefix) - 1;
+ static size_t fini_array_prefix_size = sizeof(fini_array_prefix) - 1;
+
+ if (strncmp(name, init_array_prefix, init_array_prefix_size) == 0)
+ sh_type = elfcpp::SHT_INIT_ARRAY;
+ else if (strncmp(name, preinit_array_prefix, preinit_array_prefix_size)
+ == 0)
+ sh_type = elfcpp::SHT_PREINIT_ARRAY;
+ else if (strncmp(name, fini_array_prefix, fini_array_prefix_size) == 0)
+ sh_type = elfcpp::SHT_FINI_ARRAY;
+ }
+
// In a relocatable link a grouped section must not be combined with
// any other sections.
if (parameters->options().relocatable()
&& (shdr.get_sh_flags() & elfcpp::SHF_GROUP) != 0)
{
name = this->namepool_.add(name, true, NULL);
- os = this->make_output_section(name, shdr.get_sh_type(),
- shdr.get_sh_flags());
+ os = this->make_output_section(name, sh_type, shdr.get_sh_flags(), false,
+ false, false, false, false);
}
else
{
- os = this->choose_output_section(object, name, shdr.get_sh_type(),
- shdr.get_sh_flags(), true);
+ os = this->choose_output_section(object, name, sh_type,
+ shdr.get_sh_flags(), true, false,
+ false, false, false, false);
if (os == NULL)
return NULL;
}
*off = os->add_input_section(object, shndx, name, shdr, reloc_shndx,
this->script_options_->saw_sections_clause());
+ this->have_added_input_section_ = true;
return os;
}
Output_section* os = this->choose_output_section(object, name.c_str(),
sh_type,
shdr.get_sh_flags(),
- false);
+ false, false, false,
+ false, false, false);
os->set_should_link_to_symtab();
os->set_info_section(data_section);
group_section_name = this->namepool_.add(group_section_name, true, NULL);
Output_section* os = this->make_output_section(group_section_name,
elfcpp::SHT_GROUP,
- shdr.get_sh_flags());
+ shdr.get_sh_flags(),
+ false, false, false,
+ false, false);
// We need to find a symbol with the signature in the symbol table.
// If we don't find one now, we need to look again later.
name,
elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC,
- false);
+ false, false, false,
+ false, false, false);
if (os == NULL)
return NULL;
".eh_frame_hdr",
elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC,
- false);
+ false, false, false,
+ false, false, false);
if (hdr_os != NULL)
{
Output_segment* hdr_oseg;
hdr_oseg = this->make_output_segment(elfcpp::PT_GNU_EH_FRAME,
elfcpp::PF_R);
- hdr_oseg->add_output_section(hdr_os, elfcpp::PF_R);
+ hdr_oseg->add_output_section(hdr_os, elfcpp::PF_R, false);
}
this->eh_frame_data_->set_eh_frame_hdr(hdr_posd);
bool saw_sections_clause = this->script_options_->saw_sections_clause();
*off = os->add_input_section(object, shndx, name, shdr, reloc_shndx,
saw_sections_clause);
+ this->have_added_input_section_ = true;
}
return os;
Output_section*
Layout::add_output_section_data(const char* name, elfcpp::Elf_Word type,
elfcpp::Elf_Xword flags,
- Output_section_data* posd)
+ Output_section_data* posd,
+ bool is_dynamic_linker_section,
+ bool is_relro, bool is_last_relro,
+ bool is_first_non_relro)
{
Output_section* os = this->choose_output_section(NULL, name, type, flags,
- false);
+ false, false,
+ is_dynamic_linker_section,
+ is_relro, is_last_relro,
+ is_first_non_relro);
if (os != NULL)
os->add_output_section_data(posd);
return os;
}
// Make a new Output_section, and attach it to segments as
-// appropriate.
+// appropriate. IS_INTERP is true if this is the .interp section.
+// IS_DYNAMIC_LINKER_SECTION is true if this section is used by the
+// dynamic linker. IS_RELRO is true if this is a relro section.
+// IS_LAST_RELRO is true if this is the last relro section.
+// IS_FIRST_NON_RELRO is true if this is the first non relro section.
Output_section*
Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
- elfcpp::Elf_Xword flags)
+ elfcpp::Elf_Xword flags, bool is_interp,
+ bool is_dynamic_linker_section, bool is_relro,
+ bool is_last_relro, bool is_first_non_relro)
{
Output_section* os;
if ((flags & elfcpp::SHF_ALLOC) == 0
&& is_compressible_debug_section(name))
os = new Output_compressed_section(¶meters->options(), name, type,
flags);
-
else if ((flags & elfcpp::SHF_ALLOC) == 0
&& parameters->options().strip_debug_non_line()
&& strcmp(".debug_abbrev", name) == 0)
this->debug_info_->set_abbreviations(this->debug_abbrev_);
}
else
- os = new Output_section(name, type, flags);
+ {
+ // FIXME: const_cast is ugly.
+ Target* target = const_cast<Target*>(¶meters->target());
+ os = target->make_output_section(name, type, flags);
+ }
+
+ if (is_interp)
+ os->set_is_interp();
+ if (is_dynamic_linker_section)
+ os->set_is_dynamic_linker_section();
+ if (is_relro)
+ os->set_is_relro();
+ if (is_last_relro)
+ os->set_is_last_relro();
+ if (is_first_non_relro)
+ os->set_is_first_non_relro();
parameters->target().new_output_section(os);
elfcpp::Elf_Word seg_flags = Layout::section_flags_to_segment(flags);
+ // Check for --section-start.
+ uint64_t addr;
+ bool is_address_set = parameters->options().section_start(os->name(), &addr);
+
// In general the only thing we really care about for PT_LOAD
// segments is whether or not they are writable, so that is how we
// search for them. Large data sections also go into their own
if (os->is_large_data_section() && !(*p)->is_large_data_segment())
continue;
- (*p)->add_output_section(os, seg_flags);
+ if (is_address_set)
+ {
+ if ((*p)->are_addresses_set())
+ continue;
+
+ (*p)->add_initial_output_data(os);
+ (*p)->update_flags_for_output_section(seg_flags);
+ (*p)->set_addresses(addr, addr);
+ break;
+ }
+
+ (*p)->add_output_section(os, seg_flags, true);
break;
}
seg_flags);
if (os->is_large_data_section())
oseg->set_is_large_data_segment();
- oseg->add_output_section(os, seg_flags);
+ oseg->add_output_section(os, seg_flags, true);
+ if (is_address_set)
+ oseg->set_addresses(addr, addr);
}
// If we see a loadable SHT_NOTE section, we create a PT_NOTE
&& (((*p)->flags() & elfcpp::PF_W)
== (seg_flags & elfcpp::PF_W)))
{
- (*p)->add_output_section(os, seg_flags);
+ (*p)->add_output_section(os, seg_flags, false);
break;
}
}
{
Output_segment* oseg = this->make_output_segment(elfcpp::PT_NOTE,
seg_flags);
- oseg->add_output_section(os, seg_flags);
+ oseg->add_output_section(os, seg_flags, false);
}
}
{
if (this->tls_segment_ == NULL)
this->make_output_segment(elfcpp::PT_TLS, seg_flags);
- this->tls_segment_->add_output_section(os, seg_flags);
+ this->tls_segment_->add_output_section(os, seg_flags, false);
}
// If -z relro is in effect, and we see a relro section, we create a
gold_assert(seg_flags == (elfcpp::PF_R | elfcpp::PF_W));
if (this->relro_segment_ == NULL)
this->make_output_segment(elfcpp::PT_GNU_RELRO, seg_flags);
- this->relro_segment_->add_output_section(os, seg_flags);
+ this->relro_segment_->add_output_section(os, seg_flags, false);
}
}
// Make an output section for a script.
Output_section*
-Layout::make_output_section_for_script(const char* name)
+Layout::make_output_section_for_script(
+ const char* name,
+ Script_sections::Section_type section_type)
{
name = this->namepool_.add(name, false, NULL);
+ elfcpp::Elf_Xword sh_flags = elfcpp::SHF_ALLOC;
+ if (section_type == Script_sections::ST_NOLOAD)
+ sh_flags = 0;
Output_section* os = this->make_output_section(name, elfcpp::SHT_PROGBITS,
- elfcpp::SHF_ALLOC);
+ sh_flags, false,
+ false, false, false, false);
os->set_found_in_sections_clause();
+ if (section_type == Script_sections::ST_NOLOAD)
+ os->set_is_noload();
return os;
}
elfcpp::SHT_DYNAMIC,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
- false);
- this->dynamic_section_->set_is_relro();
+ false, false, true,
+ true, false, false);
- symtab->define_in_output_data("_DYNAMIC", NULL, this->dynamic_section_, 0, 0,
- elfcpp::STT_OBJECT, elfcpp::STB_LOCAL,
- elfcpp::STV_HIDDEN, 0, false, false);
+ this->dynamic_symbol_ =
+ symtab->define_in_output_data("_DYNAMIC", NULL, Symbol_table::PREDEFINED,
+ this->dynamic_section_, 0, 0,
+ elfcpp::STT_OBJECT, elfcpp::STB_LOCAL,
+ elfcpp::STV_HIDDEN, 0, false, false);
this->dynamic_data_ = new Output_data_dynamic(&this->dynpool_);
++p)
{
const char* const name = (*p)->name();
- if (name[strspn(name,
- ("0123456789"
- "ABCDEFGHIJKLMNOPWRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "_"))]
- == '\0')
+ if (is_cident(name))
{
const std::string name_string(name);
- const std::string start_name("__start_" + name_string);
- const std::string stop_name("__stop_" + name_string);
+ const std::string start_name(cident_section_start_prefix
+ + name_string);
+ const std::string stop_name(cident_section_stop_prefix
+ + name_string);
symtab->define_in_output_data(start_name.c_str(),
NULL, // version
+ Symbol_table::PREDEFINED,
*p,
0, // value
0, // symsize
symtab->define_in_output_data(stop_name.c_str(),
NULL, // version
+ Symbol_table::PREDEFINED,
*p,
0, // value
0, // symsize
return load_seg;
}
+// Save states of all current output segments. Store saved states
+// in SEGMENT_STATES.
+
+void
+Layout::save_segments(Segment_states* segment_states)
+{
+ for (Segment_list::const_iterator p = this->segment_list_.begin();
+ p != this->segment_list_.end();
+ ++p)
+ {
+ Output_segment* segment = *p;
+ // Shallow copy.
+ Output_segment* copy = new Output_segment(*segment);
+ (*segment_states)[segment] = copy;
+ }
+}
+
+// Restore states of output segments and delete any segment not found in
+// SEGMENT_STATES.
+
+void
+Layout::restore_segments(const Segment_states* segment_states)
+{
+ // Go through the segment list and remove any segment added in the
+ // relaxation loop.
+ this->tls_segment_ = NULL;
+ this->relro_segment_ = NULL;
+ Segment_list::iterator list_iter = this->segment_list_.begin();
+ while (list_iter != this->segment_list_.end())
+ {
+ Output_segment* segment = *list_iter;
+ Segment_states::const_iterator states_iter =
+ segment_states->find(segment);
+ if (states_iter != segment_states->end())
+ {
+ const Output_segment* copy = states_iter->second;
+ // Shallow copy to restore states.
+ *segment = *copy;
+
+ // Also fix up TLS and RELRO segment pointers as appropriate.
+ if (segment->type() == elfcpp::PT_TLS)
+ this->tls_segment_ = segment;
+ else if (segment->type() == elfcpp::PT_GNU_RELRO)
+ this->relro_segment_ = segment;
+
+ ++list_iter;
+ }
+ else
+ {
+ list_iter = this->segment_list_.erase(list_iter);
+ // This is a segment created during section layout. It should be
+ // safe to remove it since we should have removed all pointers to it.
+ delete segment;
+ }
+ }
+}
+
+// Clean up after relaxation so that sections can be laid out again.
+
+void
+Layout::clean_up_after_relaxation()
+{
+ // Restore the segments to point state just prior to the relaxation loop.
+ Script_sections* script_section = this->script_options_->script_sections();
+ script_section->release_segments();
+ this->restore_segments(this->segment_states_);
+
+ // Reset section addresses and file offsets
+ for (Section_list::iterator p = this->section_list_.begin();
+ p != this->section_list_.end();
+ ++p)
+ {
+ (*p)->restore_states();
+
+ // If an input section changes size because of relaxation,
+ // we need to adjust the section offsets of all input sections.
+ // after such a section.
+ if ((*p)->section_offsets_need_adjustment())
+ (*p)->adjust_section_offsets();
+
+ (*p)->reset_address_and_file_offset();
+ }
+
+ // Reset special output object address and file offsets.
+ for (Data_list::iterator p = this->special_output_list_.begin();
+ p != this->special_output_list_.end();
+ ++p)
+ (*p)->reset_address_and_file_offset();
+
+ // A linker script may have created some output section data objects.
+ // They are useless now.
+ for (Output_section_data_list::const_iterator p =
+ this->script_output_section_data_list_.begin();
+ p != this->script_output_section_data_list_.end();
+ ++p)
+ delete *p;
+ this->script_output_section_data_list_.clear();
+}
+
+// Prepare for relaxation.
+
+void
+Layout::prepare_for_relaxation()
+{
+ // Create an relaxation debug check if in debugging mode.
+ if (is_debugging_enabled(DEBUG_RELAXATION))
+ this->relaxation_debug_check_ = new Relaxation_debug_check();
+
+ // Save segment states.
+ this->segment_states_ = new Segment_states();
+ this->save_segments(this->segment_states_);
+
+ for(Section_list::const_iterator p = this->section_list_.begin();
+ p != this->section_list_.end();
+ ++p)
+ (*p)->save_states();
+
+ if (is_debugging_enabled(DEBUG_RELAXATION))
+ this->relaxation_debug_check_->check_output_data_for_reset_values(
+ this->section_list_, this->special_output_list_);
+
+ // Also enable recording of output section data from scripts.
+ this->record_output_section_data_from_script_ = true;
+}
+
+// Relaxation loop body: If target has no relaxation, this runs only once
+// Otherwise, the target relaxation hook is called at the end of
+// each iteration. If the hook returns true, it means re-layout of
+// section is required.
+//
+// The number of segments created by a linking script without a PHDRS
+// clause may be affected by section sizes and alignments. There is
+// a remote chance that relaxation causes different number of PT_LOAD
+// segments are created and sections are attached to different segments.
+// Therefore, we always throw away all segments created during section
+// layout. In order to be able to restart the section layout, we keep
+// a copy of the segment list right before the relaxation loop and use
+// that to restore the segments.
+//
+// PASS is the current relaxation pass number.
+// SYMTAB is a symbol table.
+// PLOAD_SEG is the address of a pointer for the load segment.
+// PHDR_SEG is a pointer to the PHDR segment.
+// SEGMENT_HEADERS points to the output segment header.
+// FILE_HEADER points to the output file header.
+// PSHNDX is the address to store the output section index.
+
+off_t inline
+Layout::relaxation_loop_body(
+ int pass,
+ Target* target,
+ Symbol_table* symtab,
+ Output_segment** pload_seg,
+ Output_segment* phdr_seg,
+ Output_segment_headers* segment_headers,
+ Output_file_header* file_header,
+ unsigned int* pshndx)
+{
+ // If this is not the first iteration, we need to clean up after
+ // relaxation so that we can lay out the sections again.
+ if (pass != 0)
+ this->clean_up_after_relaxation();
+
+ // If there is a SECTIONS clause, put all the input sections into
+ // the required order.
+ Output_segment* load_seg;
+ if (this->script_options_->saw_sections_clause())
+ load_seg = this->set_section_addresses_from_script(symtab);
+ else if (parameters->options().relocatable())
+ load_seg = NULL;
+ else
+ load_seg = this->find_first_load_seg();
+
+ if (parameters->options().oformat_enum()
+ != General_options::OBJECT_FORMAT_ELF)
+ load_seg = NULL;
+
+ // If the user set the address of the text segment, that may not be
+ // compatible with putting the segment headers and file headers into
+ // that segment.
+ if (parameters->options().user_set_Ttext())
+ load_seg = NULL;
+
+ gold_assert(phdr_seg == NULL
+ || load_seg != NULL
+ || this->script_options_->saw_sections_clause());
+
+ // If the address of the load segment we found has been set by
+ // --section-start rather than by a script, then we don't want to
+ // use it for the file and segment headers.
+ if (load_seg != NULL
+ && load_seg->are_addresses_set()
+ && !this->script_options_->saw_sections_clause())
+ load_seg = NULL;
+
+ // Lay out the segment headers.
+ if (!parameters->options().relocatable())
+ {
+ gold_assert(segment_headers != NULL);
+ if (load_seg != NULL)
+ load_seg->add_initial_output_data(segment_headers);
+ if (phdr_seg != NULL)
+ phdr_seg->add_initial_output_data(segment_headers);
+ }
+
+ // Lay out the file header.
+ if (load_seg != NULL)
+ load_seg->add_initial_output_data(file_header);
+
+ if (this->script_options_->saw_phdrs_clause()
+ && !parameters->options().relocatable())
+ {
+ // Support use of FILEHDRS and PHDRS attachments in a PHDRS
+ // clause in a linker script.
+ Script_sections* ss = this->script_options_->script_sections();
+ ss->put_headers_in_phdrs(file_header, segment_headers);
+ }
+
+ // We set the output section indexes in set_segment_offsets and
+ // set_section_indexes.
+ *pshndx = 1;
+
+ // Set the file offsets of all the segments, and all the sections
+ // they contain.
+ off_t off;
+ if (!parameters->options().relocatable())
+ off = this->set_segment_offsets(target, load_seg, pshndx);
+ else
+ off = this->set_relocatable_section_offsets(file_header, pshndx);
+
+ // Verify that the dummy relaxation does not change anything.
+ if (is_debugging_enabled(DEBUG_RELAXATION))
+ {
+ if (pass == 0)
+ this->relaxation_debug_check_->read_sections(this->section_list_);
+ else
+ this->relaxation_debug_check_->verify_sections(this->section_list_);
+ }
+
+ *pload_seg = load_seg;
+ return off;
+}
+
// Finalize the layout. When this is called, we have created all the
// output sections and all the output segments which are based on
// input sections. We have several things to do, and we have to do
Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
Target* target, const Task* task)
{
- target->finalize_sections(this);
+ target->finalize_sections(this, input_objects, symtab);
this->count_local_symbols(task, input_objects);
// dynamic string table is complete.
this->create_version_sections(&versions, symtab, local_dynamic_count,
dynamic_symbols, dynstr);
+
+ // Set the size of the _DYNAMIC symbol. We can't do this until
+ // after we call create_version_sections.
+ this->set_dynamic_symbol_size(symtab);
}
if (this->incremental_inputs_)
this->create_incremental_info_sections();
}
- // If there is a SECTIONS clause, put all the input sections into
- // the required order.
- Output_segment* load_seg;
- if (this->script_options_->saw_sections_clause())
- load_seg = this->set_section_addresses_from_script(symtab);
- else if (parameters->options().relocatable())
- load_seg = NULL;
- else
- load_seg = this->find_first_load_seg();
-
- if (parameters->options().oformat_enum()
- != General_options::OBJECT_FORMAT_ELF)
- load_seg = NULL;
-
- gold_assert(phdr_seg == NULL || load_seg != NULL);
-
- // Lay out the segment headers.
- Output_segment_headers* segment_headers;
- if (parameters->options().relocatable())
- segment_headers = NULL;
- else
- {
- segment_headers = new Output_segment_headers(this->segment_list_);
- if (load_seg != NULL)
- load_seg->add_initial_output_data(segment_headers);
- if (phdr_seg != NULL)
- phdr_seg->add_initial_output_data(segment_headers);
- }
+ // Create segment headers.
+ Output_segment_headers* segment_headers =
+ (parameters->options().relocatable()
+ ? NULL
+ : new Output_segment_headers(this->segment_list_));
// Lay out the file header.
- Output_file_header* file_header;
- file_header = new Output_file_header(target, symtab, segment_headers,
- parameters->options().entry());
- if (load_seg != NULL)
- load_seg->add_initial_output_data(file_header);
+ Output_file_header* file_header
+ = new Output_file_header(target, symtab, segment_headers,
+ parameters->options().entry());
this->special_output_list_.push_back(file_header);
if (segment_headers != NULL)
this->special_output_list_.push_back(segment_headers);
- if (this->script_options_->saw_phdrs_clause()
- && !parameters->options().relocatable())
+ // Find approriate places for orphan output sections if we are using
+ // a linker script.
+ if (this->script_options_->saw_sections_clause())
+ this->place_orphan_sections_in_script();
+
+ Output_segment* load_seg;
+ off_t off;
+ unsigned int shndx;
+ int pass = 0;
+
+ // Take a snapshot of the section layout as needed.
+ if (target->may_relax())
+ this->prepare_for_relaxation();
+
+ // Run the relaxation loop to lay out sections.
+ do
{
- // Support use of FILEHDRS and PHDRS attachments in a PHDRS
- // clause in a linker script.
- Script_sections* ss = this->script_options_->script_sections();
- ss->put_headers_in_phdrs(file_header, segment_headers);
+ off = this->relaxation_loop_body(pass, target, symtab, &load_seg,
+ phdr_seg, segment_headers, file_header,
+ &shndx);
+ pass++;
}
-
- // We set the output section indexes in set_segment_offsets and
- // set_section_indexes.
- unsigned int shndx = 1;
-
- // Set the file offsets of all the segments, and all the sections
- // they contain.
- off_t off;
- if (!parameters->options().relocatable())
- off = this->set_segment_offsets(target, load_seg, &shndx);
- else
- off = this->set_relocatable_section_offsets(file_header, &shndx);
+ while (target->may_relax()
+ && target->relax(pass, input_objects, symtab, this));
// Set the file offsets of all the non-data sections we've seen so
// far which don't have to wait for the input sections. We need
flags = elfcpp::SHF_ALLOC;
Output_section* os = this->choose_output_section(NULL, section_name,
elfcpp::SHT_NOTE,
- flags, false);
+ flags, false, false,
+ false, false, false, false);
if (os == NULL)
return NULL;
elfcpp::Elf_Xword flags = 0;
if (is_stack_executable)
flags |= elfcpp::SHF_EXECINSTR;
- this->make_output_section(name, elfcpp::SHT_PROGBITS, flags);
+ this->make_output_section(name, elfcpp::SHT_PROGBITS, flags, false,
+ false, false, false, false);
}
else
{
this->namepool_.add(".gnu_incremental_inputs", false, NULL);
Output_section* inputs_os =
this->make_output_section(incremental_inputs_name,
- elfcpp::SHT_GNU_INCREMENTAL_INPUTS, 0);
+ elfcpp::SHT_GNU_INCREMENTAL_INPUTS, 0,
+ false, false, false, false, false);
Output_section_data* posd =
this->incremental_inputs_->create_incremental_inputs_section_data();
inputs_os->add_output_section_data(posd);
this->namepool_.add(".gnu_incremental_strtab", false, NULL);
Output_section* strtab_os = this->make_output_section(incremental_strtab_name,
elfcpp::SHT_STRTAB,
- 0);
+ 0, false, false,
+ false, false, false);
Output_data_strtab* strtab_data =
new Output_data_strtab(this->incremental_inputs_->get_stringpool());
strtab_os->add_output_section_data(strtab_data);
uint64_t addr;
if (parameters->options().user_set_Ttext())
addr = parameters->options().Ttext();
- else if (parameters->options().shared())
+ else if (parameters->options().output_is_position_independent())
addr = 0;
else
addr = target->default_text_segment_address();
}
}
+ unsigned int increase_relro = this->increase_relro_;
+ if (this->script_options_->saw_sections_clause())
+ increase_relro = 0;
+
const bool check_sections = parameters->options().check_sections();
Output_segment* last_load_segment = NULL;
if (!parameters->options().nmagic()
&& !parameters->options().omagic())
off = align_file_offset(off, addr, abi_pagesize);
+ else if (load_seg == NULL)
+ {
+ // This is -N or -n with a section script which prevents
+ // us from using a load segment. We need to ensure that
+ // the file offset is aligned to the alignment of the
+ // segment. This is because the linker script
+ // implicitly assumed a zero offset. If we don't align
+ // here, then the alignment of the sections in the
+ // linker script may not match the alignment of the
+ // sections in the set_section_addresses call below,
+ // causing an error about dot moving backward.
+ off = align_address(off, (*p)->maximum_alignment());
+ }
unsigned int shndx_hold = *pshndx;
uint64_t new_addr = (*p)->set_section_addresses(this, false, addr,
+ increase_relro,
&off, pshndx);
// Now that we know the size of this segment, we may be able
off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1));
off = align_file_offset(off, addr, abi_pagesize);
new_addr = (*p)->set_section_addresses(this, true, addr,
+ increase_relro,
&off, pshndx);
}
}
++p)
{
if ((*p)->type() != elfcpp::PT_LOAD)
- (*p)->set_offset();
+ (*p)->set_offset((*p)->type() == elfcpp::PT_GNU_RELRO
+ ? increase_relro
+ : 0);
}
// Set the TLS offsets for each section in the PT_TLS segment.
Output_segment*
Layout::set_section_addresses_from_script(Symbol_table* symtab)
+{
+ Script_sections* ss = this->script_options_->script_sections();
+ gold_assert(ss->saw_sections_clause());
+ return this->script_options_->set_section_addresses(symtab, this);
+}
+
+// Place the orphan sections in the linker script.
+
+void
+Layout::place_orphan_sections_in_script()
{
Script_sections* ss = this->script_options_->script_sections();
gold_assert(ss->saw_sections_clause());
if (!(*p)->found_in_sections_clause())
ss->place_orphan(*p);
}
-
- return this->script_options_->set_section_addresses(symtab, this);
}
// Count the local symbols in the regular symbol table and the dynamic
}
unsigned int local_symcount = local_symbol_index;
- gold_assert(local_symcount * symsize == off - startoff);
+ gold_assert(static_cast<off_t>(local_symcount * symsize) == off - startoff);
off_t dynoff;
size_t dyn_global_index;
const char* symtab_name = this->namepool_.add(".symtab", false, NULL);
Output_section* osymtab = this->make_output_section(symtab_name,
elfcpp::SHT_SYMTAB,
- 0);
+ 0, false, false,
+ false, false, false);
this->symtab_section_ = osymtab;
Output_section_data* pos = new Output_data_fixed_space(off - startoff,
false, NULL);
Output_section* osymtab_xindex =
this->make_output_section(symtab_xindex_name,
- elfcpp::SHT_SYMTAB_SHNDX, 0);
+ elfcpp::SHT_SYMTAB_SHNDX, 0, false,
+ false, false, false, false);
size_t symcount = (off - startoff) / symsize;
this->symtab_xindex_ = new Output_symtab_xindex(symcount);
const char* strtab_name = this->namepool_.add(".strtab", false, NULL);
Output_section* ostrtab = this->make_output_section(strtab_name,
elfcpp::SHT_STRTAB,
- 0);
+ 0, false, false,
+ false, false, false);
Output_section_data* pstr = new Output_data_strtab(&this->sympool_);
ostrtab->add_output_section_data(pstr);
const char* name = this->namepool_.add(".shstrtab", false, NULL);
- Output_section* os = this->make_output_section(name, elfcpp::SHT_STRTAB, 0);
+ Output_section* os = this->make_output_section(name, elfcpp::SHT_STRTAB, 0,
+ false, false, false, false,
+ false);
- // We can't write out this section until we've set all the section
- // names, and we don't set the names of compressed output sections
- // until relocations are complete.
- os->set_after_input_sections();
+ if (strcmp(parameters->options().compress_debug_sections(), "none") != 0)
+ {
+ // We can't write out this section until we've set all the
+ // section names, and we don't set the names of compressed
+ // output sections until relocations are complete. FIXME: With
+ // the current names we use, this is unnecessary.
+ os->set_after_input_sections();
+ }
Output_section_data* posd = new Output_data_strtab(&this->namepool_);
os->add_output_section_data(posd);
Output_section* dynsym = this->choose_output_section(NULL, ".dynsym",
elfcpp::SHT_DYNSYM,
elfcpp::SHF_ALLOC,
- false);
+ false, false, true,
+ false, false, false);
Output_section_data* odata = new Output_data_fixed_space(index * symsize,
align,
this->choose_output_section(NULL, ".dynsym_shndx",
elfcpp::SHT_SYMTAB_SHNDX,
elfcpp::SHF_ALLOC,
- false);
+ false, false, true, false, false, false);
this->dynsym_xindex_ = new Output_symtab_xindex(index);
Output_section* dynstr = this->choose_output_section(NULL, ".dynstr",
elfcpp::SHT_STRTAB,
elfcpp::SHF_ALLOC,
- false);
+ false, false, true,
+ false, false, false);
Output_section_data* strdata = new Output_data_strtab(&this->dynpool_);
dynstr->add_output_section_data(strdata);
Output_section* hashsec = this->choose_output_section(NULL, ".hash",
elfcpp::SHT_HASH,
elfcpp::SHF_ALLOC,
+ false, false, true,
+ false, false,
false);
Output_section_data* hashdata = new Output_data_const_buffer(phash,
Output_section* hashsec = this->choose_output_section(NULL, ".gnu.hash",
elfcpp::SHT_GNU_HASH,
elfcpp::SHF_ALLOC,
+ false, false, true,
+ false, false,
false);
Output_section_data* hashdata = new Output_data_const_buffer(phash,
hashsec->add_output_section_data(hashdata);
hashsec->set_link_section(dynsym);
- hashsec->set_entsize(4);
+
+ // For a 64-bit target, the entries in .gnu.hash do not have a
+ // uniform size, so we only set the entry size for a 32-bit
+ // target.
+ if (parameters->target().get_size() == 32)
+ hashsec->set_entsize(4);
odyn->add_section_address(elfcpp::DT_GNU_HASH, hashsec);
}
Output_section* vsec = this->choose_output_section(NULL, ".gnu.version",
elfcpp::SHT_GNU_versym,
elfcpp::SHF_ALLOC,
- false);
+ false, false, true,
+ false, false, false);
unsigned char* vbuf;
unsigned int vsize;
vdsec= this->choose_output_section(NULL, ".gnu.version_d",
elfcpp::SHT_GNU_verdef,
elfcpp::SHF_ALLOC,
+ false, false, true, false, false,
false);
unsigned char* vdbuf;
vnsec = this->choose_output_section(NULL, ".gnu.version_r",
elfcpp::SHT_GNU_verneed,
elfcpp::SHF_ALLOC,
+ false, false, true, false, false,
false);
unsigned char* vnbuf;
Output_section* osec = this->choose_output_section(NULL, ".interp",
elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC,
- false);
+ false, true, true,
+ false, false, false);
osec->add_output_section_data(odata);
if (!this->script_options_->saw_phdrs_clause())
{
Output_segment* oseg = this->make_output_segment(elfcpp::PT_INTERP,
elfcpp::PF_R);
- oseg->add_output_section(osec, elfcpp::PF_R);
+ oseg->add_output_section(osec, elfcpp::PF_R, false);
+ }
+}
+
+// Add dynamic tags for the PLT and the dynamic relocs. This is
+// called by the target-specific code. This does nothing if not doing
+// a dynamic link.
+
+// USE_REL is true for REL relocs rather than RELA relocs.
+
+// If PLT_GOT is not NULL, then DT_PLTGOT points to it.
+
+// If PLT_REL is not NULL, it is used for DT_PLTRELSZ, and DT_JMPREL,
+// and we also set DT_PLTREL. We use PLT_REL's output section, since
+// some targets have multiple reloc sections in PLT_REL.
+
+// If DYN_REL is not NULL, it is used for DT_REL/DT_RELA,
+// DT_RELSZ/DT_RELASZ, DT_RELENT/DT_RELAENT.
+
+// If ADD_DEBUG is true, we add a DT_DEBUG entry when generating an
+// executable.
+
+void
+Layout::add_target_dynamic_tags(bool use_rel, const Output_data* plt_got,
+ const Output_data* plt_rel,
+ const Output_data_reloc_generic* dyn_rel,
+ bool add_debug, bool dynrel_includes_plt)
+{
+ Output_data_dynamic* odyn = this->dynamic_data_;
+ if (odyn == NULL)
+ return;
+
+ if (plt_got != NULL && plt_got->output_section() != NULL)
+ odyn->add_section_address(elfcpp::DT_PLTGOT, plt_got);
+
+ if (plt_rel != NULL && plt_rel->output_section() != NULL)
+ {
+ odyn->add_section_size(elfcpp::DT_PLTRELSZ, plt_rel->output_section());
+ odyn->add_section_address(elfcpp::DT_JMPREL, plt_rel->output_section());
+ odyn->add_constant(elfcpp::DT_PLTREL,
+ use_rel ? elfcpp::DT_REL : elfcpp::DT_RELA);
+ }
+
+ if (dyn_rel != NULL && dyn_rel->output_section() != NULL)
+ {
+ odyn->add_section_address(use_rel ? elfcpp::DT_REL : elfcpp::DT_RELA,
+ dyn_rel);
+ if (plt_rel != NULL && dynrel_includes_plt)
+ odyn->add_section_size(use_rel ? elfcpp::DT_RELSZ : elfcpp::DT_RELASZ,
+ dyn_rel, plt_rel);
+ else
+ odyn->add_section_size(use_rel ? elfcpp::DT_RELSZ : elfcpp::DT_RELASZ,
+ dyn_rel);
+ const int size = parameters->target().get_size();
+ elfcpp::DT rel_tag;
+ int rel_size;
+ if (use_rel)
+ {
+ rel_tag = elfcpp::DT_RELENT;
+ if (size == 32)
+ rel_size = Reloc_types<elfcpp::SHT_REL, 32, false>::reloc_size;
+ else if (size == 64)
+ rel_size = Reloc_types<elfcpp::SHT_REL, 64, false>::reloc_size;
+ else
+ gold_unreachable();
+ }
+ else
+ {
+ rel_tag = elfcpp::DT_RELAENT;
+ if (size == 32)
+ rel_size = Reloc_types<elfcpp::SHT_RELA, 32, false>::reloc_size;
+ else if (size == 64)
+ rel_size = Reloc_types<elfcpp::SHT_RELA, 64, false>::reloc_size;
+ else
+ gold_unreachable();
+ }
+ odyn->add_constant(rel_tag, rel_size);
+
+ if (parameters->options().combreloc())
+ {
+ size_t c = dyn_rel->relative_reloc_count();
+ if (c > 0)
+ odyn->add_constant((use_rel
+ ? elfcpp::DT_RELCOUNT
+ : elfcpp::DT_RELACOUNT),
+ c);
+ }
+ }
+
+ if (add_debug && !parameters->options().shared())
+ {
+ // The value of the DT_DEBUG tag is filled in by the dynamic
+ // linker at run time, and used by the debugger.
+ odyn->add_constant(elfcpp::DT_DEBUG, 0);
}
}
(elfcpp::PF_R
| elfcpp::PF_W));
oseg->add_output_section(this->dynamic_section_,
- elfcpp::PF_R | elfcpp::PF_W);
+ elfcpp::PF_R | elfcpp::PF_W,
+ false);
}
Output_data_dynamic* const odyn = this->dynamic_data_;
p != input_objects->dynobj_end();
++p)
{
- // FIXME: Handle --as-needed.
+ if (!(*p)->is_needed()
+ && (*p)->input_file()->options().as_needed())
+ {
+ // This dynamic object was linked with --as-needed, but it
+ // is not needed.
+ continue;
+ }
+
odyn->add_string(elfcpp::DT_NEEDED, (*p)->soname());
}
odyn->add_string(elfcpp::DT_SONAME, soname);
}
- // FIXME: Support --init and --fini.
- Symbol* sym = symtab->lookup("_init");
+ Symbol* sym = symtab->lookup(parameters->options().init());
if (sym != NULL && sym->is_defined() && !sym->is_from_dynobj())
odyn->add_symbol(elfcpp::DT_INIT, sym);
- sym = symtab->lookup("_fini");
+ sym = symtab->lookup(parameters->options().fini());
if (sym != NULL && sym->is_defined() && !sym->is_from_dynobj())
odyn->add_symbol(elfcpp::DT_FINI, sym);
- // FIXME: Support DT_INIT_ARRAY and DT_FINI_ARRAY.
-
+ // Look for .init_array, .preinit_array and .fini_array by checking
+ // section types.
+ for(Layout::Section_list::const_iterator p = this->section_list_.begin();
+ p != this->section_list_.end();
+ ++p)
+ switch((*p)->type())
+ {
+ case elfcpp::SHT_FINI_ARRAY:
+ odyn->add_section_address(elfcpp::DT_FINI_ARRAY, *p);
+ odyn->add_section_size(elfcpp::DT_FINI_ARRAYSZ, *p);
+ break;
+ case elfcpp::SHT_INIT_ARRAY:
+ odyn->add_section_address(elfcpp::DT_INIT_ARRAY, *p);
+ odyn->add_section_size(elfcpp::DT_INIT_ARRAYSZ, *p);
+ break;
+ case elfcpp::SHT_PREINIT_ARRAY:
+ odyn->add_section_address(elfcpp::DT_PREINIT_ARRAY, *p);
+ odyn->add_section_size(elfcpp::DT_PREINIT_ARRAYSZ, *p);
+ break;
+ default:
+ break;
+ }
+
// Add a DT_RPATH entry if needed.
const General_options::Dir_list& rpath(parameters->options().rpath());
if (!rpath.empty())
// Add a DT_TEXTREL for compatibility with older loaders.
odyn->add_constant(elfcpp::DT_TEXTREL, 0);
flags |= elfcpp::DF_TEXTREL;
+
+ if (parameters->options().text())
+ gold_error(_("read-only segment has dynamic relocations"));
+ else if (parameters->options().warn_shared_textrel()
+ && parameters->options().shared())
+ gold_warning(_("shared library text segment is not shareable"));
}
if (parameters->options().shared() && this->has_static_tls())
flags |= elfcpp::DF_STATIC_TLS;
if (parameters->options().origin())
flags |= elfcpp::DF_ORIGIN;
+ if (parameters->options().Bsymbolic())
+ {
+ flags |= elfcpp::DF_SYMBOLIC;
+ // Add DT_SYMBOLIC for compatibility with older loaders.
+ odyn->add_constant(elfcpp::DT_SYMBOLIC, 0);
+ }
if (parameters->options().now())
flags |= elfcpp::DF_BIND_NOW;
odyn->add_constant(elfcpp::DT_FLAGS, flags);
odyn->add_constant(elfcpp::DT_FLAGS_1, flags);
}
+// Set the size of the _DYNAMIC symbol table to be the size of the
+// dynamic data.
+
+void
+Layout::set_dynamic_symbol_size(const Symbol_table* symtab)
+{
+ Output_data_dynamic* const odyn = this->dynamic_data_;
+ odyn->finalize_data_size();
+ off_t data_size = odyn->data_size();
+ const int size = parameters->target().get_size();
+ if (size == 32)
+ symtab->get_sized_symbol<32>(this->dynamic_symbol_)->set_symsize(data_size);
+ else if (size == 64)
+ symtab->get_sized_symbol<64>(this->dynamic_symbol_)->set_symsize(data_size);
+ else
+ gold_unreachable();
+}
+
// The mapping of input section name prefixes to output section names.
// In some cases one prefix is itself a prefix of another prefix; in
// such a case the longer prefix must come first. These prefixes are
MAPPING_INIT(".gnu.linkonce.lr.", ".lrodata"),
MAPPING_INIT(".gnu.linkonce.l.", ".ldata"),
MAPPING_INIT(".gnu.linkonce.lb.", ".lbss"),
- MAPPING_INIT(".ARM.extab.", ".ARM.extab"),
+ MAPPING_INIT(".ARM.extab", ".ARM.extab"),
MAPPING_INIT(".gnu.linkonce.armextab.", ".ARM.extab"),
- MAPPING_INIT(".ARM.exidx.", ".ARM.exidx"),
+ MAPPING_INIT(".ARM.exidx", ".ARM.exidx"),
MAPPING_INIT(".gnu.linkonce.armexidx.", ".ARM.exidx"),
};
#undef MAPPING_INIT