hdr_os->set_after_input_sections();
- 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);
+ if (!this->script_options_->saw_phdrs_clause())
+ {
+ 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);
+ }
this->eh_frame_data_->set_eh_frame_hdr(hdr_posd);
}
if (this->script_options_->saw_sections_clause())
return os;
+ gold_assert(!this->script_options_->saw_phdrs_clause());
+
// This output section goes into a PT_LOAD segment.
elfcpp::Elf_Word seg_flags = Layout::section_flags_to_segment(flags);
return *p;
}
+ gold_assert(!this->script_options_->saw_phdrs_clause());
+
Output_segment* load_seg = this->make_output_segment(elfcpp::PT_LOAD,
elfcpp::PF_R);
return load_seg;
// Create the PT_PHDR segment which will hold the program
// headers.
- phdr_seg = this->make_output_segment(elfcpp::PT_PHDR, elfcpp::PF_R);
+ if (!this->script_options_->saw_phdrs_clause())
+ phdr_seg = this->make_output_segment(elfcpp::PT_PHDR, elfcpp::PF_R);
// Create the dynamic symbol table, including the hash table.
Output_section* dynstr;
this->special_output_list_.push_back(file_header);
this->special_output_list_.push_back(segment_headers);
+ if (this->script_options_->saw_phdrs_clause())
+ {
+ // 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.
unsigned int shndx = 1;
}
else
{
+ if (this->script_options_->saw_phdrs_clause())
+ return;
int flags = elfcpp::PF_R | elfcpp::PF_W;
if (is_stack_executable)
flags |= elfcpp::PF_X;
false);
osec->add_output_section_data(odata);
- Output_segment* oseg = this->make_output_segment(elfcpp::PT_INTERP,
- elfcpp::PF_R);
- oseg->add_initial_output_section(osec, elfcpp::PF_R);
+ if (!this->script_options_->saw_phdrs_clause())
+ {
+ Output_segment* oseg = this->make_output_segment(elfcpp::PT_INTERP,
+ elfcpp::PF_R);
+ oseg->add_initial_output_section(osec, elfcpp::PF_R);
+ }
}
// Finish the .dynamic section and PT_DYNAMIC segment.
Layout::finish_dynamic_section(const Input_objects* input_objects,
const Symbol_table* symtab)
{
- Output_segment* oseg = this->make_output_segment(elfcpp::PT_DYNAMIC,
- (elfcpp::PF_R
- | elfcpp::PF_W));
- oseg->add_initial_output_section(this->dynamic_section_,
- elfcpp::PF_R | elfcpp::PF_W);
+ if (!this->script_options_->saw_phdrs_clause())
+ {
+ Output_segment* oseg = this->make_output_segment(elfcpp::PT_DYNAMIC,
+ (elfcpp::PF_R
+ | elfcpp::PF_W));
+ oseg->add_initial_output_section(this->dynamic_section_,
+ elfcpp::PF_R | elfcpp::PF_W);
+ }
Output_data_dynamic* const odyn = this->dynamic_data_;
return count;
}
+// Return the section attached to the list segment with the lowest
+// load address. This is used when handling a PHDRS clause in a
+// linker script.
+
+Output_section*
+Output_segment::section_with_lowest_load_address() const
+{
+ Output_section* found = NULL;
+ uint64_t found_lma = 0;
+ this->lowest_load_address_in_list(&this->output_data_, &found, &found_lma);
+
+ Output_section* found_data = found;
+ this->lowest_load_address_in_list(&this->output_bss_, &found, &found_lma);
+ if (found != found_data && found_data != NULL)
+ {
+ gold_error(_("nobits section %s may not precede progbits section %s "
+ "in same segment"),
+ found->name(), found_data->name());
+ return NULL;
+ }
+
+ return found;
+}
+
+// Look through a list for a section with a lower load address.
+
+void
+Output_segment::lowest_load_address_in_list(const Output_data_list* pdl,
+ Output_section** found,
+ uint64_t* found_lma) const
+{
+ for (Output_data_list::const_iterator p = pdl->begin();
+ p != pdl->end();
+ ++p)
+ {
+ if (!(*p)->is_section())
+ continue;
+ Output_section* os = static_cast<Output_section*>(*p);
+ uint64_t lma = (os->has_load_address()
+ ? os->load_address()
+ : os->address());
+ if (*found == NULL || lma < *found_lma)
+ {
+ *found = os;
+ *found_lma = lma;
+ }
+ }
+}
+
// Write the segment data into *OPHDR.
template<int size, bool big_endian>
this->are_addresses_set_ = true;
}
+ // Set the segment flags. This is only used if we have a PHDRS
+ // clause which explicitly specifies the flags.
+ void
+ set_flags(elfcpp::Elf_Word flags)
+ { this->flags_ = flags; }
+
// Set the address of the segment to ADDR and the offset to *POFF
// and set the addresses and offsets of all contained output
// sections accordingly. Set the section indexes of all contained
unsigned int
output_section_count() const;
+ // Return the section attached to the list segment with the lowest
+ // load address. This is used when handling a PHDRS clause in a
+ // linker script.
+ Output_section*
+ section_with_lowest_load_address() const;
+
// Write the segment header into *OPHDR.
template<int size, bool big_endian>
void
unsigned int
dynamic_reloc_count_list(const Output_data_list*) const;
+ // Find the section with the lowest load address in an
+ // Output_data_list.
+ void
+ lowest_load_address_in_list(const Output_data_list* pdl,
+ Output_section** found,
+ uint64_t* found_lma) const;
+
// Write the section headers in the list into V.
template<int size, bool big_endian>
unsigned char*
enum Section_constraint constraint;
};
-/* The information we store for an output section trailer in the bison
- parser. */
-
-struct Parser_output_section_trailer
-{
- /* The fill value. This may be NULL. */
- Expression_ptr fill;
-};
-
/* We keep vectors of strings. In order to manage this in both C and
C++, we use a pointer to a vector. This assumes that all pointers
look the same. */
typedef void* String_list_ptr;
#endif
+/* The information we store for an output section trailer in the bison
+ parser. */
+
+struct Parser_output_section_trailer
+{
+ /* The fill value. This may be NULL. */
+ Expression_ptr fill;
+ /* The program segments this section should go into. This may be
+ NULL. */
+ String_list_ptr phdrs;
+};
+
/* The different sorts we can find in a linker script. */
enum Sort_wildcard
struct Wildcard_sections input_sections;
};
+/* Information for a program header. */
+
+struct Phdr_info
+{
+ /* A boolean value: whether to include the file header. */
+ int includes_filehdr;
+ /* A boolean value: whether to include the program headers. */
+ int includes_phdrs;
+ /* A boolean value: whether the flags field is valid. */
+ int is_flags_valid;
+ /* The value to use for the flags. */
+ unsigned int flags;
+ /* The load address. */
+ Expression_ptr load_address;
+};
+
struct Version_dependency_list;
struct Version_expression_list;
struct Version_tree;
extern String_list_ptr
script_string_list_append(String_list_ptr, String_list_ptr);
+/* Define a new program header. */
+
+extern void
+script_add_phdr(void* closure, const char* name, size_t namelen,
+ unsigned int type, const struct Phdr_info*);
+
+/* Convert a program header string to a type. */
+
+extern unsigned int
+script_phdr_string_to_type(void* closure, const char*, size_t);
+
/* Called by the bison parser for expressions. */
extern Expression_ptr
#include <cstring>
#include <algorithm>
#include <list>
+#include <map>
#include <string>
#include <vector>
#include <fnmatch.h>
alternate_constraint(Output_section_definition*, Section_constraint)
{ return false; }
+ // Get the list of segments to use for an allocated section when
+ // using a PHDRS clause. If this is an allocated section, return
+ // the Output_section, and set *PHDRS_LIST to the list of PHDRS to
+ // which it should be attached. If the PHDRS were not specified,
+ // don't change *PHDRS_LIST.
+ virtual Output_section*
+ allocate_to_segment(String_list**)
+ { return NULL; }
+
// Print the element for debugging purposes.
virtual void
print(FILE* f) const = 0;
bool
alternate_constraint(Output_section_definition*, Section_constraint);
+ // Get the list of segments to use for an allocated section when
+ // using a PHDRS clause. If this is an allocated section, return
+ // the Output_section, and set *PHDRS_LIST to the list of PHDRS to
+ // which it should be attached. If the PHDRS were not specified,
+ // don't change *PHDRS_LIST.
+ Output_section*
+ allocate_to_segment(String_list** phdrs_list);
+
// Print the contents to the FILE. This is for debugging.
void
print(FILE*) const;
Section_constraint constraint_;
// The fill value. This may be NULL.
Expression* fill_;
+ // The list of segments this section should go into. This may be
+ // NULL.
+ String_list* phdrs_;
// The list of elements defining the section.
Output_section_elements elements_;
// The Output_section created for this definition. This will be
subalign_(header->subalign),
constraint_(header->constraint),
fill_(NULL),
+ phdrs_(NULL),
elements_(),
output_section_(NULL)
{
Output_section_definition::finish(const Parser_output_section_trailer* trailer)
{
this->fill_ = trailer->fill;
+ this->phdrs_ = trailer->phdrs;
}
// Add a symbol to be defined.
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;
- if (this->name_ == ".bss")
- 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->name_ == ".interp"
- || this->name_.compare(0, 5, ".note") == 0)
+ 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))
- return true;
- if (this->name_.compare(0, 4, ".rel") == 0)
+ {
+ *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;
- if (this->name_ == ".data")
- 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;
- if (this->name_ == ".text")
- return true;
}
- else if (os->type() == elfcpp::SHT_PROGBITS)
+ 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
- && (this->output_section_->flags() & elfcpp::SHF_EXECINSTR) == 0)
- return true;
- if (this->name_ == ".rodata")
+ && (this->output_section_->flags() & elfcpp::SHF_WRITE) == 0)
return true;
}
return true;
}
+// Get the list of segments to use for an allocated section when using
+// a PHDRS clause. If this is an allocated section, return the
+// Output_section, and set *PHDRS_LIST to the list of PHDRS to which
+// it should be attached. If the PHDRS were not specified, don't
+// change *PHDRS_LIST.
+
+Output_section*
+Output_section_definition::allocate_to_segment(String_list** phdrs_list)
+{
+ if (this->output_section_ == NULL)
+ return NULL;
+ if ((this->output_section_->flags() & elfcpp::SHF_ALLOC) == 0)
+ return NULL;
+ if (this->phdrs_ != NULL)
+ *phdrs_list = this->phdrs_;
+ return this->output_section_;
+}
+
// Print for debugging.
void
void
set_section_addresses(Symbol_table*, Layout*, bool*, uint64_t*);
+ // Get the list of segments to use for an allocated section when
+ // using a PHDRS clause. If this is an allocated section, return
+ // the Output_section.
+ Output_section*
+ allocate_to_segment(String_list**);
+
// Print for debugging.
void
print(FILE* f) const
*dot_value = address;
}
+// Get the list of segments to use for an allocated section when using
+// a PHDRS clause. If this is an allocated section, return the
+// Output_section. We don't change the list of segments.
+
+Output_section*
+Orphan_output_section::allocate_to_segment(String_list**)
+{
+ if ((this->os_->flags() & elfcpp::SHF_ALLOC) == 0)
+ return NULL;
+ return this->os_;
+}
+
+// Class Phdrs_element. A program header from a PHDRS clause.
+
+class Phdrs_element
+{
+ public:
+ Phdrs_element(const char* name, size_t namelen, unsigned int type,
+ bool includes_filehdr, bool includes_phdrs,
+ bool is_flags_valid, unsigned int flags,
+ Expression* load_address)
+ : name_(name, namelen), type_(type), includes_filehdr_(includes_filehdr),
+ includes_phdrs_(includes_phdrs), is_flags_valid_(is_flags_valid),
+ flags_(flags), load_address_(load_address), load_address_value_(0),
+ segment_(NULL)
+ { }
+
+ // Return the name of this segment.
+ const std::string&
+ name() const
+ { return this->name_; }
+
+ // Return the type of the segment.
+ unsigned int
+ type() const
+ { return this->type_; }
+
+ // Whether to include the file header.
+ bool
+ includes_filehdr() const
+ { return this->includes_filehdr_; }
+
+ // Whether to include the program headers.
+ bool
+ includes_phdrs() const
+ { return this->includes_phdrs_; }
+
+ // Return whether there is a load address.
+ bool
+ has_load_address() const
+ { return this->load_address_ != NULL; }
+
+ // Evaluate the load address expression if there is one.
+ void
+ eval_load_address(Symbol_table* symtab, Layout* layout)
+ {
+ if (this->load_address_ != NULL)
+ this->load_address_value_ = this->load_address_->eval(symtab, layout);
+ }
+
+ // Return the load address.
+ uint64_t
+ load_address() const
+ {
+ gold_assert(this->load_address_ != NULL);
+ return this->load_address_value_;
+ }
+
+ // Create the segment.
+ Output_segment*
+ create_segment(Layout* layout)
+ {
+ this->segment_ = layout->make_output_segment(this->type_, this->flags_);
+ return this->segment_;
+ }
+
+ // Return the segment.
+ Output_segment*
+ segment()
+ { return this->segment_; }
+
+ // Set the segment flags if appropriate.
+ void
+ set_flags_if_valid()
+ {
+ if (this->is_flags_valid_)
+ this->segment_->set_flags(this->flags_);
+ }
+
+ private:
+ // The name used in the script.
+ std::string name_;
+ // The type of the segment (PT_LOAD, etc.).
+ unsigned int type_;
+ // Whether this segment includes the file header.
+ bool includes_filehdr_;
+ // Whether this segment includes the section headers.
+ bool includes_phdrs_;
+ // Whether the flags were explicitly specified.
+ bool is_flags_valid_;
+ // The flags for this segment (PF_R, etc.) if specified.
+ unsigned int flags_;
+ // The expression for the load address for this segment. This may
+ // be NULL.
+ Expression* load_address_;
+ // The actual load address from evaluating the expression.
+ uint64_t load_address_value_;
+ // The segment itself.
+ Output_segment* segment_;
+};
+
// Class Script_sections.
Script_sections::Script_sections()
: saw_sections_clause_(false),
in_sections_clause_(false),
sections_elements_(NULL),
- output_section_(NULL)
+ output_section_(NULL),
+ phdrs_elements_(NULL)
{
}
++p)
(*p)->set_section_addresses(symtab, layout, &dot_has_value, &dot_value);
+ if (this->phdrs_elements_ != NULL)
+ {
+ for (Phdrs_elements::iterator p = this->phdrs_elements_->begin();
+ p != this->phdrs_elements_->end();
+ ++p)
+ (*p)->eval_load_address(symtab, layout);
+ }
+
return this->create_segments(layout);
}
&& (os->flags() & elfcpp::SHF_TLS) == 0);
}
+// Return the size taken by the file header and the program headers.
+
+size_t
+Script_sections::total_header_size(Layout* layout) const
+{
+ size_t segment_count = layout->segment_count();
+ size_t file_header_size;
+ size_t segment_headers_size;
+ if (parameters->get_size() == 32)
+ {
+ file_header_size = elfcpp::Elf_sizes<32>::ehdr_size;
+ segment_headers_size = segment_count * elfcpp::Elf_sizes<32>::phdr_size;
+ }
+ else if (parameters->get_size() == 64)
+ {
+ file_header_size = elfcpp::Elf_sizes<64>::ehdr_size;
+ segment_headers_size = segment_count * elfcpp::Elf_sizes<64>::phdr_size;
+ }
+ else
+ gold_unreachable();
+
+ return file_header_size + segment_headers_size;
+}
+
+// Return the amount we have to subtract from the LMA to accomodate
+// headers of the given size. The complication is that the file
+// header have to be at the start of a page, as otherwise it will not
+// be at the start of the file.
+
+uint64_t
+Script_sections::header_size_adjustment(uint64_t lma,
+ size_t sizeof_headers) const
+{
+ const uint64_t abi_pagesize = parameters->target()->abi_pagesize();
+ uint64_t hdr_lma = lma - sizeof_headers;
+ hdr_lma &= ~(abi_pagesize - 1);
+ return lma - hdr_lma;
+}
+
// Create the PT_LOAD segments when using a SECTIONS clause. Returns
// the segment which should hold the file header and segment headers,
// if any.
if (parameters->output_is_object())
return NULL;
+ if (this->saw_phdrs_clause())
+ return create_segments_from_phdrs_clause(layout);
+
Layout::Section_list sections;
layout->get_allocated_sections(§ions);
// efficient in any case. We try to use the first PT_LOAD segment
// if we can, otherwise we make a new one.
- size_t segment_count = layout->segment_count();
- size_t file_header_size;
- size_t segment_headers_size;
- if (parameters->get_size() == 32)
- {
- file_header_size = elfcpp::Elf_sizes<32>::ehdr_size;
- segment_headers_size = segment_count * elfcpp::Elf_sizes<32>::phdr_size;
- }
- else if (parameters->get_size() == 64)
- {
- file_header_size = elfcpp::Elf_sizes<64>::ehdr_size;
- segment_headers_size = segment_count * elfcpp::Elf_sizes<64>::phdr_size;
- }
- else
- gold_unreachable();
-
- size_t sizeof_headers = file_header_size + segment_headers_size;
+ size_t sizeof_headers = this->total_header_size(layout);
if (first_seg != NULL
&& (first_seg->paddr() & (abi_pagesize - 1)) >= sizeof_headers)
uint64_t vma = first_seg->vaddr();
uint64_t lma = first_seg->paddr();
- // We want a segment with the same relationship between VMA and
- // LMA, but with enough room for the headers, and aligned to
- // load at the start of a page.
- uint64_t hdr_lma = lma - sizeof_headers;
- hdr_lma &= ~(abi_pagesize - 1);
- if (lma >= hdr_lma && vma >= (lma - hdr_lma))
- load_seg->set_addresses(vma - (lma - hdr_lma), hdr_lma);
+ uint64_t subtract = this->header_size_adjustment(lma, sizeof_headers);
+ if (lma >= subtract && vma >= subtract)
+ load_seg->set_addresses(vma - subtract, lma - subtract);
else
{
// We could handle this case by create the file header
Layout* layout,
const Layout::Section_list* sections)
{
+ gold_assert(!this->saw_phdrs_clause());
+
bool saw_tls = false;
for (Layout::Section_list::const_iterator p = sections->begin();
p != sections->end();
}
}
+// Add a program header. The PHDRS clause is syntactically distinct
+// from the SECTIONS clause, but we implement it with the SECTIONS
+// support becauase PHDRS is useless if there is no SECTIONS clause.
+
+void
+Script_sections::add_phdr(const char* name, size_t namelen, unsigned int type,
+ bool includes_filehdr, bool includes_phdrs,
+ bool is_flags_valid, unsigned int flags,
+ Expression* load_address)
+{
+ if (this->phdrs_elements_ == NULL)
+ this->phdrs_elements_ = new Phdrs_elements();
+ this->phdrs_elements_->push_back(new Phdrs_element(name, namelen, type,
+ includes_filehdr,
+ includes_phdrs,
+ is_flags_valid, flags,
+ load_address));
+}
+
// Return the number of segments we expect to create based on the
// SECTIONS clause. This is used to implement SIZEOF_HEADERS.
size_t
Script_sections::expected_segment_count(const Layout* layout) const
{
+ if (this->saw_phdrs_clause())
+ return this->phdrs_elements_->size();
+
Layout::Section_list sections;
layout->get_allocated_sections(§ions);
return ret;
}
+// Create the segments from a PHDRS clause. Return the segment which
+// should hold the file header and program headers, if any.
+
+Output_segment*
+Script_sections::create_segments_from_phdrs_clause(Layout* layout)
+{
+ this->attach_sections_using_phdrs_clause(layout);
+ return this->set_phdrs_clause_addresses(layout);
+}
+
+// Create the segments from the PHDRS clause, and put the output
+// sections in them.
+
+void
+Script_sections::attach_sections_using_phdrs_clause(Layout* layout)
+{
+ typedef std::map<std::string, Output_segment*> Name_to_segment;
+ Name_to_segment name_to_segment;
+ for (Phdrs_elements::const_iterator p = this->phdrs_elements_->begin();
+ p != this->phdrs_elements_->end();
+ ++p)
+ name_to_segment[(*p)->name()] = (*p)->create_segment(layout);
+
+ // Walk through the output sections and attach them to segments.
+ // Output sections in the script which do not list segments are
+ // attached to the same set of segments as the immediately preceding
+ // output section.
+ String_list* phdr_names = NULL;
+ for (Sections_elements::const_iterator p = this->sections_elements_->begin();
+ p != this->sections_elements_->end();
+ ++p)
+ {
+ Output_section* os = (*p)->allocate_to_segment(&phdr_names);
+ if (os == NULL)
+ continue;
+
+ if (phdr_names == NULL)
+ {
+ gold_error(_("allocated section not in any segment"));
+ continue;
+ }
+
+ bool in_load_segment = false;
+ for (String_list::const_iterator q = phdr_names->begin();
+ q != phdr_names->end();
+ ++q)
+ {
+ Name_to_segment::const_iterator r = name_to_segment.find(*q);
+ if (r == name_to_segment.end())
+ gold_error(_("no segment %s"), q->c_str());
+ else
+ {
+ elfcpp::Elf_Word seg_flags =
+ Layout::section_flags_to_segment(os->flags());
+ r->second->add_output_section(os, seg_flags);
+
+ if (r->second->type() == elfcpp::PT_LOAD)
+ {
+ if (in_load_segment)
+ gold_error(_("section in two PT_LOAD segments"));
+ in_load_segment = true;
+ }
+ }
+ }
+
+ if (!in_load_segment)
+ gold_error(_("allocated section not in any PT_LOAD segment"));
+ }
+}
+
+// Set the addresses for segments created from a PHDRS clause. Return
+// the segment which should hold the file header and program headers,
+// if any.
+
+Output_segment*
+Script_sections::set_phdrs_clause_addresses(Layout* layout)
+{
+ Output_segment* load_seg = NULL;
+ for (Phdrs_elements::const_iterator p = this->phdrs_elements_->begin();
+ p != this->phdrs_elements_->end();
+ ++p)
+ {
+ // Note that we have to set the flags after adding the output
+ // sections to the segment, as adding an output segment can
+ // change the flags.
+ (*p)->set_flags_if_valid();
+
+ Output_segment* oseg = (*p)->segment();
+
+ if (oseg->type() != elfcpp::PT_LOAD)
+ {
+ // The addresses of non-PT_LOAD segments are set from the
+ // PT_LOAD segments.
+ if ((*p)->has_load_address())
+ gold_error(_("may only specify load address for PT_LOAD segment"));
+ continue;
+ }
+
+ // The output sections should have addresses from the SECTIONS
+ // clause. The addresses don't have to be in order, so find the
+ // one with the lowest load address. Use that to set the
+ // address of the segment.
+
+ Output_section* osec = oseg->section_with_lowest_load_address();
+ if (osec == NULL)
+ {
+ oseg->set_addresses(0, 0);
+ continue;
+ }
+
+ uint64_t vma = osec->address();
+ uint64_t lma = osec->has_load_address() ? osec->load_address() : vma;
+
+ // Override the load address of the section with the load
+ // address specified for the segment.
+ if ((*p)->has_load_address())
+ {
+ if (osec->has_load_address())
+ gold_warning(_("PHDRS load address overrides "
+ "section %s load address"),
+ osec->name());
+
+ lma = (*p)->load_address();
+ }
+
+ bool headers = (*p)->includes_filehdr() && (*p)->includes_phdrs();
+ if (!headers && ((*p)->includes_filehdr() || (*p)->includes_phdrs()))
+ {
+ // We could support this if we wanted to.
+ gold_error(_("using only one of FILEHDR and PHDRS is "
+ "not currently supported"));
+ }
+ if (headers)
+ {
+ size_t sizeof_headers = this->total_header_size(layout);
+ uint64_t subtract = this->header_size_adjustment(lma,
+ sizeof_headers);
+ if (lma >= subtract && vma >= subtract)
+ {
+ lma -= subtract;
+ vma -= subtract;
+ }
+ else
+ {
+ gold_error(_("sections loaded on first page without room "
+ "for file and program headers "
+ "are not supported"));
+ }
+
+ if (load_seg != NULL)
+ gold_error(_("using FILEHDR and PHDRS on more than one "
+ "PT_LOAD segment is not currently supported"));
+ load_seg = oseg;
+ }
+
+ oseg->set_addresses(vma, lma);
+ }
+
+ return load_seg;
+}
+
+// Add the file header and segment headers to non-load segments
+// specified in the PHDRS clause.
+
+void
+Script_sections::put_headers_in_phdrs(Output_data* file_header,
+ Output_data* segment_headers)
+{
+ gold_assert(this->saw_phdrs_clause());
+ for (Phdrs_elements::iterator p = this->phdrs_elements_->begin();
+ p != this->phdrs_elements_->end();
+ ++p)
+ {
+ if ((*p)->type() != elfcpp::PT_LOAD)
+ {
+ if ((*p)->includes_phdrs())
+ (*p)->segment()->add_initial_output_data(segment_headers);
+ if ((*p)->includes_filehdr())
+ (*p)->segment()->add_initial_output_data(file_header);
+ }
+ }
+}
+
// Print the SECTIONS clause to F for debugging.
void
struct Input_section_spec;
class Expression;
class Sections_element;
+class Phdrs_element;
+class Output_data;
class Output_section_definition;
class Output_section;
class Output_segment;
in_sections_clause() const
{ return this->in_sections_clause_; }
+ // Return whether we ever saw a PHDRS clause. We ignore the PHDRS
+ // clause unless we also saw a SECTIONS clause.
+ bool
+ saw_phdrs_clause() const
+ { return this->saw_sections_clause_ && this->phdrs_elements_ != NULL; }
+
// Start processing entries for an output section.
void
start_output_section(const char* name, size_t namelen,
Output_segment*
set_section_addresses(Symbol_table*, Layout*);
+ // Add a program header definition.
+ void
+ add_phdr(const char* name, size_t namelen, unsigned int type,
+ bool filehdr, bool phdrs, bool is_flags_valid, unsigned int flags,
+ Expression* load_address);
+
// Return the number of segments we expect to create based on the
// SECTIONS clause.
size_t
expected_segment_count(const Layout*) const;
+ // Add the file header and segment header to non-load segments as
+ // specified by the PHDRS clause.
+ void
+ put_headers_in_phdrs(Output_data* file_header, Output_data* segment_headers);
+
// Print the contents to the FILE. This is for debugging.
void
print(FILE*) const;
private:
typedef std::vector<Sections_element*> Sections_elements;
+ typedef std::vector<Phdrs_element*> Phdrs_elements;
+
// Create segments.
Output_segment*
create_segments(Layout*);
static bool
is_bss_section(const Output_section*);
+ // Return the total size of the headers.
+ size_t
+ total_header_size(Layout* layout) const;
+
+ // Return the amount we have to subtract from the LMA to accomodate
+ // headers of the given size.
+ uint64_t
+ header_size_adjustment(uint64_t lma, size_t sizeof_headers) const;
+
+ // Create the segments from a PHDRS clause.
+ Output_segment*
+ create_segments_from_phdrs_clause(Layout* layout);
+
+ // Attach sections to segments from a PHDRS clause.
+ void
+ attach_sections_using_phdrs_clause(Layout*);
+
+ // Set addresses of segments from a PHDRS clause.
+ Output_segment*
+ set_phdrs_clause_addresses(Layout*);
+
// True if we ever saw a SECTIONS clause.
bool saw_sections_clause_;
// True if we are currently processing a SECTIONS clause.
Sections_elements* sections_elements_;
// The current output section, if there is one.
Output_section_definition* output_section_;
+ // The list of program headers in the PHDRS clause.
+ Phdrs_elements* phdrs_elements_;
};
} // End namespace gold.
extern "C" String_list_ptr
script_string_list_push_back(String_list_ptr pv, const char* str, size_t len)
{
- pv->push_back(std::string(str, len));
- return pv;
+ if (pv == NULL)
+ return script_new_string_list(str, len);
+ else
+ {
+ pv->push_back(std::string(str, len));
+ return pv;
+ }
}
// Concatenate two string lists. Either or both may be NULL. The way
pv1->insert(pv1->end(), pv2->begin(), pv2->end());
return pv1;
}
+
+// Add a new program header.
+
+extern "C" void
+script_add_phdr(void* closurev, const char* name, size_t namelen,
+ unsigned int type, const Phdr_info* info)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ bool includes_filehdr = info->includes_filehdr != 0;
+ bool includes_phdrs = info->includes_phdrs != 0;
+ bool is_flags_valid = info->is_flags_valid != 0;
+ Script_sections* ss = closure->script_options()->script_sections();
+ ss->add_phdr(name, namelen, type, includes_filehdr, includes_phdrs,
+ is_flags_valid, info->flags, info->load_address);
+}
+
+// Convert a program header string to a type.
+
+#define PHDR_TYPE(NAME) { #NAME, sizeof(#NAME) - 1, elfcpp::NAME }
+
+static struct
+{
+ const char* name;
+ size_t namelen;
+ unsigned int val;
+} phdr_type_names[] =
+{
+ PHDR_TYPE(PT_NULL),
+ PHDR_TYPE(PT_LOAD),
+ PHDR_TYPE(PT_DYNAMIC),
+ PHDR_TYPE(PT_INTERP),
+ PHDR_TYPE(PT_NOTE),
+ PHDR_TYPE(PT_SHLIB),
+ PHDR_TYPE(PT_PHDR),
+ PHDR_TYPE(PT_TLS),
+ PHDR_TYPE(PT_GNU_EH_FRAME),
+ PHDR_TYPE(PT_GNU_STACK),
+ PHDR_TYPE(PT_GNU_RELRO)
+};
+
+extern "C" unsigned int
+script_phdr_string_to_type(void* closurev, const char* name, size_t namelen)
+{
+ for (unsigned int i = 0;
+ i < sizeof(phdr_type_names) / sizeof(phdr_type_names[0]);
+ ++i)
+ if (namelen == phdr_type_names[i].namelen
+ && strncmp(name, phdr_type_names[i].name, namelen) == 0)
+ return phdr_type_names[i].val;
+ yyerror(closurev, _("unknown PHDR type (try integer)"));
+ return elfcpp::PT_NULL;
+}
saw_sections_clause() const
{ return this->script_sections_.saw_sections_clause(); }
+ // Whether we saw a PHDRS clause.
+ bool
+ saw_phdrs_clause() const
+ { return this->script_sections_.saw_phdrs_clause(); }
+
// Set section addresses using a SECTIONS clause. Return the
// segment which should hold the file header and segment headers;
// this may return NULL, in which case the headers are not in a
$(CXXLINK) -O0 -Bgcctestdir/ -shared $(srcdir)/ver_matching_def.cc -Wl,--version-script=$(srcdir)/version_script.map
ver_matching_test.stdout: ver_matching_def.so
objdump -T ver_matching_def.so | c++filt > ver_matching_test.stdout
+
+check_PROGRAMS += script_test_3
+check_SCRIPTS += script_test_3.sh
+check_DATA += script_test_3.stdout
+MOSTLYCLEANFILES += script_test_3.stdout
+script_test_3: basic_test.o gcctestdir/ld script_test_3.t
+ $(CXXLINK) -Bgcctestdir/ basic_test.o -T $(srcdir)/script_test_3.t
+script_test_3.stdout: script_test_3
+ objdump -p script_test_3 > script_test_3.stdout
endif OBJDUMP_AND_CPPFILT
endif GCC
target_triplet = @target@
check_PROGRAMS = object_unittest$(EXEEXT) $(am__EXEEXT_1) \
$(am__EXEEXT_2) $(am__EXEEXT_3) $(am__EXEEXT_4) \
- $(am__EXEEXT_5) $(am__EXEEXT_6) $(am__EXEEXT_7)
+ $(am__EXEEXT_5) $(am__EXEEXT_6) $(am__EXEEXT_7) \
+ $(am__EXEEXT_8)
@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_1 = basic_test \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ basic_static_test basic_pic_test \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ basic_static_pic_test \
@NATIVE_LINKER_FALSE@ ../libgold.a ../../libiberty/libiberty.a \
@NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1) \
@NATIVE_LINKER_FALSE@ $(am__DEPENDENCIES_1)
-@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_11 = ver_matching_test.sh
-@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_12 = ver_matching_test.stdout
-@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_13 = ver_matching_test.stdout
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_11 = ver_matching_test.sh \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ script_test_3.sh
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_12 = ver_matching_test.stdout \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ script_test_3.stdout
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_13 = ver_matching_test.stdout \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ script_test_3.stdout
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__append_14 = script_test_3
subdir = testsuite
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@GCC_TRUE@@NATIVE_LINKER_TRUE@ ver_test$(EXEEXT) \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_1$(EXEEXT) \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_2$(EXEEXT)
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@am__EXEEXT_8 = script_test_3$(EXEEXT)
basic_pic_test_SOURCES = basic_pic_test.c
basic_pic_test_OBJECTS = basic_pic_test.$(OBJEXT)
basic_pic_test_LDADD = $(LDADD)
@GCC_TRUE@@NATIVE_LINKER_TRUE@ script_test_2b.$(OBJEXT)
script_test_2_OBJECTS = $(am_script_test_2_OBJECTS)
script_test_2_LDADD = $(LDADD)
+script_test_3_SOURCES = script_test_3.c
+script_test_3_OBJECTS = script_test_3.$(OBJEXT)
+script_test_3_LDADD = $(LDADD)
+script_test_3_DEPENDENCIES = libgoldtest.a ../libgold.a \
+ ../../libiberty/libiberty.a $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
am__tls_pic_test_SOURCES_DIST = tls_test_main.cc
@GCC_TRUE@@NATIVE_LINKER_TRUE@@TLS_TRUE@am_tls_pic_test_OBJECTS = tls_test_main.$(OBJEXT)
tls_pic_test_OBJECTS = $(am_tls_pic_test_OBJECTS)
flagstest_compress_debug_sections.c flagstest_o_specialfile.c \
flagstest_o_specialfile_and_compress_debug_sections.c \
$(object_unittest_SOURCES) $(script_test_1_SOURCES) \
- $(script_test_2_SOURCES) $(tls_pic_test_SOURCES) \
- $(tls_shared_ie_test_SOURCES) \
+ $(script_test_2_SOURCES) script_test_3.c \
+ $(tls_pic_test_SOURCES) $(tls_shared_ie_test_SOURCES) \
$(tls_shared_nonpic_test_SOURCES) $(tls_shared_test_SOURCES) \
$(tls_static_pic_test_SOURCES) $(tls_static_test_SOURCES) \
$(tls_test_SOURCES) $(two_file_mixed_2_shared_test_SOURCES) \
flagstest_compress_debug_sections.c flagstest_o_specialfile.c \
flagstest_o_specialfile_and_compress_debug_sections.c \
$(object_unittest_SOURCES) $(am__script_test_1_SOURCES_DIST) \
- $(am__script_test_2_SOURCES_DIST) \
+ $(am__script_test_2_SOURCES_DIST) script_test_3.c \
$(am__tls_pic_test_SOURCES_DIST) \
$(am__tls_shared_ie_test_SOURCES_DIST) \
$(am__tls_shared_nonpic_test_SOURCES_DIST) \
script_test_2$(EXEEXT): $(script_test_2_OBJECTS) $(script_test_2_DEPENDENCIES)
@rm -f script_test_2$(EXEEXT)
$(CXXLINK) $(script_test_2_LDFLAGS) $(script_test_2_OBJECTS) $(script_test_2_LDADD) $(LIBS)
+@GCC_FALSE@script_test_3$(EXEEXT): $(script_test_3_OBJECTS) $(script_test_3_DEPENDENCIES)
+@GCC_FALSE@ @rm -f script_test_3$(EXEEXT)
+@GCC_FALSE@ $(LINK) $(script_test_3_LDFLAGS) $(script_test_3_OBJECTS) $(script_test_3_LDADD) $(LIBS)
+@NATIVE_LINKER_FALSE@script_test_3$(EXEEXT): $(script_test_3_OBJECTS) $(script_test_3_DEPENDENCIES)
+@NATIVE_LINKER_FALSE@ @rm -f script_test_3$(EXEEXT)
+@NATIVE_LINKER_FALSE@ $(LINK) $(script_test_3_LDFLAGS) $(script_test_3_OBJECTS) $(script_test_3_LDADD) $(LIBS)
+@OBJDUMP_AND_CPPFILT_FALSE@script_test_3$(EXEEXT): $(script_test_3_OBJECTS) $(script_test_3_DEPENDENCIES)
+@OBJDUMP_AND_CPPFILT_FALSE@ @rm -f script_test_3$(EXEEXT)
+@OBJDUMP_AND_CPPFILT_FALSE@ $(LINK) $(script_test_3_LDFLAGS) $(script_test_3_OBJECTS) $(script_test_3_LDADD) $(LIBS)
tls_pic_test$(EXEEXT): $(tls_pic_test_OBJECTS) $(tls_pic_test_DEPENDENCIES)
@rm -f tls_pic_test$(EXEEXT)
$(CXXLINK) $(tls_pic_test_LDFLAGS) $(tls_pic_test_OBJECTS) $(tls_pic_test_LDADD) $(LIBS)
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script_test_2.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script_test_2a.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script_test_2b.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script_test_3.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testfile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testmain.Po@am__quote@
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ $(CXXLINK) -O0 -Bgcctestdir/ -shared $(srcdir)/ver_matching_def.cc -Wl,--version-script=$(srcdir)/version_script.map
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ver_matching_test.stdout: ver_matching_def.so
@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ objdump -T ver_matching_def.so | c++filt > ver_matching_test.stdout
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@script_test_3: basic_test.o gcctestdir/ld script_test_3.t
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ $(CXXLINK) -Bgcctestdir/ basic_test.o -T $(srcdir)/script_test_3.t
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@script_test_3.stdout: script_test_3
+@GCC_TRUE@@NATIVE_LINKER_TRUE@@OBJDUMP_AND_CPPFILT_TRUE@ objdump -p script_test_3 > script_test_3.stdout
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
--- /dev/null
+#!/bin/sh
+
+# script_test_3.sh -- test PHDRS
+
+# Copyright 2008 Free Software Foundation, Inc.
+# Written by Ian Lance Taylor <iant@google.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.
+
+# This file goes with script_test_3.t, which is a linker script which
+# uses a PHDRS clause. We run objdump -p on a program linked with
+# that linker script.
+
+check()
+{
+ if ! grep -q "$2" "$1"
+ then
+ echo "Did not find expected segment in $1:"
+ echo " $2"
+ echo ""
+ echo "Actual output below:"
+ cat "$1"
+ exit 1
+ fi
+}
+
+check_count()
+{
+ if test "`grep -c "$2" "$1"`" != "$3"
+ then
+ echo "Did not find expected segment in $1:"
+ echo " $2"
+ echo ""
+ echo "Actual output below:"
+ cat "$1"
+ exit 1
+ fi
+}
+
+check_count script_test_3.stdout "INTERP off" 1
+check_count script_test_3.stdout "LOAD off" 3
+check_count script_test_3.stdout "DYNAMIC off" 1
+
+exit 0
--- /dev/null
+/* script_test_3.t -- linker script test 3 for gold
+
+ Copyright 2008 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor <iant@google.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. */
+
+SECTIONS
+{
+ /* With luck this will work everywhere. */
+ . = 0x10000000;
+
+ /* With luck this will be enough to get the program working. */
+ .interp : { *(.interp) } :text :interp
+ .text : { *(.text) } :text
+ .dynamic : { *(.dynamic) } :text :dynamic
+ .data : { *(.data) } :data
+ .bss : { *(.bss) } :bss
+}
+
+PHDRS
+{
+ text PT_LOAD FILEHDR PHDRS FLAGS(5);
+ interp PT_INTERP;
+ dynamic PT_DYNAMIC FLAGS(4);
+ data PT_LOAD;
+ bss PT_LOAD;
+}
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include "script-c.h"
struct Wildcard_section wildcard_section;
/* A list of strings. */
String_list_ptr string_list;
+ /* Information for a program header. */
+ struct Phdr_info phdr_info;
/* Used for version scripts and within VERSION {}. */
struct Version_dependency_list* deplist;
struct Version_expression_list* versyms;
%type <output_section_header> section_header
%type <output_section_trailer> section_trailer
%type <constraint> opt_constraint
+%type <string_list> opt_phdr
%type <integer> data_length
%type <input_section_spec> input_section_no_keep
%type <wildcard_sections> wildcard_sections
%type <wildcard_section> wildcard_file wildcard_section
%type <string_list> exclude_names
%type <string> wildcard_name
+%type <integer> phdr_type
+%type <phdr_info> phdr_info
%type <versyms> vers_defns
%type <versnode> vers_tag
%type <deplist> verdep
{ script_end_group(closure); }
| OPTION '(' string ')'
{ script_parse_option(closure, $3.value, $3.length); }
+ | PHDRS '{' phdrs_defs '}'
| SEARCH_DIR '(' string ')'
{ script_add_search_dir(closure, $3.value, $3.length); }
| SECTIONS '{'
opt_memspec opt_at_memspec opt_phdr opt_fill opt_comma
{
$$.fill = $4;
+ $$.phdrs = $3;
}
;
/* The program segment an output section should go into. */
opt_phdr:
opt_phdr ':' string
- { yyerror(closure, "program headers are not supported"); }
+ { $$ = script_string_list_push_back($1, $3.value, $3.length); }
| /* empty */
+ { $$ = NULL; }
;
/* The value to use to fill an output section. FIXME: This does not
{ script_add_assertion(closure, $3, $5.value, $5.length); }
;
+/* A list of program header definitions. */
+phdrs_defs:
+ phdrs_defs phdr_def
+ | /* empty */
+ ;
+
+/* A program header definition. */
+phdr_def:
+ string phdr_type phdr_info ';'
+ { script_add_phdr(closure, $1.value, $1.length, $2, &$3); }
+ ;
+
+/* A program header type. The GNU linker accepts a general expression
+ here, but that would be a pain because we would have to dig into
+ the expression structure. It's unlikely that anybody uses anything
+ other than a string or a number here, so that is all we expect. */
+phdr_type:
+ string
+ { $$ = script_phdr_string_to_type(closure, $1.value, $1.length); }
+ | INTEGER
+ { $$ = $1; }
+ ;
+
+/* Additional information for a program header. */
+phdr_info:
+ /* empty */
+ { memset(&$$, 0, sizeof(struct Phdr_info)); }
+ | string phdr_info
+ {
+ $$ = $2;
+ if ($1.length == 7 && strncmp($1.value, "FILEHDR", 7) == 0)
+ $$.includes_filehdr = 1;
+ else
+ yyerror(closure, "PHDRS syntax error");
+ }
+ | PHDRS phdr_info
+ {
+ $$ = $2;
+ $$.includes_phdrs = 1;
+ }
+ | string '(' INTEGER ')' phdr_info
+ {
+ $$ = $5;
+ if ($1.length == 5 && strncmp($1.value, "FLAGS", 5) == 0)
+ {
+ $$.is_flags_valid = 1;
+ $$.flags = $3;
+ }
+ else
+ yyerror(closure, "PHDRS syntax error");
+ }
+ | AT '(' parse_exp ')' phdr_info
+ {
+ $$ = $5;
+ $$.load_address = $3;
+ }
+ ;
+
/* Set a symbol to a value. */
assignment:
string '=' parse_exp