Implement PHDRS.
authorIan Lance Taylor <iant@google.com>
Mon, 4 Feb 2008 22:54:31 +0000 (22:54 +0000)
committerIan Lance Taylor <iant@google.com>
Mon, 4 Feb 2008 22:54:31 +0000 (22:54 +0000)
13 files changed:
gold/layout.cc
gold/output.cc
gold/output.h
gold/script-c.h
gold/script-sections.cc
gold/script-sections.h
gold/script.cc
gold/script.h
gold/testsuite/Makefile.am
gold/testsuite/Makefile.in
gold/testsuite/script_test_3.sh [new file with mode: 0755]
gold/testsuite/script_test_3.t [new file with mode: 0644]
gold/yyscript.y

index 1e597ac..a532e09 100644 (file)
@@ -397,10 +397,13 @@ Layout::layout_eh_frame(Sized_relobj<size, big_endian>* object,
 
              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);
            }
@@ -498,6 +501,8 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
       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);
@@ -700,6 +705,8 @@ Layout::find_first_load_seg()
        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;
@@ -758,7 +765,8 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
 
       // 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;
@@ -816,6 +824,14 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
   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;
@@ -997,6 +1013,8 @@ Layout::create_executable_stack_info(const Target* target)
     }
   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;
@@ -1861,9 +1879,12 @@ Layout::create_interp(const Target* target)
                                                     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.
@@ -1872,11 +1893,14 @@ void
 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_;
 
index 8eb79fa..f0f4de2 100644 (file)
@@ -2480,6 +2480,55 @@ Output_segment::output_section_count_list(const Output_data_list* pdl) const
   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>
index 1354551..032a2bb 100644 (file)
@@ -2342,6 +2342,12 @@ class Output_segment
     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
@@ -2372,6 +2378,12 @@ class Output_segment
   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
@@ -2411,6 +2423,13 @@ class Output_segment
   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*
index 26dc556..0eacd28 100644 (file)
@@ -94,15 +94,6 @@ struct Parser_output_section_header
   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.  */
@@ -114,6 +105,18 @@ typedef String_list* String_list_ptr;
 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
@@ -165,6 +168,22 @@ struct Input_section_spec
   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;
@@ -329,6 +348,17 @@ script_string_list_push_back(String_list_ptr, const char*, size_t);
 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
index 6c8a7f5..441c3ee 100644 (file)
@@ -25,6 +25,7 @@
 #include <cstring>
 #include <algorithm>
 #include <list>
+#include <map>
 #include <string>
 #include <vector>
 #include <fnmatch.h>
@@ -96,6 +97,15 @@ class Sections_element
   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;
@@ -1172,6 +1182,14 @@ class Output_section_definition : public Sections_element
   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;
@@ -1193,6 +1211,9 @@ class Output_section_definition : public Sections_element
   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
