* README: Remove claim that MEMORY is not supported.
* expression.cc (script_exp_function_origin)
(script_exp_function_length): Move from here to ...
* script.cc: ... here.
(script_set_section_region, script_add_memory)
(script_parse_memory_attr, script_include_directive): New
functions.
* script-sections.cc
(class Memory_region): New class.
(class Output_section_definition): Add set_memory_region,
set_section_vma, set_section_lma and get_section_name methods.
(class Script_Sections): Add add_memory_region,
find_memory_region, find_memory_region_origin,
find_memory_region_length and set_memory_region methods.
Have set_section_addresses method walk the list of set memory
regions.
Extend the print methos to display memory regions.
* script-sections.h: Add prototypes for new methods.
Add enum for MEMORY region attributes.
* yyscript.y: Add support for parsing MEMORY regions.
* script-c.h: Add prototypes for new functions.
* testsuite/Makefile.am: Add test of MEMORY region functionality.
* testsuite/Makefile.in: Regenerate.
* testsuite/memory_test.sh: New script.
* testsuite/memory_test.s: New assembler source file.
* testsuite/memory_test.t: New linker script.
This commit is contained in:
parent
ab3e2b4a1c
commit
7f8cd84403
@ -1,3 +1,32 @@
|
||||
2010-09-08 Nick Clifton <nickc@redhat.com>
|
||||
|
||||
* README: Remove claim that MEMORY is not supported.
|
||||
* expression.cc (script_exp_function_origin)
|
||||
(script_exp_function_length): Move from here to ...
|
||||
* script.cc: ... here.
|
||||
(script_set_section_region, script_add_memory)
|
||||
(script_parse_memory_attr, script_include_directive): New
|
||||
functions.
|
||||
* script-sections.cc
|
||||
(class Memory_region): New class.
|
||||
(class Output_section_definition): Add set_memory_region,
|
||||
set_section_vma, set_section_lma and get_section_name methods.
|
||||
(class Script_Sections): Add add_memory_region,
|
||||
find_memory_region, find_memory_region_origin,
|
||||
find_memory_region_length and set_memory_region methods.
|
||||
Have set_section_addresses method walk the list of set memory
|
||||
regions.
|
||||
Extend the print methos to display memory regions.
|
||||
* script-sections.h: Add prototypes for new methods.
|
||||
Add enum for MEMORY region attributes.
|
||||
* yyscript.y: Add support for parsing MEMORY regions.
|
||||
* script-c.h: Add prototypes for new functions.
|
||||
* testsuite/Makefile.am: Add test of MEMORY region functionality.
|
||||
* testsuite/Makefile.in: Regenerate.
|
||||
* testsuite/memory_test.sh: New script.
|
||||
* testsuite/memory_test.s: New assembler source file.
|
||||
* testsuite/memory_test.t: New linker script.
|
||||
|
||||
2010-08-27 Doug Kwan <dougkwan@google.com>
|
||||
|
||||
* gold/resolve.cc (Symbol_table::should_override): Let a weak
|
||||
|
||||
@ -15,7 +15,6 @@ documentation for features which gold supports. gold supports most of
|
||||
the features of the GNU linker for ELF targets. Notable
|
||||
omissions--features of the GNU linker not currently supported in
|
||||
gold--are:
|
||||
* MEMORY regions in linker scripts
|
||||
* MRI compatible linker scripts
|
||||
* cross-reference reports (--cref)
|
||||
* various other minor options
|
||||
|
||||
@ -1237,19 +1237,4 @@ script_exp_function_segment_start(const char* segment_name,
|
||||
default_value);
|
||||
}
|
||||
|
||||
// Functions for memory regions. These can not be implemented unless
|
||||
// and until we implement memory regions.
|
||||
|
||||
extern "C" Expression*
|
||||
script_exp_function_origin(const char*, size_t)
|
||||
{
|
||||
gold_fatal(_("ORIGIN not implemented"));
|
||||
}
|
||||
|
||||
extern "C" Expression*
|
||||
script_exp_function_length(const char*, size_t)
|
||||
{
|
||||
gold_fatal(_("LENGTH not implemented"));
|
||||
}
|
||||
|
||||
} // End namespace gold.
|
||||
|
||||
@ -503,10 +503,15 @@ Layout::choose_output_section(const Relobj* relobj, const char* name,
|
||||
const char* file_name = relobj == NULL ? NULL : relobj->name().c_str();
|
||||
Output_section** output_section_slot;
|
||||
Script_sections::Section_type script_section_type;
|
||||
const char* orig_name = name;
|
||||
name = ss->output_section_name(file_name, name, &output_section_slot,
|
||||
&script_section_type);
|
||||
if (name == NULL)
|
||||
{
|
||||
gold_debug(DEBUG_SCRIPT, _("Unable to create output section '%s' "
|
||||
"because it is not allowed by the "
|
||||
"SECTIONS clause of the linker script"),
|
||||
orig_name);
|
||||
// The SECTIONS clause says to discard this input section.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -421,6 +421,21 @@ script_data_segment_relro_end(void* closure);
|
||||
extern void
|
||||
script_saw_segment_start_expression(void* closure);
|
||||
|
||||
/* Called by the bison parser for MEMORY regions. */
|
||||
|
||||
extern void
|
||||
script_add_memory(void*, const char*, size_t, unsigned int,
|
||||
Expression_ptr, Expression_ptr);
|
||||
|
||||
extern unsigned int
|
||||
script_parse_memory_attr(void*, const char*, size_t, int);
|
||||
|
||||
extern void
|
||||
script_set_section_region(void*, const char*, size_t, int);
|
||||
|
||||
extern void
|
||||
script_include_directive(void *, const char*, size_t);
|
||||
|
||||
/* Called by the bison parser for expressions. */
|
||||
|
||||
extern Expression_ptr
|
||||
@ -488,9 +503,9 @@ script_exp_function_addr(const char*, size_t);
|
||||
extern Expression_ptr
|
||||
script_exp_function_loadaddr(const char*, size_t);
|
||||
extern Expression_ptr
|
||||
script_exp_function_origin(const char*, size_t);
|
||||
script_exp_function_origin(void*, const char*, size_t);
|
||||
extern Expression_ptr
|
||||
script_exp_function_length(const char*, size_t);
|
||||
script_exp_function_length(void*, const char*, size_t);
|
||||
extern Expression_ptr
|
||||
script_exp_function_constant(const char*, size_t);
|
||||
extern Expression_ptr
|
||||
|
||||
@ -43,6 +43,174 @@
|
||||
namespace gold
|
||||
{
|
||||
|
||||
// A region of memory.
|
||||
class Memory_region
|
||||
{
|
||||
public:
|
||||
Memory_region(const char* name, size_t namelen, unsigned int attributes,
|
||||
Expression* start, Expression* length)
|
||||
: name_(name, namelen),
|
||||
attributes_(attributes),
|
||||
start_(start),
|
||||
length_(length),
|
||||
current_vma_offset_(0),
|
||||
current_lma_offset_(0),
|
||||
vma_sections_(NULL),
|
||||
lma_sections_(NULL)
|
||||
{ }
|
||||
|
||||
// Return the name of this region.
|
||||
const std::string&
|
||||
name() const
|
||||
{ return this->name_; }
|
||||
|
||||
// Return the start address of this region.
|
||||
Expression*
|
||||
start_address() const
|
||||
{ return this->start_; }
|
||||
|
||||
// Return the length of this region.
|
||||
Expression*
|
||||
length() const
|
||||
{ return this->length_; }
|
||||
|
||||
// Print the region (when debugging).
|
||||
void
|
||||
print(FILE*) const;
|
||||
|
||||
// Return true if <name,namelen> matches this region.
|
||||
bool
|
||||
name_match(const char* name, size_t namelen)
|
||||
{
|
||||
return (this->name_.length() == namelen
|
||||
&& strncmp(this->name_.c_str(), name, namelen) == 0);
|
||||
}
|
||||
|
||||
Expression*
|
||||
get_current_vma_address(void) const
|
||||
{
|
||||
return
|
||||
script_exp_binary_add(this->start_,
|
||||
script_exp_integer(this->current_vma_offset_));
|
||||
}
|
||||
|
||||
Expression*
|
||||
get_current_lma_address(void) const
|
||||
{
|
||||
return
|
||||
script_exp_binary_add(this->start_,
|
||||
script_exp_integer(this->current_lma_offset_));
|
||||
}
|
||||
|
||||
void
|
||||
increment_vma_offset(std::string section_name, uint64_t amount,
|
||||
const Symbol_table* symtab, const Layout* layout)
|
||||
{
|
||||
this->current_vma_offset_ += amount;
|
||||
|
||||
if (this->current_vma_offset_
|
||||
> this->length_->eval(symtab, layout, false))
|
||||
gold_error (_("section %s overflows end of region %s"),
|
||||
section_name.c_str(), this->name_.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
increment_lma_offset(std::string section_name, uint64_t amount,
|
||||
const Symbol_table* symtab, const Layout* layout)
|
||||
{
|
||||
this->current_lma_offset_ += amount;
|
||||
|
||||
if (this->current_lma_offset_
|
||||
> this->length_->eval(symtab, layout, false))
|
||||
gold_error (_("section %s overflows end of region %s (based on load address)"),
|
||||
section_name.c_str(), this->name_.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
add_section(Output_section_definition* sec, bool vma)
|
||||
{
|
||||
if (vma)
|
||||
this->vma_sections_.push_back(sec);
|
||||
else
|
||||
this->lma_sections_.push_back(sec);
|
||||
}
|
||||
|
||||
typedef std::vector<Output_section_definition*> Section_list;
|
||||
|
||||
// Return the start of the list of sections
|
||||
// whose VMAs are taken from this region.
|
||||
Section_list::const_iterator
|
||||
get_vma_section_list_start(void) const
|
||||
{ return this->vma_sections_.begin(); }
|
||||
|
||||
// Return the start of the list of sections
|
||||
// whose LMAs are taken from this region.
|
||||
Section_list::const_iterator
|
||||
get_lma_section_list_start(void) const
|
||||
{ return this->lma_sections_.begin(); }
|
||||
|
||||
// Return the end of the list of sections
|
||||
// whose VMAs are taken from this region.
|
||||
Section_list::const_iterator
|
||||
get_vma_section_list_end(void) const
|
||||
{ return this->vma_sections_.end(); }
|
||||
|
||||
// Return the end of the list of sections
|
||||
// whose LMAs are taken from this region.
|
||||
Section_list::const_iterator
|
||||
get_lma_section_list_end(void) const
|
||||
{ return this->lma_sections_.end(); }
|
||||
|
||||
private:
|
||||
|
||||
std::string name_;
|
||||
unsigned int attributes_;
|
||||
Expression* start_;
|
||||
Expression* length_;
|
||||
uint64_t current_vma_offset_;
|
||||
uint64_t current_lma_offset_;
|
||||
// A list of sections whose VMAs are set inside this region.
|
||||
Section_list vma_sections_;
|
||||
// A list of sections whose LMAs are set inside this region.
|
||||
Section_list lma_sections_;
|
||||
};
|
||||
|
||||
// Print a memory region.
|
||||
|
||||
void
|
||||
Memory_region::print(FILE* f) const
|
||||
{
|
||||
fprintf(f, " %s", this->name_.c_str());
|
||||
|
||||
unsigned int attrs = this->attributes_;
|
||||
if (attrs != 0)
|
||||
{
|
||||
fprintf(f, " (");
|
||||
do
|
||||
{
|
||||
switch (attrs & - attrs)
|
||||
{
|
||||
case MEM_EXECUTABLE: fputc('x', f); break;
|
||||
case MEM_WRITEABLE: fputc('w', f); break;
|
||||
case MEM_READABLE: fputc('r', f); break;
|
||||
case MEM_ALLOCATABLE: fputc('a', f); break;
|
||||
case MEM_INITIALIZED: fputc('i', f); break;
|
||||
default:
|
||||
gold_unreachable();
|
||||
}
|
||||
attrs &= ~ (attrs & - attrs);
|
||||
}
|
||||
while (attrs != 0);
|
||||
fputc(')', f);
|
||||
}
|
||||
|
||||
fprintf(f, " : origin = ");
|
||||
this->start_->print(f);
|
||||
fprintf(f, ", length = ");
|
||||
this->length_->print(f);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -415,6 +583,11 @@ class Sections_element
|
||||
get_output_section() const
|
||||
{ return NULL; }
|
||||
|
||||
// Set the section's memory regions.
|
||||
virtual void
|
||||
set_memory_region(Memory_region*, bool)
|
||||
{ gold_error(_("Attempt to set a memory region for a non-output section")); }
|
||||
|
||||
// Print the element for debugging purposes.
|
||||
virtual void
|
||||
print(FILE* f) const = 0;
|
||||
@ -1675,6 +1848,22 @@ class Output_section_definition : public Sections_element
|
||||
Script_sections::Section_type
|
||||
section_type() const;
|
||||
|
||||
// Store the memory region to use.
|
||||
void
|
||||
set_memory_region(Memory_region*, bool set_vma);
|
||||
|
||||
void
|
||||
set_section_vma(Expression* address)
|
||||
{ this->address_ = address; }
|
||||
|
||||
void
|
||||
set_section_lma(Expression* address)
|
||||
{ this->load_address_ = address; }
|
||||
|
||||
std::string
|
||||
get_section_name(void) const
|
||||
{ return this->name_; }
|
||||
|
||||
private:
|
||||
static const char*
|
||||
script_section_type_name(Script_section_type);
|
||||
@ -2331,6 +2520,14 @@ Output_section_definition::script_section_type_name(
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Output_section_definition::set_memory_region(Memory_region* mr, bool set_vma)
|
||||
{
|
||||
gold_assert(mr != NULL);
|
||||
// Add the current section to the specified region's list.
|
||||
mr->add_section(this, set_vma);
|
||||
}
|
||||
|
||||
// An output section created to hold orphaned input sections. These
|
||||
// do not actually appear in linker scripts. However, for convenience
|
||||
// when setting the output section addresses, we put a marker to these
|
||||
@ -2580,6 +2777,85 @@ Phdrs_element::print(FILE* f) const
|
||||
fprintf(f, ";\n");
|
||||
}
|
||||
|
||||
// Add a memory region.
|
||||
|
||||
void
|
||||
Script_sections::add_memory_region(const char* name, size_t namelen,
|
||||
unsigned int attributes,
|
||||
Expression* start, Expression* length)
|
||||
{
|
||||
if (this->memory_regions_ == NULL)
|
||||
this->memory_regions_ = new Memory_regions();
|
||||
else if (this->find_memory_region(name, namelen))
|
||||
{
|
||||
gold_error (_("region '%.*s' already defined"), namelen, name);
|
||||
// FIXME: Add a GOLD extension to allow multiple regions with the same
|
||||
// name. This would amount to a single region covering disjoint blocks
|
||||
// of memory, which is useful for embedded devices.
|
||||
}
|
||||
|
||||
// FIXME: Check the length and start values. Currently we allow
|
||||
// non-constant expressions for these values, whereas LD does not.
|
||||
|
||||
// FIXME: Add a GOLD extension to allow NEGATIVE LENGTHS. This would
|
||||
// describe a region that packs from the end address going down, rather
|
||||
// than the start address going up. This would be useful for embedded
|
||||
// devices.
|
||||
|
||||
this->memory_regions_->push_back(new Memory_region(name, namelen, attributes,
|
||||
start, length));
|
||||
}
|
||||
|
||||
// Find a memory region.
|
||||
|
||||
Memory_region*
|
||||
Script_sections::find_memory_region(const char* name, size_t namelen)
|
||||
{
|
||||
if (this->memory_regions_ == NULL)
|
||||
return NULL;
|
||||
|
||||
for (Memory_regions::const_iterator m = this->memory_regions_->begin();
|
||||
m != this->memory_regions_->end();
|
||||
++m)
|
||||
if ((*m)->name_match(name, namelen))
|
||||
return *m;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find a memory region's origin.
|
||||
|
||||
Expression*
|
||||
Script_sections::find_memory_region_origin(const char* name, size_t namelen)
|
||||
{
|
||||
Memory_region* mr = find_memory_region(name, namelen);
|
||||
if (mr == NULL)
|
||||
return NULL;
|
||||
|
||||
return mr->start_address();
|
||||
}
|
||||
|
||||
// Find a memory region's length.
|
||||
|
||||
Expression*
|
||||
Script_sections::find_memory_region_length(const char* name, size_t namelen)
|
||||
{
|
||||
Memory_region* mr = find_memory_region(name, namelen);
|
||||
if (mr == NULL)
|
||||
return NULL;
|
||||
|
||||
return mr->length();
|
||||
}
|
||||
|
||||
// Set the memory region to use for the current section.
|
||||
|
||||
void
|
||||
Script_sections::set_memory_region(Memory_region* mr, bool set_vma)
|
||||
{
|
||||
gold_assert(!this->sections_elements_->empty());
|
||||
this->sections_elements_->back()->set_memory_region(mr, set_vma);
|
||||
}
|
||||
|
||||
// Class Script_sections.
|
||||
|
||||
Script_sections::Script_sections()
|
||||
@ -2587,6 +2863,7 @@ Script_sections::Script_sections()
|
||||
in_sections_clause_(false),
|
||||
sections_elements_(NULL),
|
||||
output_section_(NULL),
|
||||
memory_regions_(NULL),
|
||||
phdrs_elements_(NULL),
|
||||
orphan_section_placement_(NULL),
|
||||
data_segment_align_start_(),
|
||||
@ -2910,6 +3187,41 @@ Script_sections::set_section_addresses(Symbol_table* symtab, Layout* layout)
|
||||
{
|
||||
gold_assert(this->saw_sections_clause_);
|
||||
|
||||
// Walk the memory regions specified in this script, if any.
|
||||
if (this->memory_regions_ != NULL)
|
||||
{
|
||||
for (Memory_regions::const_iterator mr = this->memory_regions_->begin();
|
||||
mr != this->memory_regions_->end();
|
||||
++mr)
|
||||
{
|
||||
// FIXME: What should we do with the attributes of the regions ?
|
||||
|
||||
// For each region, set the VMA of the sections associated with it.
|
||||
for (Memory_region::Section_list::const_iterator s =
|
||||
(*mr)->get_vma_section_list_start();
|
||||
s != (*mr)->get_vma_section_list_end();
|
||||
++s)
|
||||
{
|
||||
(*s)->set_section_vma((*mr)->get_current_vma_address());
|
||||
(*mr)->increment_vma_offset((*s)->get_section_name(),
|
||||
(*s)->get_output_section()->current_data_size(),
|
||||
symtab, layout);
|
||||
}
|
||||
|
||||
// Similarly, set the LMA values.
|
||||
for (Memory_region::Section_list::const_iterator s =
|
||||
(*mr)->get_lma_section_list_start();
|
||||
s != (*mr)->get_lma_section_list_end();
|
||||
++s)
|
||||
{
|
||||
(*s)->set_section_lma((*mr)->get_current_lma_address());
|
||||
(*mr)->increment_lma_offset((*s)->get_section_name(),
|
||||
(*s)->get_output_section()->current_data_size(),
|
||||
symtab, layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implement ONLY_IF_RO/ONLY_IF_RW constraints. These are a pain
|
||||
// for our representation.
|
||||
for (Sections_elements::iterator p = this->sections_elements_->begin();
|
||||
@ -3654,6 +3966,26 @@ Script_sections::release_segments()
|
||||
void
|
||||
Script_sections::print(FILE* f) const
|
||||
{
|
||||
if (this->phdrs_elements_ != NULL)
|
||||
{
|
||||
fprintf(f, "PHDRS {\n");
|
||||
for (Phdrs_elements::const_iterator p = this->phdrs_elements_->begin();
|
||||
p != this->phdrs_elements_->end();
|
||||
++p)
|
||||
(*p)->print(f);
|
||||
fprintf(f, "}\n");
|
||||
}
|
||||
|
||||
if (this->memory_regions_ != NULL)
|
||||
{
|
||||
fprintf(f, "MEMORY {\n");
|
||||
for (Memory_regions::const_iterator m = this->memory_regions_->begin();
|
||||
m != this->memory_regions_->end();
|
||||
++m)
|
||||
(*m)->print(f);
|
||||
fprintf(f, "}\n");
|
||||
}
|
||||
|
||||
if (!this->saw_sections_clause_)
|
||||
return;
|
||||
|
||||
@ -3665,16 +3997,6 @@ Script_sections::print(FILE* f) const
|
||||
(*p)->print(f);
|
||||
|
||||
fprintf(f, "}\n");
|
||||
|
||||
if (this->phdrs_elements_ != NULL)
|
||||
{
|
||||
fprintf(f, "PHDRS {\n");
|
||||
for (Phdrs_elements::const_iterator p = this->phdrs_elements_->begin();
|
||||
p != this->phdrs_elements_->end();
|
||||
++p)
|
||||
(*p)->print(f);
|
||||
fprintf(f, "}\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // End namespace gold.
|
||||
|
||||
@ -37,6 +37,7 @@ struct Parser_output_section_trailer;
|
||||
struct Input_section_spec;
|
||||
class Expression;
|
||||
class Sections_element;
|
||||
class Memory_region;
|
||||
class Phdrs_element;
|
||||
class Output_data;
|
||||
class Output_section_definition;
|
||||
@ -220,6 +221,27 @@ class Script_sections
|
||||
set_saw_segment_start_expression(bool value)
|
||||
{ this->saw_segment_start_expression_ = value; }
|
||||
|
||||
// Add a memory region.
|
||||
void
|
||||
add_memory_region(const char*, size_t, unsigned int,
|
||||
Expression*, Expression*);
|
||||
|
||||
// Find a memory region's origin.
|
||||
Expression*
|
||||
find_memory_region_origin(const char*, size_t);
|
||||
|
||||
// Find a memory region's length.
|
||||
Expression*
|
||||
find_memory_region_length(const char*, size_t);
|
||||
|
||||
// Find a memory region.
|
||||
Memory_region*
|
||||
find_memory_region(const char*, size_t);
|
||||
|
||||
// Set the memory region of the section.
|
||||
void
|
||||
set_memory_region(Memory_region*, bool);
|
||||
|
||||
// Print the contents to the FILE. This is for debugging.
|
||||
void
|
||||
print(FILE*) const;
|
||||
@ -228,6 +250,7 @@ class Script_sections
|
||||
typedef Sections_elements::iterator Elements_iterator;
|
||||
|
||||
private:
|
||||
typedef std::vector<Memory_region*> Memory_regions;
|
||||
typedef std::vector<Phdrs_element*> Phdrs_elements;
|
||||
|
||||
// Create segments.
|
||||
@ -271,6 +294,8 @@ class Script_sections
|
||||
Sections_elements* sections_elements_;
|
||||
// The current output section, if there is one.
|
||||
Output_section_definition* output_section_;
|
||||
// The list of memory regions in the MEMORY clause.
|
||||
Memory_regions* memory_regions_;
|
||||
// The list of program headers in the PHDRS clause.
|
||||
Phdrs_elements* phdrs_elements_;
|
||||
// Where to put orphan sections.
|
||||
@ -286,6 +311,17 @@ class Script_sections
|
||||
bool saw_segment_start_expression_;
|
||||
};
|
||||
|
||||
// Attributes for memory regions.
|
||||
enum
|
||||
{
|
||||
MEM_EXECUTABLE = (1 << 0),
|
||||
MEM_WRITEABLE = (1 << 1),
|
||||
MEM_READABLE = (1 << 2),
|
||||
MEM_ALLOCATABLE = (1 << 3),
|
||||
MEM_INITIALIZED = (1 << 4),
|
||||
MEM_ATTR_MASK = (1 << 5) - 1
|
||||
};
|
||||
|
||||
} // End namespace gold.
|
||||
|
||||
#endif // !defined(GOLD_SCRIPT_SECTIONS_H
|
||||
|
||||
119
gold/script.cc
119
gold/script.cc
@ -3220,3 +3220,122 @@ script_saw_segment_start_expression(void* closurev)
|
||||
Script_sections* ss = closure->script_options()->script_sections();
|
||||
ss->set_saw_segment_start_expression(true);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
script_set_section_region(void* closurev, const char* name, size_t namelen,
|
||||
int set_vma)
|
||||
{
|
||||
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||
if (!closure->script_options()->saw_sections_clause())
|
||||
{
|
||||
gold_error(_("%s:%d:%d: MEMORY region '%.*s' referred to outside of "
|
||||
"SECTIONS clause"),
|
||||
closure->filename(), closure->lineno(), closure->charpos(),
|
||||
namelen, name);
|
||||
return;
|
||||
}
|
||||
|
||||
Script_sections* ss = closure->script_options()->script_sections();
|
||||
Memory_region* mr = ss->find_memory_region(name, namelen);
|
||||
if (mr == NULL)
|
||||
{
|
||||
gold_error(_("%s:%d:%d: MEMORY region '%.*s' not declared"),
|
||||
closure->filename(), closure->lineno(), closure->charpos(),
|
||||
namelen, name);
|
||||
return;
|
||||
}
|
||||
|
||||
ss->set_memory_region(mr, set_vma);
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
script_add_memory(void* closurev, const char* name, size_t namelen,
|
||||
unsigned int attrs, Expression* origin, Expression* length)
|
||||
{
|
||||
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||
Script_sections* ss = closure->script_options()->script_sections();
|
||||
ss->add_memory_region(name, namelen, attrs, origin, length);
|
||||
}
|
||||
|
||||
extern "C" unsigned int
|
||||
script_parse_memory_attr(void* closurev, const char* attrs, size_t attrlen,
|
||||
int invert)
|
||||
{
|
||||
int attributes = 0;
|
||||
|
||||
while (attrlen--)
|
||||
switch (*attrs++)
|
||||
{
|
||||
case 'R':
|
||||
case 'r':
|
||||
attributes |= MEM_READABLE; break;
|
||||
case 'W':
|
||||
case 'w':
|
||||
attributes |= MEM_READABLE | MEM_WRITEABLE; break;
|
||||
case 'X':
|
||||
case 'x':
|
||||
attributes |= MEM_EXECUTABLE; break;
|
||||
case 'A':
|
||||
case 'a':
|
||||
attributes |= MEM_ALLOCATABLE; break;
|
||||
case 'I':
|
||||
case 'i':
|
||||
case 'L':
|
||||
case 'l':
|
||||
attributes |= MEM_INITIALIZED; break;
|
||||
default:
|
||||
yyerror(closurev, _("unknown MEMORY attribute"));
|
||||
}
|
||||
|
||||
if (invert)
|
||||
attributes = (~ attributes) & MEM_ATTR_MASK;
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
script_include_directive(void* closurev, const char*, size_t)
|
||||
{
|
||||
// FIXME: Implement ?
|
||||
yyerror (closurev, _("GOLD does not currently support INCLUDE directives"));
|
||||
}
|
||||
|
||||
// Functions for memory regions.
|
||||
|
||||
extern "C" Expression*
|
||||
script_exp_function_origin(void* closurev, const char* name, size_t namelen)
|
||||
{
|
||||
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||
Script_sections* ss = closure->script_options()->script_sections();
|
||||
Expression* origin = ss->find_memory_region_origin(name, namelen);
|
||||
|
||||
if (origin == NULL)
|
||||
{
|
||||
gold_error(_("undefined memory region '%s' referenced "
|
||||
"in ORIGIN expression"),
|
||||
name);
|
||||
// Create a dummy expression to prevent crashes later on.
|
||||
origin = script_exp_integer(0);
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
|
||||
extern "C" Expression*
|
||||
script_exp_function_length(void* closurev, const char* name, size_t namelen)
|
||||
{
|
||||
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
|
||||
Script_sections* ss = closure->script_options()->script_sections();
|
||||
Expression* length = ss->find_memory_region_length(name, namelen);
|
||||
|
||||
if (length == NULL)
|
||||
{
|
||||
gold_error(_("undefined memory region '%s' referenced "
|
||||
"in LENGTH expression"),
|
||||
name);
|
||||
// Create a dummy expression to prevent crashes later on.
|
||||
length = script_exp_integer(0);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
@ -1779,6 +1779,16 @@ start_lib_test: start_lib_test_main.o libstart_lib_test.a start_lib_test_2.o sta
|
||||
libstart_lib_test.a: start_lib_test_1.o
|
||||
$(TEST_AR) rc $@ $^
|
||||
|
||||
# Test that MEMORY region support works.
|
||||
check_SCRIPTS += memory_test.sh
|
||||
check_DATA += memory_test.stdout
|
||||
MOSTLYCLEANFILES += memory_test.stdout memory_test memory_test.o
|
||||
memory_test: memory_test.s
|
||||
$(COMPILE) -c $< -o memory_test.o
|
||||
$(LINK) -Bgcctestdir/ -nostartfiles -nostdlib -T $(srcdir)/memory_test.t -o $@ memory_test.o
|
||||
memory_test.stdout: memory_test
|
||||
$(TEST_READELF) -lS $< > $@
|
||||
|
||||
endif GCC
|
||||
endif NATIVE_LINKER
|
||||
|
||||
|
||||
@ -312,13 +312,15 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \
|
||||
|
||||
# Test that a strong weak reference remains strong if there is another
|
||||
# weak reference in a DSO.
|
||||
|
||||
# Test that MEMORY region support works.
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_27 = exclude_libs_test.sh \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ discard_locals_test.sh \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ hidden_test.sh \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ retain_symbols_file_test.sh \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ no_version_test.sh \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ strong_ref_weak_def.sh \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.sh
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.sh memory_test.sh
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_28 = exclude_libs_test.syms \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ discard_locals_test.syms \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ discard_locals_relocatable_test1.syms \
|
||||
@ -327,7 +329,8 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ retain_symbols_file_test.stdout \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ no_version_test.stdout \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ strong_ref_weak_def.stdout \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.stdout
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.stdout \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ memory_test.stdout
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_29 = exclude_libs_test.syms \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ libexclude_libs_test_1.a \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ libexclude_libs_test_2.a \
|
||||
@ -351,7 +354,9 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ strong_ref_weak_def.stdout \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref_1.so \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref_2.so \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.stdout
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.stdout \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ memory_test.stdout memory_test \
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ memory_test.o
|
||||
@GCC_TRUE@@MCMODEL_MEDIUM_TRUE@@NATIVE_LINKER_TRUE@am__append_30 = large
|
||||
@GCC_FALSE@large_DEPENDENCIES =
|
||||
@MCMODEL_MEDIUM_FALSE@large_DEPENDENCIES =
|
||||
@ -3282,6 +3287,8 @@ strong_ref_weak_def.sh.log: strong_ref_weak_def.sh
|
||||
@p='strong_ref_weak_def.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
|
||||
dyn_weak_ref.sh.log: dyn_weak_ref.sh
|
||||
@p='dyn_weak_ref.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
|
||||
memory_test.sh.log: memory_test.sh
|
||||
@p='memory_test.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
|
||||
split_i386.sh.log: split_i386.sh
|
||||
@p='split_i386.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
|
||||
split_x86_64.sh.log: split_x86_64.sh
|
||||
@ -4476,6 +4483,11 @@ uninstall-am:
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ -Wl,--start-lib start_lib_test_2.o start_lib_test_3.o -Wl,--end-lib
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@libstart_lib_test.a: start_lib_test_1.o
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_AR) rc $@ $^
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@memory_test: memory_test.s
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(COMPILE) -c $< -o memory_test.o
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(LINK) -Bgcctestdir/ -nostartfiles -nostdlib -T $(srcdir)/memory_test.t -o $@ memory_test.o
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@memory_test.stdout: memory_test
|
||||
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) -lS $< > $@
|
||||
@DEFAULT_TARGET_I386_TRUE@split_i386_1.o: split_i386_1.s
|
||||
@DEFAULT_TARGET_I386_TRUE@ $(TEST_AS) -o $@ $<
|
||||
@DEFAULT_TARGET_I386_TRUE@split_i386_2.o: split_i386_2.s
|
||||
|
||||
14
gold/testsuite/memory_test.s
Normal file
14
gold/testsuite/memory_test.s
Normal file
@ -0,0 +1,14 @@
|
||||
.section .sec0, "a"
|
||||
.word 0
|
||||
|
||||
.section .sec1, "a"
|
||||
.word 0x11
|
||||
|
||||
.section .sec2, "a"
|
||||
.word 0x22
|
||||
|
||||
.section .sec3, "a"
|
||||
.word 0x33
|
||||
|
||||
.section .sec4, "a"
|
||||
.word 0x44
|
||||
48
gold/testsuite/memory_test.sh
Executable file
48
gold/testsuite/memory_test.sh
Executable file
@ -0,0 +1,48 @@
|
||||
#!/bin/sh
|
||||
|
||||
# memory_test.sh -- test MEMORY regions.
|
||||
|
||||
# Copyright 2010 Free Software Foundation, Inc.
|
||||
# Written by Nick Clifton <nickc@redhat.com>
|
||||
|
||||
# This file is part of gold.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
||||
# MA 02110-1301, USA.
|
||||
|
||||
|
||||
check()
|
||||
{
|
||||
file=$1
|
||||
pattern=$2
|
||||
found=`grep "$pattern" $file`
|
||||
if test -z "$found"; then
|
||||
echo "pattern \"$pattern\" not found in file $file."
|
||||
echo $found
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check memory_test.stdout \
|
||||
" LOAD 0x001000 0x00000000 0x00000000 0x00002 0x00002 R 0x1000"
|
||||
check memory_test.stdout \
|
||||
" LOAD 0x00112c 0x00001000 0x0000012c 0x00002 0x00002 R 0x1000"
|
||||
check memory_test.stdout \
|
||||
" LOAD 0x002000 0x00005000 0x00005000 0x00002 0x00002 R 0x1000"
|
||||
check memory_test.stdout \
|
||||
" LOAD 0x00203c 0x00004000 0x0000603c 0x0002a 0x0002a R E 0x1000"
|
||||
|
||||
|
||||
exit 0
|
||||
29
gold/testsuite/memory_test.t
Normal file
29
gold/testsuite/memory_test.t
Normal file
@ -0,0 +1,29 @@
|
||||
MEMORY
|
||||
{
|
||||
region1 : ORIGIN = 0x1000, LENGTH = 0x1000 ,
|
||||
region2 (r) : org = 0x2000, len = 300
|
||||
region3 (wx) : o = 0x4000, l = 4
|
||||
region4 (!r) : o = 0x6000 + 60, len = 0x30 * 0x6
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.sec0 : { *(*.sec0) }
|
||||
|
||||
.sec1 ORIGIN (region1) : AT(LENGTH (region2)) { *(*.sec1) }
|
||||
|
||||
fred = ORIGIN (region1) + LENGTH (region1) ;
|
||||
|
||||
.sec2 : { *(*.sec2) } > region3 AT> region4
|
||||
|
||||
.sec3 0x5000 : { *(*.sec3) }
|
||||
|
||||
/* In theory we could put:
|
||||
|
||||
/DISCARD/ : { *(*) }
|
||||
|
||||
here as we do not need any other sections for this test.
|
||||
In practice however doing so breaks GOLD as it relies upon
|
||||
being able to create/find various other sections such as
|
||||
.dynamic, .dynsym and .gnu.hash. */
|
||||
}
|
||||
@ -143,6 +143,7 @@
|
||||
%token INFO
|
||||
%token INPUT
|
||||
%token KEEP
|
||||
%token LEN
|
||||
%token LENGTH /* LENGTH, l, len */
|
||||
%token LOADADDR
|
||||
%token LOCAL /* local */
|
||||
@ -157,6 +158,7 @@
|
||||
%token NOLOAD
|
||||
%token ONLY_IF_RO
|
||||
%token ONLY_IF_RW
|
||||
%token ORG
|
||||
%token ORIGIN /* ORIGIN, o, org */
|
||||
%token OUTPUT
|
||||
%token OUTPUT_ARCH
|
||||
@ -215,7 +217,7 @@
|
||||
%type <wildcard_section> wildcard_file wildcard_section
|
||||
%type <string_list> exclude_names
|
||||
%type <string> wildcard_name
|
||||
%type <integer> phdr_type
|
||||
%type <integer> phdr_type memory_attr
|
||||
%type <phdr_info> phdr_info
|
||||
%type <versyms> vers_defns
|
||||
%type <versnode> vers_tag
|
||||
@ -250,6 +252,7 @@ file_cmd:
|
||||
| INHIBIT_COMMON_ALLOCATION
|
||||
{ script_set_common_allocation(closure, 0); }
|
||||
| INPUT '(' input_list ')'
|
||||
| MEMORY '{' memory_defs '}'
|
||||
| OPTION '(' string ')'
|
||||
{ script_parse_option(closure, $3.value, $3.length); }
|
||||
| OUTPUT_FORMAT '(' string ')'
|
||||
@ -471,14 +474,14 @@ section_trailer:
|
||||
/* A memory specification for an output section. */
|
||||
opt_memspec:
|
||||
'>' string
|
||||
{ yyerror(closure, "memory regions are not supported"); }
|
||||
{ script_set_section_region(closure, $2.value, $2.length, 1); }
|
||||
| /* empty */
|
||||
;
|
||||
|
||||
/* A memory specification for where to load an output section. */
|
||||
opt_at_memspec:
|
||||
AT '>' string
|
||||
{ yyerror(closure, "memory regions are not supported"); }
|
||||
{ script_set_section_region(closure, $3.value, $3.length, 0); }
|
||||
| /* empty */
|
||||
;
|
||||
|
||||
@ -688,6 +691,50 @@ file_or_sections_cmd:
|
||||
{ script_add_assertion(closure, $3, $5.value, $5.length); }
|
||||
;
|
||||
|
||||
/* A list of MEMORY definitions. */
|
||||
memory_defs:
|
||||
memory_defs opt_comma memory_def
|
||||
| /* empty */
|
||||
;
|
||||
|
||||
/* A single MEMORY definition. */
|
||||
memory_def:
|
||||
string memory_attr ':' memory_origin '=' parse_exp opt_comma memory_length '=' parse_exp
|
||||
{ script_add_memory(closure, $1.value, $1.length, $2, $6, $10); }
|
||||
|
|
||||
/* LD supports an INCLUDE directive here, currently GOLD does not. */
|
||||
INCLUDE string
|
||||
{ script_include_directive(closure, $2.value, $2.length); }
|
||||
|
|
||||
;
|
||||
|
||||
/* The (optional) attributes of a MEMORY region. */
|
||||
memory_attr:
|
||||
'(' string ')'
|
||||
{ $$ = script_parse_memory_attr(closure, $2.value, $2.length, 0); }
|
||||
| /* Inverted attributes. */
|
||||
'(' '!' string ')'
|
||||
{ $$ = script_parse_memory_attr(closure, $3.value, $3.length, 1); }
|
||||
| /* empty */
|
||||
{ $$ = 0; }
|
||||
;
|
||||
|
||||
memory_origin:
|
||||
ORIGIN
|
||||
|
|
||||
ORG
|
||||
|
|
||||
'o'
|
||||
;
|
||||
|
||||
memory_length:
|
||||
LENGTH
|
||||
|
|
||||
LEN
|
||||
|
|
||||
'l'
|
||||
;
|
||||
|
||||
/* A list of program header definitions. */
|
||||
phdrs_defs:
|
||||
phdrs_defs phdr_def
|
||||
@ -885,9 +932,9 @@ exp:
|
||||
| LOADADDR '(' string ')'
|
||||
{ $$ = script_exp_function_loadaddr($3.value, $3.length); }
|
||||
| ORIGIN '(' string ')'
|
||||
{ $$ = script_exp_function_origin($3.value, $3.length); }
|
||||
{ $$ = script_exp_function_origin(closure, $3.value, $3.length); }
|
||||
| LENGTH '(' string ')'
|
||||
{ $$ = script_exp_function_length($3.value, $3.length); }
|
||||
{ $$ = script_exp_function_length(closure, $3.value, $3.length); }
|
||||
| CONSTANT '(' string ')'
|
||||
{ $$ = script_exp_function_constant($3.value, $3.length); }
|
||||
| ABSOLUTE '(' exp ')'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user