Use nops when doing alignment padding between code sections.
authorIan Lance Taylor <iant@google.com>
Fri, 21 Sep 2007 05:31:19 +0000 (05:31 +0000)
committerIan Lance Taylor <iant@google.com>
Fri, 21 Sep 2007 05:31:19 +0000 (05:31 +0000)
gold/i386.cc
gold/layout.cc
gold/output.cc
gold/output.h
gold/target.h

index 6c1b987d0e3a8bb48acaef9b5528a4806523bbcb..6eb0b2c0f3702f3fabb51cf7e6aa0b31c20ec225 100644 (file)
@@ -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<char*>(&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
index 3e8a223c7fd2d495d5c8279ced0292c387fce5a3..35de9e46563a679abd6e010f3eb383b47f47bf7b 100644 (file)
@@ -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.
index ddf2ebb4e63c2c9f14fcdddc7f3340c6ed35423b..bbd7af161de137687646ecd7730f4e70b8ced9cf 100644 (file)
@@ -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),
index 49cc7ca07347b0a572360021fa9f1480d7915530..b2198053186c1ee4811666d6e5f0cf6aea479b2c 100644 (file)
@@ -1533,6 +1533,37 @@ class Output_section : public Output_data
 
   typedef std::vector<Input_section> 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> 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.
index 9181a93193d53fd5e4d5f673e48105ec56ac10ef..06c7c3d20847fcb7e81ffe50849c2e323c4b2950 100644 (file)
@@ -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&);