@@ -1213,6 +1234,7 @@ Output_section_definition::Output_section_definition(
     subalign_(header->subalign),
     constraint_(header->constraint),
     fill_(NULL),
+    phdrs_(NULL),
     elements_(),
     output_section_(NULL)
 {
@@ -1224,6 +1246,7 @@ void
 Output_section_definition::finish(const Parser_output_section_trailer* trailer)
 {
   this->fill_ = trailer->fill;
+  this->phdrs_ = trailer->phdrs;
 }
 
 // Add a symbol to be defined.
@@ -1392,58 +1415,92 @@ Output_section_definition::place_orphan_here(const Output_section *os,
 
   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;
     }
 
@@ -1651,6 +1708,24 @@ Output_section_definition::alternate_constraint(
   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
@@ -1725,6 +1800,12 @@ class Orphan_output_section : public Sections_element
   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
@@ -1797,13 +1878,125 @@ Orphan_output_section::set_section_addresses(Symbol_table*, Layout*,
   *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)
 {
 }
 
@@ -2071,6 +2264,14 @@ Script_sections::set_section_addresses(Symbol_table* symtab, Layout* layout)
        ++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);
 }
 
@@ -2129,6 +2330,45 @@ Script_sections::is_bss_section(const Output_section* os)
          && (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.
@@ -2141,6 +2381,9 @@ Script_sections::create_segments(Layout* layout)
   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(&sections);
 
@@ -2240,23 +2483,7 @@ Script_sections::create_segments(Layout* layout)
   // 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)
@@ -2275,13 +2502,9 @@ Script_sections::create_segments(Layout* layout)
       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
@@ -2304,6 +2527,8 @@ Script_sections::create_note_and_tls_segments(
     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();
@@ -2356,12 +2581,34 @@ Script_sections::create_note_and_tls_segments(
     }
 }
 
+// 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(&sections);
 
@@ -2398,6 +2645,189 @@ Script_sections::expected_segment_count(const Layout* layout) const
   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
index ec708bd..138f144 100644 (file)
@@ -36,6 +36,8 @@ struct Parser_output_section_trailer;
 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;
@@ -64,6 +66,12 @@ class Script_sections
   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,
@@ -134,11 +142,22 @@ class Script_sections
   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;
@@ -146,6 +165,8 @@ class Script_sections
  private:
   typedef std::vector<Sections_element*> Sections_elements;
 
+  typedef std::vector<Phdrs_element*> Phdrs_elements;
+
   // Create segments.
   Output_segment*
   create_segments(Layout*);
@@ -158,6 +179,27 @@ class Script_sections
   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.
@@ -166,6 +208,8 @@ class Script_sections
   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.
index 973c05c..da2a228 100644 (file)
@@ -2431,8 +2431,13 @@ script_new_string_list(const char* str, size_t len)
 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
@@ -2449,3 +2454,55 @@ script_string_list_append(String_list_ptr pv1, String_list_ptr pv2)
   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;
+}
index 257b479..09f104f 100644 (file)
@@ -331,6 +331,11 @@ class Script_options
   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
index 43d2a66..b72e300 100644 (file)
@@ -540,6 +540,15 @@ ver_matching_def.so: ver_matching_def.cc gcctestdir/ld
        $(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
index 4e0972b..182c89e 100644 (file)
@@ -44,7 +44,8 @@ host_triplet = @host@
 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 \
@@ -189,9 +190,13 @@ check_PROGRAMS = object_unittest$(EXEEXT) $(am__EXEEXT_1) \
 @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
@@ -258,6 +263,7 @@ libgoldtest_a_OBJECTS = $(am_libgoldtest_a_OBJECTS)
 @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)
@@ -386,6 +392,12 @@ am__script_test_2_SOURCES_DIST = script_test_2.cc script_test_2a.cc \
 @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)
@@ -555,8 +567,8 @@ SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \
        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) \
@@ -590,7 +602,7 @@ DIST_SOURCES = $(libgoldtest_a_SOURCES) basic_pic_test.c \
        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) \
@@ -1101,6 +1113,15 @@ script_test_1$(EXEEXT): $(script_test_1_OBJECTS) $(script_test_1_DEPENDENCIES)
 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)
@@ -1202,6 +1223,7 @@ distclean-compile:
 @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@
@@ -1654,6 +1676,10 @@ uninstall-am: uninstall-info-am
 @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:
diff --git a/gold/testsuite/script_test_3.sh b/gold/testsuite/script_test_3.sh
new file mode 100755 (executable)
index 0000000..338e5ce
--- /dev/null
@@ -0,0 +1,59 @@
+#!/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
diff --git a/gold/testsuite/script_test_3.t b/gold/testsuite/script_test_3.t
new file mode 100644 (file)
index 0000000..1dbbfa3
--- /dev/null
@@ -0,0 +1,43 @@
+/* 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;
+}
index ad76709..3605158 100644 (file)
@@ -30,6 +30,7 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "script-c.h"
 
@@ -70,6 +71,8 @@
   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
@@ -232,6 +238,7 @@ file_cmd:
            { 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 '{'
@@ -365,6 +372,7 @@ section_trailer:
          opt_memspec opt_at_memspec opt_phdr opt_fill opt_comma
            {
              $$.fill = $4;
+             $$.phdrs = $3;
            }
        ;
 
@@ -385,8 +393,9 @@ opt_at_memspec:
 /* 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
@@ -587,6 +596,64 @@ file_or_sections_cmd:
            { 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