From: Ian Lance Taylor Date: Mon, 4 Feb 2008 22:54:31 +0000 (+0000) Subject: Implement PHDRS. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1c4f3631bde4d056e02555d72009693de26da4bf;p=binutils-gdb.git Implement PHDRS. --- diff --git a/gold/layout.cc b/gold/layout.cc index 1e597ac6f25..a532e09f716 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -397,10 +397,13 @@ Layout::layout_eh_frame(Sized_relobj* 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_; diff --git a/gold/output.cc b/gold/output.cc index 8eb79fa9e1a..f0f4de26fdf 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -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(*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 diff --git a/gold/output.h b/gold/output.h index 13545514763..032a2bb65c0 100644 --- a/gold/output.h +++ b/gold/output.h @@ -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 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 unsigned char* diff --git a/gold/script-c.h b/gold/script-c.h index 26dc5566042..0eacd28fcad 100644 --- a/gold/script-c.h +++ b/gold/script-c.h @@ -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 diff --git a/gold/script-sections.cc b/gold/script-sections.cc index 6c8a7f5b2b3..441c3ee53e1 100644 --- a/gold/script-sections.cc +++ b/gold/script-sections.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -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(§ions); @@ -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(§ions); @@ -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 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 diff --git a/gold/script-sections.h b/gold/script-sections.h index ec708bd50d1..138f144c29d 100644 --- a/gold/script-sections.h +++ b/gold/script-sections.h @@ -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_elements; + typedef std::vector 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. diff --git a/gold/script.cc b/gold/script.cc index 973c05ce6df..da2a228b52a 100644 --- a/gold/script.cc +++ b/gold/script.cc @@ -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(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; +} diff --git a/gold/script.h b/gold/script.h index 257b479dcd1..09f104f66c2 100644 --- a/gold/script.h +++ b/gold/script.h @@ -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 diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index 43d2a66b825..b72e300b1dd 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -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 diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index 4e0972be68b..182c89ed624 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -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 index 00000000000..338e5ce418a --- /dev/null +++ b/gold/testsuite/script_test_3.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +# script_test_3.sh -- test PHDRS + +# Copyright 2008 Free Software Foundation, Inc. +# Written by Ian Lance Taylor . + +# 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 index 00000000000..1dbbfa33e5e --- /dev/null +++ b/gold/testsuite/script_test_3.t @@ -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 . + + 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; +} diff --git a/gold/yyscript.y b/gold/yyscript.y index ad7670904f4..36051580923 100644 --- a/gold/yyscript.y +++ b/gold/yyscript.y @@ -30,6 +30,7 @@ #include #include #include +#include #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; @@ -198,12 +201,15 @@ %type section_header %type section_trailer %type opt_constraint +%type opt_phdr %type data_length %type input_section_no_keep %type wildcard_sections %type wildcard_file wildcard_section %type exclude_names %type wildcard_name +%type phdr_type +%type phdr_info %type vers_defns %type vers_tag %type 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