aboutsummaryrefslogtreecommitdiff
path: root/gold/layout.cc
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2006-09-27 22:53:42 +0000
committerIan Lance Taylor <iant@google.com>2006-09-27 22:53:42 +0000
commit75f65a3e309b8cd885c782f6af106d1e2a1876f6 (patch)
tree0f21c32a5e40156d007fd6a676b5d74a8c423c90 /gold/layout.cc
parent6b89cc2108a525fdc4186bae5365acc258e9c23c (diff)
downloadgdb-75f65a3e309b8cd885c782f6af106d1e2a1876f6.zip
gdb-75f65a3e309b8cd885c782f6af106d1e2a1876f6.tar.gz
gdb-75f65a3e309b8cd885c782f6af106d1e2a1876f6.tar.bz2
Finished layout code.
Diffstat (limited to 'gold/layout.cc')
-rw-r--r--gold/layout.cc420
1 files changed, 323 insertions, 97 deletions
diff --git a/gold/layout.cc b/gold/layout.cc
index 1584380..d91f731 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -52,15 +52,14 @@ Layout_task::run(Workqueue*)
p != this->input_objects_->end();
++p)
(*p)->layout(&layout);
- layout.finalize(this->input_objects_);
+ layout.finalize(this->input_objects_, this->symtab_);
}
// Layout methods.
Layout::Layout(const General_options& options)
- : options_(options), namepool_(), signatures_(),
- section_name_map_(), segment_list_(), section_list_(),
- data_list_()
+ : options_(options), namepool_(), sympool_(), signatures_(),
+ section_name_map_(), segment_list_(), section_list_()
{
}
@@ -169,82 +168,6 @@ Layout::layout(Object* object, const char* name,
return os;
}
-// Return whether SEG1 should be before SEG2 in the output file. This
-// is based entirely on the segment type and flags. When this is
-// called the segment addresses has normally not yet been set.
-
-bool
-Layout::segment_precedes(const Output_segment* seg1,
- const Output_segment* seg2)
-{
- elfcpp::Elf_Word type1 = seg1->type();
- elfcpp::Elf_Word type2 = seg2->type();
-
- // The single PT_PHDR segment is required to precede any loadable
- // segment. We simply make it always first.
- if (type1 == elfcpp::PT_PHDR)
- {
- assert(type2 != elfcpp::PT_PHDR);
- return true;
- }
- if (type2 == elfcpp::PT_PHDR)
- return false;
-
- // The single PT_INTERP segment is required to precede any loadable
- // segment. We simply make it always second.
- if (type1 == elfcpp::PT_INTERP)
- {
- assert(type2 != elfcpp::PT_INTERP);
- return true;
- }
- if (type2 == elfcpp::PT_INTERP)
- return false;
-
- // We then put PT_LOAD segments before any other segments.
- if (type1 == elfcpp::PT_LOAD && type2 != elfcpp::PT_LOAD)
- return true;
- if (type2 == elfcpp::PT_LOAD && type1 != elfcpp::PT_LOAD)
- return false;
-
- const elfcpp::Elf_Word flags1 = seg1->flags();
- const elfcpp::Elf_Word flags2 = seg2->flags();
-
- // The order of non-PT_LOAD segments is unimportant. We simply sort
- // by the numeric segment type and flags values. There should not
- // be more than one segment with the same type and flags.
- if (type1 != elfcpp::PT_LOAD)
- {
- if (type1 != type2)
- return type1 < type2;
- assert(flags1 != flags2);
- return flags1 < flags2;
- }
-
- // We sort PT_LOAD segments based on the flags. Readonly segments
- // come before writable segments. Then executable segments come
- // before non-executable segments. Then the unlikely case of a
- // non-readable segment comes before the normal case of a readable
- // segment. If there are multiple segments with the same type and
- // flags, we require that the address be set, and we sort by
- // virtual address and then physical address.
- if ((flags1 & elfcpp::PF_W) != (flags2 & elfcpp::PF_W))
- return (flags1 & elfcpp::PF_W) == 0;
- if ((flags1 & elfcpp::PF_X) != (flags2 & elfcpp::PF_X))
- return (flags1 & elfcpp::PF_X) != 0;
- if ((flags1 & elfcpp::PF_R) != (flags2 & elfcpp::PF_R))
- return (flags1 & elfcpp::PF_R) == 0;
-
- uint64_t vaddr1 = seg1->vaddr();
- uint64_t vaddr2 = seg2->vaddr();
- if (vaddr1 != vaddr2)
- return vaddr1 < vaddr2;
-
- uint64_t paddr1 = seg1->paddr();
- uint64_t paddr2 = seg2->paddr();
- assert(paddr1 != paddr2);
- return paddr1 < paddr2;
-}
-
// Map section flags to segment flags.
elfcpp::Elf_Word
@@ -289,7 +212,7 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
if ((*p)->type() == elfcpp::PT_LOAD
&& ((*p)->flags() & elfcpp::PF_W) == (seg_flags & elfcpp::PF_W))
{
- (*p)->add_output_section(os);
+ (*p)->add_output_section(os, seg_flags);
break;
}
}
@@ -299,7 +222,7 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
Output_segment* oseg = new Output_segment(elfcpp::PT_LOAD,
seg_flags);
this->segment_list_.push_back(oseg);
- oseg->add_output_section(os);
+ oseg->add_output_section(os, seg_flags);
}
// If we see a loadable SHT_NOTE section, we create a PT_NOTE
@@ -315,7 +238,7 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
&& (((*p)->flags() & elfcpp::PF_W)
== (seg_flags & elfcpp::PF_W)))
{
- (*p)->add_output_section(os);
+ (*p)->add_output_section(os, seg_flags);
break;
}
}
@@ -325,7 +248,7 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
Output_segment* oseg = new Output_segment(elfcpp::PT_NOTE,
seg_flags);
this->segment_list_.push_back(oseg);
- oseg->add_output_section(os);
+ oseg->add_output_section(os, seg_flags);
}
}
@@ -342,7 +265,7 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
&& (((*p)->flags() & elfcpp::PF_W)
== (seg_flags & elfcpp::PF_W)))
{
- (*p)->add_output_section(os);
+ (*p)->add_output_section(os, seg_flags);
break;
}
}
@@ -352,7 +275,7 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
Output_segment* oseg = new Output_segment(elfcpp::PT_TLS,
seg_flags);
this->segment_list_.push_back(oseg);
- oseg->add_output_section(os);
+ oseg->add_output_section(os, seg_flags);
}
}
}
@@ -360,11 +283,25 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
return os;
}
-// Create the sections for the symbol table.
+// Find the first read-only PT_LOAD segment, creating one if
+// necessary.
-void
-Layout::create_symtab_sections()
+Output_segment*
+Layout::find_first_load_seg()
{
+ for (Segment_list::const_iterator p = this->segment_list_.begin();
+ p != this->segment_list_.end();
+ ++p)
+ {
+ if ((*p)->type() == elfcpp::PT_LOAD
+ && ((*p)->flags() & elfcpp::PF_R) != 0
+ && ((*p)->flags() & elfcpp::PF_W) == 0)
+ return *p;
+ }
+
+ Output_segment* load_seg = new Output_segment(elfcpp::PT_LOAD, elfcpp::PF_R);
+ this->segment_list_.push_back(load_seg);
+ return load_seg;
}
// Finalize the layout. When this is called, we have created all the
@@ -383,13 +320,13 @@ Layout::create_symtab_sections()
// 4) Determine the final file offset of all the SHF_ALLOC output
// sections.
-// 5) Finalize the symbol table: set symbol values to their final
+// 5) Create the symbol table sections and the section name table
+// section.
+
+// 6) Finalize the symbol table: set symbol values to their final
// value and make a final determination of which symbols are going
// into the output symbol table.
-// 6) Create the symbol table sections and the section name table
-// section.
-
// 7) Create the section table header.
// 8) Determine the final file offset of all the output sections which
@@ -397,8 +334,10 @@ Layout::create_symtab_sections()
// 9) Finalize the ELF file header.
-void
-Layout::finalize(const Input_objects* input_objects)
+// This function returns the size of the output file.
+
+off_t
+Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
{
if (input_objects->any_dynamic())
{
@@ -411,11 +350,298 @@ Layout::finalize(const Input_objects* input_objects)
// FIXME: Handle PT_GNU_STACK.
+ Output_segment* load_seg = this->find_first_load_seg();
+
+ // Lay out the segment headers.
+ int size = input_objects->target()->get_size();
+ Output_segment_headers* segment_headers;
+ segment_headers = new Output_segment_headers(size, this->segment_list_);
+ load_seg->add_initial_output_data(segment_headers);
+ // FIXME: Attach them to PT_PHDRS if necessary.
+
+ // Lay out the file header.
+ Output_file_header* file_header;
+ file_header = new Output_file_header(size,
+ this->options_,
+ input_objects->target(),
+ symtab,
+ segment_headers);
+ load_seg->add_initial_output_data(file_header);
+
+ // Set the file offsets of all the segments.
+ off_t off = this->set_segment_offsets(input_objects->target(), load_seg);
+
+ // Create the symbol table sections.
+ // FIXME: We don't need to do this if we are stripping symbols.
+ Output_section* osymtab;
+ Output_section* ostrtab;
+ this->create_symtab_sections(input_objects, symtab, &osymtab, &ostrtab);
+
+ // Create the .shstrtab section.
+ Output_section* shstrtab_section = this->create_shstrtab();
+
+ // Set the file offsets of all the sections not associated with
+ // segments.
+ off = this->set_section_offsets(off);
+
+ // Create the section table header.
+ Output_section_headers* oshdrs = this->create_shdrs(size, off);
+ off += oshdrs->data_size();
+
+ file_header->set_section_info(oshdrs, shstrtab_section);
+
+ // Now we know exactly where everything goes in the output file.
+
+ return off;
+}
+
+// Return whether SEG1 should be before SEG2 in the output file. This
+// is based entirely on the segment type and flags. When this is
+// called the segment addresses has normally not yet been set.
+
+bool
+Layout::segment_precedes(const Output_segment* seg1,
+ const Output_segment* seg2)
+{
+ elfcpp::Elf_Word type1 = seg1->type();
+ elfcpp::Elf_Word type2 = seg2->type();
+
+ // The single PT_PHDR segment is required to precede any loadable
+ // segment. We simply make it always first.
+ if (type1 == elfcpp::PT_PHDR)
+ {
+ assert(type2 != elfcpp::PT_PHDR);
+ return true;
+ }
+ if (type2 == elfcpp::PT_PHDR)
+ return false;
+
+ // The single PT_INTERP segment is required to precede any loadable
+ // segment. We simply make it always second.
+ if (type1 == elfcpp::PT_INTERP)
+ {
+ assert(type2 != elfcpp::PT_INTERP);
+ return true;
+ }
+ if (type2 == elfcpp::PT_INTERP)
+ return false;
+
+ // We then put PT_LOAD segments before any other segments.
+ if (type1 == elfcpp::PT_LOAD && type2 != elfcpp::PT_LOAD)
+ return true;
+ if (type2 == elfcpp::PT_LOAD && type1 != elfcpp::PT_LOAD)
+ return false;
+
+ const elfcpp::Elf_Word flags1 = seg1->flags();
+ const elfcpp::Elf_Word flags2 = seg2->flags();
+
+ // The order of non-PT_LOAD segments is unimportant. We simply sort
+ // by the numeric segment type and flags values. There should not
+ // be more than one segment with the same type and flags.
+ if (type1 != elfcpp::PT_LOAD)
+ {
+ if (type1 != type2)
+ return type1 < type2;
+ assert(flags1 != flags2);
+ return flags1 < flags2;
+ }
+
+ // We sort PT_LOAD segments based on the flags. Readonly segments
+ // come before writable segments. Then executable segments come
+ // before non-executable segments. Then the unlikely case of a
+ // non-readable segment comes before the normal case of a readable
+ // segment. If there are multiple segments with the same type and
+ // flags, we require that the address be set, and we sort by
+ // virtual address and then physical address.
+ if ((flags1 & elfcpp::PF_W) != (flags2 & elfcpp::PF_W))
+ return (flags1 & elfcpp::PF_W) == 0;
+ if ((flags1 & elfcpp::PF_X) != (flags2 & elfcpp::PF_X))
+ return (flags1 & elfcpp::PF_X) != 0;
+ if ((flags1 & elfcpp::PF_R) != (flags2 & elfcpp::PF_R))
+ return (flags1 & elfcpp::PF_R) == 0;
+
+ uint64_t vaddr1 = seg1->vaddr();
+ uint64_t vaddr2 = seg2->vaddr();
+ if (vaddr1 != vaddr2)
+ return vaddr1 < vaddr2;
+
+ uint64_t paddr1 = seg1->paddr();
+ uint64_t paddr2 = seg2->paddr();
+ assert(paddr1 != paddr2);
+ return paddr1 < paddr2;
+}
+
+// Set the file offsets of all the segments. They have all been
+// created. LOAD_SEG must be be laid out first. Return the offset of
+// the data to follow.
+
+off_t
+Layout::set_segment_offsets(const Target* target, Output_segment* load_seg)
+{
+ // Sort them into the final order.
std::sort(this->segment_list_.begin(), this->segment_list_.end(),
Layout::Compare_segments());
- Output_segment_headers* segment_headers;
- segment_headers = new Output_segment_headers(this->segment_list_);
+ // Find the PT_LOAD segments, and set their addresses and offsets
+ // and their section's addresses and offsets.
+ uint64_t addr = target->text_segment_address();
+ off_t off = 0;
+ bool was_readonly = false;
+ for (Segment_list::iterator p = this->segment_list_.begin();
+ p != this->segment_list_.end();
+ ++p)
+ {
+ if ((*p)->type() == elfcpp::PT_LOAD)
+ {
+ if (load_seg != NULL && load_seg != *p)
+ abort();
+ load_seg = NULL;
+
+ // If the last segment was readonly, and this one is not,
+ // then skip the address forward one page, maintaining the
+ // same position within the page. This lets us store both
+ // segments overlapping on a single page in the file, but
+ // the loader will put them on different pages in memory.
+
+ uint64_t orig_addr = addr;
+ uint64_t orig_off = off;
+
+ uint64_t aligned_addr = addr;
+ uint64_t abi_pagesize = target->abi_pagesize();
+ if (was_readonly && ((*p)->flags() & elfcpp::PF_W) != 0)
+ {
+ uint64_t align = (*p)->max_data_align();
+
+ addr = (addr + align - 1) & ~ (align - 1);
+ aligned_addr = addr;
+ if ((addr & (abi_pagesize - 1)) != 0)
+ addr = addr + abi_pagesize;
+ }
+
+ off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1));
+ uint64_t new_addr = (*p)->set_section_addresses(addr, &off);
+
+ // Now that we know the size of this segment, we may be able
+ // to save a page in memory, at the cost of wasting some
+ // file space, by instead aligning to the start of a new
+ // page. Here we use the real machine page size rather than
+ // the ABI mandated page size.
+
+ if (aligned_addr != addr)
+ {
+ uint64_t common_pagesize = target->common_pagesize();
+ uint64_t first_off = (common_pagesize
+ - (aligned_addr
+ & (common_pagesize - 1)));
+ uint64_t last_off = new_addr & (common_pagesize - 1);
+ if (first_off > 0
+ && last_off > 0
+ && ((aligned_addr & ~ (common_pagesize - 1))
+ != (new_addr & ~ (common_pagesize - 1)))
+ && first_off + last_off <= common_pagesize)
+ {
+ addr = ((aligned_addr + common_pagesize - 1)
+ & ~ (common_pagesize - 1));
+ off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1));
+ new_addr = (*p)->set_section_addresses(addr, &off);
+ }
+ }
+
+ addr = new_addr;
+
+ if (((*p)->flags() & elfcpp::PF_W) == 0)
+ was_readonly = true;
+ }
+ }
+
+ // Handle the non-PT_LOAD segments, setting their offsets from their
+ // section's offsets.
+ for (Segment_list::iterator p = this->segment_list_.begin();
+ p != this->segment_list_.end();
+ ++p)
+ {
+ if ((*p)->type() != elfcpp::PT_LOAD)
+ (*p)->set_offset();
+ }
+
+ return off;
+}
+
+// Set the file offset of all the sections not associated with a
+// segment.
+
+off_t
+Layout::set_section_offsets(off_t off)
+{
+ for (Layout::Section_list::iterator p = this->section_list_.begin();
+ p != this->section_list_.end();
+ ++p)
+ {
+ uint64_t addralign = (*p)->addralign();
+ off = (off + addralign - 1) & ~ (addralign - 1);
+ (*p)->set_address(0, off);
+ off += (*p)->data_size();
+ }
+ return off;
+}
+
+// Create the symbol table sections.
+
+void
+Layout::create_symtab_sections(const Input_objects* input_objects,
+ Symbol_table* symtab,
+ Output_section** posymtab,
+ Output_section** postrtab)
+{
+ off_t off = 0;
+ for (Input_objects::Object_list::const_iterator p = input_objects->begin();
+ p != input_objects->end();
+ ++p)
+ {
+ Task_lock_obj<Object> tlo(**p);
+ off = (*p)->finalize_local_symbols(off, &this->sympool_);
+ }
+
+ off = symtab->finalize(off, &this->sympool_);
+
+ *posymtab = new Output_section_symtab(this->namepool_.add(".symtab"), off);
+ *postrtab = new Output_section_strtab(this->namepool_.add(".strtab"),
+ &this->sympool_);
+}
+
+// Create the .shstrtab section, which holds the names of the
+// sections. At the time this is called, we have created all the
+// output sections except .shstrtab itself.
+
+Output_section*
+Layout::create_shstrtab()
+{
+ // FIXME: We don't need to create a .shstrtab section if we are
+ // stripping everything.
+
+ const char* name = this->namepool_.add(".shstrtab");
+
+ Output_section* os = new Output_section_strtab(name,
+ &this->namepool_);
+
+ this->section_list_.push_back(os);
+
+ return os;
+}
+
+// Create the section headers. SIZE is 32 or 64. OFF is the file
+// offset.
+
+Output_section_headers*
+Layout::create_shdrs(int size, off_t off)
+{
+ Output_section_headers* oshdrs;
+ oshdrs = new Output_section_headers(size, this->segment_list_,
+ this->section_list_);
+ uint64_t addralign = oshdrs->addralign();
+ off = (off + addralign - 1) & ~ (addralign - 1);
+ oshdrs->set_address(0, off);
+ return oshdrs;
}
// The mapping of .gnu.linkonce section names to real section names.