diff options
author | Ian Lance Taylor <iant@google.com> | 2006-09-27 22:53:42 +0000 |
---|---|---|
committer | Ian Lance Taylor <iant@google.com> | 2006-09-27 22:53:42 +0000 |
commit | 75f65a3e309b8cd885c782f6af106d1e2a1876f6 (patch) | |
tree | 0f21c32a5e40156d007fd6a676b5d74a8c423c90 /gold/layout.cc | |
parent | 6b89cc2108a525fdc4186bae5365acc258e9c23c (diff) | |
download | gdb-75f65a3e309b8cd885c782f6af106d1e2a1876f6.zip gdb-75f65a3e309b8cd885c782f6af106d1e2a1876f6.tar.gz gdb-75f65a3e309b8cd885c782f6af106d1e2a1876f6.tar.bz2 |
Finished layout code.
Diffstat (limited to 'gold/layout.cc')
-rw-r--r-- | gold/layout.cc | 420 |
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. |