* script-sections.h: Include <list>.

(class Script_sections): Change Sections_elements from std::vector
	to std::list.  Typedef public Elements_iterator.  Add
	orphan_section_placement_, data_segment_align_start_, and
	saw_data_segment_align_ fields.  Remove data_segment_align_index_
	field.
	* script-sections.cc (class Orphan_section_placement): New class.
	(class Sections_element): Add virtual functions is_relro and
	orphan_section_init.  Remove virtual function place_orphan_here.
	(class Output_section_definition): Add is_relro and
	orphan_section_init.  Remove place_orphan_here.
	(class Orphan_output_section): Likewise.
	(Script_sections::Script_sections): Update for field changes.
	(Script_sections::data_segment_align): Set saw_data_segment_align_
	and data_segment_align_start_, not data_segment_align_index.
	(Script_sections::data_segment_relro_end): Check
	saw_data_segment_align_.  Use data_segment_align_start_ rather
	than data_segment_align_index_.
	(Script_sections::place_orphan): Rewrite to use
	Orphan_section_placement.
This commit is contained in:
Ian Lance Taylor 2009-03-19 05:51:49 +00:00
parent 13e4f8ad17
commit 0d371ad356
3 changed files with 376 additions and 182 deletions

View File

@ -1,3 +1,26 @@
2009-03-18 Ian Lance Taylor <iant@google.com>
* script-sections.h: Include <list>.
(class Script_sections): Change Sections_elements from std::vector
to std::list. Typedef public Elements_iterator. Add
orphan_section_placement_, data_segment_align_start_, and
saw_data_segment_align_ fields. Remove data_segment_align_index_
field.
* script-sections.cc (class Orphan_section_placement): New class.
(class Sections_element): Add virtual functions is_relro and
orphan_section_init. Remove virtual function place_orphan_here.
(class Output_section_definition): Add is_relro and
orphan_section_init. Remove place_orphan_here.
(class Orphan_output_section): Likewise.
(Script_sections::Script_sections): Update for field changes.
(Script_sections::data_segment_align): Set saw_data_segment_align_
and data_segment_align_start_, not data_segment_align_index.
(Script_sections::data_segment_relro_end): Check
saw_data_segment_align_. Use data_segment_align_start_ rather
than data_segment_align_index_.
(Script_sections::place_orphan): Rewrite to use
Orphan_section_placement.
2009-03-17 Ian Lance Taylor <iant@google.com>
* archive.cc (Archive::add_symbols): Check for a version attached

View File

