From c51e6221b8abc026554349b0e8aa59477753b57b Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 21 Sep 2007 05:31:19 +0000 Subject: [PATCH] Use nops when doing alignment padding between code sections. --- gold/i386.cc | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ gold/layout.cc | 3 ++- gold/output.cc | 46 +++++++++++++++++++++++++++++----- gold/output.h | 44 +++++++++++++++++++++++++++++++- gold/target.h | 20 +++++++++++++++ 5 files changed, 173 insertions(+), 8 deletions(-) diff --git a/gold/i386.cc b/gold/i386.cc index 6c1b987d0e3..6eb0b2c0f37 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -63,6 +63,10 @@ class Target_i386 : public Sized_target<32, false> elfcpp::Elf_types<32>::Elf_Addr view_address, off_t view_size); + // Return a string used to fill a code section with nops. + std::string + do_code_fill(off_t length); + private: // The class which scans relocations. struct Scan @@ -212,6 +216,7 @@ const Target::Target_info Target_i386::i386_info = elfcpp::EM_386, // machine_code false, // has_make_symbol false, // has_resolve + true, // has_code_fill "/usr/lib/libc.so.1", // dynamic_linker 0x08048000, // text_segment_address 0x1000, // abi_pagesize @@ -1490,6 +1495,69 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, view_size); } +// Return a string used to fill a code section with nops to take up +// the specified length. + +std::string +Target_i386::do_code_fill(off_t length) +{ + if (length >= 16) + { + // Build a jmp instruction to skip over the bytes. + unsigned char jmp[5]; + jmp[0] = 0xe9; + elfcpp::Swap_unaligned<32, false>::writeval(jmp + 1, length - 5); + return (std::string(reinterpret_cast(&jmp[0]), 5) + + std::string(length - 5, '\0')); + } + + // Nop sequences of various lengths. + const char nop1[1] = { 0x90 }; // nop + const char nop2[2] = { 0x66, 0x90 }; // xchg %ax %ax + const char nop3[3] = { 0x8d, 0x76, 0x00 }; // leal 0(%esi),%esi + const char nop4[4] = { 0x8d, 0x74, 0x26, 0x00}; // leal 0(%esi,1),%esi + const char nop5[5] = { 0x90, 0x8d, 0x74, 0x26, // nop + 0x00 }; // leal 0(%esi,1),%esi + const char nop6[6] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi + 0x00, 0x00 }; + const char nop7[7] = { 0x8d, 0xb4, 0x26, 0x00, // leal 0L(%esi,1),%esi + 0x00, 0x00, 0x00 }; + const char nop8[8] = { 0x90, 0x8d, 0xb4, 0x26, // nop + 0x00, 0x00, 0x00, 0x00 }; // leal 0L(%esi,1),%esi + const char nop9[9] = { 0x89, 0xf6, 0x8d, 0xbc, // movl %esi,%esi + 0x27, 0x00, 0x00, 0x00, // leal 0L(%edi,1),%edi + 0x00 }; + const char nop10[10] = { 0x8d, 0x76, 0x00, 0x8d, // leal 0(%esi),%esi + 0xbc, 0x27, 0x00, 0x00, // leal 0L(%edi,1),%edi + 0x00, 0x00 }; + const char nop11[11] = { 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi + 0x8d, 0xbc, 0x27, 0x00, // leal 0L(%edi,1),%edi + 0x00, 0x00, 0x00 }; + const char nop12[12] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi + 0x00, 0x00, 0x8d, 0xbf, // leal 0L(%edi),%edi + 0x00, 0x00, 0x00, 0x00 }; + const char nop13[13] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi + 0x00, 0x00, 0x8d, 0xbc, // leal 0L(%edi,1),%edi + 0x27, 0x00, 0x00, 0x00, + 0x00 }; + const char nop14[14] = { 0x8d, 0xb4, 0x26, 0x00, // leal 0L(%esi,1),%esi + 0x00, 0x00, 0x00, 0x8d, // leal 0L(%edi,1),%edi + 0xbc, 0x27, 0x00, 0x00, + 0x00, 0x00 }; + const char nop15[15] = { 0xeb, 0x0d, 0x90, 0x90, // jmp .+15 + 0x90, 0x90, 0x90, 0x90, // nop,nop,nop,... + 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90 }; + + const char* nops[16] = { + NULL, + nop1, nop2, nop3, nop4, nop5, nop6, nop7, + nop8, nop9, nop10, nop11, nop12, nop13, nop14, nop15 + }; + + return std::string(nops[length], length); +} + // The selector for i386 object files. class Target_selector_i386 : public Target_selector diff --git a/gold/layout.cc b/gold/layout.cc index 3e8a223c7fd..35de9e46563 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -28,7 +28,8 @@ Layout_task_runner::run(Workqueue* workqueue) // Now we know the final size of the output file and we know where // each piece of information goes. - Output_file* of = new Output_file(this->options_); + Output_file* of = new Output_file(this->options_, + this->input_objects_->target()); of->open(file_size); // Queue up the final set of tasks. diff --git a/gold/output.cc b/gold/output.cc index ddf2ebb4e63..bbd7af161de 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -863,6 +863,7 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type, dynsym_index_(0), input_sections_(), first_input_offset_(0), + fills_(), needs_symtab_index_(false), needs_dynsym_index_(false), should_link_to_symtab_(false), @@ -923,19 +924,41 @@ Output_section::add_input_section(Relobj* object, unsigned int shndx, } } - off_t ssize = this->data_size(); - ssize = align_address(ssize, addralign); - this->set_data_size(ssize + shdr.get_sh_size()); + off_t offset_in_section = this->data_size(); + off_t aligned_offset_in_section = align_address(offset_in_section, + addralign); + + if (aligned_offset_in_section > offset_in_section + && (shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0 + && object->target()->has_code_fill()) + { + // We need to add some fill data. Using fill_list_ when + // possible is an optimization, since we will often have fill + // sections without input sections. + off_t fill_len = aligned_offset_in_section - offset_in_section; + if (this->input_sections_.empty()) + this->fills_.push_back(Fill(offset_in_section, fill_len)); + else + { + // FIXME: When relaxing, the size needs to adjust to + // maintain a constant alignment. + std::string fill_data(object->target()->code_fill(fill_len)); + Output_data_const* odc = new Output_data_const(fill_data, 1); + this->input_sections_.push_back(Input_section(odc)); + } + } + + this->set_data_size(aligned_offset_in_section + shdr.get_sh_size()); // We need to keep track of this section if we are already keeping // track of sections, or if we are relaxing. FIXME: Add test for // relaxing. - if (! this->input_sections_.empty()) + if (!this->input_sections_.empty()) this->input_sections_.push_back(Input_section(object, shndx, shdr.get_sh_size(), addralign)); - return ssize; + return aligned_offset_in_section; } // Add arbitrary data to an output section. @@ -1105,6 +1128,16 @@ Output_section::write_header(const Layout* layout, void Output_section::do_write(Output_file* of) { + off_t output_section_file_offset = this->offset(); + for (Fill_list::iterator p = this->fills_.begin(); + p != this->fills_.end(); + ++p) + { + std::string fill_data(of->target()->code_fill(p->length())); + of->write(output_section_file_offset + p->section_offset(), + fill_data.data(), fill_data.size()); + } + for (Input_section_list::iterator p = this->input_sections_.begin(); p != this->input_sections_.end(); ++p) @@ -1496,8 +1529,9 @@ Output_segment::write_section_headers_list(const Layout* layout, // Output_file methods. -Output_file::Output_file(const General_options& options) +Output_file::Output_file(const General_options& options, Target* target) : options_(options), + target_(target), name_(options.output_file_name()), o_(-1), file_size_(0), diff --git a/gold/output.h b/gold/output.h index 49cc7ca0734..b2198053186 100644 --- a/gold/output.h +++ b/gold/output.h @@ -1533,6 +1533,37 @@ class Output_section : public Output_data typedef std::vector Input_section_list; + // Fill data. This is used to fill in data between input sections. + // When we have to keep track of the input sections, we can use an + // Output_data_const, but we don't want to have to keep track of + // input sections just to implement fills. For a fill we record the + // offset, and the actual data to be written out. + class Fill + { + public: + Fill(off_t section_offset, off_t length) + : section_offset_(section_offset), length_(length) + { } + + // Return section offset. + off_t + section_offset() const + { return this->section_offset_; } + + // Return fill length. + off_t + length() const + { return this->length_; } + + private: + // The offset within the output section. + off_t section_offset_; + // The length of the space to fill. + off_t length_; + }; + + typedef std::vector Fill_list; + // Add a new output section by Input_section. void add_output_section_data(Input_section*); @@ -1590,6 +1621,10 @@ class Output_section : public Output_data Input_section_list input_sections_; // The offset of the first entry in input_sections_. off_t first_input_offset_; + // The fill data. This is separate from input_sections_ because we + // often will need fill sections without needing to keep track of + // input sections. + Fill_list fills_; // Whether this output section needs a STT_SECTION symbol in the // normal symbol table. This will be true if there is a relocation // which needs it. @@ -1765,7 +1800,12 @@ class Output_segment class Output_file { public: - Output_file(const General_options& options); + Output_file(const General_options& options, Target*); + + // Get a pointer to the target. + Target* + target() const + { return this->target_; } // Open the output file. FILE_SIZE is the final size of the file. void @@ -1801,6 +1841,8 @@ class Output_file private: // General options. const General_options& options_; + // Target. + Target* target_; // File name. const char* name_; // File descriptor. diff --git a/gold/target.h b/gold/target.h index 9181a93193d..06c7c3d2084 100644 --- a/gold/target.h +++ b/gold/target.h @@ -63,6 +63,11 @@ class Target has_resolve() const { return this->pti_->has_resolve; } + // Whether this target has a specific code fill function. + bool + has_code_fill() const + { return this->pti_->has_code_fill; } + // Return the default name of the dynamic linker. const char* dynamic_linker() const @@ -89,6 +94,13 @@ class Target finalize_sections(const General_options* options, Layout* layout) { return this->do_finalize_sections(options, layout); } + // Return a string to use to fill out a code section. This is + // basically one or more NOPS which must fill out the specified + // length in bytes. + std::string + code_fill(off_t length) + { return this->do_code_fill(length); } + protected: // This struct holds the constant information for a child class. We // use a struct to avoid the overhead of virtual function calls for @@ -105,6 +117,8 @@ class Target bool has_make_symbol; // Whether this target has a specific resolve function. bool has_resolve; + // Whether this target has a specific code fill function. + bool has_code_fill; // The default dynamic linker name. const char* dynamic_linker; // The default text segment address. @@ -124,6 +138,12 @@ class Target do_finalize_sections(const General_options*, Layout*) { } + // Virtual function which must be implemented by the child class if + // needed. + virtual std::string + do_code_fill(off_t) + { gold_unreachable(); } + private: Target(const Target&); Target& operator=(const Target&); -- 2.30.2