@ -1,6 +1,6 @@
// script-sections.cc -- linker script SECTIONS for gold
// Copyright 2008 Free Software Foundation, Inc.
// Copyright 2008, 2009 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
@ -43,6 +43,259 @@
namespace gold
{
// Manage orphan sections. This is intended to be largely compatible
// with the GNU linker. The Linux kernel implicitly relies on
// something similar to the GNU linker's orphan placement. We
// originally used a simpler scheme here, but it caused the kernel
// build to fail, and was also rather inefficient.
class Orphan_section_placement
{
private:
typedef Script_sections::Elements_iterator Elements_iterator;
public:
Orphan_section_placement();
// Handle an output section during initialization of this mapping.
void
output_section_init(const std::string& name, Output_section*,
Elements_iterator location);
// Initialize the last location.
void
last_init(Elements_iterator location);
// Set *PWHERE to the address of an iterator pointing to the
// location to use for an orphan section. Return true if the
// iterator has a value, false otherwise.
bool
find_place(Output_section*, Elements_iterator** pwhere);
// Return the iterator being used for sections at the very end of
// the linker script.
Elements_iterator
last_place() const;
private:
// The places that we specifically recognize. This list is copied
// from the GNU linker.
enum Place_index
{
PLACE_TEXT,
PLACE_RODATA,
PLACE_DATA,
PLACE_BSS,
PLACE_REL,
PLACE_INTERP,
PLACE_NONALLOC,
PLACE_LAST,
PLACE_MAX
};
// The information we keep for a specific place.
struct Place
{
// The name of sections for this place.
const char* name;
// Whether we have a location for this place.
bool have_location;
// The iterator for this place.
Elements_iterator location;
};
// Initialize one place element.
void
initialize_place(Place_index, const char*);
// The places.
Place places_[PLACE_MAX];
// True if this is the first call to output_section_init.
bool first_init_;
};
// Initialize Orphan_section_placement.
Orphan_section_placement::Orphan_section_placement()
: first_init_(true)
{
this->initialize_place(PLACE_TEXT, ".text");
this->initialize_place(PLACE_RODATA, ".rodata");
this->initialize_place(PLACE_DATA, ".data");
this->initialize_place(PLACE_BSS, ".bss");
this->initialize_place(PLACE_REL, NULL);
this->initialize_place(PLACE_INTERP, ".interp");
this->initialize_place(PLACE_NONALLOC, NULL);
this->initialize_place(PLACE_LAST, NULL);
}
// Initialize one place element.
void
Orphan_section_placement::initialize_place(Place_index index, const char* name)
{
this->places_[index].name = name;
this->places_[index].have_location = false;
}
// While initializing the Orphan_section_placement information, this
// is called once for each output section named in the linker script.
// If we found an output section during the link, it will be passed in
// OS.
void
Orphan_section_placement::output_section_init(const std::string& name,
Output_section* os,
Elements_iterator location)
{
bool first_init = this->first_init_;
this->first_init_ = false;
for (int i = 0; i < PLACE_MAX; ++i)
{
if (this->places_[i].name != NULL && this->places_[i].name == name)
{
if (this->places_[i].have_location)
{
// We have already seen a section with this name.
return;
}
this->places_[i].location = location;
this->places_[i].have_location = true;
// If we just found the .bss section, restart the search for
// an unallocated section. This follows the GNU linker's
// behaviour.
if (i == PLACE_BSS)
this->places_[PLACE_NONALLOC].have_location = false;
return;
}
}
// Relocation sections.
if (!this->places_[PLACE_REL].have_location
&& os != NULL
&& (os->type() == elfcpp::SHT_REL || os->type() == elfcpp::SHT_RELA)
&& (os->flags() & elfcpp::SHF_ALLOC) != 0)
{
this->places_[PLACE_REL].location = location;
this->places_[PLACE_REL].have_location = true;
}
// We find the location for unallocated sections by finding the
// first debugging or comment section after the BSS section (if
// there is one).
if (!this->places_[PLACE_NONALLOC].have_location
&& (name == ".comment" || Layout::is_debug_info_section(name.c_str())))
{
// We add orphan sections after the location in PLACES_. We
// want to store unallocated sections before LOCATION. If this
// is the very first section, we can't use it.
if (!first_init)
{
--location;
this->places_[PLACE_NONALLOC].location = location;
this->places_[PLACE_NONALLOC].have_location = true;
}
}
}
// Initialize the last location.
void
Orphan_section_placement::last_init(Elements_iterator location)
{
this->places_[PLACE_LAST].location = location;
this->places_[PLACE_LAST].have_location = true;
}
// Set *PWHERE to the address of an iterator pointing to the location
// to use for an orphan section. Return true if the iterator has a
// value, false otherwise.
bool
Orphan_section_placement::find_place(Output_section* os,
Elements_iterator** pwhere)
{
// Figure out where OS should go. This is based on the GNU linker
// code. FIXME: The GNU linker handles small data sections
// specially, but we don't.
elfcpp::Elf_Word type = os->type();
elfcpp::Elf_Xword flags = os->flags();
Place_index index;
if ((flags & elfcpp::SHF_ALLOC) == 0
&& !Layout::is_debug_info_section(os->name()))
index = PLACE_NONALLOC;
else if ((flags & elfcpp::SHF_ALLOC) == 0)
index = PLACE_LAST;
else if (type == elfcpp::SHT_NOTE)
index = PLACE_INTERP;
else if (type == elfcpp::SHT_NOBITS)
index = PLACE_BSS;
else if ((flags & elfcpp::SHF_WRITE) != 0)
index = PLACE_DATA;
else if (type == elfcpp::SHT_REL || type == elfcpp::SHT_RELA)
index = PLACE_REL;
else if ((flags & elfcpp::SHF_EXECINSTR) == 0)
index = PLACE_RODATA;
else
index = PLACE_TEXT;
// If we don't have a location yet, try to find one based on a
// plausible ordering of sections.
if (!this->places_[index].have_location)
{
Place_index follow;
switch (index)
{
default:
follow = PLACE_MAX;
break;
case PLACE_RODATA:
follow = PLACE_TEXT;
break;
case PLACE_BSS:
follow = PLACE_DATA;
break;
case PLACE_REL:
follow = PLACE_TEXT;
break;
case PLACE_INTERP:
follow = PLACE_TEXT;
break;
}
if (follow != PLACE_MAX && this->places_[follow].have_location)
{
// Set the location of INDEX to the location of FOLLOW. The
// location of INDEX will then be incremented by the caller,
// so anything in INDEX will continue to be after anything
// in FOLLOW.
this->places_[index].location = this->places_[follow].location;
this->places_[index].have_location = true;
}
}
*pwhere = &this->places_[index].location;
bool ret = this->places_[index].have_location;
// The caller will set the location.
this->places_[index].have_location = true;
return ret;
}
// Return the iterator being used for sections at the very end of the
// linker script.
Orphan_section_placement::Elements_iterator
Orphan_section_placement::last_place() const
{
gold_assert(this->places_[PLACE_LAST].have_location);
return this->places_[PLACE_LAST].location;
}
// An element in a SECTIONS clause.
class Sections_element
@ -54,6 +307,11 @@ class Sections_element
virtual ~Sections_element()
{ }
// Return whether an output section is relro.
virtual bool
is_relro() const
{ return false; }
// Record that an output section is relro.
virtual void
set_is_relro()
@ -82,11 +340,11 @@ class Sections_element
output_section_name(const char*, const char*, Output_section***)
{ return NULL; }
// Return whether to place an orphan output section after this
// element.
virtual bool
place_orphan_here(const Output_section *, bool*, bool*) const
{ return false; }
// Initialize OSP with an output section.
virtual void
orphan_section_init(Orphan_section_placement*,
Script_sections::Elements_iterator)
{ }
// Set section addresses. This includes applying assignments if the
// the expression is an absolute value.
@ -1241,6 +1499,11 @@ class Output_section_definition : public Sections_element
void
add_input_section(const Input_section_spec* spec, bool keep);
// Return whether the output section is relro.
bool
is_relro() const
{ return this->is_relro_; }
// Record that the output section is relro.
void
set_is_relro()
@ -1264,9 +1527,11 @@ class Output_section_definition : public Sections_element
output_section_name(const char* file_name, const char* section_name,
Output_section***);
// Return whether to place an orphan section after this one.
bool
place_orphan_here(const Output_section *os, bool* exact, bool*) const;
// Initialize OSP with an output section.
void
orphan_section_init(Orphan_section_placement* osp,
Script_sections::Elements_iterator p)
{ osp->output_section_init(this->name_, this->output_section_, p); }
// Set the section address.
void
@ -1538,124 +1803,6 @@ Output_section_definition::output_section_name(const char* file_name,
return NULL;
}
// Return whether to place an orphan output section after this
// section.
bool
Output_section_definition::place_orphan_here(const Output_section *os,
bool* exact,
bool* is_relro) const
{
*is_relro = this->is_relro_;
// Check for the simple case first.
if (this->output_section_ != NULL
&& this->output_section_->type() == os->type()
&& this->output_section_->flags() == os->flags())
{
*exact = true;
return true;
}
// Otherwise use some heuristics.
if ((os->flags() & elfcpp::SHF_ALLOC) == 0)
return false;
if (os->type() == elfcpp::SHT_NOBITS)
{
if (this->name_ == ".bss")
{
*exact = true;
return true;
}
if (this->output_section_ != NULL
&& this->output_section_->type() == elfcpp::SHT_NOBITS)
return true;
}
else if (os->type() == elfcpp::SHT_NOTE)
{
if (this->output_section_ != NULL
&& this->output_section_->type() == elfcpp::SHT_NOTE)
{
*exact = true;
return true;
}
if (this->name_.compare(0, 5, ".note") == 0)
{
*exact = true;
return true;
}
if (this->name_ == ".interp")
return true;
if (this->output_section_ != NULL
&& this->output_section_->type() == elfcpp::SHT_PROGBITS
&& (this->output_section_->flags() & elfcpp::SHF_WRITE) == 0)
return true;
}
else if (os->type() == elfcpp::SHT_REL || os->type() == elfcpp::SHT_RELA)
{
if (this->name_.compare(0, 4, ".rel") == 0)
{
*exact = true;
return true;
}
if (this->output_section_ != NULL
&& (this->output_section_->type() == elfcpp::SHT_REL
|| this->output_section_->type() == elfcpp::SHT_RELA))
{
*exact = true;
return true;
}
if (this->output_section_ != NULL
&& this->output_section_->type() == elfcpp::SHT_PROGBITS
&& (this->output_section_->flags() & elfcpp::SHF_WRITE) == 0)
return true;
}
else if (os->type() == elfcpp::SHT_PROGBITS
&& (os->flags() & elfcpp::SHF_WRITE) != 0)
{
if (this->name_ == ".data")
{
*exact = true;
return true;
}
if (this->output_section_ != NULL
&& this->output_section_->type() == elfcpp::SHT_PROGBITS
&& (this->output_section_->flags() & elfcpp::SHF_WRITE) != 0)
return true;
}
else if (os->type() == elfcpp::SHT_PROGBITS
&& (os->flags() & elfcpp::SHF_EXECINSTR) != 0)
{
if (this->name_ == ".text")
{
*exact = true;
return true;
}
if (this->output_section_ != NULL
&& this->output_section_->type() == elfcpp::SHT_PROGBITS
&& (this->output_section_->flags() & elfcpp::SHF_EXECINSTR) != 0)
return true;
}
else if (os->type() == elfcpp::SHT_PROGBITS
|| (os->type() != elfcpp::SHT_PROGBITS
&& (os->flags() & elfcpp::SHF_WRITE) == 0))
{
if (this->name_ == ".rodata")
{
*exact = true;
return true;
}
if (this->output_section_ != NULL
&& this->output_section_->type() == elfcpp::SHT_PROGBITS
&& (this->output_section_->flags() & elfcpp::SHF_WRITE) == 0)
return true;
}
return false;
}
// Set the section address. Note that the OUTPUT_SECTION_ field will
// be NULL if no input sections were mapped to this output section.
// We still have to adjust dot and process symbol assignments.
@ -2008,9 +2155,19 @@ class Orphan_output_section : public Sections_element
: os_(os)
{ }
// Return whether to place an orphan section after this one.
// Return whether the orphan output section is relro. We can just
// check the output section because we always set the flag, if
// needed, just after we create the Orphan_output_section.
bool
place_orphan_here(const Output_section *os, bool* exact, bool*) const;
is_relro() const
{ return this->os_->is_relro(); }
// Initialize OSP with an output section. This should have been
// done already.
void
orphan_section_init(Orphan_section_placement*,
Script_sections::Elements_iterator)
{ gold_unreachable(); }
// Set section addresses.
void
@ -2038,23 +2195,6 @@ class Orphan_output_section : public Sections_element
Output_section* os_;
};
// Whether to place another orphan section after this one.
bool
Orphan_output_section::place_orphan_here(const Output_section* os,
bool* exact,
bool* is_relro) const
{
if (this->os_->type() == os->type()
&& this->os_->flags() == os->flags())
{
*exact = true;
*is_relro = this->os_->is_relro();
return true;
}
return false;
}
// Set section addresses.
void
@ -2256,7 +2396,9 @@ Script_sections::Script_sections()
sections_elements_(NULL),
output_section_(NULL),
phdrs_elements_(NULL),
data_segment_align_index_(-1U),
orphan_section_placement_(NULL),
data_segment_align_start_(),
saw_data_segment_align_(false),
saw_relro_end_(false)
{
}
@ -2391,9 +2533,13 @@ Script_sections::add_input_section(const Input_section_spec* spec, bool keep)
void
Script_sections::data_segment_align()
{
if (this->data_segment_align_index_ != -1U)
if (this->saw_data_segment_align_)
gold_error(_("DATA_SEGMENT_ALIGN may only appear once in a linker script"));
this->data_segment_align_index_ = this->sections_elements_->size();
gold_assert(!this->sections_elements_->empty());
Sections_elements::iterator p = this->sections_elements_->end();
--p;
this->data_segment_align_start_ = p;
this->saw_data_segment_align_ = true;
}
// This is called when we see DATA_SEGMENT_RELRO_END. It means that
@ -2407,14 +2553,13 @@ Script_sections::data_segment_relro_end()
"in a linker script"));
this->saw_relro_end_ = true;
if (this->data_segment_align_index_ == -1U)
if (!this->saw_data_segment_align_)
gold_error(_("DATA_SEGMENT_RELRO_END must follow DATA_SEGMENT_ALIGN"));
else
{
for (size_t i = this->data_segment_align_index_;
i < this->sections_elements_->size();
++i)
(*this->sections_elements_)[i]->set_is_relro();
Sections_elements::iterator p = this->data_segment_align_start_;
for (++p; p != this->sections_elements_->end(); ++p)
(*p)->set_is_relro();
}
}
@ -2500,35 +2645,49 @@ Script_sections::output_section_name(const char* file_name,
void
Script_sections::place_orphan(Output_section* os)
{
// Look for an output section definition which matches the output
// section. Put a marker after that section.
bool is_relro = false;
Sections_elements::iterator place = this->sections_elements_->end();
for (Sections_elements::iterator p = this->sections_elements_->begin();
p != this->sections_elements_->end();
++p)
Orphan_section_placement* osp = this->orphan_section_placement_;
if (osp == NULL)
{
bool exact = false;
bool is_relro_here;
if ((*p)->place_orphan_here(os, &exact, &is_relro_here))
{
place = p;
is_relro = is_relro_here;
if (exact)
break;
}
// Initialize the Orphan_section_placement structure.
osp = new Orphan_section_placement();
for (Sections_elements::iterator p = this->sections_elements_->begin();
p != this->sections_elements_->end();
++p)
(*p)->orphan_section_init(osp, p);
gold_assert(!this->sections_elements_->empty());
Sections_elements::iterator last = this->sections_elements_->end();
--last;
osp->last_init(last);
this->orphan_section_placement_ = osp;
}
// The insert function puts the new element before the iterator.
if (place != this->sections_elements_->end())
++place;
Orphan_output_section* orphan = new Orphan_output_section(os);
this->sections_elements_->insert(place, new Orphan_output_section(os));
// Look for where to put ORPHAN.
Sections_elements::iterator* where;
if (osp->find_place(os, &where))
{
if ((**where)->is_relro())
os->set_is_relro();
else
os->clear_is_relro();
if (is_relro)
os->set_is_relro();
// We want to insert ORPHAN after *WHERE, and then update *WHERE
// so that the next one goes after this one.
Sections_elements::iterator p = *where;
gold_assert(p != this->sections_elements_->end());
++p;
*where = this->sections_elements_->insert(p, orphan);
}
else
os->clear_is_relro();
{
os->clear_is_relro();
// We don't have a place to put this orphan section. Put it,
// and all other sections like it, at the end, but before the
// sections which always come at the end.
Sections_elements::iterator last = osp->last_place();
*where = this->sections_elements_->insert(last, orphan);
}
}
// Set the addresses of all the output sections. Walk through all the

View File

@ -1,6 +1,6 @@
// script-sections.h -- linker script SECTIONS for gold -*- C++ -*-
// Copyright 2008 Free Software Foundation, Inc.
// Copyright 2008, 2009 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
@ -26,6 +26,7 @@
#define GOLD_SCRIPT_SECTIONS_H
#include <cstdio>
#include <list>
#include <vector>
namespace gold
@ -41,9 +42,15 @@ class Output_data;
class Output_section_definition;
class Output_section;
class Output_segment;
class Orphan_section_placement;
class Script_sections
{
private:
// This is a list, not a vector, because we insert orphan sections
// in the middle.
typedef std::list<Sections_element*> Sections_elements;
public:
Script_sections();
@ -184,9 +191,10 @@ class Script_sections
void
print(FILE*) const;
private:
typedef std::vector<Sections_element*> Sections_elements;
// Used for orphan sections.
typedef Sections_elements::iterator Elements_iterator;
private:
typedef std::vector<Phdrs_element*> Phdrs_elements;
// Create segments.
@ -232,9 +240,13 @@ class Script_sections
Output_section_definition* output_section_;
// The list of program headers in the PHDRS clause.
Phdrs_elements* phdrs_elements_;
// The index of the next Sections_element when we see
// Where to put orphan sections.
Orphan_section_placement* orphan_section_placement_;
// A pointer to the last Sections_element when we see
// DATA_SEGMENT_ALIGN.
size_t data_segment_align_index_;
Sections_elements::iterator data_segment_align_start_;
// Whether we have seen DATA_SEGMENT_ALIGN.
bool saw_data_segment_align_;
// Whether we have seen DATA_SEGMENT_RELRO_END.
bool saw_relro_end_;
};